其他线程中的繁忙循环延迟了EDT处理 [英] Busy loop in other thread delays EDT processing

查看:111
本文介绍了其他线程中的繁忙循环延迟了EDT处理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Java程序,它在一个单独的(非EDT)线程上执行紧密循环。虽然我认为Swing UI仍然应该响应,但事实并非如此。下面的示例程序显示了问题:单击试用我按钮应该在大约一半时间后弹出一个对话框,并且应该可以通过单击任何响应立即关闭该对话框。相反,对话框需要花费更长的时间才能显示,并且/或者在单击其中一个按钮后需要很长时间才能关闭。

I have a Java program which executes a tight loop on a separate (non-EDT) thread. Although I would think the Swing UI should still be responsive, it is not. The example program below exhibits the problem: clicking the "Try me" button should pop up a dialog more-or-less half a second later, and it should be possible to close that dialog immediately by clicking any of its responses. Instead, the dialog takes much longer to appear, and/or takes a long time to close after clicking one of the buttons.


  • 出现问题在Linux上(两个不同发行版的不同机器),在Windows上,在Raspberry Pi上(仅限服务器VM)和在Mac OS X上(由另一个SO用户报告)。

  • Java版本1.8 .0_65和1.8.0_72(试过两者)

  • 具有多个内核的i7处理器。 EDT应该有足够的备用处理能力。

有没有人知道为什么EDT处理被延迟,即使有只是一个繁忙的线程?

Does anyone have any idea why the EDT processing is being delayed, even though there is only a single busy thread?

