2016-06-17 11 views
11

Już wiem, jak działa Event Dispatch thread. Jeśli w wątku Dispatch zdarzenia występują krótkie i długie wydarzenia, aplikacja nie może reagować.Jak wstawić zdarzenie do początku kolejki wątków procesu wyświetlania zdarzeń w java?

enter image description here

ze względu na szybkość reakcji w Swing, gwint Wysłanie zdarzenia powinny być wykorzystywane jedynie do krótkich imprez. natomiast długie zdarzenia powinny być wykonywane na SwingWorkers.

enter image description here

Wyobraźmy sobie, że istnieje wiele krótkich zdarzeń.

enter image description here Zdarzenia powinny być wykonywane w wątku Dispatch zdarzenia i istnieje specjalne zdarzenie, które ma zostać wykonane przed innymi zdarzeniami istniejącymi w kolejce wątku zdarzenia. Ale zdarzenia będą domyślnie zamykane na końcu kolejki, a nawet InvokeLater zrobić to samo.

Czy istnieje zatem jakieś rozwiązanie, aby umieścić wydarzenie na początku wątku wysyłki zdarzeń?

+1

Prawdopodobny duplikat [tego] (http://stackoverflow.com/q/37841987/230513). Synchronizuj wiele instancji 'SwingWorker' takich jak [this] (http://stackoverflow.com/a/11372932/230513). – trashgod

+1

Jeśli zdarzenia blokują EDT, nie wykonuj ich w EDT. Nadal nie podałeś konkretnego przykładu tego, co próbujesz zrobić. Nigdy go nie używałem, ale czy próbowałeś invokeAndWait (...)? – camickr

+0

Tak, zrobiłem to. 'InvokeLater' i' InvokeAndWait' są używane dla nowego zdarzenia w odniesieniu do wątku, do którego został wywołany. – hamed

Odpowiedz

4

Chociaż zastąpienie EventQueue jest właściwym podejściem, nie jest to konieczne, ponieważ wbudowana EventQueue już obsługuje priorytetyzację.Jedyną rzeczą jest to, że obsługuje tylko wewnętrzne API, więc musimy tylko zrozumieć, jak to działa;

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

Jak widać EventQueue mieć 4 różne kolejki jak LOW, NORM, HIGH and ULTIMATE, SwingUtilities.invokeLater(Runnable) lub EventQueue.invokeLater(Runnable) owija swoje Runnable do InvocationEvent i wzywa postEvent(AWTEvent) metody. Ta metoda umożliwia synchronizację wątków i wywołań postEvent(AWTEvent, int), takich jak ta postEvent(theEvent, getPriority(theEvent));. Teraz interesującą częścią jest to, jak działa getPriority(AWTEvent). Zasadniczo daje to normalny priorytet każdemu zdarzeniu z wyjątkiem niektórych PaintEvent s oraz PeerEvent s.

Co należy zrobić, to zastąpić Runnable na PeerEvent z ULTIMATE_PRIORTY zamiast na InvocationEvent w ten sposób;

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

Można sprawdzić pełny kod źródłowy EventQueue i PeerEvent.

+0

To jest dokładnie to, czego szukałem. Dzięki @Onur – hamed

2

Moją pierwszą myślą było

Nie sądzę, możemy kontrolować zadania, które musi zostać odebrany przez temat zdarzeń wysyłki, ale w pewien sposób możemy spróbować ustawić priorytet jak poniżej

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

Ponownie nie ma gwarancji, że zostanie to odebrane do natychmiastowej realizacji przez EDT.

Ale powyższy kod jest nieprawidłowy. Do czasu, gdy zostanie wywołany, jest już wykonywanie zadań. Dzięki za komentarze Onur.

Poniższy kod powinien pomóc.

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

Ale jest jeden punkt, który musimy zauważyć.

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

Więc jeśli jesteśmy ustawienie zbyt wiele ULTIMATE_PRIORITY zadania, nie ma gwarancji, że ostatnie zadanie będzie realizowane natychmiast.

+0

Ten kod nie powoduje ustawienia opcji Runnable/Tasks. Ustawia priorytet wątku wysyłki zdarzeń na maksimum. Który nie tylko wpłynie na bieżące zadanie, ale także wpłynie na każde inne zadanie w kolejce. – Onur

+0

@Onur Zauważyłem, że zaraz po twoich komentarzach. Dziękuję za pomoc w nauce. – Beniton

+0

jesteś mile widziany, ale chodzi o to, że wewnętrzne API Javy nigdy nie używa ** ULTIMATE_PRIORITY **. Możesz to zobaczyć tutaj: [Korzystanie z sun.awt.PeerEvent] (http://grepcode.com/search/[email protected][email protected]@sun$ awt @ PeerEvent & type = type & k = u) 'PeerEvent' zawsze jest tworzony z **' PeerEvent.PRIORITY_EVENT' ** – Onur

2

Możesz tworzyć i używać własnej kolejki zdarzeń, która wstawia nowe wydarzenia w wybrany sposób. Zobacz fragment kodu poniżej, jak skonfigurować zdarzenie niestandardowe kolejki:

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

niestandardowego kolejce zdarzeń mógł potem po konkretne zdarzenia, które mają być dołączany do kolejki o najwyższym priorytecie. Może to nie zagwarantować, że jest to następne zdarzenie, które ma zostać przetworzone, ale prawdopodobnie najlepiej pasowałoby do istniejącego projektu.

Powiązane problemy