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

发布于 2024-10-03 22:02:56 字数 663 浏览 0 评论 0原文

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

I have this ActionListener that gets called in the EDT. My plot() function is computationally heavy, it can easily take five seconds. It made the GUI hang as expected. I added the SwingUtilities.invokeLater code and it still hangs. Shouldn't the GUI be responsive now that I am spawning a separate thread for my heave computation?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

落叶缤纷 2024-10-10 22:02:56

一点也不。 InvokeLater 不会生成新线程。 invokeLater 的存在是为了告诉Swing 显式地“为此使用事件调度线程,但不是现在”。 invoke 和 invokeLater 的存在是为了让您可以从其他线程执行仅对事件调度线程安全的操作 - 而不是在这些线程上执行这些操作,但是通过告诉 EDT 去做。

您的 ActionListener 将运行得非常快,将 Runnable 扔到 Swing 的事件调度队列中。然后,当到达那么远时,运行plot()将需要五秒钟。

唯一的解决方法是重构plot()。使用 SwingWorker (或类似的多线程策略,但 SwingWorker 可能是最好的选择)实际上将plot()的逻辑移动到不同的线程上。该线程无法安全地绘制任何内容,因为它不是 Swing 事件调度线程,因此它的所有绘制操作都需要通过 invokeLater() 执行。出于效率原因,您应该尝试使用计算中存储的结果在一个 invokeLater() 上一次性执行所有绘图操作。

Not at all. InvokeLater is not producing a new thread. invokeLater exists to tell Swing explicitly "use the Event Dispatching Thread for this, but not right now". invoke and invokeLater exist to let you do operations that are only safe for the Event Dispatching Thread from other threads- not by doing them on those threads, but by telling the EDT to do them.

Your ActionListener will run very quickly, throwing the Runnable on Swing's event dispatching queue. Then when it gets that far, it will take five seconds to run the plot().

The only workaround is to refactor plot(). Use a SwingWorker (or similar multithreading strategy, but SwingWorker is probably the best for this) to actually move the logic of plot() onto a different thread. That thread cannot safely draw anything because it is not the Swing Event Dispatching Thread, so all of its draw operations need to be performed via invokeLater(). For efficiency reasons, you should try to do all of the drawing operations at once, on one invokeLater(), using results stored from your calculation.

捶死心动 2024-10-10 22:02:56

你所做的与你认为的相反。您不是在 EDT 之外运行计算线程,而是在其中显式调用它!

SwingUtilities.invokeLater() 将可运行对象排队以便稍后在 EDT 中调用!您想改用 SwingWorker

You're doing the opposite of what you think you are. Instead of running your computation thread outside of the EDT, you're explicitly calling it within it!

SwingUtilities.invokeLater() queues up the runnable for invocation at a later time, in the EDT! You want to use SwingWorker instead.

风筝有风,海豚有海 2024-10-10 22:02:56

您不会显示plot() 函数内部的内容,但您不应该在其中放置任何绘画。在新线程中计算您想要的任何内容,并在 EDT 中进行绘制。为此,最好使用 SwingWorker

You don't show what is inside the plot() function, but you should not put any painting in there. Compute whatever you want in the new thread, and do the painting in the EDT. To do this, it is better to use SwingWorker

め可乐爱微笑 2024-10-10 22:02:56

invokeLater 将任务添加到 GUI 工作队列。它将在执行完所有其他任务后调用,但它仍然使用 GUI 线程。

我建议您考虑使用 ExecutorService。

正如@Adam 所建议的,它所做的任何实际绘图都需要通过invokeLater 来完成。

invokeLater add a task to the GUIs work queue. It will be invoked after all other tasks have been performed, however it still uses the GUI thread.

I suggest you look at using an ExecutorService.

As @Adam suggests, any actual drawing it does needs to be done via invokeLater.

囚我心虐我身 2024-10-10 22:02:56

这是我为我公司的应用程序所做的,这是一些出于法律原因的伪代码,但其要点是,如果屏幕没有响应,它将重新启动 GUI。每当您使用 SwingUtilities 启动 EDT 时,请在同一 init 块中创建两个观察程序线程。一个线程将使用 Swing 实用程序在 EDT 线程上简单地执行一项操作。另一个线程将监视第一个线程,以查看第一个线程是否有响应。第一个线程仅在可以执行非常简单的命令时才会确认响应。

以正常方式运行时将 isEDTCheck 设置为 true,在调试模式下将 isEDTCheck 设置为 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);
       }

Here is what I did for my company's app, this is some pseudo code because of legal reasons, but the jist of it is that if the screen is unresponsive, it will reboot the GUI. Whenever you use SwingUtilities to kick off the EDT, in that same init block, create two watcher threads. One thread will simply perform an action on the EDT thread using Swing utilities. Another thread will monitor the first thread to see if feels the first thread is responsive. The first thread will only acknowledge responsiveness if it can perform a very simple command.

set isEDTCheck to true when running in normal fashion, false in debug mode (otherwise you'll constantly get rebooted.

    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);
       }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文