当前位置: 首页 > 面试题库 >

事件分派线程如何工作?

洪高阳
2023-03-14
问题内容

在人们的帮助下,我能够获得以下简单的GUI倒数的工作代码(它仅显示一个倒数秒的窗口)。我的这段代码的主要问题是invokeLater东西。

据我了解invokeLater,它将任务发送到事件分发线程(EDT),然后EDT在“可以”时执行该任务(无论如何)。 那正确吗?

据我了解,代码的工作方式如下:

  1. main方法中,我们invokeLater用来显示窗口(showGUI方法)。换句话说,显示窗口的代码将在EDT中执行。

  2. 在该main方法中,我们还启动counter,计数器(通过构造)在另一个线程中执行(因此不在事件调度线程中执行)。对?

  3. counter是在一个单独的线程中执行,并定期调用updateGUIupdateGUI应该更新GUI。而且GUI正在EDT中工作。因此,updateGUI也应在EDT中执行。这就是的代码updateGUI包含在中的原因invokeLater。那正确吗?

对我来说不清楚的是为什么我们counter从EDT 致电。无论如何,它不会在EDT中执行。它立即启动,并在其中counter执行新线程。那么,
为什么counterinvokeLater块之后不能在main方法中调用in 呢?

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

public class CountdownNew {

    static JLabel label;

    // Method which defines the appearance of the window.   
    public static void showGUI() {
        JFrame frame = new JFrame("Simple Countdown");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        label = new JLabel("Some Text");
        frame.add(label);
        frame.pack();
        frame.setVisible(true);
    }

    // Define a new thread in which the countdown is counting down.
    public static Thread counter = new Thread() {
        public void run() {
            for (int i=10; i>0; i=i-1) {
                updateGUI(i,label);
                try {Thread.sleep(1000);} catch(InterruptedException e) {};
            }
        }
    };

    // A method which updates GUI (sets a new value of JLabel).
    private static void updateGUI(final int i, final JLabel label) {
        SwingUtilities.invokeLater( 
            new Runnable() {
                public void run() {
                    label.setText("You have " + i + " seconds.");
                }
            }
        );
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                showGUI();
                counter.start();
            }
        });
    }

}

问题答案:

如果我正确理解了您的问题,您会想知道为什么不能这样做:

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            showGUI();
        }
    });
    counter.start();
}

之所以无法执行此操作,是因为调度程序不做任何保证……仅因为您调用了showGUI()之后又调用counter.start()它并不意味着in中的代码showGUI()将在.run方法中的代码之前执行counter

这样想:

  • invokeLater的 启动一个线程和线程的 调度,其任务是在美国东部时间异步事件 产生JLabel
  • 计数器是一个独立的线程,它取决于JLabelto的存在,因此它可以调用label.setText("You have " + i + " seconds.");

现在您有一个竞争条件:
JLabel必须在counter线程启动之前创建,如果未在计数器线程启动之前创建它,那么您的计数器线程将setText在未初始化的对象上进行调用。

为了保证比赛的条件被消除,我们必须保证执行和顺序 的一种方式
,以保证它是执行showGUI()counter.start()在同一线程上按顺序:

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            showGUI();
            counter.start();
        }
    });
}

现在showGUI();counter.start();从同一线程执行,因此JLabel将在counter开始之前创建。

更新:

问: 我不了解此线程有什么特别之处。
答:
Swing事件处理代码在称为事件调度线程的特殊线程上运行。大多数调用Swing方法的代码也都在此线程上运行。这是必需的,因为大多数Swing对象方法都不是“线程安全的”:从多个线程中调用它们可能会导致线程干扰或内存一致性错误。1个

问: 因此,如果有GUI,为什么要在单独的线程中启动它?
答: 可能有比我更好的答案,但是,如果要从EDT更新GUI(您需要这样做),则必须从EDT启动它。

问: 为什么我们不能像其他线程一样直接启动线程?
答: 请参阅先前的答案。

问: 为什么我们使用一些invokeLater,以及为什么此线程(EDT)在就绪后开始执行请求。 为什么它并不总是准备就绪?
答: EDT可能还需要处理其他一些AWT事件。
invokeLater使doRun.run()在AWT事件分配线程上异步执行。处理完所有待处理的AWT事件后,就会发生这种情况。当应用程序线程需要更新GUI时,应使用此方法。2



 类似资料:
  • 问题内容: 我了解了swing是不是线程安全的。深入研究,我发现必须对事件分发线程进行任何对swing组件的修改,以防止与多线程相关的各种问题。但是,这些信息似乎完全到此为止。似乎没有一个很好的教程可以解释如何在Internet上可以访问的任何地方进行此操作。 从与其他问题有关的已发布代码中收集信息,似乎我将不得不在程序中的每个单独的swing修改周围放置一个不整洁的代码块(例如本例中的代码):

  • 问题内容: 1.考虑我的代码在我拥有的JPanel的某行上,我是否自动在EDT上运行? 2.对于不属于GUI,JPanels或其他视图类的其他所有类的相同问题,简单逻辑类。 3.如果我有正在播放音乐的JPanel,则音乐应该在事件分发线程或非EDT的其他线程上运行(因为没有阻塞GUI,尽管我感觉运行没有问题) (来自EDT)? 注意:我想要一个一般规则,而无需使用“ 谢谢” 问题答案: 没有。 没

  • 所以我经常从朋友那里听到Node.js是如何使用单个线程来完成所有工作的。我认为这意味着它通过一个等待“accept”方法的循环线程接受新的套接字连接(像大多数web服务器一样),然后它不是将接收到的套接字委托给一个新线程来处理,而是以某种方式获得一个线程来处理所有的连接。 但每个人都告诉我情况并非如此,它实际上是事件驱动的--这样事情只会按需发生(即来自客户机的请求)。 但是,在使用过最低级别的

  • 问题内容: 我有一个在EDT上运行的方法,在其中我想使它在新的(非EDT)线程上执行某些操作。我当前的代码如下: 问题答案: 您可以创建并启动一个新的Java线程,该线程从EDT线程中执行您的方法:

  • 我想把线切成百分比。例如: -我的测试计划 -注册线程(此任务应为) -登录线程(此任务应为@) -搜索线程(此任务应为0) -添加新主题(此任务应为) 我怎么做这个分区? 谢谢

  • 在将Android Studio更新到2.2版本(在Windows10上)之后,第二天早上,当gradle在任何项目上构建时,我都收到了这样的错误: 只允许从事件调度线程进行写访问 我提出这个问题只是为了与社区分享我的经验,因为我在这个问题上浪费了两个小时。