2011-02-10 10 views
9

Ho un requisito per l'esecuzione di un'attività in modo asincrono mentre si eliminano ulteriori richieste fino al completamento dell'attività.Singolo threading di un'attività senza accodamento ulteriori richieste

La sincronizzazione del metodo solo accoda le attività e non salta. Inizialmente pensavo di utilizzare un SingleThreadExecutor ma che accoda anche le attività. Ho quindi esaminato ThreadPoolExecutor ma legge la coda per ottenere l'esecuzione dell'attività e pertanto eseguirà un task in esecuzione e almeno un task in coda (gli altri possono essere eliminati utilizzando ThreadPoolExecutor.DiscardPolicy).

L'unica cosa che posso pensare è usare un semaforo per bloccare la coda. Sono venuto con il seguente esempio per mostrare quello che sto cercando di ottenere. c'è un modo più facile? Ho perso qualcosa di ovvio?

import java.util.concurrent.*; 

public class ThreadPoolTester { 
    private static ExecutorService executor = Executors.newSingleThreadExecutor(); 
    private static Semaphore processEntry = new Semaphore(1); 

    public static void main(String[] args) throws InterruptedException { 
     for (int i = 0; i < 20; i++) { 
      kickOffEntry(i); 

      Thread.sleep(200); 
     } 

     executor.shutdown(); 
    } 

    private static void kickOffEntry(final int index) { 
     if (!processEntry.tryAcquire()) return; 
     executor. 
      submit(
       new Callable<Void>() { 
        public Void call() throws InterruptedException { 
         try { 
          System.out.println("start " + index); 
          Thread.sleep(1000); // pretend to do work 
          System.out.println("stop " + index); 
          return null; 

         } finally { 
          processEntry.release(); 
         } 
        } 
       } 
      ); 
    } 
} 

uscita Esempio

start 0 
stop 0 
start 5 
stop 5 
start 10 
stop 10 
start 15 
stop 15 

Prendendo la risposta di axtavt e trasformando l'esempio precedente ha pronunciato la seguente soluzione più semplice.

import java.util.concurrent.*; 

public class SyncQueueTester { 
    private static ExecutorService executor = new ThreadPoolExecutor(1, 1, 
      1000, TimeUnit.SECONDS, 
      new SynchronousQueue<Runnable>(), 
      new ThreadPoolExecutor.DiscardPolicy()); 

    public static void main(String[] args) throws InterruptedException { 
     for (int i = 0; i < 20; i++) { 
      kickOffEntry(i); 

      Thread.sleep(200); 
     } 

     executor.shutdown(); 
    } 

    private static void kickOffEntry(final int index) { 
     executor. 
      submit(
       new Callable<Void>() { 
        public Void call() throws InterruptedException { 
         System.out.println("start " + index); 
         Thread.sleep(1000); // pretend to do work 
         System.out.println("stop " + index); 
         return null; 
        } 
       } 
      ); 
    } 
} 

risposta

10

Sembra esecutore sostenuta da SynchronousQueue con la politica desiderata fa quello che si vuole:

executor = new ThreadPoolExecutor(
    1, 1, 
    1000, TimeUnit.SECONDS, 
    new SynchronousQueue<Runnable>(), 
    new ThreadPoolExecutor.DiscardPolicy()); 
+0

scovare mi dici, perché si preferisce usare un DiscardPolicy sulla cattura della RejectedExecutionException? –

0

se non v'è nessuna coda, non v'è alcuna necessità di un esecutore direi. usare un semaforo da solo sembra abbastanza. sto usando il codice qui sotto per evitare di eseguire lo stesso codice quando è già in esecuzione. basta assicurarsi che il semaphore è static volatile, il che rende il semaforo l'unico semaforo per la classe e si propaga il riferimento semaforo mucchio altri thread non appena viene modificato

if (this.getSemaphore().tryAcquire()) { 
     try { 
      process(); 
     } catch (Exception e) { 
     } finally { 
      this.getSemaphore().release(); 
     } 
} 
else { 
    logger.info(">>>>> Job already running, skipping go"); 
} 
+0

L'attività deve essere eseguita in un thread separato, in modo che il thread principale non sia bloccato o influenzato in altro modo (la "mainline" è in realtà un'app swing) –