如何在java中的Event Dispatch Thread队列的开头插入一个事件? [英] How to insert an event to the beginning of Event Dispatch Thread queue in java?

查看:155
本文介绍了如何在java中的Event Dispatch Thread队列的开头插入一个事件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经知道



关于Swing,事件响应的清醒Dispatch线程仅应用于短事件。而应该在SwingWorkers上执行长事件。





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




事件应该在事件调度线程中执行,并且您有一个特殊的事件,你想成为在Event Dispatch Thread队列中存在的其他事件之前执行。但是,默认情况下,事件将排队到队列的末尾,甚至 InvokeLater 也会这样做。



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

解决方案

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

  //来自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;

/ *
*我们为EventQueue支持的每个优先级维护一个队列。
*也就是说,Eve​​ntQueue对象实际上实现为
* NUM_PRIORITIES个队列,特定内部队列
*上的所有事件具有相同的优先级。事件从EventQueue开始,以优先级最高的队列开始
*。我们在所有队列中按递减顺序
*进度。
* /
private Queue [] queues = new Queue [NUM_PRIORITIES];

//...skipped some parts ...

/ **
*原因< code> runnable< / code>让它的< code>运行< / code>
*在
* {@link Toolkit#getSystemEventQueue系统EventQueue}的{@link #isDispatchThread调度线程}中调用的方法。
*这将在处理完所有待处理事件后发生。
*
* @param runnable< code> Runnable< / code>其< code>运行< / code>
*方法应该在
* {@link #isDispatchThread事件调度线程}中异步执行
* {$ link Toolkit#getSystemEventQueue系统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));
}

/ **
*在< code> EventQueue< / code>上发布1.1风格的活动。
*如果队列中存在具有相同ID
*和事件源的现有事件,则源< code>组件< / code>的
*< code> coalesceEvents< ; /代码>方法将被调用。
*
* @param theEvent< code> java.awt.AWTEvent< / code> ;,
*或其子类
*的实例@throws NullPointerException如果< ;代码> theEvent< /代码>是< code> null< / code>
* /
public void postEvent(AWTEvent theEvent){
SunToolkit.flushPendingEvents(appContext);
postEventPrivate(theEvent);
}

/ **
*在< code> EventQueue< / code>上发布1.1风格的活动。
*如果队列中存在具有相同ID
*和事件源的现有事件,则源< code>组件< / code>的
*< code> coalesceEvents< ; /代码>方法将被调用。
*
* @param theEvent< code> java.awt.AWTEvent< / code> ;,
*或其子类
* /
的实例private final void postEventPrivate(AWTEvent theEvent){
theEvent.isPosted = true;
pushPopLock.lock();
try {
if(nextQueue!= null){
//将事件转发到EventQueue堆栈顶部
nextQueue.postEventPrivate(theEvent);
返回;
}
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;
}
返回NORM_PRIORITY;
}

/ **
*将事件发布到指定优先级的内部队列,
*合并为适当的。
*
* @param theEvent< code> java.awt.AWTEvent< / code> ;,
*或其子类
* @param priority的实例事件的优先级
* /
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 {
//事件未合并或具有非组件源。
//将其插入相应队列的末尾。
queues [priority] .tail.next = newItem;
queues [priority] .tail = newItem;
if(notifyID){
pushPopCond.signalAll();
}
}
}

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



所以你需要做的是包装 Runnable PeerEvent ,带 ULTIMATE_PRIORTY 而不是 InvocationEvent 喜欢这个;

  Toolkit.getDefaultToolkit()。getSystemEventQueue()
.postEvent( new PeerEvent(Toolkit.getDefaultToolkit(),() - > {


//执行你的高优先级任务!
System.out.println(我是最终在EventQueue中优先考虑!;;


},PeerEvent.ULTIMATE_PRIORITY_EVENT));

您可以查看 EventQueue PeerEvent


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.

For the Sake of responsiveness in Swing, Event Dispatch thread should only be used for short events. while long events should be executed on SwingWorkers.

Imagine that there is a lot of short events.

The events should be executed in Event Dispatch thread and you have a special event which you want to be executed before other events existing in Event Dispatch Thread queue. But, events will be enqueued to the end of the queue by default and even InvokeLater do the same.

So, is there any solution to enqueue an event to the beginning of the Event Dispatch Thread?

解决方案

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

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 PaintEvents and PeerEvents.

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 .

这篇关于如何在java中的Event Dispatch Thread队列的开头插入一个事件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