什么时候创建Swing UI线程?

时间:2014-10-11 18:19:18

标签: java swing event-dispatch-thread

在运行Swing程序的过程中,是 UI线程(事件调度线程,EDT)首先产生? 据推测,任何给定的JVM都可以做任何想做的事情 (例如,无论是在启动时总是产生EDT 或不是它曾经使用过),但作为一个实际问题 EDT通常是创建的吗?

是否在SwingUtilities.invokeLater()时创建 是第一次打电话?首次实例化JPanel时? 如果事件泵与创建分开启动 美国东部时间,这通常会发生什么时候?

1 个答案:

答案 0 :(得分: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;
}

使用package-private方法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();
    }
}

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

  1. 在私人方法EventQueue#wakeup(boolean)
  2. 在私有方法EventQueue#postEventPrivate(AWTEvent)中(由公共方法EventQueue#postEvent(AWTEvent)调用)
  3. 在包私有方法EventQueue#createSecondaryLoop(Conditional, EventFilter, long)
  4. 在调用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;
    }
    

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

    一旦我们拥有了工具包,我们就可以使用Toolkit#getSystemEventQueue()访问它的EventQueue。这个望远镜采用受保护的抽象方法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;
    }
    

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

    快速概述:

    1. EDT位于EventQueue
    2. EventQueue位于Toolkit
    3. 创建工具包时会创建队列
    4. 手动创建工具包(通过调用Toolkit.getDefaultToolkit(),或者每当程序的其他部分(例如向队列发布数据的Swing组件)调用它时)
    5. 只要事件发布到队列(并且EDT尚未运行),就会创建EDT。
    6. 如果您对此有任何疑问,请与我们联系。