我在EDT中调用此ActionListener
。我的plot()函数计算量很大,很容易花费五秒钟。它使GUI按预期挂起。我添加了SwingUtilities.invokeLater
代码,它仍然挂起。由于我正在为我的升沉计算产生一个单独的线程,GUI不应该响应吗?
final ActionListener applyListener = new ActionListener()
{
@CommitingFunction
public void actionPerformed(ActionEvent arg0)
{
/*Don't do plotting in the EDT :)*/
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
plot();
}
});
}
};
答案 0 :(得分:15)
完全没有。 InvokeLater没有生成新线程。存在invokeLater exists to tell Swing explicitly "use the Event Dispatching Thread for this, but not right now". invoke和invokeLater,允许您从其他线程执行仅对事件调度线程安全的操作 - 不是通过在这些线程上执行它们,而是告诉EDT执行它们。
您的ActionListener将非常快速地运行,在Swing的事件调度队列中抛出Runnable。然后,当它到达那么远时,将需要五秒钟来运行plot()。
唯一的解决方法是重构plot()。使用SwingWorker(或类似的多线程策略,但SwingWorker可能是最好的)将plot()的逻辑实际移到另一个线程上。该线程无法安全地绘制任何内容,因为它不是Swing Event Dispatching Thread,因此需要通过invokeLater()执行所有绘制操作。出于效率原因,您应该尝试使用计算中存储的结果在一个invokeLater()上一次完成所有绘制操作。
答案 1 :(得分:3)
你正在做与你的想法相反的事情。而不是在EDT之外运行计算线程,而是在中明确地将其命名为!
SwingUtilities.invokeLater()在EDT中稍后将runnable排队等待调用!您想改用SwingWorker。
答案 2 :(得分:1)
invokeLater将任务添加到GUI工作队列。它将在执行所有其他任务后调用,但它仍然使用GUI线程。
我建议你看一下使用ExecutorService。
正如@Adam建议的那样,它所做的任何实际绘图都需要通过invokeLater来完成。
答案 3 :(得分:1)
你没有显示plot()函数中的内容,但你应该不将任何绘画放在那里。在新线程中计算你想要的任何东西,然后在EDT中进行绘画。为此,最好使用SwingWorker
答案 4 :(得分:1)
这是我为我公司的应用程序所做的,这是一些伪代码,因为法律原因,但它的主旨是,如果屏幕没有响应,它将重启GUI。每当您使用SwingUtilities启动EDT时,在同一个init块中创建两个观察程序线程。一个线程将使用Swing实用程序在EDT线程上执行操作。另一个线程将监视第一个线程,看看第一个线程是否响应。第一个线程只有在能够执行非常简单的命令时才会确认响应性。
以正常方式运行时将isEDTCheck设置为true,在调试模式下设置为false(否则您将不断重启。
if (isEDTCheck) {
new Thread("EDTHeartbeat") {
@Override
public void run() {
Runnable thisThingYouDo = new Runnable() {
public void run() {
int x = 0;
}
};
while (true) {
// first thread says we are waiting, aka bad state
edtwait=true;
try {
javax.swing.SwingUtilities.invokeAndWait(thisThingYouDo);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// first thread says we are not waiting, good state
edtwait=false;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
new Thread("EDTValidator") {
@Override
public void run() {
while (true) {
// is first thread in bad state?
if (edtwait) {
try {
Thread.sleep(3000);
// after 3 seconds are we still in bad state? if so, get rid of initial frame, pop up a dialog box in AWT that does no commands
if (edtwait) {
mainFrame.setVisible(false);
new Dialog();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
}
public class Dialog extends Frame {
private static final int WIDTH = 400;
private static final int HEIGHT = 300;
Frame f = null;
public Dialog() {
f = this;
hasSomethingBeenEntered=false;
this.setTitle("APP PROBLEM DETECTED");
this.setSize(WIDTH, HEIGHT);
this.setLocation((int)Toolkit.getDefaultToolkit().getScreenSize().getWidth() - myapp.width, 0);
Panel p1 = new Panel() {
@Override
public void paint(final Graphics g) {
int left = Dialog.WIDTH/2 - 45; // don't use WIDTH shadowed by Panel class
int top = Dialog.HEIGHT/2 - 20; // same as above
g.drawString("APP HAS DETECTED A PROBLEM", left, top);
}
};
this.add("Center", p1);
this.setAlwaysOnTop(true);
TextArea tb = new TextArea("APP HAS DETECTED A MAJOR PROBLEM\nIT WILL NOW RESTART IN 5 SECONDS");
this.add(tb);
this.setVisible(true);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
restartApp();
}
private void restartApp() {
Runtime.getRuntime().exec("cmd /c start cmd.exe /K \"cd C:\\Progra~1\\Common~1 && C:\\Progra~1\\Common~1\\MyAppDir\\myjavaapp.jar\"");
System.exit(0);
}