Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Oppiskele Semafori ja Este | Korkean Tason Synkronointimekanismit
Quizzes & Challenges
Quizzes
Challenges
/
Monisäikeisyys Javassa

bookSemafori ja Este

Monisäikeisissä ohjelmissa on usein tarpeen hallita resurssien käyttöä tai synkronoida säikeiden suoritusta. Semaphore ja Barrier ovat korkean tason synkronointimekanismeja, jotka auttavat ratkaisemaan näitä haasteita.

Tänään tarkastelemme näitä mekanismeja peräkkäin ja ymmärrämme niiden erot. Aloitetaan Semaphore-mekanismilla.

Semaphore-luokka on toteutettu Javassa java.util.concurrent.Semaphore luokan avulla.

Rakentajat

Semaphore(int permits): Rakentaja, joka luo semaphore-olion määritellyllä määrällä oikeuksia. Oikeudet edustavat jaetun resurssin käyttöjen määrää.

Main.java

Main.java

copy
1
Semaphore semaphore = new Semaphore(20);

Semaphore(int permits, boolean fair): Konstruktori, joka tarjoaa saapumisjärjestyksen mukaisen käsittelyn.

Main.java

Main.java

copy
1
Semaphore semaphore = new Semaphore(20, true);

Jos fair on asetettu true-arvoon, semaphore myöntää käyttöoikeudet saapumisjärjestyksessä (FIFO), mikä auttaa välttämään nälkiintymistä. Oletus - false.

Päämenetelmät

acquire() metodi pyytää yhtä käyttöoikeutta. Jos käyttöoikeus on saatavilla, se myönnetään välittömästi; muussa tapauksessa säie estetään, kunnes käyttöoikeus vapautuu. Kun tehtävä on suoritettu, release() metodia käytetään vapauttamaan käyttöoikeus, palauttaen sen semaphore:lle. Jos muut säikeet odottivat käyttöoikeutta, yksi niistä vapautetaan.

Kuvittele pysäköintialue, jossa on rajoitettu määrä paikkoja. semaphore toimii valvojana, joka seuraa vapaiden paikkojen määrää ja estää pääsyn, kun alue on täynnä.

Main.java

Main.java

