事件发送线程如何工作? [英] How does the event dispatch thread work?

查看:99
本文介绍了事件发送线程如何工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

stackoverflow 中的人员帮助下我能够得到一个简单的GUI倒计时的下面的工作代码(它只显示一个窗口倒数秒)。我的这个代码的主要问题是 invokeLater 的东西。



据我所知 invokeLater ,它会将任务发送到事件调度线程(EDT),然后只要可以(无论什么意思),EDT执行此任务。



根据我的理解,代码的工作原理如下:


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


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


  3. 计数器在单独的线程中执行,并定期调用 updateGUI updateGUI 应该更新GUI。而且,GUI正在美国东部时间(EDT)工作。所以, updateGUI 也应该在EDT中执行。这就是为什么 updateGUI 的代码包含在 invokeLater 中。这是对的吗?


我不清楚的是为什么我们调用 / code>从EDT。无论如何,它不在EDT中执行。它立即启动,一个新的线程和计数器在那里执行。所以,为什么我们不能在 invokeLater 块之后的main方法中调用计数器

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

public class CountdownNew {

static JLabel label;

//定义窗口外观的方法。
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);
}

//定义倒数计时的新线程。
public static Thread counter = new Thread(){
public void run(){
for(int i = 10; i> 0; i = i-1){
updateGUI(I,标签);
try {Thread.sleep(1000);} catch(InterruptedException e){};
}
}
};

//更新GUI(设置JLabel的新值)的方法。
private static void updateGUI(final int i,final JLabel label){
SwingUtilities.invokeLater(
new Runnable(){
public void run(){
label .setText(你有+ i +秒。);
}
}
);
}

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()并不意味着 showGUI()中的代码将在计数器的运行方法中的代码之前执行。



以这种方式想想:




  • invokeLater 启动一个线程,该线程是在EDT上安排异步事件,该事件由创建 JLabel

  • 计数器是一个单独的线程,它取决于 JLabel 存在,因此它可以调用 label.setText(你有+ i +秒)。 );



现在你有比赛条件: <$ c必须先创建$ c> JLabel ,如果它不是在计数器线程启动之前创建的,那么您的计数器就会在计数器线程将在未初始化对象上调用 setText



为了确保消除竞争条件,我们必须保证执行顺序和单向,以确保执行 showGUI() counter.start()顺序地在同一个线程上:



$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ b showGUI();
counter.start();
}
});
}

现在 showGUI(); counter.start(); 从同一个线程执行,因此 JLabel 将在计数器已启动。



更新:


Q : 我不明白这个线程是什么特别的。

A: Swing事件处理代码运行在一个已知的专用线程上作为事件调度线程。大多数调用Swing方法的代码也在这个线程上运行。这是必要的,因为大多数Swing对象方法不是线程安全:从多个线程调用它们会冒着线程干扰或内存一致性错误。 1



Q: 那么,如果我们有一个GUI,为什么我们应该在一个单独的线程中启动?

A: 可能比我的更好的答案,但如果你想从EDT更新GUI(你所做的),那么你必须从EDT开始。



Q: 为什么我们不能像任何其他线程一样启动线程?

A:

Q: 为什么我们使用一些invokeLater,为什么这个线程(EDT)开始执行请求,当它是准备。为什么它不总是准备好?

A: EDT可能会有一些其他必须处理的AWT事件。
invokeLater 导致doRun.run()在AWT事件调度线程上异步执行。这将在所有待处理的AWT事件处理完毕之后发生。当应用程序线程需要更新GUI时,应使用此方法。 2



With the help of people on stackoverflow I was able to get the following working code of a simple GUI countdown (which just displays a window counting down seconds). My main problem with this code is the invokeLater stuff.

As far as I understand invokeLater, it sends a task to the event dispatching thread (EDT) and then the EDT executes this task whenever it "can" (whatever that means). Is that right?

To my understanding, the code works like this:

  1. In the main method we use invokeLater to show the window (showGUI method). In other words, the code displaying the window will be executed in the EDT.

  2. In the main method we also start the counter and the counter (by construction) is executed in another thread (so it is not in the event dispatching thread). Right?

  3. The counter is executed in a separate thread and periodically it calls updateGUI. updateGUI is supposed to update the GUI. And the GUI is working in the EDT. So, updateGUI should also be executed in the EDT. It is the reason why the code for the updateGUI is enclosed in invokeLater. Is that right?

What is not clear to me is why we call the counter from the EDT. Anyway, it is not executed in the EDT. It starts immediately, a new thread and the counter is executed there. So, why can we not call the counter in the main method after the invokeLater block?

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();
            }
        });
    }

}

解决方案

If I understand your question correctly you're wonder why you can't do this:

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

The reason why you can't do it is because the scheduler makes no guarantees... just because you invoked showGUI() and then you invoked counter.start() doesn't mean that the code in showGUI() will be executed before the code in the run method of the counter.

Think of it this way:

  • invokeLater starts a thread and that thread is schedules an asynchronous event on the EDT which is tasked with creating the JLabel.
  • the counter is a separate thread that depends on the JLabel to exists so it can call label.setText("You have " + i + " seconds.");

Now you have a race condition: JLabel must be created BEFORE the counter thread starts, if it's not created before the counter thread starts, then your counter thread will be calling setText on an uninitialized object.

In order to ensure that the race condition is eliminated we must guarantee the order of execution and one way to guarantee it is to execute showGUI() and counter.start() sequentially on the same thread:

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

Now showGUI(); and counter.start(); are executed from the same thread, thus the JLabel will be created before the counter is started.

Update:

Q: And I do not understand what is special about this thread.
A: Swing event handling code runs on a special thread known as the event dispatch thread. Most code that invokes Swing methods also runs on this thread. This is necessary because most Swing object methods are not "thread safe": invoking them from multiple threads risks thread interference or memory consistency errors. 1

Q: So, if we have a GUI why should we start it in a separate thread?
A: There is probably a better answer than mine, but if you want to update the GUI from the EDT (which you do), then you have to start it from the EDT.

Q: And why we cannot just start the thread like any other other thread?
A: See previous answer.

Q: Why we use some invokeLater and why this thread (EDT) start to execute request when it's ready. Why it is not always ready?
A: The EDT might have some other AWT events it has to process. invokeLater Causes doRun.run() to be executed asynchronously on the AWT event dispatching thread. This will happen after all pending AWT events have been processed. This method should be used when an application thread needs to update the GUI. 2

这篇关于事件发送线程如何工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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