基于Oracle Concurrency guidelines我正在尝试更新我的 GUI。我的问题是,GUI 更新是在 EDT 内的新线程中调用的,这导致了我很多不同的失败。
下面的代码已准备好运行,它是我的 GUI 更新示例,一个简单的倒计时:
import java.awt.Component;
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class Chronometer2 {
int COUNT = 5000;
CountTask countTask;
private static final Insets INSETS = new Insets(1, 1, 1, 1);
public Chronometer2(){
(countTask= new CountTask()).execute();
}
private static void addComponent(Container container, Component component, int gridx, int gridy,
int gridwidth, int gridheight, int anchor, int fill) {
GridBagConstraints gbc = new GridBagConstraints(gridx, gridy, gridwidth, gridheight, 1.0, 1.0,
anchor, fill, INSETS, 0, 0);
container.add(component, gbc);
}
public int returnCount(){
System.out.println("My count: " + COUNT);
return COUNT;
}
public void decreaseTime() {
COUNT--;
}
public int getTime() {
return COUNT;
}
private class CountTask extends SwingWorker<Void, Chronometer2>{
@Override
protected Void doInBackground() throws Exception {
// TODO Auto-generated method stub
final JFrame messageFrame = new JFrame();
final JLabel message = new JLabel("Next query in " + COUNT/1000 + " seconds.");
messageFrame.setLayout(new GridBagLayout());
messageFrame.setTitle("Warning");
messageFrame.setSize(500,100);
messageFrame.setLocationRelativeTo(null);
addComponent(messageFrame, message, 0, 0, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.CENTER);
messageFrame.setVisible(true);
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
decreaseTime();
message.setText("Preventing connection block. Next query in " + COUNT/1000 + " seconds.");
if(getTime()==0){
messageFrame.dispose();
timer.cancel();
}
}
}, 1, 1);
return null;
}
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Chronometer2();
}
});
}
}
由于该示例是从主线程调用的,因此它工作正常。但从我的应用程序中,调用是在 EDT 内进行的,因为我需要在显示消息之前按下按钮并触发操作。我创建了一个新线程来调用 Swing 实用程序(无法在 EDT 内直接调用),尽管我遇到了问题,因为到目前为止我无法让 EDT 等待后台线程完成,然后继续进行下一个查询。这是 EDT 内的代码片段:
Thread test = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
new CountScreen();
}
});
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
test.start();
我也尝试用 wait()
、notifiy()
和 Timer Task 来处理这个问题,但问题是一样的;这是准备运行的其他代码:
import java.awt.Component;
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Chronometer {
private static final Insets INSETS = new Insets(1, 1, 1, 1);
private int TIMER = 3000;
public Chronometer(){
double timeToNext = System.currentTimeMillis() + 30000;
double timeDifference = 30000;
final JFrame messageFrame = new JFrame();
messageFrame.setLayout(new GridBagLayout());
messageFrame.setTitle("Warning");
final JLabel message = new JLabel("Next query in " + timeDifference/1000 + " seconds.");
messageFrame.setSize(500,100);
messageFrame.setLocationRelativeTo(null);
addComponent(messageFrame, message, 0, 0, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.CENTER);
messageFrame.setVisible(true);
System.out.println(Thread.currentThread().getName());
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
synchronized (timer) {
decreaseTime();
message.setText("Preventing connection block. Next query in " + TIMER/1000 + " seconds.");
if(getTime()==0){
System.out.println("Im here");
timer.notify();
System.out.println("Im here 2");
messageFrame.dispose();
System.out.println("Im here 3");
timer.cancel();
}
}
}
}, 1, 1);
synchronized (timer) {
try {
timer.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("Done");
}
private static void addComponent(Container container, Component component, int gridx, int gridy,
int gridwidth, int gridheight, int anchor, int fill) {
GridBagConstraints gbc = new GridBagConstraints(gridx, gridy, gridwidth, gridheight, 1.0, 1.0,
anchor, fill, INSETS, 0, 0);
container.add(component, gbc);
}
public void decreaseTime() {
TIMER--;
}
public int getTime() {
return TIMER;
}
public static void main(String[] args){
Chronometer c = new Chronometer();
}
}
所以,我有近 3 天的时间想知道,我该如何解决这个问题?非常欢迎任何建议;提前致谢。
最佳答案
我找到了自己的答案。停止 EDT 永远不会是一个选择。绝不。如果你想解决像我这样的问题,解决方案比我想象的要简单得多。在监听器中触发操作后,只需调用另一个线程来处理您想要的任何内容。这样可以防止 EDT 变得繁忙并卡住 GUI。这样,您将能够尽可能以多种方式锁定线程,我的意思是,同步方法、循环或您想使用的其他方法。我的解决方案我可以考虑阅读这篇文章:main and the GUI Event Dispatch 。这确实是一篇很棒的文章,它打开了我的视野,让我了解有关 Swing 和线程的新东西。希望这对您也有帮助。
关于java - 如何让 EDT 等待后台线程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20170262/