如何在java中的Event Dispatch Thread队列的开头插入一个事件?

我已经知道Event Dispatch线程是如何工作的。 如果事件调度线程中存在短事件和长事件,如下所示,则应用程序无法响应。

在此处输入图像描述

对于Swing中的响应性,Event Dispatch线程仅应用于短事件。 而应该在SwingWorkers上执行长事件。

在此处输入图像描述

想象一下,有很多短暂的事件。

在此处输入图像描述 事件应该在Event Dispatch线程中执行,并且您有一个特殊事件,您希望在Event Dispatch Thread队列中存在其他事件之前执行该事件。 但是,默认情况下,事件将排队到队列的末尾,甚至InvokeLater也会这样做。

那么,是否有任何解决方案将事件排入事件调度线程的开头?

尽管替换EventQueue是一种正确的方法,但由于内置的​​EventQueue已经支持优先级,因此它并不是必需的。 唯一的事情是它只支持内部API使用,所以我们只需要了解它是如何工作的;

 //from EventQueue.java... private static final int LOW_PRIORITY = 0; private static final int NORM_PRIORITY = 1; private static final int HIGH_PRIORITY = 2; private static final int ULTIMATE_PRIORITY = 3; private static final int NUM_PRIORITIES = ULTIMATE_PRIORITY + 1; /* * We maintain one Queue for each priority that the EventQueue supports. * That is, the EventQueue object is actually implemented as * NUM_PRIORITIES queues and all Events on a particular internal Queue * have identical priority. Events are pulled off the EventQueue starting * with the Queue of highest priority. We progress in decreasing order * across all Queues. */ private Queue[] queues = new Queue[NUM_PRIORITIES]; //...skipped some parts... /** * Causes runnable to have its run * method called in the {@link #isDispatchThread dispatch thread} of * {@link Toolkit#getSystemEventQueue the system EventQueue}. * This will happen after all pending events are processed. * * @param runnable the Runnable whose run * method should be executed * asynchronously in the * {@link #isDispatchThread event dispatch thread} * of {@link Toolkit#getSystemEventQueue the system EventQueue} * @see #invokeAndWait * @see Toolkit#getSystemEventQueue * @see #isDispatchThread * @since 1.2 */ public static void invokeLater(Runnable runnable) { Toolkit.getEventQueue().postEvent( new InvocationEvent(Toolkit.getDefaultToolkit(), runnable)); } /** * Posts a 1.1-style event to the EventQueue. * If there is an existing event on the queue with the same ID * and event source, the source Component's * coalesceEvents method will be called. * * @param theEvent an instance of java.awt.AWTEvent, * or a subclass of it * @throws NullPointerException if theEvent is null */ public void postEvent(AWTEvent theEvent) { SunToolkit.flushPendingEvents(appContext); postEventPrivate(theEvent); } /** * Posts a 1.1-style event to the EventQueue. * If there is an existing event on the queue with the same ID * and event source, the source Component's * coalesceEvents method will be called. * * @param theEvent an instance of java.awt.AWTEvent, * or a subclass of it */ private final void postEventPrivate(AWTEvent theEvent) { theEvent.isPosted = true; pushPopLock.lock(); try { if (nextQueue != null) { // Forward the event to the top of EventQueue stack nextQueue.postEventPrivate(theEvent); return; } if (dispatchThread == null) { if (theEvent.getSource() == AWTAutoShutdown.getInstance()) { return; } else { initDispatchThread(); } } postEvent(theEvent, getPriority(theEvent)); } finally { pushPopLock.unlock(); } } private static int getPriority(AWTEvent theEvent) { if (theEvent instanceof PeerEvent) { PeerEvent peerEvent = (PeerEvent)theEvent; if ((peerEvent.getFlags() & PeerEvent.ULTIMATE_PRIORITY_EVENT) != 0) { return ULTIMATE_PRIORITY; } if ((peerEvent.getFlags() & PeerEvent.PRIORITY_EVENT) != 0) { return HIGH_PRIORITY; } if ((peerEvent.getFlags() & PeerEvent.LOW_PRIORITY_EVENT) != 0) { return LOW_PRIORITY; } } int id = theEvent.getID(); if ((id >= PaintEvent.PAINT_FIRST) && (id <= PaintEvent.PAINT_LAST)) { return LOW_PRIORITY; } return NORM_PRIORITY; } /** * Posts the event to the internal Queue of specified priority, * coalescing as appropriate. * * @param theEvent an instance of java.awt.AWTEvent, * or a subclass of it * @param priority the desired priority of the event */ private void postEvent(AWTEvent theEvent, int priority) { if (coalesceEvent(theEvent, priority)) { return; } EventQueueItem newItem = new EventQueueItem(theEvent); cacheEQItem(newItem); boolean notifyID = (theEvent.getID() == this.waitForID); if (queues[priority].head == null) { boolean shouldNotify = noEvents(); queues[priority].head = queues[priority].tail = newItem; if (shouldNotify) { if (theEvent.getSource() != AWTAutoShutdown.getInstance()) { AWTAutoShutdown.getInstance().notifyThreadBusy(dispatchThread); } pushPopCond.signalAll(); } else if (notifyID) { pushPopCond.signalAll(); } } else { // The event was not coalesced or has non-Component source. // Insert it at the end of the appropriate Queue. queues[priority].tail.next = newItem; queues[priority].tail = newItem; if (notifyID) { pushPopCond.signalAll(); } } } 

