启动线程时Swing伪块或执行计算的SwingWorker

时间:2011-02-03 00:41:33

标签: java multithreading swing swingworker

我的应用非常简单,行为非常奇怪。

它本质上就是SwingWorker示例,但是当我按下按钮时,GUI的行为就像EDT被阻止一样。我可以同时触发两个并且它们并行运行(具有接近相同的运行时间)但是在它们运行时仍然会冻结菜单。当我使用带有runnable的Thread时,会发生完全相同的行为。同样有趣的是,如果循环被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);     
                        }
                });
        }
}

2 个答案:

答案 0 :(得分:2)

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

我认为主要问题来自抢占线程的实现细节。可能这是由JVM和本机操作系统之间的编译和抢占控制的代码优化的某种组合引起的。 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()的调用。这确保了编译代码为调度程序提供了每次迭代抢占线程的机会。

答案 1 :(得分:0)

我认为问题只是代码可以很容易地使用100%的CPU。如果每个核心运行一个线程,那么其他任何东西都没有空间。