copy
1234567891011121314151617181920212223242526272829303132
package com.example; import java.util.concurrent.Semaphore; public class Main { private final Semaphore semaphore; public Main(int slots) { semaphore = new Semaphore(slots); } public void parkCar() { try { semaphore.acquire(); // Request a parking spot System.out.println("Car parked. Available slots: " + semaphore.availablePermits()); Thread.sleep(2000); // Simulate parking time } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { semaphore.release(); // Release the parking spot System.out.println("Car left. Available slots: " + semaphore.availablePermits()); } } public static void main(String[] args) { Main parking = new Main(3); // Parking lot with 3 spots for (int i = 0; i < 5; i++) { new Thread(parking::parkCar).start(); } } }

Voit myös selvittää, kuinka monta lupaa on tällä hetkellä saatavilla Semaphore-oliolla käyttämällä int availablePermits() -metodia. Voit myös yrittää hankkia luvan käyttämällä boolean tryAcquire() -metodia, joka palauttaa true, jos lupa saatiin, ja false, jos ei saatu.

Main.java

Main.java

copy
1234567891011121314151617181920212223242526272829303132333435363738394041
package com.example; import java.util.concurrent.Semaphore; public class Main { // Define the maximum number of permits available private static final int MAX_PERMITS = 3; private static Semaphore semaphore = new Semaphore(MAX_PERMITS); public static void main(String[] args) { // Create and start 5 worker threads for (int i = 1; i <= 5; i++) { new Thread(new Worker(), "Worker-" + i).start(); } } static class Worker implements Runnable { @Override public void run() { String name = Thread.currentThread().getName(); System.out.println(name + " trying to acquire a permit..."); // Try to acquire a permit if (semaphore.tryAcquire()) { try { System.out.println(name + " acquired a permit! Available permits: " + semaphore.availablePermits()); Thread.sleep(1000); // Simulate work } catch (InterruptedException e) { e.printStackTrace(); } finally { // Release the permit after the work is done semaphore.release(); System.out.println(name + " released a permit. Available permits: " + semaphore.availablePermits()); } } else { // Inform if the permit could not be acquired System.out.println(name + " could not acquire a permit. Available permits: " + semaphore.availablePermits()); } } } }
Note
Huomio

Toisin sanoen, Semaphore on hyödyllinen, kun täytyy tarjota rajoitettu samanaikainen pääsy tiettyyn koodisegmenttiin. Ainoa haittapuoli on mahdollinen deadlock, jos säikeet estetään väärässä järjestyksessä.

Seuraavaksi siirrytään toiseen synkronointimekanismiin, joka on vieläkin helpompi käyttää, mutta on täysin hyödyllinen tarpeisiisi.

CyclicBarrier

Barrier Java-kielessä on toteutettu luokalla java.util.concurrent.CyclicBarrier. Tärkeimmät CyclicBarrier-luokan metodit ovat:

CyclicBarrier-rakentajat

CyclicBarrier(int parties): Rakentaja, joka luo esteen, joka estää säikeet kunnes tietty määrä säikeitä (parties) on saapunut.

Main.java

Main.java

copy
1
CyclicBarrier cyclicBarrier = new CyclicBarrier(10);

CyclicBarrier(int parties, Runnable barrierAction): Konstruktori, joka luo esteen annetulla osallistujamäärällä ja toiminnolla (barrierAction), joka suoritetaan, kun kaikki osallistujat saapuvat esteelle.

Main.java

Main.java

copy
1234567
Runnable task = () -> { // This task will be executed when all parties have reached the barrier System.out.println("Hello))"); }; // Create a `CyclicBarrier` for 10 parties with a barrier action CyclicBarrier cyclicBarrier = new CyclicBarrier(10, task);

CyclicBarrier-luokan metodit

Päämetodi await(), jota käytetään esteenä eikä anna säikeen jatkaa ennen kuin kaikki säikeet saavuttavat tämän metodin. Palauttaa järjestysnumeron, joka ilmaisee osallistujien saapumisjärjestyksen.

Main.java

Main.java

copy
1234567891011121314151617181920212223242526272829303132333435363738394041424344
package com.example; import java.util.concurrent.CyclicBarrier; public class Main { public static void main(String[] args) { // Create a `CyclicBarrier` for 3 parties with a barrier action CyclicBarrier barrier = new CyclicBarrier(3, () -> { System.out.println("All parties have reached the barrier. Barrier action executed."); }); // Create and start 3 worker threads for (int i = 1; i <= 3; i++) { new Thread(new Worker(barrier), "Worker-" + i).start(); } } static class Worker implements Runnable { private CyclicBarrier barrier; Worker(CyclicBarrier barrier) { this.barrier = barrier; } @Override public void run() { String name = Thread.currentThread().getName(); System.out.println(name + " is working..."); try { // Simulate work Thread.sleep((int) (Math.random() * 1000)); System.out.println(name + " is waiting at the barrier."); barrier.await(); // Wait at the barrier // This code will execute after all parties have reached the barrier System.out.println(name + " has crossed the barrier."); } catch (Exception e) { e.printStackTrace(); } } } }

On mahdollista, että kaikki säikeet eivät saavu esteelle, jolloin ohjelma jää odottamaan. Tätä varten käytetään int await(long timeout, TimeUnit unit) metodia, joka on samanlainen kuin await(), mutta siinä on aikakatkaisu. Jos aikakatkaisu umpeutuu ennen kuin kaikki osallistujat saapuvat, metodi heittää poikkeuksen TimeoutException.

Voit myös selvittää osallistujien määrän, joka vaaditaan esteen täyttymiseen int getParties() sekä vastaavan metodin int getNumberWaiting(), joka palauttaa tällä hetkellä estettä odottavien osallistujien määrän.

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
package com.example; import java.util.concurrent.CyclicBarrier; public class Main { public static void main(String[] args) { // Create a `CyclicBarrier` for 3 parties with a barrier action CyclicBarrier barrier = new CyclicBarrier(3, () -> { System.out.println("All parties have reached the barrier. Barrier action executed."); }); System.out.println("Total number of parties required to complete the barrier: " + barrier.getParties()); // Create and start 3 worker threads for (int i = 1; i <= 3; i++) { new Thread(new Worker(barrier), "Worker-" + i).start(); } } static class Worker implements Runnable { private CyclicBarrier barrier; Worker(CyclicBarrier barrier) { this.barrier = barrier; } @Override public void run() { String name = Thread.currentThread().getName(); System.out.println(name + " is working..."); try { // Simulate work Thread.sleep((int) (Math.random() * 1000)); System.out.println(name + " is waiting at the barrier."); barrier.await(); // Wait at the barrier // This code will execute after all parties have reached the barrier System.out.println(name + " has crossed the barrier."); } catch (Exception e) { e.printStackTrace(); } // Print the number of participants currently waiting at the barrier System.out.println("Number of participants currently waiting at the barrier: " + barrier.getNumberWaiting()); } } }

On myös mahdollista tarkistaa, onko barrier rikottu, jos jokin säikeistä keskeytetään tai odotusaika umpeutuu, käyttämällä boolean isBroken() metodia. Jos se on rikottu, voit käyttää void reset() metodia, joka palauttaa barrierin alkuperäiseen tilaansa.

Main.java

Main.java

copy
12345
// Check if the barrier is broken and reset it if necessary if (barrier.isBroken()) { System.out.println("Barrier is broken. Resetting the barrier."); barrier.reset(); }
Note
Huomio

On otettava huomioon, että jokin säie ei välttämättä saavuta estettä esimerkiksi virheen tai muiden syiden vuoksi, jolloin on selvää, että este ei päästä läpi niitä säikeitä, jotka odottavat tällä hetkellä esteellä.

Oliko kaikki selvää?

Miten voimme parantaa sitä?

Kiitos palautteestasi!

Osio 3. Luku 3

Kysy tekoälyä

expand

Kysy tekoälyä

ChatGPT

Kysy mitä tahansa tai kokeile jotakin ehdotetuista kysymyksistä aloittaaksesi keskustelumme

Awesome!

Completion rate improved to 3.33

bookSemafori ja Este

Pyyhkäise näyttääksesi valikon

Monisäikeisissä ohjelmissa on usein tarpeen hallita resurssien käyttöä tai synkronoida säikeiden suoritusta. Semaphore ja Barrier ovat korkean tason synkronointimekanismeja, jotka auttavat ratkaisemaan näitä haasteita.

Tänään tarkastelemme näitä mekanismeja peräkkäin ja ymmärrämme niiden erot. Aloitetaan Semaphore-mekanismilla.

Semaphore-luokka on toteutettu Javassa java.util.concurrent.Semaphore luokan avulla.

Rakentajat

Semaphore(int permits): Rakentaja, joka luo semaphore-olion määritellyllä määrällä oikeuksia. Oikeudet edustavat jaetun resurssin käyttöjen määrää.

Main.java

Main.java

copy
1
Semaphore semaphore = new Semaphore(20);

Semaphore(int permits, boolean fair): Konstruktori, joka tarjoaa saapumisjärjestyksen mukaisen käsittelyn.

Main.java

Main.java

copy
1
Semaphore semaphore = new Semaphore(20, true);

Jos fair on asetettu true-arvoon, semaphore myöntää käyttöoikeudet saapumisjärjestyksessä (FIFO), mikä auttaa välttämään nälkiintymistä. Oletus - false.

Päämenetelmät

acquire() metodi pyytää yhtä käyttöoikeutta. Jos käyttöoikeus on saatavilla, se myönnetään välittömästi; muussa tapauksessa säie estetään, kunnes käyttöoikeus vapautuu. Kun tehtävä on suoritettu, release() metodia käytetään vapauttamaan käyttöoikeus, palauttaen sen semaphore:lle. Jos muut säikeet odottivat käyttöoikeutta, yksi niistä vapautetaan.

Kuvittele pysäköintialue, jossa on rajoitettu määrä paikkoja. semaphore toimii valvojana, joka seuraa vapaiden paikkojen määrää ja estää pääsyn, kun alue on täynnä.

Main.java

Main.java

copy
1234567891011121314151617181920212223242526272829303132
package com.example; import java.util.concurrent.Semaphore; public class Main { private final Semaphore semaphore; public Main(int slots) { semaphore = new Semaphore(slots); } public void parkCar() { try { semaphore.acquire(); // Request a parking spot System.out.println("Car parked. Available slots: " + semaphore.availablePermits()); Thread.sleep(2000); // Simulate parking time } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { semaphore.release(); // Release the parking spot System.out.println("Car left. Available slots: " + semaphore.availablePermits()); } } public static void main(String[] args) { Main parking = new Main(3); // Parking lot with 3 spots for (int i = 0; i < 5; i++) { new Thread(parking::parkCar).start(); } } }

Voit myös selvittää, kuinka monta lupaa on tällä hetkellä saatavilla Semaphore-oliolla käyttämällä int availablePermits() -metodia. Voit myös yrittää hankkia luvan käyttämällä boolean tryAcquire() -metodia, joka palauttaa true, jos lupa saatiin, ja false, jos ei saatu.

Main.java

Main.java

copy
1234567891011121314151617181920212223242526272829303132333435363738394041
package com.example; import java.util.concurrent.Semaphore; public class Main { // Define the maximum number of permits available private static final int MAX_PERMITS = 3; private static Semaphore semaphore = new Semaphore(MAX_PERMITS); public static void main(String[] args) { // Create and start 5 worker threads for (int i = 1; i <= 5; i++) { new Thread(new Worker(), "Worker-" + i).start(); } } static class Worker implements Runnable { @Override public void run() { String name = Thread.currentThread().getName(); System.out.println(name + " trying to acquire a permit..."); // Try to acquire a permit if (semaphore.tryAcquire()) { try { System.out.println(name + " acquired a permit! Available permits: " + semaphore.availablePermits()); Thread.sleep(1000); // Simulate work } catch (InterruptedException e) { e.printStackTrace(); } finally { // Release the permit after the work is done semaphore.release(); System.out.println(name + " released a permit. Available permits: " + semaphore.availablePermits()); } } else { // Inform if the permit could not be acquired System.out.println(name + " could not acquire a permit. Available permits: " + semaphore.availablePermits()); } } } }
Note
Huomio

Toisin sanoen, Semaphore on hyödyllinen, kun täytyy tarjota rajoitettu samanaikainen pääsy tiettyyn koodisegmenttiin. Ainoa haittapuoli on mahdollinen deadlock, jos säikeet estetään väärässä järjestyksessä.

Seuraavaksi siirrytään toiseen synkronointimekanismiin, joka on vieläkin helpompi käyttää, mutta on täysin hyödyllinen tarpeisiisi.

CyclicBarrier

Barrier Java-kielessä on toteutettu luokalla java.util.concurrent.CyclicBarrier. Tärkeimmät CyclicBarrier-luokan metodit ovat:

CyclicBarrier-rakentajat

CyclicBarrier(int parties): Rakentaja, joka luo esteen, joka estää säikeet kunnes tietty määrä säikeitä (parties) on saapunut.

Main.java

Main.java

copy
1
CyclicBarrier cyclicBarrier = new CyclicBarrier(10);

CyclicBarrier(int parties, Runnable barrierAction): Konstruktori, joka luo esteen annetulla osallistujamäärällä ja toiminnolla (barrierAction), joka suoritetaan, kun kaikki osallistujat saapuvat esteelle.

Main.java

Main.java

copy
1234567
Runnable task = () -> { // This task will be executed when all parties have reached the barrier System.out.println("Hello))"); }; // Create a `CyclicBarrier` for 10 parties with a barrier action CyclicBarrier cyclicBarrier = new CyclicBarrier(10, task);

CyclicBarrier-luokan metodit

Päämetodi await(), jota käytetään esteenä eikä anna säikeen jatkaa ennen kuin kaikki säikeet saavuttavat tämän metodin. Palauttaa järjestysnumeron, joka ilmaisee osallistujien saapumisjärjestyksen.

Main.java

Main.java

copy
1234567891011121314151617181920212223242526272829303132333435363738394041424344
package com.example; import java.util.concurrent.CyclicBarrier; public class Main { public static void main(String[] args) { // Create a `CyclicBarrier` for 3 parties with a barrier action CyclicBarrier barrier = new CyclicBarrier(3, () -> { System.out.println("All parties have reached the barrier. Barrier action executed."); }); // Create and start 3 worker threads for (int i = 1; i <= 3; i++) { new Thread(new Worker(barrier), "Worker-" + i).start(); } } static class Worker implements Runnable { private CyclicBarrier barrier; Worker(CyclicBarrier barrier) { this.barrier = barrier; } @Override public void run() { String name = Thread.currentThread().getName(); System.out.println(name + " is working..."); try { // Simulate work Thread.sleep((int) (Math.random() * 1000)); System.out.println(name + " is waiting at the barrier."); barrier.await(); // Wait at the barrier // This code will execute after all parties have reached the barrier System.out.println(name + " has crossed the barrier."); } catch (Exception e) { e.printStackTrace(); } } } }

On mahdollista, että kaikki säikeet eivät saavu esteelle, jolloin ohjelma jää odottamaan. Tätä varten käytetään int await(long timeout, TimeUnit unit) metodia, joka on samanlainen kuin await(), mutta siinä on aikakatkaisu. Jos aikakatkaisu umpeutuu ennen kuin kaikki osallistujat saapuvat, metodi heittää poikkeuksen TimeoutException.

Voit myös selvittää osallistujien määrän, joka vaaditaan esteen täyttymiseen int getParties() sekä vastaavan metodin int getNumberWaiting(), joka palauttaa tällä hetkellä estettä odottavien osallistujien määrän.

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
package com.example; import java.util.concurrent.CyclicBarrier; public class Main { public static void main(String[] args) { // Create a `CyclicBarrier` for 3 parties with a barrier action CyclicBarrier barrier = new CyclicBarrier(3, () -> { System.out.println("All parties have reached the barrier. Barrier action executed."); }); System.out.println("Total number of parties required to complete the barrier: " + barrier.getParties()); // Create and start 3 worker threads for (int i = 1; i <= 3; i++) { new Thread(new Worker(barrier), "Worker-" + i).start(); } } static class Worker implements Runnable { private CyclicBarrier barrier; Worker(CyclicBarrier barrier) { this.barrier = barrier; } @Override public void run() { String name = Thread.currentThread().getName(); System.out.println(name + " is working..."); try { // Simulate work Thread.sleep((int) (Math.random() * 1000)); System.out.println(name + " is waiting at the barrier."); barrier.await(); // Wait at the barrier // This code will execute after all parties have reached the barrier System.out.println(name + " has crossed the barrier."); } catch (Exception e) { e.printStackTrace(); } // Print the number of participants currently waiting at the barrier System.out.println("Number of participants currently waiting at the barrier: " + barrier.getNumberWaiting()); } } }

On myös mahdollista tarkistaa, onko barrier rikottu, jos jokin säikeistä keskeytetään tai odotusaika umpeutuu, käyttämällä boolean isBroken() metodia. Jos se on rikottu, voit käyttää void reset() metodia, joka palauttaa barrierin alkuperäiseen tilaansa.

Main.java

Main.java

copy
12345
// Check if the barrier is broken and reset it if necessary if (barrier.isBroken()) { System.out.println("Barrier is broken. Resetting the barrier."); barrier.reset(); }
Note
Huomio

On otettava huomioon, että jokin säie ei välttämättä saavuta estettä esimerkiksi virheen tai muiden syiden vuoksi, jolloin on selvää, että este ei päästä läpi niitä säikeitä, jotka odottavat tällä hetkellä esteellä.

Oliko kaikki selvää?

Miten voimme parantaa sitä?

Kiitos palautteestasi!

Osio 3. Luku 3
some-alt