Napa sampeyan mbutuhake antarmuka Executor?

Sadurunge Java 5, sampeyan kudu nulis kabeh manajemen thread kode dhewe ing aplikasi sampeyan. Kajaba iku, nggawe aUtas anyarobyek punika operasi sumber-intensif, lan iku ora nggawe pangertèn kanggo nggawe thread anyar kanggo saben tugas entheng. Lan amarga masalah iki wis dikenal kanggo saben pangembang aplikasi multi-Utas, padha mutusaké kanggo nggawa fungsi iki menyang Jawa minangka framework Executor .

Apa ide gedhe? Iku prasaja: tinimbang nggawe utas anyar kanggo saben tugas anyar, utas disimpen ing jenis "panyimpenan", lan nalika tugas anyar teka, kita njupuk thread sing wis ana tinimbang nggawe sing anyar.

Antarmuka utama kerangka iki yaiku Executor , ExecutorService lan ScheduledExecutorService , sing saben-saben ngluwihi fungsi sadurunge.

Antarmuka Executor minangka antarmuka dhasar. Iki nyatakake metode eksekusi void (perintah Runnable) sing ditindakake dening obyek Runnable .

Antarmuka ExecutorService luwih menarik. Wis cara kanggo ngatur completion saka karya, uga cara kanggo bali sawetara jinis asil. Ayo dipikirake kanthi luwih rinci babagan metode kasebut:

Metode Katrangan
void shutdown (); Nelpon cara iki mungkasi ExecutorService . Kabeh tugas sing wis dikirim kanggo diproses bakal rampung, nanging tugas anyar ora bakal ditampa.
List<Runnable> shutdownNow();

Nelpon cara iki mungkasi ExecutorService . Thread.interrupt bakal ditelpon kanggo kabeh tugas sing wis dikirim kanggo diproses. Cara iki ngasilake dhaptar tugas sing antri.

Cara kasebut ora ngenteni rampung kabeh tugas sing "in progress" nalika metode kasebut diarani.

Pènget: Nelpon cara iki bisa bocor sumber daya.

boolean isShutdown(); Priksa manawa ExecutorService mandheg.
boolean isTerminated(); Ngasilake bener yen kabeh tugas wis rampung sawise mati ExecutorService . Nganti shutdown () utawa shutdownNow () diarani, bakal tansah bali false .
boolean awaitTermination (wektu entek dawa, unit TimeUnit) mbuwang InterruptedException;

Sawise metode shutdown () diarani, metode iki mblokir thread sing diarani, nganti salah siji saka kahanan ing ngisor iki bener:

  • kabeh tugas sing dijadwal wis rampung;
  • wektu entek liwati kanggo cara wis liwati;
  • thread saiki diselani.

Ngasilake bener yen kabeh tugas wis rampung, lan salah yen wektu entek liwati sadurunge mandap.

<T> Future<T> submit (Tugas<T> Callable);

Nambahake tugas Callable kanggo ExecutorService lan ngasilake obyek sing ngleksanakake antarmuka Future .

<T> minangka jinis asil saka tugas sing ditindakake.

<T> Future<T> submit(Tugas Runnable, T asil);

Nambahake tugas Runnable kanggo ExecutorService lan ngasilake obyek sing ngleksanakake antarmuka Future .

Parameter asil T apa bakal bali dening telpon kanggo njaluk () cara ing asilObjek mangsa ngarep.

Future<?> submit(Tugas Runnable);

Nambahake tugas Runnable kanggo ExecutorService lan ngasilake obyek sing ngleksanakake antarmuka Future .

Yen kita nelpon cara njaluk () ing obyek Future asil , banjur kita njaluk null.

<T> List<Future<T>> invokeAll(Koleksi<? ngluwihi Callable<T>> tugas) mbuwang InterruptedException;

Maringake dhaptar tugas sing Bisa Ditelpon menyang ExecutorService . Ngasilake dhaptar Futures saka ngendi kita bisa entuk asil karya. Dhaptar iki bali nalika kabeh tugas sing dikirim wis rampung.

Yen koleksi tugas diowahi nalika cara mlaku, asil saka metode iki ora ditemtokake.

<T> List<Future<T>> invokeAll(Koleksi<? ngluwihi Callable<T>> tugas, wektu entek dawa, unit TimeUnit) mbuwang InterruptedException;

Nglewati dhaptar tugas sing Bisa Ditelpon menyang ExecutorService . Ngasilake dhaptar Futures saka ngendi kita bisa entuk asil karya. Dhaptar iki bali nalika kabeh tugas liwati wis rampung, utawa sawise wektu entek liwati kanggo cara wis liwati, whichever rawuh dhisikan.

Yen wektu entek wis entek, tugas sing durung rampung dibatalake.

Cathetan: Sampeyan bisa uga yen tugas sing dibatalake ora bakal mandheg (kita bakal weruh efek sisih iki ing conto).

Yen koleksi tugas diowahi nalika cara mlaku, asil saka metode iki ora ditemtokake.

<T> T invokeAny(Koleksi<? ngluwihi Callable<T>> tugas) mbuwang InterruptedException, ExecutionException;

