我在应用程序中处理线程时遇到问题。它创建JFrame并启动一个新的Thread。最后一个将执行外部应用程序并更新GUI。然后
我有问题让Main类等待第二个线程完成,而且还要同时更新GUI。
这是我的例子(缩短):
class Main {
public int status;
public Main() {
// Creating GUI etc.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JDialog id = new JDialog();
id.button.addMouseListener(new MouseListener()); // Calls generate() method
}
});
}
public void generate() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Make changes to GUI
}
});
GeneratorThread genTest = new GeneratorThread(this, 1, 1, 1);
genTest.start();
//while (status == 0);
System.out.println("Next step.");
}
}
和Thread类:
public class GeneratorThread extends Thread {
protected Main main;
protected int setSize, minValue, maxValue;
public GeneratorThread(Main main, int setSize, int minValue, int maxValue) {
this.main = main;
this.setSize = setSize;
this.minValue = minValue;
this.maxValue = maxValue;
}
public void run() {
// Execute program etc.
// Change GUI from main in the same time
// About 3 seconds
main.status = 1;
}
}
我正在进行中,我想查看它到目前为止的工作原理。尽管工作得很好,但它会以某种方式锁定Swing,只有在GeneratorThread
完成时才能看到任何更改。我想实时更新GUI。
我试过join()
,效果是一样的。我也试过wait()
(在Main
上),但后来我得到了IllegalStateMonitorException。
任何提示?
答案 0 :(得分:1)
使用SwingUtilitied.invokeLater在线程内更新GUI,或者同步主变量!
http://www.vogella.com/articles/JavaConcurrency/article.html#concurrencyjava
也许已经足以使“状态”变得不稳定了?
答案 1 :(得分:1)
Swing是一个单线程环境。也就是说,有一个线程负责管理Swing UI的所有交互和更新 - 事件调度线程。
Swing的黄金法则是......
Thread.sleep
,Thread#join
,Object#wait
,阻止IO和/或耗时的任务(以及其他)永远不应该从EDT中调用,这样做会阻止EDT调度事件和绘制更新(以及其他内容)这提出了一个问题......你如何“等待”一个线程?
最好的方法是使用Observer模式。基本上,您为Thread
提供了某种类型的引用,它将调用该引用来提供事件通知,例如错误和完成...
这将要求您仔细考虑应用程序的设计,因为您不能依赖简单的A到B执行代码。
例如......
public class TestThreadCallBack {
public static void main(String[] args) {
new TestThreadCallBack();
}
public TestThreadCallBack() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface ThreadCallBack {
public void threadCompleted(Runnable source);
public void threadFailed(Runnable source);
}
public class TestPane extends JPanel implements ThreadCallBack {
private JLabel message;
private JLabel dots;
private int count;
private Timer timer;
public TestPane() {
setLayout(new GridBagLayout());
message = new JLabel("Running background task, please wait");
dots = new JLabel(" ");
add(message);
add(dots);
timer = new Timer(250, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
count++;
if (count > 3) {
count = 0;
}
StringBuilder sb = new StringBuilder(3);
for (int index = 0; index < count; index++) {
sb.append(".");
}
for (int index = count; index < 3; index++) {
sb.append(" ");
}
dots.setText(sb.toString());
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
Thread thread = new Thread(new BackgroundTask(this));
thread.start();
}
@Override
public void threadCompleted(Runnable source) {
timer.stop();
message.setText("Task completed successfully");
}
@Override
public void threadFailed(Runnable source) {
timer.stop();
message.setText("Task failed");
}
}
public class BackgroundTask implements Runnable {
private ThreadCallBack callBack;
public BackgroundTask(ThreadCallBack callBack) {
this.callBack = callBack;
}
@Override
public void run() {
System.out.println("Background task underway...");
try {
Thread.sleep(2000);
} catch (InterruptedException interruptedException) {
}
int result = (int) Math.round((Math.random() * 1));
if (result == 0) {
callBack.threadCompleted(this);
} else {
callBack.threadFailed(this);
}
}
}
}
从Thread
以外的其他地方更新用户界面,然后EDT就是凌乱的。更简单的解决方案实际上是使用SwingWorker
。这有发布/处理方法,可以轻松更新UI和进度方法,可用于提供有关当前任务进度的反馈。
您可以使用它的done
方法在工人完成时通知相关方。