什么时候创建Swing UI线程?

cal*_*cal 3 java swing event-dispatch-thread

在运行Swing程序的过程中,是否首先生成了UI线程(事件派发线程,EDT)?据推测,任何给定的JVM都可以做任何想做的事情(例如,在启动时始终产生EDT,无论是否曾经使用过),但实际上EDT通常是在什么时候创建的?

首次调用SwingUtilities.invokeLater()时是否会创建它?首次实例化JPanel时?如果事件泵与创建EDT分开启动,那么这种情况何时会发生?

Vin*_*igh 9

在查看代码之后,似乎它"懒洋洋地初始化",意味着它在需要时尽快初始化,如果尚未初始化的话.在这种情况下,只要有任何事件发布到它的队列中.


这是完整的故事:

EventDispatchThread被包裹内EventQueue.每个EventQueue都有自己的EDT:

/**
 * Just a summary of the class
 */
public class EventQueue {
     private static final int ULTIMATE_PRIORITY = 3;
     private static final int NUM_PRIORITIES = ULTIMATE_PRIORITY + 1;

     private Queue[] queues = new Queue[NUM_PRIORITIES];
     private EventQueue nextQueue;
     private EventQueue previousQueue;
     private EventDispatchThread dispatchThread;
}
Run Code Online (Sandbox Code Playgroud)

dispatchThread是使用包私有方法初始化initDispatchThread():

final void initDispatchThread() {
    pushPopLock.lock();
    try {
        if (dispatchThread == null && !threadGroup.isDestroyed() && !appContext.isDisposed()) {
            dispatchThread = AccessController.doPrivileged(
                new PrivilegedAction<EventDispatchThread>() {
                    public EventDispatchThread run() {
                        EventDispatchThread t =
                            new EventDispatchThread(threadGroup,
                                                    name,
                                                    EventQueue.this);
                        t.setContextClassLoader(classLoader);
                        t.setPriority(Thread.NORM_PRIORITY + 1);
                        t.setDaemon(false);
                        AWTAutoShutdown.getInstance().notifyThreadBusy(t);
                        return t;
                    }
                }
            );
            dispatchThread.start();
        }
    } finally {
        pushPopLock.unlock();
    }
}
Run Code Online (Sandbox Code Playgroud)

在检查对此方法的引用之后,有3个地方调用此方法:

  1. 在私人方法中 EventQueue#wakeup(boolean)
  2. 在私有方法EventQueue#postEventPrivate(AWTEvent)(由公共方法调用EventQueue#postEvent(AWTEvent))中
  3. 在package-private方法中EventQueue#createSecondaryLoop(Conditional, EventFilter, long).

initDispatchThread()调用之前,dispatchThread检查确保它尚未初始化.有几种方法可以在JDK中查看类的完整源代码(最简单的是附加源代码); 如果您真的感兴趣,请查看这些方法.

所以现在我们知道EventQueue包含线程,并且只要实际需要(事件被发布)就创建线程.是时候谈谈这个队列的位置以及与之通信的方式.

如果你检查代码EventQueue#invokeLater(Runnable)(由它的SwingUtilities对应方调用),你会看到它调用Toolkit.getEventQueue().postEvent(...).这告诉我们队列位于Toolkit.

Toolkit课堂上,我们可以看到它是在我们呼唤它的时候创建的(如果还没有).它使用反射来创建对象:

public static synchronized Toolkit getDefaultToolkit() {
    if (toolkit == null) {
        try {
            java.lang.Compiler.disable();

            java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedAction<Void>() {
                public Void run() {
                    String nm = null;
                    Class<?> cls = null;
                    try {
                        nm = System.getProperty("awt.toolkit");
                        try {
                            cls = Class.forName(nm);
                        } catch (ClassNotFoundException e) {
                            ClassLoader cl = ClassLoader.getSystemClassLoader();
                            if (cl != null) {
                                try {
                                    cls = cl.loadClass(nm);
                                } catch (ClassNotFoundException ee) {
                                    throw new AWTError("Toolkit not found: " + nm);
                                }
                            }
                        }
                        if (cls != null) {
                            toolkit = (Toolkit)cls.newInstance();
                            if (GraphicsEnvironment.isHeadless()) {
                                toolkit = new HeadlessToolkit(toolkit);
                            }
                        }
                    } catch (InstantiationException e) {
                        throw new AWTError("Could not instantiate Toolkit: " + nm);
                    } catch (IllegalAccessException e) {
                        throw new AWTError("Could not access Toolkit: " + nm);
                    }
                    return null;
                }
            });
            loadAssistiveTechnologies();
        } finally {
            // Make sure to always re-enable the JIT.
            java.lang.Compiler.enable();
        }
    }
    return toolkit;
}
Run Code Online (Sandbox Code Playgroud)

Toolkit是一个抽象类.我们不是实例化这个类的对象,而是创建Toolkit子类的实例:SunToolkit.我们需要知道这一点以查看队列的创建位置.

一旦我们拥有了Toolkit,我们就可以使用它来访问它的EventQueue Toolkit#getSystemEventQueue().这个望远镜采用受保护的抽象方法getSystemEventQueueImpl().我们必须检查子类以查看此方法的实现.在SunToolkit类中,我们有:

protected EventQueue getSystemEventQueueImpl() {
    return getSystemEventQueueImplPP();
}

// Package private implementation
static EventQueue getSystemEventQueueImplPP() {
    return getSystemEventQueueImplPP(AppContext.getAppContext());
}

public static EventQueue getSystemEventQueueImplPP(AppContext appContext) {
    EventQueue theEventQueue = (EventQueue) appContext.get(AppContext.EVENT_QUEUE_KEY);
    return theEventQueue;
}
Run Code Online (Sandbox Code Playgroud)

(EventQueue) appContext.get(AppContext.EVENT_QUEUE_KEY)队列来自appContext工具包.现在我们要做的就是找到将队列添加到应用程序上下文的位置:

public SunToolkit() {
    Runnable initEQ = new Runnable() {
        public void run() {
            EventQueue eventQueue;

            String eqName = System.getProperty("AWT.EventQueueClass", "java.awt.EventQueue");

            try {
                eventQueue = (EventQueue) Class.forName(eqName).newInstance();
            } catch (Exception e) {
                e.printStackTrace();
                System.err.println("Failed loading " + eqName + ": " + e);
                eventQueue = new EventQueue();
            }
            AppContext appContext = AppContext.getAppContext();
            appContext.put(AppContext.EVENT_QUEUE_KEY, eventQueue); //queue added here

            PostEventQueue postEventQueue = new PostEventQueue(eventQueue);
            appContext.put(POST_EVENT_QUEUE_KEY, postEventQueue);
        }
    };

    initEQ.run();
}
Run Code Online (Sandbox Code Playgroud)

快速概述:

  1. EDT位于EventQueue中
  2. EventQueue位于Toolkit中
  3. 创建工具箱时会创建队列
  4. 工具包是手动创建的(通过调用Toolkit.getDefaultToolkit(),或者每当程序的另一部分(例如向队列发布数据的Swing组件)调用它时)
  5. 只要事件发布到队列(并且EDT尚未运行),就会创建EDT

如果您对此有任何疑问,请与我们联系

  • +1用于非常彻底的解释,但OP应该建立在[事件调度线程](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html)上而不关心(相当可观) )实施细节. (2认同)