java - 启动 Thread 或进行计算的 SwingWorker 时的 Swing 伪 block

标签 java multithreading swing swingworker

我有一个非常简单的应用程序,但有一个非常奇怪的行为。

它本质上是 SwingWorker 示例,但当我按下按钮时,GUI 的行为就像 EDT 被阻止一样。我可以同时触发两个并且它们并行运行(具有几乎相同的运行时间)但是菜单在它们运行时仍然卡住。当我使用带有可运行对象的线程时,会发生完全相同的行为。同样有趣的是,如果循环被 Thread.sleep 替换,GUI 会正常运行。

有什么想法吗?

public class DummyFrame extends JFrame {

        public DummyFrame() {
                JMenuBar bar = new JMenuBar();
                JMenu menu = new JMenu("File");
                menu.add(new JMenuItem("TEST1"));
                menu.add(new JMenuItem("Test2"));
                bar.add(menu);
                setJMenuBar(bar);

                JButton button = new JButton("FOOBAR");
                button.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent arg0) {
                                final long start = System.currentTimeMillis();

                                SwingWorker<Void, Integer> testTask = new SwingWorker<Void, Integer>() {        
                                        @Override
                                        protected Void doInBackground()
                                        throws Exception {
                                                int k = 0;
                                                for (int i=0; i<200000; i++) {
                                                        for (int j=0; j<100000; j++) {
                                                                if (i==j && i%10000 == 0)
                                                                        k++;
                                                        }
                                                }
                                                System.out.println(k+" "+(System.currentTimeMillis()-start));
                                                return null;
                                        }
                                };
                                testTask.execute();
                        }

                });
                getContentPane().add(button);
                pack();
        }

        public static void main(String[] args) {
                SwingUtilities.invokeLater(new Runnable() {
                        public void run() {
                                DummyFrame f = new DummyFrame();
                                f.setVisible(true);     
                        }
                });
        }
}

最佳答案

问题出在 VM 的线程实现上。规范没有具体说明这是如何完成的。 Java 线程应该映射到 native Windows 线程,然后使用 Windows 调度程序共享时间片。目前还不清楚这是不是真的发生了,所有的官方文档都只支持在 Solaris 上运行的线程信息。

我认为主要问题来自线程抢占的实现细节。这可能是由 JVM 和 native 操作系统之间的编译代码优化和抢占控制的某种组合引起的。 JVM 可以使用方法调用作为抢占线程的指向,我认为这里的部分问题是您在另一个循环之上调用一个循环。如果你用一个函数调用来分解它们,它在我的机器上的表现会好得多。我在 Windows 7 上使用 1.6.0_23 64 位服务器 VM。

SwingWorker<Void, Integer> testTask = new SwingWorker<Void, Integer>() {

    private int k;

    private void inc() {
        this.k++;
    }

    private void innerLoop(int i) {
        for (int j=0; j<100000; j++) {
            if (i==j && i%10000 == 0)
                this.inc();
        }
    }

    @Override
    protected Void doInBackground()
    throws Exception {
        System.out.println("Started");
        for (int i=0; i<200000; i++) {
            this.innerLoop(i);
        }
        System.out.println(k+" "+(System.currentTimeMillis()-start));
        return null;
    }
};

然而,在同时启动其中几个之后,即使这样也会出现问题。最好的解决方案是在每次启动内部循环时添加对 Thread.yield() 的调用。这可确保编译后的代码为调度程序提供每次迭代抢占线程的机会。

关于java - 启动 Thread 或进行计算的 SwingWorker 时的 Swing 伪 block ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4881609/

相关文章:

java - 正则表达式在一行中多次出现

java - Wicket DropDownChoice 不能与 PropertyModels 一起正常工作

java - Spring 2.5.6 中的 ThreadPooler

java - 半透明窗口内的半透明 JPopupMenu - 替代方案?

java - 如何动态更改 JXTreeTable 中特定单元格的颜色

java - 使用类中介在 WSO2 ESB 中创建自定义代理时出错

java - FileInputStream 从特定位置读取文件?

c# - ASP.NET MVC 的异步 Controller 与多线程

c++ - 如何并行化这个结构?

java - 在 swing 应用程序中显示网页