Miért van szüksége az Executor felületre?
A Java 5 előtt az összes saját kódszál-kezelést be kellett írnia az alkalmazásba. Emellett létrehozva aúj témaAz objektum erőforrás-igényes művelet, és nincs értelme minden könnyű feladathoz új szálat létrehozni. És mivel ez a probléma abszolút minden többszálas alkalmazások fejlesztője számára ismerős, úgy döntöttek, hogy végrehajtják a Java-ban ezt a funkciót Executor keretrendszerként.
Mi a nagy ötlet? Egyszerű: ahelyett, hogy minden új feladathoz új szálat hoznánk létre, a szálak egyfajta „tárhelyen” vannak tárolva, és amikor új feladat érkezik, új létrehozása helyett egy meglévő szálat kérünk le.
Ennek a keretrendszernek a fő felületei az Executor , az ExecutorService és a ScheduledExecutorService , amelyek mindegyike kiterjeszti az előző funkcionalitását.
Az Executor felület az alap interfész. Egyetlen void execute (Futtatható parancs) metódust deklarál , amelyet egy futtatható objektum valósít meg .
Az ExecutorService felület érdekesebb. Módszerei vannak a munka befejezésének kezelésére, valamint módszerek valamilyen eredmény visszaadására. Nézzük meg közelebbről a módszereit:
Módszer | Leírás |
---|---|
void shutdown(); | A metódus meghívása leállítja az ExecutorService-t . A már feldolgozásra leadott összes feladat elvégzésre kerül, de új feladatokat nem fogadunk el. |
Lista<Futtatható> shutdownNow(); |
A metódus meghívása leállítja az ExecutorService-t . A Thread.interrupt minden olyan feladat esetén meghívásra kerül, amelyet már feldolgozásra elküldtek. Ez a metódus a sorban álló feladatok listáját adja vissza. A metódus nem várja meg az összes olyan feladat befejezését, amely a metódus meghívásakor "folyamatban van". Figyelmeztetés: Ennek a metódusnak a meghívásával források szivároghatnak ki. |
logikai isShutdown(); | Ellenőrzi, hogy az ExecutorService le van-e állítva. |
logikai isTerminated(); | Igaz értéket ad vissza, ha az ExecutorService leállítása után minden feladat befejeződött . Amíg a shutdown() vagy shutdownNow() meg nem hívjuk, mindig false értéket ad vissza . |
boolean awaitTermination (hosszú időtúllépés, TimeUnit egység) InterruptedExceptiont dob; |
A shutdown() metódus meghívása után ez a metódus blokkolja azt a szálat, amelyen meghívásra került, amíg a következő feltételek egyike nem teljesül:
Igaz értéket ad vissza , ha minden feladat befejeződött, és hamis értéket , ha az időtúllépés letelik a befejezés előtt. |
<T> Future<T> submit(Hívható<T> feladat); |
Hozzáad egy hívható feladatot az ExecutorService- hez , és visszaad egy objektumot, amely megvalósítja a Future felületet. A <T> az átadott feladat eredményének típusa. |
<T> Future<T> submit(Futtatható feladat, T eredmény); |
Hozzáad egy futtatható feladatot az ExecutorService- hez , és visszaad egy objektumot, amely megvalósítja a Future felületet. A T eredmény paraméter az, amit a get() metódus hívása visszaad az eredményülJövő tárgya. |
Future<?> submit(Futtatható feladat); |
Hozzáad egy futtatható feladatot az ExecutorService- hez , és visszaad egy objektumot, amely megvalósítja a Future felületet. Ha a kapott Future objektumon meghívjuk a get() metódust , akkor nullát kapunk. |
<T> List<Jövő<T>> invokeAll(Gyűjtemény<? kiterjeszti a Hívható<T>> feladatokat) InterruptedException; |
A hívható feladatok listáját átadja az ExecutorService- nek . Visszaadja a Futures listát, amelyből megkaphatjuk a munka eredményét. Ez a lista akkor jelenik meg, amikor az összes beküldött feladatot befejezte. Ha a feladatgyűjtemény módosul a metódus futása közben, akkor ennek a metódusnak az eredménye nincs meghatározva. |
<T> Lista<Jövő<T>> invokeAll(Gyűjtemény<? kiterjeszti a hívható<T>> feladatokat, hosszú időtúllépés, TimeUnit egység) InterruptedException; |
A hívható feladatok listáját átadja az ExecutorService- nek . Visszaadja a Futures listát, amelyből megkaphatjuk a munka eredményét. Ez a lista akkor jelenik meg, amikor az összes átadott feladat befejeződött, vagy a metódusnak átadott időtúllépés lejárta után, attól függően, hogy melyik következik be előbb. Ha az időkorlát letelik, a befejezetlen feladatok törlődnek. Megjegyzés: Lehetséges, hogy a törölt feladat futása nem áll le (a példában ezt a mellékhatást fogjuk látni). Ha a feladatgyűjtemény módosul a metódus futása közben, akkor ennek a metódusnak az eredménye nincs meghatározva. |
<T> T invokeAny(Collection<? expands Callable<T>> task) dob InterruptedException, ExecutionException; |
A hívható feladatok listáját átadja az ExecutorService- nek . Az egyik olyan feladat eredményét adja vissza (ha van), amelyet kivétel nélkül teljesítettek (ha van ilyen). Ha a feladatgyűjtemény módosul a metódus futása közben, akkor ennek a metódusnak az eredménye nincs meghatározva. |
<T> T invokeAny(Gyűjtemény<? kiterjeszti a Hívható<T>> feladatokat, hosszú időtúllépés, TimeUnit egység) InterruptedException, ExecutionException, TimeoutException; |
A hívható feladatok listáját átadja az ExecutorService- nek . Az egyik olyan feladat eredményét adja vissza (ha van), amely kivétel nélkül fejeződött be, mielőtt a metódusnak adott időtúllépés lejárt volna. Ha a feladatgyűjtemény módosul a metódus futása közben, akkor ennek a metódusnak az eredménye nincs meghatározva. |
Nézzünk egy kis példát az ExecutorService szolgáltatással való együttműködésre .
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;
}
}
}
Kimenet:
kész
Határidős fogadott
alvás 1: alvás megszakítva
alvás 1: alvás megszakítva
kész
kész igaz
igaz
Minden feladat 5 másodpercig fut. Létrehoztunk egy készletet két szál számára, így a kimenet első két sora teljesen logikus.
Hat másodperccel a program indítása után az invokeAll metódus időtúllépése következik be, és az eredmény a Futures listájaként jelenik meg . Ez látható a Futures kapott kimeneti karakterláncból .
Az első két feladat elvégzése után újabb kettő kezdődik. De mivel az invokeAll metódusban beállított időkorlát letelik, ennek a két feladatnak nincs ideje befejezni. „Mégse” parancsot kapnak . Ez az oka annak, hogy a kimenetnek két sora van alvás 1-el: alvás megszakítva .
És akkor még két sort láthat a done mellett . Ez az a mellékhatás, amelyet az invokeAll metódus leírásakor említettem .
Az ötödik, egyben utolsó feladat el sem indul, így a kimenetben nem látunk róla semmit.
Az utolsó két sor az isShutdown és isTerminated metódusok meghívásának eredménye .
Az is érdekes, hogy ezt a példát hibakeresési módban futtatjuk, és megnézzük a feladat állapotát az időkorlát letelte után (állítsunk be egy töréspontot a sorban az executorService.shutdown(); ) paranccsal:
Azt látjuk, hogy két feladat normálisan befejeződött , és három feladat „törölve” lett .
ScheduledExecutorService
A végrehajtókról szóló vitánk befejezéseként vessünk egy pillantást a ScheduledExecutorService oldalra .
4 módszere van:
Módszer | Leírás |
---|---|
public ScheduledFuture<?> ütemterv (Futtatható parancs, hosszú késleltetés, TimeUnit egység); | Ütemezi az átadott futtatható feladat egyszeri futtatását az argumentumként megadott késleltetés után. |
public <V> ScheduledFuture<V> menetrend(Hívható<V> hívható, hosszú késleltetés, Időegység egység); | Ütemezi az átadott hívható feladat egyszeri futását az argumentumként megadott késleltetés után. |
public ScheduledFuture<?> scheduleAtFixedRate(Futtatható parancs, hosszú kezdetiKésleltetés, hosszú periódus, Időegység egység); | Ütemezi az átadott feladat időszakos végrehajtását, amely először a inicialDelay után kerül végrehajtásra, és minden további futtatás a period után kezdődik . |
public ScheduledFuture<?> scheduleWithFixedDelay(Futtatható parancs, hosszú kezdeti késleltetés, hosszú késleltetés, időegység egység); | Ütemezi az átadott feladat időszakos végrehajtását, amely először az inicialDelay után kerül végrehajtásra, és minden további futás késleltetés után kezdődik (az előző futás befejezése és az aktuális indítása közötti időszak). |
GO TO FULL VERSION