繁体   English   中英

有没有办法设置两个或多个事件调度线程(EDT)?

[英]Is there a way to set up two or more the event dispatch thread (EDT)?

Java 是否能够一次创建多个 EDT?

我正在尝试设置 EDT,以及它如何更新“重型”面板的内容,其中可能嵌入了十几个面板,总共有数百个组件。 目前我有

        public void run() {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    panel.update();
                }
            });
        }

我看过以下帖子:

测量事件调度线程的“忙”

事件调度线程是如何工作的?

Java 事件-调度线程解释

http://en.wiki2.org/wiki/Event_dispatching_thread

等等。

我有点理解,如果有一个 EDT 必须处理的十几个事件,Java 已经有一个内部调度机制来对这些事件进行分组/优先级排序。

根据http://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html

"This is necessary because most Swing object methods are not "thread safe": invoking them from multiple threads risks thread interference or memory consistency errors."

那么,如果我在下面创建一个带有 new Thread(new Runnable() {... }.start() 的第二个 EDT 怎么办?

java 会因为担心线程安全而自动将两个 EDT 合并回一个吗?

       new Thread(new Runnable() {
        public void run() {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    panel.update();
                }
            });
        }
    }).start();

只能有一个Event Dispatch Thread!

但是为什么你甚至想要有多个线程? 即使对于具有许多组件的“重型”面板(在我目前正在进行的应用中,必须有1000个组件),一个EDT就足够了。 请记住,您不应该在EDT上执行任何占用大量CPU时间的任务。 否则,您将阻止EDT更新事件,并且您的GUI将在响应用户输入时变得“迟缓”。

还要记住,所有GUI组件都应该仅在EDT中创建和操作,因为许多组件不是线程保存。 忽略此指南可能适用于特定任务,但迟早会出现奇怪的行为和/或崩溃!

Swing GUI是单线程的。 那个单线程是EDT。 如果你想引入第二个EDT(并且仍然有GUI工作),你还必须重写很多内部Swing代码,以解决线程安全性增加的复杂性。

添加另一个EDT会为性能的未知量增加(或减少)带来更多复杂性。

以下适用于使用 Sun 工具包的情况。 我不确定其他 Java 实现(尚未测试)。

如果工具包是从来自不同线程组的多个线程初始化的,则可以有多个 EDT 线程。

PoC 代码:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

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

public class Test {
    public static void main(String[] argv) {
        for (int i = 0; i < 5; i++) {
            // the separate thread group is needed at least for the Sun toolkit
            ThreadGroup tg = new ThreadGroup("Test Group " + i);
            Thread t = new Thread(tg, new Runnable() {
                @Override
                public void run() {
                    startApp();
                }
            }, "Test " + i);
            t.start();
        }
    }
    
    private static void startApp() {
        sun.awt.SunToolkit.createNewAppContext();
        final JFrame frm = new JFrame(Thread.currentThread().getName()) {
            @Override
            public void setVisible(boolean b) {
                super.setVisible(b);
                System.out.println("Closed");
                if (!b) dispose();
            }
        };
        final JTextArea ta = new JTextArea();
        frm.add(ta);
        JButton btn = new JButton("Dialog");
        // Showing a modal dialog will block only this frame
        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(frm, "Test message from " + frm.getTitle());
            }
        });
        frm.add(btn, BorderLayout.SOUTH);
        frm.setPreferredSize(new Dimension(300, 300));
        frm.pack();
        frm.show();
        Thread t = new Thread(new Runnable() {
            int i = 0;
            @Override
            public void run() {
                try {
                    while (true) {
                        i++;
                        if (!frm.isVisible()) break;
                        SwingUtilities.invokeAndWait(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    ta.getDocument().insertString(0, "Test " + i + " " +
                                        Thread.currentThread().hashCode()  + // This is to show that we are actually on a different thread as invokeAndWait is static and one may suspect a different behaviour
                                        "\n", null);
                                } catch (Throwable t) {
                                    t.printStackTrace();
                                }
                            }
                        });
                        Thread.sleep(1000 + (int)(Math.random() * 500));
                    }
                    Thread.sleep(4000); // This is just to show that the deamon thread might not have ended before the toolkit calls System.exit
                } catch (Throwable t) {
                    t.printStackTrace();
                }
                System.out.println("Thread " + Thread.currentThread().getName() + " exit");
            }
        });
        t.setDaemon(true);
        t.start();
    }
}

上面的代码演示了在单独的 EDT 上打开 5 帧。 当每个框架关闭并设置时,相应的 EDT 结束。 当所有框架都关闭并处理后,应用程序将退出(自动)。

笔记:

  • 不应使用(混合)在一个 EDT 中从另一个 EDT 创建的组件;
  • 从示例中关闭模态对话框的副作用是不会将焦点返回到父框架(正如人们所期望的那样),而是返回到任何其他框架。

我使用上述方法来测量启用了换行的 JTextArea 的高度,该 JTextArea 填充了可能需要一些时间的大量不同文本,在此期间我不想阻塞主 UI。 结果(测量的高度)用于主 UI。

使用 Java 1.6.0_25 和 1.8.0_60 进行测试。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM