Suorittajat ja Säieallas
Olemme jo tarkastelleet useita mekanismeja monisäikeisyyden tukemiseen, ja Executors on yksi niistä!
Mitä ovat Executors ja säiepoolit?
Executors on mekanismi, joka tarjoaa korkean tason abstraktioita säikeiden käsittelyyn. Sen avulla voidaan luoda ja hallita säiepoolia, joka koostuu joukosta valmiiksi olemassa olevia säikeitä, jotka ovat valmiita suorittamaan tehtäviä. Sen sijaan, että jokaiselle tehtävälle luotaisiin uusi säie, tehtävät lähetetään pooliin, jossa niiden suoritus jakautuu säikeiden kesken.
Mikä sitten on säiepooli? Se on joukko valmiiksi olemassa olevia säikeitä, jotka ovat valmiita suorittamaan tehtäviä. Käyttämällä säiepoolia vältetään säikeiden luomisen ja tuhoamisen aiheuttama ylimääräinen kuormitus, sillä samoja säikeitä voidaan käyttää useisiin tehtäviin.
Jos tehtäviä on enemmän kuin säikeitä, tehtävät odottavat Task Queue -jonossa. Jonosta otettu tehtävä käsitellään vapaalla säikeellä säiepoolista, ja kun tehtävä on suoritettu, säie ottaa uuden tehtävän jonosta. Kun kaikki jonon tehtävät on suoritettu, säikeet pysyvät aktiivisina ja odottavat uusia tehtäviä.
Esimerkki Elävästä Elämästä
Kuvittele ravintola, jossa kokit (säikeet) valmistavat tilauksia (tehtäviä). Sen sijaan, että jokaista tilausta varten palkattaisiin uusi kokki, ravintolassa on rajoitettu määrä kokkeja, jotka käsittelevät tilauksia niiden saapuessa. Kun yksi kokki saa tilauksen valmiiksi, hän ottaa seuraavan, mikä auttaa käyttämään ravintolan resursseja tehokkaasti.
Main-metodi
newFixedThreadPool(int n): Luo säiliön, jossa on kiinteä määrä säikeitä, määrä n.
Main.java
1ExecutorService executorService = Executors.newFixedThreadPool(20);
newCachedThreadPool(): Luo säiliön, joka voi luoda uusia säikeitä tarpeen mukaan, mutta käyttää uudelleen olemassa olevia säikeitä, jos niitä on saatavilla.
Main.java
1ExecutorService executorService = Executors.newCachedThreadPool();
newSingleThreadExecutor(): Luo yhden säikeen säiepoolin, joka varmistaa, että tehtävät suoritetaan peräkkäin, eli yksi kerrallaan. Hyödyllinen tehtäville, jotka on suoritettava tarkassa järjestyksessä.
Main.java
1ExecutorService executorService = Executors.newSingleThreadExecutor();
Kaikissa esimerkeissä Executors-luokan metodit palauttavat ExecutorService-rajapinnan toteutuksen, jota käytetään säikeiden hallintaan.
ExecutorService tarjoaa menetelmiä säiepoolin hallintaan. Esimerkiksi submit(Runnable task) ottaa tehtävän Runnable-oliona ja asettaa sen jonoon suoritettavaksi. Se palauttaa Future-olion, jonka avulla voidaan tarkistaa tehtävän tila ja saada tulos, jos tehtävä tuottaa tuloksen.
Main.java
12345678910111213141516171819202122232425262728293031323334package com.example; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Main { public static void main(String[] args) { // Create a thread pool with 5 threads ExecutorService executor = Executors.newFixedThreadPool(5); // Define the task to be executed Runnable task = () -> { System.out.println("Task is running: " + Thread.currentThread().getName()); try { Thread.sleep(2000); // Simulate some work } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("Task completed: " + Thread.currentThread().getName()); }; // Submit the task for execution and get a `Future` Future<?> future = executor.submit(task); // Check if the task is done System.out.println("Is task done? " + future.isDone()); // You can use `future` to check the status of the task or wait for its completion // Example: future.get() - blocks until the task is completed (not used in this example) // Initiate an orderly shutdown of the executor service executor.shutdown(); } }
Metodi shutdown() käynnistää säiepoolin hallitun alasajon. Se ei ota vastaan uusia tehtäviä, mutta suorittaa nykyiset tehtävät loppuun. Kun tämä metodi on kutsuttu, poolia ei voi käynnistää uudelleen.
Metodi awaitTermination(long timeout, TimeUnit unit) odottaa, että kaikki tehtävät poolissa valmistuvat annetun ajan kuluessa. Tämä on estävä odotus, jonka avulla voidaan varmistaa, että kaikki tehtävät on suoritettu ennen poolin lopullista sulkemista.
Lisäksi emme ole maininneet tärkeintä rajapintaa, joka auttaa seuraamaan säikeen tilaa, eli Future-rajapintaa. submit()-rajapinnan ExecutorService-metodi palauttaa Future-rajapinnan toteutuksen.
Jos haluat saada säikeen suorituksen tuloksen, voit käyttää get()-metodia. Jos säie toteuttaa Runnable-rajapinnan, get()-metodi ei palauta mitään, mutta jos se toteuttaa Callable<T>, se palauttaa tyypin T.
Main.java
12345678910111213141516171819202122232425262728293031package com.example; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Main { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(3); // Callable task that returns a result Callable<String> task = () -> { Thread.sleep(1000); // Simulate some work return "Task result"; }; Future<String> future = executor.submit(task); try { // Get the result of the task String result = future.get(); System.out.println("Task completed with result: " + result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } executor.shutdown(); } }
Voit myös käyttää cancel(boolean mayInterruptIfRunning) metodia yrittääksesi peruuttaa tehtävän suorituksen. Jos tehtävää ei ole vielä aloitettu, se peruutetaan. Jos tehtävä on jo käynnissä, se voidaan keskeyttää mayInterruptIfRunning-lipun perusteella.
true: Jos tehtävä on käynnissä, se keskeytetään kutsumalla Thread.interrupt() suorittavassa säikeessä.
false: Jos tehtävä on käynnissä, sitä ei keskeytetä, eikä peruutusyrityksellä ole vaikutusta parhaillaan käynnissä olevaan tehtävään.
Hyvin ja 2 menetelmää, joiden tarkoitus on helposti ymmärrettävissä:
isCancelled(): Tarkistaa, onko tehtävä peruutettu;isDone(): Tarkistaa, onko tehtävä valmistunut.
Käyttöesimerkki
Säiepoolin koko riippuu suoritettavien tehtävien luonteesta. Yleensä säiepoolin kokoa ei tulisi kovakoodata, vaan sen tulisi olla muokattavissa. Optimaalinen koko määritetään seuraamalla suoritettavien tehtävien läpimenotehoa.
On tehokkainta käyttää määrää threads = processor cores. Tämä voidaan nähdä koodissa käyttämällä Runtime.getRuntime().availableProcessors().
Main.java
1int availableProcessors = Runtime.getRuntime().availableProcessors();
Eroja säikeiden luomisen ja ExecutorService:n käytön välillä
Tärkeimmät erot suoran säikeiden luonnin ja ExecutorService:n käytön välillä ovat käytännöllisyys ja resurssien hallinta. Säikeiden manuaalinen luominen vaatii jokaisen säikeen yksittäistä hallintaa, mikä monimutkaistaa koodia ja ylläpitoa.
ExecutorService helpottaa hallintaa käyttämällä säieallasta, mikä yksinkertaistaa tehtävien käsittelyä. Lisäksi manuaalinen säikeiden luonti voi johtaa suureen resurssien kulutukseen, kun taas ExecutorService mahdollistaa säiealtaan koon mukauttamisen.
Kiitos palautteestasi!
Kysy tekoälyä
Kysy tekoälyä
Kysy mitä tahansa tai kokeile jotakin ehdotetuista kysymyksistä aloittaaksesi keskustelumme
Awesome!
Completion rate improved to 3.33
Suorittajat ja Säieallas
Pyyhkäise näyttääksesi valikon
Olemme jo tarkastelleet useita mekanismeja monisäikeisyyden tukemiseen, ja Executors on yksi niistä!
Mitä ovat Executors ja säiepoolit?
Executors on mekanismi, joka tarjoaa korkean tason abstraktioita säikeiden käsittelyyn. Sen avulla voidaan luoda ja hallita säiepoolia, joka koostuu joukosta valmiiksi olemassa olevia säikeitä, jotka ovat valmiita suorittamaan tehtäviä. Sen sijaan, että jokaiselle tehtävälle luotaisiin uusi säie, tehtävät lähetetään pooliin, jossa niiden suoritus jakautuu säikeiden kesken.
Mikä sitten on säiepooli? Se on joukko valmiiksi olemassa olevia säikeitä, jotka ovat valmiita suorittamaan tehtäviä. Käyttämällä säiepoolia vältetään säikeiden luomisen ja tuhoamisen aiheuttama ylimääräinen kuormitus, sillä samoja säikeitä voidaan käyttää useisiin tehtäviin.
Jos tehtäviä on enemmän kuin säikeitä, tehtävät odottavat Task Queue -jonossa. Jonosta otettu tehtävä käsitellään vapaalla säikeellä säiepoolista, ja kun tehtävä on suoritettu, säie ottaa uuden tehtävän jonosta. Kun kaikki jonon tehtävät on suoritettu, säikeet pysyvät aktiivisina ja odottavat uusia tehtäviä.
Esimerkki Elävästä Elämästä
Kuvittele ravintola, jossa kokit (säikeet) valmistavat tilauksia (tehtäviä). Sen sijaan, että jokaista tilausta varten palkattaisiin uusi kokki, ravintolassa on rajoitettu määrä kokkeja, jotka käsittelevät tilauksia niiden saapuessa. Kun yksi kokki saa tilauksen valmiiksi, hän ottaa seuraavan, mikä auttaa käyttämään ravintolan resursseja tehokkaasti.
Main-metodi
newFixedThreadPool(int n): Luo säiliön, jossa on kiinteä määrä säikeitä, määrä n.
Main.java
1ExecutorService executorService = Executors.newFixedThreadPool(20);
newCachedThreadPool(): Luo säiliön, joka voi luoda uusia säikeitä tarpeen mukaan, mutta käyttää uudelleen olemassa olevia säikeitä, jos niitä on saatavilla.
Main.java
1ExecutorService executorService = Executors.newCachedThreadPool();
newSingleThreadExecutor(): Luo yhden säikeen säiepoolin, joka varmistaa, että tehtävät suoritetaan peräkkäin, eli yksi kerrallaan. Hyödyllinen tehtäville, jotka on suoritettava tarkassa järjestyksessä.
Main.java
1ExecutorService executorService = Executors.newSingleThreadExecutor();
Kaikissa esimerkeissä Executors-luokan metodit palauttavat ExecutorService-rajapinnan toteutuksen, jota käytetään säikeiden hallintaan.
ExecutorService tarjoaa menetelmiä säiepoolin hallintaan. Esimerkiksi submit(Runnable task) ottaa tehtävän Runnable-oliona ja asettaa sen jonoon suoritettavaksi. Se palauttaa Future-olion, jonka avulla voidaan tarkistaa tehtävän tila ja saada tulos, jos tehtävä tuottaa tuloksen.
Main.java
12345678910111213141516171819202122232425262728293031323334package com.example; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Main { public static void main(String[] args) { // Create a thread pool with 5 threads ExecutorService executor = Executors.newFixedThreadPool(5); // Define the task to be executed Runnable task = () -> { System.out.println("Task is running: " + Thread.currentThread().getName()); try { Thread.sleep(2000); // Simulate some work } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("Task completed: " + Thread.currentThread().getName()); }; // Submit the task for execution and get a `Future` Future<?> future = executor.submit(task); // Check if the task is done System.out.println("Is task done? " + future.isDone()); // You can use `future` to check the status of the task or wait for its completion // Example: future.get() - blocks until the task is completed (not used in this example) // Initiate an orderly shutdown of the executor service executor.shutdown(); } }
Metodi shutdown() käynnistää säiepoolin hallitun alasajon. Se ei ota vastaan uusia tehtäviä, mutta suorittaa nykyiset tehtävät loppuun. Kun tämä metodi on kutsuttu, poolia ei voi käynnistää uudelleen.
Metodi awaitTermination(long timeout, TimeUnit unit) odottaa, että kaikki tehtävät poolissa valmistuvat annetun ajan kuluessa. Tämä on estävä odotus, jonka avulla voidaan varmistaa, että kaikki tehtävät on suoritettu ennen poolin lopullista sulkemista.
Lisäksi emme ole maininneet tärkeintä rajapintaa, joka auttaa seuraamaan säikeen tilaa, eli Future-rajapintaa. submit()-rajapinnan ExecutorService-metodi palauttaa Future-rajapinnan toteutuksen.
Jos haluat saada säikeen suorituksen tuloksen, voit käyttää get()-metodia. Jos säie toteuttaa Runnable-rajapinnan, get()-metodi ei palauta mitään, mutta jos se toteuttaa Callable<T>, se palauttaa tyypin T.
Main.java
12345678910111213141516171819202122232425262728293031package com.example; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Main { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(3); // Callable task that returns a result Callable<String> task = () -> { Thread.sleep(1000); // Simulate some work return "Task result"; }; Future<String> future = executor.submit(task); try { // Get the result of the task String result = future.get(); System.out.println("Task completed with result: " + result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } executor.shutdown(); } }
Voit myös käyttää cancel(boolean mayInterruptIfRunning) metodia yrittääksesi peruuttaa tehtävän suorituksen. Jos tehtävää ei ole vielä aloitettu, se peruutetaan. Jos tehtävä on jo käynnissä, se voidaan keskeyttää mayInterruptIfRunning-lipun perusteella.
true: Jos tehtävä on käynnissä, se keskeytetään kutsumalla Thread.interrupt() suorittavassa säikeessä.
false: Jos tehtävä on käynnissä, sitä ei keskeytetä, eikä peruutusyrityksellä ole vaikutusta parhaillaan käynnissä olevaan tehtävään.
Hyvin ja 2 menetelmää, joiden tarkoitus on helposti ymmärrettävissä:
isCancelled(): Tarkistaa, onko tehtävä peruutettu;isDone(): Tarkistaa, onko tehtävä valmistunut.
Käyttöesimerkki
Säiepoolin koko riippuu suoritettavien tehtävien luonteesta. Yleensä säiepoolin kokoa ei tulisi kovakoodata, vaan sen tulisi olla muokattavissa. Optimaalinen koko määritetään seuraamalla suoritettavien tehtävien läpimenotehoa.
On tehokkainta käyttää määrää threads = processor cores. Tämä voidaan nähdä koodissa käyttämällä Runtime.getRuntime().availableProcessors().
Main.java
1int availableProcessors = Runtime.getRuntime().availableProcessors();
Eroja säikeiden luomisen ja ExecutorService:n käytön välillä
Tärkeimmät erot suoran säikeiden luonnin ja ExecutorService:n käytön välillä ovat käytännöllisyys ja resurssien hallinta. Säikeiden manuaalinen luominen vaatii jokaisen säikeen yksittäistä hallintaa, mikä monimutkaistaa koodia ja ylläpitoa.
ExecutorService helpottaa hallintaa käyttämällä säieallasta, mikä yksinkertaistaa tehtävien käsittelyä. Lisäksi manuaalinen säikeiden luonti voi johtaa suureen resurssien kulutukseen, kun taas ExecutorService mahdollistaa säiealtaan koon mukauttamisen.
Kiitos palautteestasi!