为什么即使在使用SwingUtilities.invokeLater后我的GUI仍然挂起?

时间:2010-11-19 17:34:44

标签: java swing asynchronous blocking event-dispatch-thread

我在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();
                    }
                });
            }
        };

5 个答案:

答案 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);
       }