当前位置: 首页 > 知识库问答 >
问题:

如何在java中将事件插入事件调度线程队列的开头?

赏育
2023-03-14

我已经知道事件调度线程是如何工作的。如果事件调度线程中有短事件和长事件,应用程序就不能响应。

为了在 Swing 中实现响应,事件调度线程应仅用于短事件。而长事件应该在SwingWorkers上执行。

想象一下有很多短赛事。

那么,有没有什么解决方案可以将一个事件排到事件调度线程的开头呢?

共有3个答案

司空宗清
2023-03-14

我最初的想法是

我认为我们无法控制需要由事件调度线程执行的任务,但在某些方面,我们可以尝试设置优先级,如下所示

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

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

乜业
2023-03-14

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

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

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

喻昀
2023-03-14

虽然替换事件队列是正确的方法,但实际上没有必要,因为内置事件队列已经支持优先级排序。唯一的问题是它只支持内部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,NORM,HIGH和ULTIMATESwingUtilities.invokeLater(Runnable)EventQueue.invokeLater(Runnable)Runnable包装成InvocationEvent并调用postEvent(AWTEvent)方法。此方法在线程和调用 postEvent(AWTEvent, int) 之间进行一些同步,就像这个 postEvent(theEvent, getPriority(theEvent));现在有趣的部分是getPriority(AWTEvent)的工作原理,基本上它为每个事件提供了正常的优先级,除了一些PaintEvents和PeerEvents。

因此,您需要做的是将<code>Runnable;

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 的完整源代码。

 类似资料:
  • 问题内容: 我最近开始学习和探索Java中GUI编程的基础知识。 经过一段时间的编程,我只完成了后端工作或其他工作,因此,我最接近用户界面的是命令控制台(令人尴尬的是,我知道)。 我正在使用Swing,据我所知,通过扩展,我也正在使用AWT。 我的问题基于以下代码: 我已经研究了一段时间,因为我想完全理解这段奇怪的代码,并且多次遇到“事件分派线程”一词。如果我错了,请纠正我,但据我了解;它与使用多

  • 问题内容: 我了解了swing是不是线程安全的。深入研究,我发现必须对事件分发线程进行任何对swing组件的修改,以防止与多线程相关的各种问题。但是,这些信息似乎完全到此为止。似乎没有一个很好的教程可以解释如何在Internet上可以访问的任何地方进行此操作。 从与其他问题有关的已发布代码中收集信息,似乎我将不得不在程序中的每个单独的swing修改周围放置一个不整洁的代码块(例如本例中的代码):

  • 问题内容: 我知道“线程”的含义,并且如果我将事件分发线程(EDT)理解为“只是一个线程”,它可以解释很多,但是显然,它并不能解释所有内容。 我不了解此线程有什么特别之处。例如,我不明白为什么要在EDT中启动GUI?为什么“主”线程是GUI的床?好吧,如果我们只是不想占用主线程,为什么为什么不能仅在“另一个线程”中启动GUI,为什么它应该是一些称为“ EDT”的“特殊”线程呢? 那我不明白为什么我

  • Java Swing教程使用SwingUtilities。invokeLater创建GUI(如这里所示)。这里解释了为什么要这样做——大多数Swing对象都不是线程安全的。另一方面,我所看到的大多数JRubySwing示例只是设置了脚本中可见的顶部框架(如这里所示——归档中的“gui”目录)。 问题是,在 JRuby 脚本中创建应用程序顶部帧时,我是否应该遵循使用 SwingUtilities.i

  • 问题内容: 我正在使用一个称为类的类,该类可实现处理项目中的正常异常。 据我了解,此类无法捕获EDT异常,因此我尝试在该方法中使用此类来处理EDT异常: 但是直到现在,它仍无法正常工作。例如,在初始化JFrame时,我从构造器中的捆绑文件中加载其标签,如下所示: 我从捆绑文件中删除了密钥以测试异常处理程序,但是它没有用!异常通常打印在日志中。 我在这里做错什么了吗? 问题答案: EDT异常处理程序

  • 当一个组织中有N名员工时,我们会得到N个日期偏移范围。类似于 1-4(即员工将在第一、第二、第三和第四天来) 2-6 8-9 ... 1-14 我们必须在最少的天数上组织一次活动,以便每个员工至少可以参加两次活动。请建议算法(可能是贪婪的)来做到这一点。 PS:事件是一天的事件。