正如您所看到的,EventQueue有4个不同的队列,如LOW, NORM, HIGH and ULTIMATESwingUtilities.invokeLater(Runnable)EventQueue.invokeLater(Runnable)Runnable包装到InvocationEvent并调用postEvent(AWTEvent)方法。 这个方法在线程之间进行一些同步,并调用postEvent(AWTEvent, int)就像这个postEvent(theEvent, getPriority(theEvent)); 现在有趣的部分是getPriority(AWTEvent)如何工作,基本上它为除了一些PaintEventPeerEvent之外的每个事件提供了正常的优先级。

所以你需要做的是将Runnable包装成带有ULTIMATE_PRIORTYPeerEvent而不是像这样的InvocationEvent ;

 Toolkit.getDefaultToolkit().getSystemEventQueue() .postEvent(new PeerEvent(Toolkit.getDefaultToolkit(), () -> { //execute your high priority task here! System.out.println("I'm ultimate prioritized in EventQueue!"); }, PeerEvent.ULTIMATE_PRIORITY_EVENT)); 

您可以查看EventQueue和PeerEvent的完整源代码。

您可以创建和使用自己的事件队列,以您希望的方式插入新事件。 请参阅下面的代码段,了解如何设置自定义事件队列:

 public class QueueTest { public static void main(String[] args) throws InterruptedException, InvocationTargetException { EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue(); eventQueue.push(new MyEventQueue()); EventQueue.invokeAndWait(new Runnable() { public void run() { System.out.println("Run"); } }); } private static class MyEventQueue extends EventQueue { public void postEvent(AWTEvent theEvent) { System.out.println("Event Posted"); super.postEvent(theEvent); } } } 

然后,您的自定义事件队列可以发布您希望添加到具有最高优先级的队列的特定事件。 这可能无法确保它是下一个要处理的事件,但可能最适合现有设计。

我最初的想法是

我不认为我们可以控制Event Dispatch Thread需要获取的任务,但在某些方面我们可以尝试设置优先级,如下所示

 SwingUtilities.invokeAndWait(new Runnable() { public void run() { Thread.currentThread().setPriority(Thread.MAX_PRIORITY); // The task which need immediate attention. }}); 

同样,不能保证EDT会立即执行此操作。

但上面的代码是错误的。 到运行时被调用它已经在执行任务。 感谢Onur的评论。

所以下面的代码应该有所帮助。

  EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue(); Runnable runnable = new Runnable() { @Override public void run() { //My high priority task } }; PeerEvent event = new PeerEvent(this, runnable, PeerEvent.ULTIMATE_PRIORITY_EVENT); queue.postEvent(event); 

但我们需要注意一点。

  private static final int NUM_PRIORITIES = ULTIMATE_PRIORITY + 1; /* * We maintain one Queue for each priority that the EventQueue supports. * That is, the EventQueue object is actually implemented as * NUM_PRIORITIES queues and all Events on a particular internal Queue * have identical priority. Events are pulled off the EventQueue starting * with the Queue of highest priority. We progress in decreasing order * across all Queues. */ private Queue[] queues = new Queue[NUM_PRIORITIES]; public EventQueue() { for (int i = 0; i < NUM_PRIORITIES; i++) { queues[i] = new Queue(); } .... } 

因此,如果我们设置了太多ULTIMATE_PRIORITY任务,则无法保证最新任务会立即执行。