2011-02-10 9 views
9

Tengo un requisito para que una tarea se ejecute de forma asincrónica, descartando cualquier solicitud adicional hasta que la tarea finalice.Enhebrado sencillo de una tarea sin encolamiento de solicitudes adicionales

Sincronizando el método, solo pone en cola las tareas y no omite. Inicialmente pensé usar un SingleThreadExecutor pero eso también pone en cola tareas. Luego miré al ThreadPoolExecutor pero se lee la cola para ejecutar la tarea y, por lo tanto, se ejecutará una tarea y se pondrá en cola al menos una tarea (las otras se pueden descartar usando ThreadPoolExecutor.DiscardPolicy).

Lo único que puedo pensar es utilizar un semáforo para bloquear la cola. He venido con el siguiente ejemplo para mostrar lo que estoy tratando de lograr. ¿Hay alguna forma más simple? ¿Me he perdido algo obvio?

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

Ejemplo de salida

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

Tomando la respuesta de axtavt y transformando el ejemplo anterior dicta la siguiente solución más simple.

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

Respuesta

10

Parece ejecutor respaldado por SynchronousQueue con la política deseada hace lo que quiere:

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

desvivían usted me dice, ¿por qué prefiere utilizar una ¿DiscardPolicy sobre la captura de RejectedExecutionException? –

0

si no hay cola, no hay necesidad de un ejecutor, diría. usar un semáforo solo parece suficiente. Estoy usando el siguiente código para evitar ejecutar el mismo código cuando ya se está ejecutando. Sólo asegúrese de que el semaphore es static volatile, lo que hace que el semáforo el único semáforo de la clase y se propaga la referencia semáforo para heap otros hilos tan pronto como se cambia

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

La tarea debe ejecutarse en un subproceso separado para que el subproceso principal no se bloquee ni se vea afectado (la "línea principal" es en realidad una aplicación dinámica) –