2016-06-17 11 views
11

So già come funziona Event Dispatch thread. Se ci sono eventi brevi e lunghi nel thread di Invio eventi come di seguito, l'applicazione non può essere reattiva.Come inserire un evento all'inizio della coda Thread di invio eventi in java?

enter image description here

per il gusto di reattività in Swing, filo Evento spedizione dovrebbe essere utilizzato solo per eventi brevi. mentre gli eventi lunghi dovrebbero essere eseguiti su SwingWorkers.

enter image description here

Immaginate che ci sia un sacco di eventi brevi.

enter image description here Gli eventi devono essere eseguiti nel thread Event Dispatch e si dispone di un evento speciale che si desidera eseguire prima degli altri eventi esistenti nella coda Thread di invio eventi. Ma gli eventi saranno accodati alla fine della coda di default e anche lo InvokeLater fa lo stesso.

Quindi, esiste una soluzione per accodare un evento all'inizio del thread di invio eventi?

+1

Possibile duplicato di [questo] (http://stackoverflow.com/q/37841987/230513). Sincronizza più istanze di 'SwingWorker' come [this] (http://stackoverflow.com/a/11372932/230513). – trashgod

+1

Se gli eventi bloccano l'EDT, non li eseguono sull'EDT. Non hai ancora fornito un esempio concreto di ciò che stai tentando di fare. Non l'ho mai usato ma hai provato invokeAndWait (...)? – camickr

+0

Sì, l'avevo fatto. 'InvokeLater' e' InvokeAndWait' vengono utilizzati per il nuovo evento rispetto al thread che è stato chiamato. – hamed

risposta

4

Sebbene la sostituzione dello EventQueue sia un approccio corretto, non è realmente necessaria in quanto EventQueue supporta già l'assegnazione delle priorità.L'unica cosa è che lo supporta solo per uso interno dell'API, quindi dobbiamo solo capire come funziona;

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

Come si può vedere EventQueue avere 4 code di diversi come LOW, NORM, HIGH and ULTIMATE, SwingUtilities.invokeLater(Runnable) o EventQueue.invokeLater(Runnable) avvolge il tuo Runnable in un InvocationEvent e chiama postEvent(AWTEvent) metodo. Questo metodo fa un po 'di sincronizzazione tra thread e chiamate postEvent(AWTEvent, int) come questo postEvent(theEvent, getPriority(theEvent)); Ora la parte interessante è come funziona getPriority(AWTEvent), in pratica dà la priorità normale a ogni evento tranne alcuni PaintEvent s e PeerEvent s.

Così che cosa dovete fare è di avvolgere il vostro Runnable in un PeerEvent con ULTIMATE_PRIORTY invece di una InvocationEvent come questo;

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

È possibile controllare il codice sorgente completo di EventQueue e PeerEvent.

+0

Questo è esattamente quello che stavo cercando. Grazie a @Onur – hamed

2

Il mio pensiero iniziale era

Non credo che siamo in grado di controllare i compiti che deve essere raccolto da Event spedizione Thread, ma in un certo modo possiamo provare a impostare la priorità come qui di seguito

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

Anche in questo caso non è possibile garantire che questo venga prelevato per l'esecuzione immediata da parte di EDT.

Ma il codice precedente è sbagliato. Prima che venga chiamato, esegue già le attività. Grazie per i commenti Onur.

Quindi il codice qui sotto dovrebbe aiutare.

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

Ma c'è un punto che dobbiamo notare.

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

Quindi, se stiamo creando troppi ULTIMATE_PRIORITY compiti, non v'è alcuna garanzia che l'ultima operazione sarebbe stata eseguita immediatamente.

+0

Questo codice non sta impostando il priory di Runnable/Task. Sta impostando la priorità del thread di invio eventi sul massimo. Il che non influisce solo sull'attività corrente, ma influisce anche su ogni altra attività in coda. – Onur

+0

@Onur l'ho notato subito dopo i vostri commenti. Grazie per avermi aiutato a imparare. – Beniton

+0

sei il benvenuto, ma il fatto è che l'API interna di Java non usa mai ** ULTIMATE_PRIORITY **. Puoi vederlo qui: [Usi di sun.awt.PeerEvent] (http://grepcode.com/search/[email protected][email protected]@sun$ awt @ PeerEvent & type = tipo & k = u) 'PeerEvent' viene sempre creato con **' PeerEvent.PRIORITY_EVENT' ** – Onur

2

È possibile creare e utilizzare la propria coda eventi che inserisce nuovi eventi nel modo desiderato. Vedere il frammento di codice di seguito come impostare una coda di evento personalizzato: coda eventi

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

vostra abitudine potrebbe eventi allora post specifico che si desidera essere anteposto alla coda con la massima priorità. Questo potrebbe non garantire che sia il prossimo evento da elaborare, ma probabilmente si adatterebbe meglio al design esistente.

Problemi correlati