8

Ho usato Java Esecutori nelle mie applicazioni multi-threading, ma io non riesco a capire quando è il migliore da usare ciascuno dei seguenti modi:Come utilizzare correttamente Java Executor?

1.

ExecutorService executor=Executors.newFixedThreadPool(50); 
executor.execute(new A_Runner(... some parameter ...)); 
executor.shutdown(); 
while (!executor.isTerminated()) { Thread.sleep(100); } 

2 .

int Page_Count=200; 
ExecutorService executor=Executors.newFixedThreadPool(50); 
doneSignal=new CountDownLatch(Page_Count); 
for (int i=0;i<Page_Count;i++) executor.execute(new A_Runner(doneSignal, ... some parameter ...)); 
doneSignal.await(); 
executor.shutdown(); 
while (!executor.isTerminated()) { Thread.sleep(100); } 

3.

int Executor_Count=30; 
ThreadPoolExecutor executor=new ThreadPoolExecutor(Executor_Count,Executor_Count*2,1,TimeUnit.SECONDS,new LinkedBlockingQueue()); 
List<Future<String>> futures=new ArrayList<>(3330); 

for (int i=0;i<50;i++) futures.add(executor.submit(new A_Runner(... some parameter ...)); 
executor.shutdown(); 
while (!executor.isTerminated()) { executor.awaitTermination(1,TimeUnit.SECONDS); } 
for (Future<String> future : futures) 
{ 
    String f=future.get(); 
    // ... 
} 

in particolare, in [2] se ho saltare the doneSignal, allora sarà come [1], quindi a cosa serve per il doneSignal?

Inoltre, in [3], cosa succede se aggiungo un segno done? O è possibile?

Quello che mi piacerebbe sapere è: questi approcci sono intercambiabili, o c'è una certa situazione che dovrei usare un tipo specifico sopra?

risposta

10
  1. ExecutorService

    ExecutorService executor=Executors.newFixedThreadPool(50);

    E 'semplice e facile da usare. Nasconde i dettagli di basso livello di ThreadPoolExecutor.

    Preferire questo quando il numero di attività Callable/Runnable è di numero ridotto e l'accumulo di attività nella coda illimitata non aumenta la memoria & degradare le prestazioni del sistema. Se si dispone dei vincoli CPU/Memory, utilizzare ThreadPoolExecutor con limiti di capacità & RejectedExecutionHandler per gestire il rifiuto delle attività.

  2. CountDownLatch

    Hai inizializzato CountDownLatch con un dato numero. Questo conteggio viene decrementato dalle chiamate al metodo countDown(). Suppongo che tu stia chiamando decrementa nella tua attività Runnable in seguito. I thread in attesa che questo conteggio raggiunga lo zero possono chiamare uno dei metodi await(). La chiamata await() blocca il thread finché il conteggio non raggiunge lo zero. Questa classe abilita un thread java ad attendere fino a quando un altro set di thread completa le loro attività.

    I casi d'uso:

    1. raggiungimento della massima Parallelismo: A volte vogliamo iniziare un numero di thread allo stesso tempo, per ottenere il massimo parallelismo

    2. Wait N fili a completa prima di iniziare esecuzione

    3. Rilevamento deadlock.

      Dai un'occhiata a questo article di Lokesh Gupta per maggiori dettagli.

  3. ThreadPoolExecutor: Si fornisce un maggiore controllo per perfezionare vari parametri pool di thread. Se l'applicazione è vincolata dal numero di attività Runnable/Callable attive, è necessario utilizzare la coda limitata impostando la capacità massima. Una volta che la coda raggiunge la capacità massima, è possibile definire RejectionHandler. Java fornisce quattro tipi di RejectedExecutionHandlerpolicies.

    1. Nel predefinita ThreadPoolExecutor.AbortPolicy, il conduttore lancia un RejectedExecutionException runtime su di rifiuto.

    2. In ThreadPoolExecutor.CallerRunsPolicy, il thread che richiama execute esegue l'attività. Ciò fornisce un semplice meccanismo di controllo di feedback che rallenta la velocità con cui vengono inviate nuove attività.

    3. In ThreadPoolExecutor.DiscardPolicy, un'attività che non può essere eseguita viene semplicemente eliminata.

    4. In ThreadPoolExecutor.DiscardOldestPolicy, se l'esecutore non viene arrestato, il compito alla testa della coda di lavoro è caduto, e quindi l'esecuzione viene ritentata (che può fallire di nuovo, causando questo da ripetere.)

      Se si desidera simulare il comportamento di CountDownLatch, è possibile utilizzare il metodo invokeAll().

  4. più Un meccanismo non l'hai fatto citazione è ForkJoinPool

    Il ForkJoinPool è stato aggiunto a Java in Java 7. Il ForkJoinPool è simile a Java ExecutorService ma con una differenza. Lo ForkJoinPool lo rende facile per le attività in cui suddividere il proprio lavoro in attività più piccole, che sono quindi inoltrate allo ForkJoinPool. Il furto di attività avviene in ForkJoinPool quando i thread di lavoro gratuiti rubano le attività dalla coda di thread di lavoro occupata.

    Java 8 ha introdotto un'altra API in ExecutorService per creare pool di sottrazione di lavoro. Non è necessario creare RecursiveTask e RecursiveAction ma è ancora possibile utilizzare ForkJoinPool.

    public static ExecutorService newWorkStealingPool() 
    

    Crea un pool di thread di lavoro furto utilizzando tutti i processori disponibili come il suo livello obiettivo parallelismo.

    Per impostazione predefinita, il numero di core della CPU richiederà un parametro.

Tutti questi quattro meccanismi sono complementari l'uno all'altro. A seconda del livello di granularità che si desidera controllare, è necessario scegliere quelli giusti.