Nglewati dhaptar tugas sing Bisa Ditelpon menyang ExecutorService . Ngasilake asil salah sawijining tugas (yen ana) sing rampung tanpa mbuwang pangecualian (yen ana).

Yen koleksi tugas diowahi nalika cara mlaku, asil saka metode iki ora ditemtokake.

<T> T invokeAny(Koleksi<? ngluwihi Callable<T>> tugas, wektu entek dawa, unit TimeUnit) mbuwang InterruptedException, ExecutionException, TimeoutException;

Nglewati dhaptar tugas sing Bisa Ditelpon menyang ExecutorService . Ngasilake asil saka salah sawijining tugas (yen ana) sing wis rampung tanpa mbuwang pangecualian sadurunge wektu entek liwati kanggo cara wis liwati.

Yen koleksi tugas diowahi nalika cara mlaku, asil saka metode iki ora ditemtokake.

Ayo goleki conto cilik nggarap ExecutorService .


import java.util.List;
import java.util.concurrent.*;

public class ExecutorServiceTest {
   public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
//Create an ExecutorService for 2 threads
       java.util.concurrent.ExecutorService executorService = new ThreadPoolExecutor(2, 2, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));
// Create 5 tasks
       MyRunnable task1 = new MyRunnable();
       MyRunnable task2 = new MyRunnable();
       MyRunnable task3 = new MyRunnable();
       MyRunnable task4 = new MyRunnable();
       MyRunnable task5 = new MyRunnable();

       final List<MyRunnable> tasks = List.of(task1, task2, task3, task4, task5);
// Pass a list that contains the 5 tasks we created
       final List<Future<Void>> futures = executorService.invokeAll(tasks, 6, TimeUnit.SECONDS);
       System.out.println("Futures received");

// Stop the ExecutorService
       executorService.shutdown();

       try {
           TimeUnit.SECONDS.sleep(3);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }

       System.out.println(executorService.isShutdown());
       System.out.println(executorService.isTerminated());
   }

   public static class MyRunnable implements Callable<Void> {

       @Override
       public void call() {
// Add 2 delays. When the ExecutorService is stopped, we will see which delay is in progress when the attempt is made to stop execution of the task
           try {
               TimeUnit.SECONDS.sleep(3);
           } catch (InterruptedException e) {
               System.out.println("sleep 1: " + e.getMessage());
           }
           try {
               TimeUnit.SECONDS.sleep(2);
           } catch (InterruptedException e) {
               System.out.println("sleep 2: " + e.getMessage());
           }
           System.out.println("done");
           return null;
       }
   }
}

Output:

rampung
rampung
Future ditampa turu 1 :
turu diselani
turu 1: turu diselani
rampung
rampung
bener

Saben tugas mlaku 5 detik. Kita nggawe blumbang kanggo loro Utas, supaya loro baris pisanan output nggawe pangertèn sampurna.

Enem detik sawise program diwiwiti, metode invokeAll kaping metu lan asil bali minangka dhaptar Futures . Iki bisa dideleng saka output string Futures ditampa .

Sawise rong tugas pisanan rampung, loro maneh diwiwiti. Nanging amarga wektu entek sing disetel ing metode invokeAll wis entek, rong tugas iki ora duwe wektu kanggo ngrampungake. Dheweke nampa perintah "batal" . Mulane output duwe rong baris karo sleep 1: sleep interrupted .

Banjur sampeyan bisa ndeleng rong baris liyane kanthi rampung . Iki minangka efek samping sing dakkandhakake nalika nerangake metode invokeAll .

Tugas kaping lima lan pungkasan ora nate diwiwiti, mula kita ora bisa ndeleng apa-apa babagan output kasebut.

Rong baris pungkasan minangka asil nelpon metode isShutdown lan isTerminated .

Sampeyan uga menarik kanggo mbukak conto iki ing mode debug lan katon ing status tugas sawise wektu entek elapses (nyetel breakpoint ing baris karo executorService.shutdown (); ):

We ndeleng sing loro tugas Rampung biasane , lan telung tugas padha "Batal" .

ScheduledExecutorService

Kanggo ngrampungake diskusi para pelaksana, ayo deleng ing ScheduledExecutorService .

Ana 4 cara:

Metode Katrangan
public ScheduledFuture<?> jadwal(Runnable command, long delay, unit TimeUnit); Jadwal tugas Runnable liwati kanggo mbukak sapisan sawise wektu tundha kasebut minangka argumen.
public <V> ScheduledFuture<V> jadwal (Callable<V> callable, long delay, unit TimeUnit); Jadwal tugas Callable liwati kanggo mbukak sapisan sawise wektu tundha kasebut minangka argumen.
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, unit TimeUnit); Jadwal eksekusi periodik saka tugas liwati, kang bakal kaleksanan pisanan sawise initialDelay , lan saben roto sakteruse bakal diwiwiti sawise periode .
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, unit TimeUnit); Jadwal eksekusi periodik saka tugas liwati, kang bakal kaleksanan pisanan sawise initialDelay , lan saben roto sakteruse bakal diwiwiti sawise wektu tundha (periode antarane completion saka roto sadurunge lan wiwitan saiki).