在java中,如何将事件插入事件分派线程队列的开头?
我已经知道如何工作了。如果在事件分派线程中存在短事件和长事件(如下所示),则应用程序无法响应 为了在Swing中实现响应,事件分派线程应该只用于短事件。而长事件应该在SwingWorkers上执行 想象一下,有很多短事件 事件应该在事件调度线程中执行,并且您有一个特殊的事件,您希望在事件调度线程队列中存在其他事件之前执行该事件。但是,默认情况下,事件将排队到队列的末尾,甚至在java中,如何将事件插入事件分派线程队列的开头?,java,swing,event-dispatch-thread,Java,Swing,Event Dispatch Thread,我已经知道如何工作了。如果在事件分派线程中存在短事件和长事件(如下所示),则应用程序无法响应 为了在Swing中实现响应,事件分派线程应该只用于短事件。而长事件应该在SwingWorkers上执行 想象一下,有很多短事件 事件应该在事件调度线程中执行,并且您有一个特殊的事件,您希望在事件调度线程队列中存在其他事件之前执行该事件。但是,默认情况下,事件将排队到队列的末尾,甚至InvokeLater也会这样做 那么,有没有办法将事件排队到事件调度线程的开头?我最初的想法是 我不认为我们可以控制
InvokeLater
也会这样做
那么,有没有办法将事件排队到事件调度线程的开头?我最初的想法是 我不认为我们可以控制需要由事件分派线程拾取的任务,但在某些方面,我们可以尝试如下设置优先级
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
// The task which need immediate attention.
}});
同样,也不能保证EDT会立即执行该命令
但是上面的代码是错误的。在调用run时,它已经在执行任务了。谢谢你的评论
因此下面的代码应该会有所帮助
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();
}
....
}
private static final int NUM_PRIORITIES=最终优先级+1;
/*
*我们为EventQueue支持的每个优先级维护一个队列。
*也就是说,EventQueue对象实际上实现为
*NUM_优先级队列和特定内部队列上的所有事件
*具有相同的优先级。事件从事件队列中拉出,并开始
*具有最高优先级的队列。我们按降序前进
*跨越所有队列。
*/
专用队列[]队列=新队列[NUM_优先级];
公共事件队列(){
对于(int i=0;i
因此,如果我们设置了太多的最终\u优先级任务,则无法保证会立即执行最新的任务。您可以创建并使用自己的事件队列,以希望的方式插入新事件。请参阅下面的代码片段,了解如何设置自定义事件队列:
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);
}
}
}
然后,您的自定义事件队列可以将您希望预先添加到具有最高优先级的队列中的特定事件发布到队列中。这可能无法确保它是下一个要处理的事件,但可能最适合现有的设计。虽然替换
事件队列是一种正确的方法,但实际上没有必要,因为内置的事件队列已经支持优先级排序。唯一的问题是它只支持内部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 <code>runnable</code> to have its <code>run</code>
* 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 <code>Runnable</code> whose <code>run</code>
* 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 <code>EventQueue</code>.
* If there is an existing event on the queue with the same ID
* and event source, the source <code>Component</code>'s
* <code>coalesceEvents</code> method will be called.
*
* @param theEvent an instance of <code>java.awt.AWTEvent</code>,
* or a subclass of it
* @throws NullPointerException if <code>theEvent</code> is <code>null</code>
*/
public void postEvent(AWTEvent theEvent) {
SunToolkit.flushPendingEvents(appContext);
postEventPrivate(theEvent);
}
/**
* Posts a 1.1-style event to the <code>EventQueue</code>.
* If there is an existing event on the queue with the same ID
* and event source, the source <code>Component</code>'s
* <code>coalesceEvents</code> method will be called.
*
* @param theEvent an instance of <code>java.awt.AWTEvent</code>,
* 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 <code>java.awt.AWTEvent</code>,
* 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、Normal、HIGH和ULTIMATE
、SwingUtilities.invokeLater(Runnable)
或EventQueue.invokeLater(Runnable)
将Runnable
包装成InvokeEvent
并调用postEvent(AWTEvent)
方法。此方法在线程和调用postEvent(awteEvent,int)
之间执行一些同步,如postEvent(theEvent,getPriority(theEvent))代码>现在有趣的部分是getPriority(AWTEvent)
的工作原理,基本上它为每个事件赋予正常的优先级,除了一些PaintEvent
s和peevent
s
因此,您需要做的是将Runnable
包装成PeerEvent
,使用ULTIMATE\u PRIORTY
而不是像这样的调用事件
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));
您可以查看和的完整源代码。的可能副本。同步多个SwingWorker
实例,如。如果事件阻止EDT,则不要在EDT上执行它们。您仍然没有提供一个具体的例子来说明您正在尝试做什么。我从未用过它,但你试过调用EANDWAIT(…)?是的,我用过InvokeLater
和InvokeAndWait
用于它所调用的线程的新事件。EDT被指定为绘制、重新绘制、刷新值、设置值、应用更改,仅用于Swing API中实现的方法,所有这些事件都在同一时间内完成,在一瞬间,EDT中包含队列的所有事件,其他一切都是错误的想法,除了调用EDT中的invokeAndWait required false之外,我以前从未使用过它,但我认为问题的解决方法很简单。您可以简单地将短任务放入一个队列中,该队列将任务提供给EDT。但我猜由于Swing的机制,您无法控制它。该代码并没有设置Runnable/Task的隐修会。它将事件分派线程的优先级设置为最大。这不仅会影响当前任务,还会影响队列中的所有其他任务。@Onur在您的评论之后我确实注意到了。谢谢你帮助我学习。不客气,但问题是Java的内部API从不使用最高优先级。您可以在这里看到:PeerEvent
总是使用PeerEvent.PRIORITY\u事件创建的,这正是我要找的。谢谢@Onur