Reputation: 1686
When a user clicks a button, a long task of approximately 10 seconds will run. During this time I want to show a progress bar to the user. But the main thread has to wait for the worker thread to finish because the worker thread will set a variable that the main thread will use. If I don't wait the worker thread I will get a NullPointerException
when using the variable. So after the worker thread finishes, I will also close the progress bar dialog.
When I wait for the worker thread using join()
the progress bar dialog shows (interestingly without the progress bar though) and hangs there.
Thread runnable = new Thread() {
public void run() {
try {
System.out.println("thread basladi");
threadAddSlaveReturnMessage = request.addSlave(
ipField.getText(), passField.getText(),
nicknameField.getText());
System.out.println("thread bitti");
} catch (LMCTagNotFoundException e) {
e.printStackTrace();
}
}
};
Thread runnable_progress = new Thread() {
public void run() {
JTextArea msgLabel;
JDialog dialog;
JProgressBar progressBar;
final int MAXIMUM = 100;
JPanel panel;
progressBar = new JProgressBar(0, MAXIMUM);
progressBar.setIndeterminate(true);
msgLabel = new JTextArea("deneme");
msgLabel.setEditable(false);
panel = new JPanel(new BorderLayout(5, 5));
panel.add(msgLabel, BorderLayout.PAGE_START);
panel.add(progressBar, BorderLayout.CENTER);
panel.setBorder(BorderFactory.createEmptyBorder(11, 11, 11, 11));
dialog = new JDialog(Frame.getFrames()[0], "baslik", true);
dialog.getContentPane().add(panel);
dialog.setResizable(false);
dialog.pack();
dialog.setSize(500, dialog.getHeight());
dialog.setLocationRelativeTo(null);
dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
dialog.setAlwaysOnTop(false);
dialog.setVisible(true);
msgLabel.setBackground(panel.getBackground());
}
};
runnable.start();
System.out.println("runnable start");
runnable_progress.start();
System.out.println("progress start");
runnable.join();
System.out.println("runnable join");
runnable_progress.join();
System.out.println("progress join");
if (threadAddSlaveReturnMessage.equalsIgnoreCase("OK")) {
fillInventoryTable(inventoryTable);
JOptionPane.showMessageDialog(this, messages.getString("centrum.addslavepanel.SUCCESS"), null, JOptionPane.INFORMATION_MESSAGE);
}
"progress join"
doesn't get printed.
Upvotes: 4
Views: 10385
Reputation: 349
As the previous answers already mentioned, SwingWorker
is the way to go, if use want to use concurrency with Swing.
I found this SwingWorker and ProgressBar tutorial quite useful in understanding how it all works together.
Coming back to your actual problem: I assume you use a GUI, because you stated the user has to click a button. The question is, what is your "main thread" doing? Does it really have to run all the time? Doesn't look like it. Because you said the thread needs a variable which is set by another thread, which is a result of a user interaction. In short: Why does it need to run if it's dependent on a user interaction anyway?
The usual way would be to first get all the data you need and then run the calculations or whatever. In your case, either run everything in a single background thread(set the variable first then do the rest), started by the ActionListener of your button or run the other thread after the thread where you set the variable has completed.
You could for example use the method done()
, provided by SwingWorker
, to launch the next task. Or if you really have to, you could wait in a loop for task.isDone()
to return true. But don't forget to check for isCancelled()
too.
Anyway, I think you should rethink your design. Because what I can see from the limited information provided looks overly complicated to me.
Upvotes: 2
Reputation: 596
You can use a SwingWorker here. A short example :
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.godel.nio;
import java.awt.BorderLayout;
import java.util.List;
import javax.swing.*;
/**
*
* @author internet_2
*/
public class Test {
public static void main(String[] args) {
new Test().doJob();
}
public void doJob() {
JTextArea msgLabel;
JProgressBar progressBar;
final int MAXIMUM = 100;
JPanel panel;
progressBar = new JProgressBar(0, MAXIMUM);
progressBar.setIndeterminate(true);
msgLabel = new JTextArea("deneme");
msgLabel.setEditable(false);
panel = new JPanel(new BorderLayout(5, 5));
panel.add(msgLabel, BorderLayout.PAGE_START);
panel.add(progressBar, BorderLayout.CENTER);
panel.setBorder(BorderFactory.createEmptyBorder(11, 11, 11, 11));
final JDialog dialog = new JDialog();
dialog.getContentPane().add(panel);
dialog.setResizable(false);
dialog.pack();
dialog.setSize(500, dialog.getHeight());
dialog.setLocationRelativeTo(null);
dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
dialog.setAlwaysOnTop(false);
dialog.setVisible(true);
msgLabel.setBackground(panel.getBackground());
SwingWorker worker = new SwingWorker() {
@Override
protected void done() {
// Close the dialog
dialog.dispose();
}
@Override
protected void process(List chunks) {
// Here you can process the result of "doInBackGround()"
// Set a variable in the dialog or etc.
}
@Override
protected Object doInBackground() throws Exception {
// Do the long running task here
// Call "publish()" to pass the data to "process()"
// return something meaningful
return null;
}
};
worker.execute();
}
}
Edit : "publish()" should be called in "doInBackground()" to pass the data to "process()".
Upvotes: 4
Reputation: 109813
you have the issue with Concurency is Swing, your GUI is visible after all thread are done
is possible to moving with JProgressBar (I'm talking about you code) but you have to
create and show JDialog, create once time and reuse this container
then to start Thread
,
better could be from Runnable#Thread
output to the Swing GUI must be wrapped into invokeLater()
this is exactly job for using SwingWorker and with PropertyChangeListener
Upvotes: 3