(请注意,尽管 Thread.sleep 调用的各种建议是问题的原因,它不是。它可以删除,问题仍然可以重现,虽然它表现得稍微不那么频繁,并且通常表现出上述第二种行为 - 即无响应 JOptionPane 对话框而不是延迟对话框外观。此外,没有理由睡眠调用应该屈服于另一个线程,因为有如上所述的备用处理器内核; EDT可以继续运行在调用 sleep 之后的另一个核心上。

(Please note that despite various suggestions of the Thread.sleep call being the cause of the problem, it is not. It can be removed and the problem can still be reproduced, though it manifests slightly less frequently and usually exhibits the second behavior described above - i.e. non-responsive JOptionPane dialog rather than delayed dialog appearance. Furthermore, there is no reason that the sleep call should yield to the other thread because there are spare processor cores as mentioned above; the EDT could continue to run on another core after the call to sleep).

import java.awt.EventQueue;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

public class MFrame extends JFrame
{
    public static void main(String[] args)
    {
        EventQueue.invokeLater(() -> {
            new MFrame();
        });
    }

    public MFrame()
    {
        JButton tryme = new JButton("Try me!");

        tryme.addActionListener((e) -> {
            Thread t = new Thread(() -> {
                int a = 4;
                for (int i = 0; i < 100000; i++) {
                    for (int j = 0; j < 100000; j++) {
                        a *= (i + j);
                        a += 7;
                    }
                }
                System.out.println("a = " + a);
            });

            t.start();

            // Sleep to give the other thread a chance to get going.
            // (Included because it provokes the problem more reliably,
            // but not necessary; issue still occurs without sleep call).
            try {
                Thread.sleep(500);
            }
            catch (InterruptedException ie) {
                ie.printStackTrace();
            }

            // Now display a dialog
            JOptionPane.showConfirmDialog(null, "You should see this immediately");
        });

        getContentPane().add(tryme);

        pack();
        setVisible(true);
    }
}

更新:问题仅适用于服务器VM(但请参阅进一步更新)。指定客户端VM( -client java可执行文件的命令行参数)似乎可以解决一台计算机上的问题(更新2 但不是另一个

Update: The problem occurs only with the server VM (but see further update). Specifying the client VM (-client command line argument to java executable) seems to suppress the problem (update 2) on one machine but not another.

更新3:点击按钮后我看到Java进程使用了​​200%的处理器,暗示那里2个处理器核心满载。这对我来说根本没有意义。

Update 3: I see 200% processor usage by the Java process after clicking the button, implying that there are 2 processor cores fully loaded. This doesn't make sense to me at all.

更新4:也发生在Windows上。

Update 4: Also occurs on Windows.

更新5:使用调试器(Eclipse)证明存在问题;调试器似乎无法阻止线程。这是非常不寻常的,我怀疑VM中存在某种活锁或竞争条件,所以我向Oracle提交了一个错误(评论ID为JI-9029194)。

Update 5: Using a debugger (Eclipse) proves problematic; the debugger seems unable to stop the threads. This is highly unusual, and I suspect there is some sort of livelock or race condition in the VM, so I've filed a bug with Oracle (review ID JI-9029194).

更新6:我在OpenJDK中找到了我的错误报告错误数据库。 (我没有被告知它已被接受,我不得不搜索它)。那里的讨论最有趣,并且已经解释了可能导致这个问题的原因。

Update 6: I found my bug report in the OpenJDK bug database. (I was not informed that it had been accepted, I had to search for it). The discussion there is most interesting and already sheds some light on what may be the cause of this problem.

推荐答案

我的观察:




  • 用swingworker替换线程:

    My observations:

    • Replace thread with swingworker:


      • 没有区别

      用swingworker替换线程并在第一个for循环中做一些工作:

      Replace thread with swingworker and do some work inside the first for loop:


      • 得到了ui没有冻结的预期结果,它从这里开始顺利航行

      使用此代码,可以观察到预期的行为:

      With this code, the expected behaviour is observed:

      public class MFrame extends JFrame {
          public static void main(String[] args) {
              new MFrame();
          }
      
          public MFrame() {
              JButton tryme = new JButton("Try me!");
      
              tryme.addActionListener((e) -> {
                  SwingWorker<Void, Void> longProcess = new SwingWorker<Void, Void>() {
                      private StringBuilder sb = new StringBuilder();
      
                      @Override
                      protected Void doInBackground() throws Exception {
                          int a = 4;
                          for (int i = 0; i < 100000; i++) {
                              for (int j = 0; j < 100000; j++) {
                                  a *= (i + j);
                                  a += 7;
                              }
                              sb.append(a); // <-- this seems to be the key
                          }
                          System.out.println("a = " + a);
                          return null;
                      }
      
                      @Override
                      protected void done() {
                          try {
                              get();
                              System.out.println(sb.toString());
                          } catch (InterruptedException | ExecutionException e1) {
                              e1.printStackTrace();
                          }
                      }
                  };
      
                  longProcess.execute();
      
                  // Sleep to give the other thread a chance to get going.
                  // (Included because it provokes the problem more reliably,
                  // but not necessary; issue still occurs without sleep call).
                  try {
                      Thread.sleep(500);
                  }
                  catch (InterruptedException ie) {
                      ie.printStackTrace();
                  }
      
                  // Now display a dialog
                  SwingUtilities.invokeLater(() -> JOptionPane.showConfirmDialog(this, "You should see this immediately"));
              });
      
              getContentPane().add(tryme);
              pack();
              setLocationRelativeTo(null);
              setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
              setVisible(true);
          }
      }
      

      使用OP的原始代码进行相同的观察:

      The same observation is made by using OP's original code:


      • 在第一个for循环中做一些其他的工作


        • 得到了预期结果

        示例

        tryme.addActionListener((e) -> {
        
            Thread t = new Thread(() -> {
                StringBuilder sb = new StringBuilder();
        
                int a = 4;
                for (int i = 0; i < 100000; i++) {
                    for (int j = 0; j < 100000; j++) {
                        a *= (i + j);
                        a += 7;
                    }
                    sb.append(a); // <-- again the magic seems to be on this line
                }
                System.out.println(sb.toString());
            });
            ...
        });
        

        我在一台半功能强大的机器上运行Ubuntu 14.04。

        I am running Ubuntu 14.04 on a semi powerful machine.

        java version "1.8.0_72"
        Java(TM) SE Runtime Environment (build 1.8.0_72-b15)
        Java HotSpot(TM) 64-Bit Server VM (build 25.72-b15, mixed mode)
        



        我的观察意味着什么?



        除了所有没有丢失之外没有多少人可能已经对编译器进行了一些优化,这使得它以某种方式阻止了UI线程。老实说,我不确定这一切意味着什么,但我相信有人会弄清楚

        What do my observations mean?

        Not much other than all is not lost and someone might have optimized the compiler a bit too much which makes it somehow block the UI thread. Honestly I'm not sure what it all means but I'm sure someone will figure it out

        这篇关于其他线程中的繁忙循环延迟了EDT处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