2016-06-17 9 views
11

Ich weiß bereits, wie Event Dispatch thread funktioniert. Wenn im Ereignis-Dispatch-Thread kurze und lange Ereignisse wie unten vorkommen, kann die Anwendung nicht reagieren.Wie füge ich ein Ereignis an den Anfang der Event Dispatch Thread Queue in Java?

enter image description here

Aus Gründen der Ansprechempfindlichkeit in Swing, Gewinde Event Dispatch sollten nur für kurze Veranstaltungen genutzt werden. während lange Ereignisse auf SwingWorker ausgeführt werden sollten.

enter image description here

Stellen Sie sich vor, dass es eine Menge von kurzen Ereignissen ist.

Die Ereignisse sollten im Event Dispatch-Thread ausgeführt werden, und Sie haben ein spezielles Ereignis, das vor anderen Ereignissen in der Event Dispatch Thread-Warteschlange ausgeführt werden soll. Ereignisse werden jedoch standardmäßig an das Ende der Warteschlange eingereiht, und selbst InvokeLater macht dasselbe.

Gibt es also eine Lösung, ein Ereignis an den Anfang des Ereignis-Dispatch-Threads zu reihen?

+1

Mögliches Duplikat von [this] (http://stackoverflow.com/q/37841987/230513). Synchronisieren Sie mehrere 'SwingWorker'-Instanzen wie [this] (http://stackoverflow.com/a/11372932/230513). – trashgod

+1

Wenn die Ereignisse den EDT blockieren, führen Sie sie nicht auf dem EDT aus. Sie haben noch kein konkretes Beispiel dafür angegeben, was Sie zu tun versuchen. Ich habe es nie benutzt, aber hast du versucht invokeAndWait (...)? – camickr

+0

Ja, ich hatte es getan. 'InvokeLater' und' InvokeAndWait' werden für das neue Ereignis in Bezug auf den Thread verwendet, den es aufgerufen hat. – hamed

Antwort

4

Obwohl das Ersetzen der EventQueue ein richtiger Ansatz ist, ist es nicht wirklich notwendig, da die integrierte EventQueue bereits die Priorisierung unterstützt.Die einzige Sache ist, dass es nur die innere API-Nutzung unterstützt, also müssen wir nur verstehen, wie das funktioniert;

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

Wie Sie Eventqueue haben 4 verschiedene Warteschlangen als LOW, NORM, HIGH and ULTIMATE, SwingUtilities.invokeLater(Runnable) oder EventQueue.invokeLater(Runnable) hüllt Ihren Runnable in eine InvocationEvent und ruft postEvent(AWTEvent) Methode sehen kann. Diese Methode führt einige Synchronisation zwischen Threads und Aufrufe postEvent(AWTEvent, int) wie folgt postEvent(theEvent, getPriority(theEvent)); Nun ist der interessante Teil, wie getPriority(AWTEvent) funktioniert, im Grunde gibt es normale Priorität für alle Ereignisse außer einigen PaintEvent s und PeerEvent s.

Also, was Sie tun müssen, ist wickeln Sie Ihre Runnable in eine PeerEvent mit ULTIMATE_PRIORTY anstelle eines InvocationEvent wie folgt;

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

können Sie den Code in voller Quelle EventQueue und PeerEvent überprüfen.

+0

Genau das habe ich gesucht. Danke @Onur – hamed

2

Mein erster Gedanke war

Ich glaube nicht, dass wir die Aufgaben kontrollieren kann, die von Event Dispatch Thread werden muss abgeholt, aber in gewisser Weise können wir versuchen, die Priorität zu setzen, wie unten

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

Wiederum gibt es keine Garantie, dass dies zur sofortigen Ausführung durch EDT übernommen wird.

Aber der obige Code ist falsch. Zu dem Zeitpunkt, an dem der Aufruf ausgeführt wird, werden die Aufgaben bereits ausgeführt. Danke für die Kommentare Onur.

Der folgende Code sollte also helfen.

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

Aber es gibt einen Punkt, den wir beachten müssen.

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

Also, wenn wir zu viele ULTIMATE_PRIORITY Aufgaben setzen, gibt es keine Garantie, dass die neueste Aufgabe sofort ausgeführt werden würde.

+0

Dieser Code legt nicht das Priority des Runnable/Task fest. Es setzt die Priorität des Ereignis-Versand-Threads auf das Maximum. Dies wirkt sich nicht nur auf die aktuelle Aufgabe, sondern auch auf jede andere Aufgabe in der Warteschlange aus. – Onur

+0

@Onur Ich habe gerade nach Ihren Kommentaren bemerkt. Danke, dass du mir beim Lernen geholfen hast. – Beniton

+0

Sie sind willkommen, aber die Sache ist Java's innere API verwendet nie ** ULTIMATE_PRIORITY **. Sie können das hier sehen: [Usage of sun.awt.PeerEvent] (http://grepcode.com/search/[email protected][email protected]@sun$ awt @ PeerEvent & type = type & k = u) 'PeerEvent' wird immer mit **' PeerEvent.PRIORITY_EVENT' erstellt ** – Onur

2

Sie können eine eigene Ereigniswarteschlange erstellen und verwenden, die neue Ereignisse auf die von Ihnen gewünschte Art und Weise einfügt. Sehen Sie das Code-Snippet unten, wie die Einrichtung einer benutzerdefinierten Event-Queue:

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

Ihre benutzerdefinierten Event-Queue könnte dann senden Sie bestimmte Ereignisse, die Sie mit der höchsten Priorität in die Warteschlange vorangestellt werden soll. Dies stellt möglicherweise nicht sicher, dass es das nächste zu verarbeitende Ereignis ist, würde aber wahrscheinlich am besten in das vorhandene Design passen.