ExecutorService - Menunggu Benang Selesai

1. Gambaran keseluruhan

The ExecutorService rangka menjadikannya mudah untuk memproses tugas dalam pelbagai benang. Kami akan memberi contoh beberapa senario di mana kami menunggu utas menyelesaikan pelaksanaannya.

Juga, kami akan menunjukkan cara mematikan perkhidmatan ExecutorService dan menunggu untuk menjalankan thread untuk menyelesaikan pelaksanaannya.

2. Selepas Penutupan Pelaksana

Semasa menggunakan Executor, kita dapat mematikannya dengan memanggil kaedah shutdown () atau shutdownNow () . Walaupun begitu, ia tidak akan menunggu sehingga semua utas berhenti dijalankan.

Menunggu benang yang ada untuk menyelesaikan pelaksanaannya dapat dicapai dengan menggunakan kaedah awaitTermination () .

Ini menyekat utas sehingga semua tugas menyelesaikan pelaksanaannya atau tamat waktu yang ditentukan:

public void awaitTerminationAfterShutdown(ExecutorService threadPool) { threadPool.shutdown(); try { if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) { threadPool.shutdownNow(); } } catch (InterruptedException ex) { threadPool.shutdownNow(); Thread.currentThread().interrupt(); } }

3. Menggunakan CountDownLatch

Seterusnya, mari kita lihat pendekatan lain untuk menyelesaikan masalah ini - menggunakan CountDownLatch untuk memberi isyarat penyelesaian tugas.

Kami dapat menginisialisasi dengan nilai yang mewakili berapa kali ia dapat dikurangkan sebelum semua utas, yang memanggil kaedah menunggu () , diberitahu.

Sebagai contoh, jika kita memerlukan utas semasa untuk menunggu utas N lain untuk menyelesaikan pelaksanaannya, kita dapat menginisialkan selak menggunakan N :

ExecutorService WORKER_THREAD_POOL = Executors.newFixedThreadPool(10); CountDownLatch latch = new CountDownLatch(2); for (int i = 0; i  { try { // ... latch.countDown(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); } // wait for the latch to be decremented by the two remaining threads latch.await();

4. Menggunakan invokeAll ()

Pendekatan pertama yang boleh kita gunakan untuk menjalankan utas adalah kaedah invokeAll () . Kaedah ini mengembalikan senarai objek Masa Depan setelah semua tugas selesai atau tamat waktu tamat .

Juga, kita mesti perhatikan bahawa urutan objek Future yang dikembalikan adalah sama dengan senarai objek Callable yang disediakan :

ExecutorService WORKER_THREAD_POOL = Executors.newFixedThreadPool(10); List
    
      callables = Arrays.asList( new DelayedCallable("fast thread", 100), new DelayedCallable("slow thread", 3000)); long startProcessingTime = System.currentTimeMillis(); List
     
       futures = WORKER_THREAD_POOL.invokeAll(callables); awaitTerminationAfterShutdown(WORKER_THREAD_POOL); long totalProcessingTime = System.currentTimeMillis() - startProcessingTime; assertTrue(totalProcessingTime >= 3000); String firstThreadResponse = futures.get(0).get(); assertTrue("fast thread".equals(firstThreadResponse)); String secondThreadResponse = futures.get(1).get(); assertTrue("slow thread".equals(secondThreadResponse));
     
    

5. Menggunakan ExecutorCompletionService

Pendekatan lain untuk menjalankan beberapa utas adalah dengan menggunakan ExecutorCompletionService. Ia menggunakan ExecutorService yang disediakan untuk melaksanakan tugas.

Satu perbezaan berbanding invokeAll () adalah urutan di mana Masa Depan, yang mewakili tugas yang dilaksanakan dikembalikan. ExecutorCompletionService menggunakan giliran untuk menyimpan hasil mengikut urutan mereka selesai , sementara invokeAll () mengembalikan senarai yang mempunyai urutan berurutan yang sama seperti yang dihasilkan oleh iterator untuk senarai tugas yang diberikan:

CompletionService service = new ExecutorCompletionService(WORKER_THREAD_POOL); List
    
      callables = Arrays.asList( new DelayedCallable("fast thread", 100), new DelayedCallable("slow thread", 3000)); for (Callable callable : callables) { service.submit(callable); } 
    

Hasilnya dapat diakses dengan menggunakan metode take () :

long startProcessingTime = System.currentTimeMillis(); Future future = service.take(); String firstThreadResponse = future.get(); long totalProcessingTime = System.currentTimeMillis() - startProcessingTime; assertTrue("First response should be from the fast thread", "fast thread".equals(firstThreadResponse)); assertTrue(totalProcessingTime >= 100 && totalProcessingTime = 3000 && totalProcessingTime < 4000); LOG.debug("Thread finished after: " + totalProcessingTime + " milliseconds"); awaitTerminationAfterShutdown(WORKER_THREAD_POOL);

6. Kesimpulannya

Bergantung pada kes penggunaan, kami mempunyai pelbagai pilihan untuk menunggu utas menyelesaikan pelaksanaannya.

A CountDownLatch berguna apabila kita memerlukan satu mekanisme untuk memberitahu satu atau lebih benang yang satu set operasi yang dilakukan oleh benang lain telah selesai.

ExecutorCompletionService berguna ketika kita perlu mengakses hasil tugas secepat mungkin dan pendekatan lain ketika kita ingin menunggu semua tugas berjalan selesai.

Kod sumber untuk artikel tersebut terdapat di GitHub.