I already know how Event Dispatch thread works. If there be short and long events in Event Dispatch thread like below, the application can\'t be responsive.
<
Although replacing the EventQueue
is a right approach, it's not really necessary since built-in EventQueue already supports prioritizing. Only thing is it only supports it for inner API use so we only need to understand how that works;
//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();
}
}
}
As you can see EventQueue have 4 different queues as LOW, NORM, HIGH and ULTIMATE
, SwingUtilities.invokeLater(Runnable)
or EventQueue.invokeLater(Runnable)
wraps your Runnable
into a InvocationEvent
and calls postEvent(AWTEvent)
method. This method does some syncronizing between threads and calls postEvent(AWTEvent, int)
like this postEvent(theEvent, getPriority(theEvent));
Now the interesting part is how getPriority(AWTEvent)
works, basicly it gives normal priority to the every event except some PaintEvent
s and PeerEvent
s.
So what you need to do is wrap your Runnable
into a PeerEvent
with ULTIMATE_PRIORTY
instead of a InvocationEvent
like this;
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));
You can check the full source code of EventQueue and PeerEvent .