Reputation: 390
I'm simulating multiple Tasks inside a thread - when all of them should report to a GUI. So I have a unique form that has 4 Panels inside which should reply and do different Tasks.. they should each keep pooling a database and reporting stuff to the GUI. In this example, I just made it to write numbers to a textArea.
public class FormMain extends JFrame {
private JButton btnStart;
private JPanel _panelTop, _panelMid, _panelBot;
private PanelFoo panelFooA, panelFooB, panelFooC, panelFooD;
private final List<String> ugsRj = Arrays.asList("AB");
private final List<String> ugsMg = Arrays.asList("CD", "EF");
private final List<String> ugsBA = Arrays.asList("GH", "IJ", "KL");
private final List<String> ugsPE = Arrays.asList("MN", "OP", "RS", "TU");
private void initialize() {
this._panelTop = new JPanel();
this._panelMid = new JPanel();
this._panelBot = new JPanel();
this.btnStart = new JButton("Start");
this._panelBot.add(this.btnStart);
this.panelFooA = new PanelFoo(this.ugsRj);
this.panelFooB = new PanelFoo(this.ugsMg);
this.panelFooC = new PanelFoo(this.ugsBA);
this.panelFooD = new PanelFoo(this.ugsPE);
_panelMid.setLayout(new BoxLayout(_panelMid, BoxLayout.X_AXIS));
this._panelMid.add(this.panelFooA);
this._panelMid.add(this.panelFooB);
this._panelMid.add(this.panelFooC);
this._panelMid.add(this.panelFooD);
}
public FormMain() {
initialize();
getContentPane().setLayout(new BorderLayout());
setSize(800, 516);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane().add(this._panelTop, BorderLayout.NORTH);
getContentPane().add(this._panelMid, BorderLayout.CENTER);
getContentPane().add(this._panelBot, BorderLayout.SOUTH);
this.btnStart.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
final AuthenticationUser auth = new AuthenticationUser();
auth.setUser("test");
auth.setPassword("p@ss");
//
final SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
final WorkerDoSomething w1 = new WorkerDoSomething(panelFooA);
w1.addPropertyChangeListener(new ProgressListener(panelFooA.getProgressBar()));
final WorkerDoSomething w2 = new WorkerDoSomething(panelFooB);
w2.addPropertyChangeListener(new ProgressListener(panelFooB.getProgressBar()));
final WorkerDoSomething w3 = new WorkerDoSomething(panelFooC);
w3.addPropertyChangeListener(new ProgressListener(panelFooC.getProgressBar()));
final WorkerDoSomething w4 = new WorkerDoSomething(panelFooD);
w4.addPropertyChangeListener(new ProgressListener(panelFooD.getProgressBar()));
w1.execute();
w2.execute();
w3.execute();
w4.execute();
return null;
}
};
worker.execute();
}
});
}
}
public class ProgressListener implements PropertyChangeListener {
private JProgressBar bar;
ProgressListener() {
}
ProgressListener(final JProgressBar b) {
this.bar = b;
this.bar.setValue(0);
}
@Override
public void propertyChange(final PropertyChangeEvent evt) {
// Determine whether the property is progress type
if ("progress".equals(evt.getPropertyName())) {
this.bar.setValue((int) evt.getNewValue());
}
}
}
public class PanelFoo extends JPanel {
/**
*
*/
private static final long serialVersionUID = -1400188281877395934L;
private JLabel label;
private JTextArea textArea;
private JScrollPane scrollPanel;
private JProgressBar progressBar;
public PanelFoo(final List<String> listOfStates) {
setLayout(new FlowLayout());
setSize(180, 400);
final ImageIcon icon = createImageIcon("/images/waiting-list.png", "waiting start");
this.label = new JLabel(listOfStates.get(0), icon, SwingConstants.HORIZONTAL);
add(this.label);
this.textArea = new JTextArea("Numbers: \n");
this.textArea.setWrapStyleWord(true);
this.scrollPanel = new JScrollPane(this.textArea);
this.scrollPanel.setPreferredSize(new Dimension(150, 350));
this.progressBar = new JProgressBar(0, 100);
add(this.scrollPanel);
add(this.progressBar);
setVisible(true);
}
/** Returns an ImageIcon, or null if the path was invalid. */
public ImageIcon createImageIcon(final String path, final String description) {
if (path != null) {
ImageIcon imageIcon = new ImageIcon(getClass().getResource(path));
final Image image = imageIcon.getImage();
final Image newimg = image.getScaledInstance(30, 30, Image.SCALE_SMOOTH);
imageIcon = new ImageIcon(newimg, description);
return imageIcon;
} else {
System.err.println("Couldn't find file: " + path);
return null;
}
}
public final JLabel getLabel() {
return this.label;
}
public final void setLabel(final JLabel label) {
this.label = label;
}
public final JTextArea getTextArea() {
return this.textArea;
}
public final void setTextArea(final JTextArea textArea) {
this.textArea = textArea;
}
public final JProgressBar getProgressBar() {
return this.progressBar;
}
public final void setProgressBar(final JProgressBar progressBar) {
this.progressBar = progressBar;
}
}
public class WorkerDoSomething extends SwingWorker<Void, Void> {
private JTextArea txtArea;
private JLabel label;
private Random r = new Random();
WorkerDoSomething() {
}
public WorkerDoSomething(final PanelFoo panelFooInstance) {
this.txtArea = panelFooInstance.getTextArea();
this.label = panelFooInstance.getLabel();
}
private Integer randomInt(final int min, final int max) {
final Integer randomNumber = this.r.nextInt((max - min) + 1) + min;
return randomNumber;
}
@Override
protected Void doInBackground() throws Exception {
final Integer randomNumber = randomInt(10000000, 1000000000);
long j;
int progress = 0;
final int onePerCent = randomNumber / 100;
final int onePerMillion = onePerCent / 10;
for (j = 0; j <= randomNumber; j++) {
if (j % onePerCent == 0) {
progress = (int) j / onePerCent;
setProgress(progress);
}
if (j % onePerMillion == 0) {
publish(j);
}
//Thread.sleep(randomInt(1000, 5000));
}
return null;
}
private void publish(final long num) {
this.txtArea.append(num + "\n");
this.txtArea.setCaretPosition(this.txtArea.getDocument().getLength());
}
}
This is the main GUI after all:
and this is the execution:
I just need to wait for a time on each WorkerDoSomething
's task, adding that line (previously comment out): Thread.sleep(randomInt(1000, 5000));
but when I do.. the whole execution freezes, of course..because it uses a single thread to run all the tasks - I suppose.
Is there a solution for this?
Oh...I have to use java 1.8 in the business:
java version "1.8.0_251"
Java(TM) SE Runtime Environment (build 1.8.0_251-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.251-b08, mixed mode)
The whole project it's on my personnal git
--- first edit with debug perspective
Upvotes: 3
Views: 183
Reputation: 20914
Did you read the javadoc for class SwingWorker? Here is an excerpt:
Class SwingWorker<T,V>
Type Parameters:
T - the result type returned by this SwingWorker's doInBackground and get methods
V - the type used for carrying out intermediate results by this SwingWorker's publish and process methods
You correctly call method publish()
in your doInBackground()
method, but you have not overridden method process()
. Refer to Tasks that Have Interim Results. Assuming that your publish()
method is really supposed to be the process()
method, then this should be the declaration of class WorkerDoSomething
public class WorkerDoSomething extends SwingWorker<Void, Long> {
And this should be method process()
(instead of method publish()
)
protected void process(List<Long> nums) {
this.txtArea.append(nums.get(nums.size() - 1) + "\n");
this.txtArea.setCaretPosition(this.txtArea.getDocument().getLength());
}
But doing that will not solve your problem. I can't explain why but the problem is resolved if you change the location of the call to Thread.sleep()
in method doInBackground()
. If I place the call to method sleep()
immediately before the call to setProgress()
, then the GUI does not "freeze". I got the idea to move the location of the call to sleep from this Web page, entitled Java Swing How to - Put lengthy task in SwingWorker
Here is my rewrite of your application. I took the liberty to rewrite parts of it, but essentially you just need to pay attention to the WorkerDoSomething
class.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingWorker;
import javax.swing.WindowConstants;
public class FormMain implements Runnable, ActionListener {
private static final int PANELS = 4;
private static final String START = "Start";
private JFrame frame;
private PanelFoo[] fooPanels;
public FormMain() {
fooPanels = new PanelFoo[PANELS];
for (int i = 0; i < PANELS; i++) {
fooPanels[i] = new PanelFoo();
}
}
@Override // java.awt.event.ActionListener
public void actionPerformed(ActionEvent event) {
String actionCommand = event.getActionCommand();
switch (actionCommand) {
case START:
start();
break;
default:
JOptionPane.showMessageDialog(frame,
actionCommand,
"Unhandled",
JOptionPane.WARNING_MESSAGE);
}
}
@Override // java.lang.Runnable
public void run() {
createGui();
}
private JButton createButton(String text, int mnemonic, String tooltip) {
JButton button = new JButton(text);
button.setMnemonic(mnemonic);
button.setToolTipText(tooltip);
button.addActionListener(this);
return button;
}
private JPanel createButtonsPanel() {
JPanel buttonsPanel = new JPanel();
buttonsPanel.add(createButton(START, KeyEvent.VK_S, "Start your engines!"));
return buttonsPanel;
}
private void createGui() {
frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(createMainPanel(), BorderLayout.CENTER);
frame.add(createButtonsPanel(), BorderLayout.PAGE_END);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createMainPanel() {
JPanel mainPanel = new JPanel(new GridLayout(0, 4, 10, 10));
for (PanelFoo pf : fooPanels) {
mainPanel.add(pf);
}
return mainPanel;
}
private void start() {
for (PanelFoo pf : fooPanels) {
pf.start();
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new FormMain());
}
}
class PanelFoo extends JPanel implements PropertyChangeListener {
private static final long serialVersionUID = 4029040375048696554L;
private JProgressBar progressBar;
private JTextArea textArea;
public PanelFoo() {
super(new BorderLayout());
textArea = new JTextArea(20, 10);
JScrollPane scrollPane = new JScrollPane(textArea);
add(scrollPane, BorderLayout.CENTER);
progressBar = new JProgressBar(0, 100);
add(progressBar, BorderLayout.PAGE_END);
}
public void appendText(String text) {
textArea.append(text);
textArea.append("\n");
}
@Override // java.beans.PropertyChangeListener
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
int progress = (Integer) evt.getNewValue();
progressBar.setValue(progress);
}
}
public void start() {
WorkerDoSomething worker = new WorkerDoSomething(this);
worker.addPropertyChangeListener(this);
worker.execute();
}
}
class WorkerDoSomething extends SwingWorker<Void, Long> {
private PanelFoo fooPanel;
private Random r = new Random();
public WorkerDoSomething(PanelFoo pf) {
fooPanel = pf;
}
@Override
protected Void doInBackground() throws Exception {
Integer randomNumber = randomInt(10000000, 1000000000);
long j;
int progress = 0;
final int onePerCent = randomNumber / 100;
final int onePerMillion = onePerCent / 10;
for (j = 0; j <= randomNumber; j++) {
if (j % onePerCent == 0) {
try {
Thread.sleep(1000);
}
catch (InterruptedException xInterrupted) {
// Ignore
}
progress = (int) j / onePerCent;
setProgress(progress);
}
if (j % onePerMillion == 0) {
publish(j);
}
}
return null;
}
@Override
protected void process(List<Long> numbers) {
Long number = numbers.get(numbers.size() - 1);
fooPanel.appendText(String.valueOf(number));
}
private Integer randomInt(final int min, final int max) {
final Integer randomNumber = this.r.nextInt((max - min) + 1) + min;
return randomNumber;
}
}
I would say that technically, the above answers your question, which was:
Is there a solution for this?
I believe I have provided a solution, even if I can't explain why it works.
Upvotes: 2
Reputation: 8576
The execution is not freezing since Swing uses a thread pool of 10 threads to run the workers.
You can see it work properly if you comment out this part:
if (j % onePerMillion == 0) {
publish(j);
}
PS - Why do you create a new SwingWorker in the actionPerformed method?
Why not simply write like this:
this.btnStart.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
final WorkerDoSomething w1 = new WorkerDoSomething(panelFooA);
w1.addPropertyChangeListener(new ProgressListener(panelFooA.getProgressBar()));
final WorkerDoSomething w2 = new WorkerDoSomething(panelFooB);
w2.addPropertyChangeListener(new ProgressListener(panelFooB.getProgressBar()));
final WorkerDoSomething w3 = new WorkerDoSomething(panelFooC);
w3.addPropertyChangeListener(new ProgressListener(panelFooC.getProgressBar()));
final WorkerDoSomething w4 = new WorkerDoSomething(panelFooD);
w4.addPropertyChangeListener(new ProgressListener(panelFooD.getProgressBar()));
w1.execute();
w2.execute();
w3.execute();
w4.execute();
}
});
Upvotes: 2