Lukko ja Ehto
Javassa vakiomuotoinen synkronointi perustuu synchronized-avainsanaan ja sisäänrakennettuihin monitoriolioihin. Joissakin tapauksissa synchronized ei kuitenkaan riitä, erityisesti kun tarvitaan joustavampaa säikeiden hallintaa.
Yleiskuvaus
Lock-rajapinta ja Condition-rajapinta, jotka esiteltiin java.util.concurrent.locks -paketissa, tarjoavat kehittyneitä säikeiden hallinnan ominaisuuksia.
Tässä kuvassa näet, että ensimmäinen säie ottaa lukon käyttöön lock()-metodilla, ja tällä hetkellä toinen säie ei voi ottaa samaa lukkoa. Heti kun kaikki lukon sisällä oleva koodi on suoritettu, kutsutaan unlock()-metodia ja lukko vapautetaan. Vasta tämän jälkeen toinen säie voi ottaa lukon.
Ero
Ero näiden kahden rajapinnan välillä on, että Lock-toteutukset ovat korkean tason vaihtoehto synchronized-lohkolle, ja Condition-rajapinnan toteutukset ovat vaihtoehto notify()/wait()-metodeille. Molemmat rajapinnat kuuluvat java.util.concurrent.locks -pakettiin.
Käytännön esimerkkejä
Kuvittele, että hallinnoit jonotusjärjestelmää tapahtuman rekisteröintiä varten. Estääksesi ylivuodon ja varmistaaksesi asianmukaisen paikkajaon, sinun täytyy käyttää estomekanismeja ja ehtoja pitämään uusien rekisteröintien virta odottamassa, kunnes vapaa paikka tulee saataville.
ReentrantLock-luokka
ReentrantLock -luokka java.util.concurrent.locks -paketista on Lock -rajapinnan toteutus. Se tarjoaa toiminnallisuudet lukkojen eksplisiittiseen hallintaan.
ReentrantLock-luokan päämenetelmät:
lock(): Ottaa lukon haltuun;unlock(): Vapauttaa lukon;tryLock(): Yrittää ottaa lukon haltuun ja palauttaa true, jos onnistuu;tryLock(long timeout, TimeUnit unit): Yrittää ottaa lukon haltuun määrätyn ajan;newCondition(): Luo ehdon nykyiselleLock:lle.
Main.java
12345678910111213141516171819202122232425262728293031package com.example; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Main { // Creating a ReentrantLock object private final Lock lock = new ReentrantLock(); private int count = 0; // Method to increment the `count` variable public void increment() { lock.lock(); // Acquiring the `lock` try { count++; System.out.println("Count incremented to: " + count); } finally { lock.unlock(); // Releasing the `lock` } } public static void main(String[] args) { Main example = new Main(); // Runnable task to call the increment method Runnable task = example::increment; // Starting multiple threads to execute the task for (int i = 0; i < 5; i++) { new Thread(task).start(); } } }
Kuten huomaat, increment()-metodissa käytetään lukitusta Lock-olion avulla. Kun säie siirtyy metodiin, se ottaa lukon käyttöön komennolla lock.lock(), suorittaa koodin ja vapauttaa lukon finally-lohkossa komennolla lock.unlock(), jotta muut säikeet voivat päästä sisään.
Lukko vapautetaan finally-lohkossa syystä: tämä lohko suoritetaan lähes aina, myös poikkeustilanteissa, paitsi jos ohjelma lopetetaan.
Condition-rajapinta
Condition voidaan luoda vain sitomalla se tiettyyn Lock-toteutukseen. Tästä syystä Condition-metodit vaikuttavat vain kyseisen Lock-toteutuksen lukitukseen.
Main.java
12private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition();
Condition-rajapinnan pääasialliset metodit:
await(): Odottaa signaalia toiselta säikeeltä;signal(): Vapauttaa yhden säikeen, joka odottaa ehtoa;signalAll(): Vapauttaa kaikki säikeet, jotka odottavat ehtoa.
Main.java
12345678910111213141516private final ReentrantLock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); private boolean ready = false; public void waitForCondition() throws InterruptedException { lock.lock(); // Acquire the `lock` try { while (!ready) { // Check if the condition is not met System.out.println("Waiting..."); // Print a waiting message condition.await(); // Wait for the condition to be signaled } System.out.println("Condition met!"); // Print a message when the condition is met } finally { lock.unlock(); // Release the `lock` } }
waitForCondition() -metodi estää säiettä jatkamasta, kunnes ready-muuttuja on true, mikä ilmaisee, että ehto on täyttynyt. Kun ehto täyttyy, säie jatkaa suoritustaan ja näyttää viestin “Condition met!”
Kun await()-metodia kutsutaan, säie keskeytetään ja se myös vapauttaa lukon, jonka se on ottanut haltuunsa. Kun säie herää, sen tulee ottaa lukko uudelleen haltuun ja vasta sitten se alkaa suorittaa!
Koodiesimerkki
Tarkastellaan nyt esimerkkiä, jossa käytetään ReentrantLock- ja Condition-luokkia tapahtumaan rekisteröitymisen hallintaan:
Lyhyt videoleike
Lukitus ReentrantLockilla: register()-metodi ottaa lukon käyttöön lock.lock()-kutsulla estääkseen useita säikeitä suorittamasta koodia samanaikaisesti.
Ehto Condition-oliolla: Jos vapaita paikkoja ei ole, säie kutsuu spaceAvailable.await() odottaakseen, kunnes tila vapautuu.
Lukituksen vapautus: Kun säie on vapauttanut tilan cancel()-metodilla, se kutsuu spaceAvailable.signalAll() ilmoittaakseen kaikille odottaville säikeille.
Poikkeusten käsittely: try-finally-lohkojen käyttö varmistaa, että lukko vapautetaan, vaikka poikkeus tapahtuisi.
Lock- ja Condition-rakenteiden käyttö Javassa mahdollistaa joustavamman säikeiden ja synkronoinnin hallinnan kuin perinteinen synchronized-mekanismi. Tämä on erityisen hyödyllistä monimutkaisissa tilanteissa, joissa tarvitaan tarkempaa hallintaa säikeiden ja odotusehtojen suhteen.
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
Lukko ja Ehto
Pyyhkäise näyttääksesi valikon
Javassa vakiomuotoinen synkronointi perustuu synchronized-avainsanaan ja sisäänrakennettuihin monitoriolioihin. Joissakin tapauksissa synchronized ei kuitenkaan riitä, erityisesti kun tarvitaan joustavampaa säikeiden hallintaa.
Yleiskuvaus
Lock-rajapinta ja Condition-rajapinta, jotka esiteltiin java.util.concurrent.locks -paketissa, tarjoavat kehittyneitä säikeiden hallinnan ominaisuuksia.
Tässä kuvassa näet, että ensimmäinen säie ottaa lukon käyttöön lock()-metodilla, ja tällä hetkellä toinen säie ei voi ottaa samaa lukkoa. Heti kun kaikki lukon sisällä oleva koodi on suoritettu, kutsutaan unlock()-metodia ja lukko vapautetaan. Vasta tämän jälkeen toinen säie voi ottaa lukon.
Ero
Ero näiden kahden rajapinnan välillä on, että Lock-toteutukset ovat korkean tason vaihtoehto synchronized-lohkolle, ja Condition-rajapinnan toteutukset ovat vaihtoehto notify()/wait()-metodeille. Molemmat rajapinnat kuuluvat java.util.concurrent.locks -pakettiin.
Käytännön esimerkkejä
Kuvittele, että hallinnoit jonotusjärjestelmää tapahtuman rekisteröintiä varten. Estääksesi ylivuodon ja varmistaaksesi asianmukaisen paikkajaon, sinun täytyy käyttää estomekanismeja ja ehtoja pitämään uusien rekisteröintien virta odottamassa, kunnes vapaa paikka tulee saataville.
ReentrantLock-luokka
ReentrantLock -luokka java.util.concurrent.locks -paketista on Lock -rajapinnan toteutus. Se tarjoaa toiminnallisuudet lukkojen eksplisiittiseen hallintaan.
ReentrantLock-luokan päämenetelmät:
lock(): Ottaa lukon haltuun;unlock(): Vapauttaa lukon;tryLock(): Yrittää ottaa lukon haltuun ja palauttaa true, jos onnistuu;tryLock(long timeout, TimeUnit unit): Yrittää ottaa lukon haltuun määrätyn ajan;newCondition(): Luo ehdon nykyiselleLock:lle.
Main.java
12345678910111213141516171819202122232425262728293031package com.example; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Main { // Creating a ReentrantLock object private final Lock lock = new ReentrantLock(); private int count = 0; // Method to increment the `count` variable public void increment() { lock.lock(); // Acquiring the `lock` try { count++; System.out.println("Count incremented to: " + count); } finally { lock.unlock(); // Releasing the `lock` } } public static void main(String[] args) { Main example = new Main(); // Runnable task to call the increment method Runnable task = example::increment; // Starting multiple threads to execute the task for (int i = 0; i < 5; i++) { new Thread(task).start(); } } }
Kuten huomaat, increment()-metodissa käytetään lukitusta Lock-olion avulla. Kun säie siirtyy metodiin, se ottaa lukon käyttöön komennolla lock.lock(), suorittaa koodin ja vapauttaa lukon finally-lohkossa komennolla lock.unlock(), jotta muut säikeet voivat päästä sisään.
Lukko vapautetaan finally-lohkossa syystä: tämä lohko suoritetaan lähes aina, myös poikkeustilanteissa, paitsi jos ohjelma lopetetaan.
Condition-rajapinta
Condition voidaan luoda vain sitomalla se tiettyyn Lock-toteutukseen. Tästä syystä Condition-metodit vaikuttavat vain kyseisen Lock-toteutuksen lukitukseen.
Main.java
12private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition();
Condition-rajapinnan pääasialliset metodit:
await(): Odottaa signaalia toiselta säikeeltä;signal(): Vapauttaa yhden säikeen, joka odottaa ehtoa;signalAll(): Vapauttaa kaikki säikeet, jotka odottavat ehtoa.
Main.java
12345678910111213141516private final ReentrantLock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); private boolean ready = false; public void waitForCondition() throws InterruptedException { lock.lock(); // Acquire the `lock` try { while (!ready) { // Check if the condition is not met System.out.println("Waiting..."); // Print a waiting message condition.await(); // Wait for the condition to be signaled } System.out.println("Condition met!"); // Print a message when the condition is met } finally { lock.unlock(); // Release the `lock` } }
waitForCondition() -metodi estää säiettä jatkamasta, kunnes ready-muuttuja on true, mikä ilmaisee, että ehto on täyttynyt. Kun ehto täyttyy, säie jatkaa suoritustaan ja näyttää viestin “Condition met!”
Kun await()-metodia kutsutaan, säie keskeytetään ja se myös vapauttaa lukon, jonka se on ottanut haltuunsa. Kun säie herää, sen tulee ottaa lukko uudelleen haltuun ja vasta sitten se alkaa suorittaa!
Koodiesimerkki
Tarkastellaan nyt esimerkkiä, jossa käytetään ReentrantLock- ja Condition-luokkia tapahtumaan rekisteröitymisen hallintaan:
Lyhyt videoleike
Lukitus ReentrantLockilla: register()-metodi ottaa lukon käyttöön lock.lock()-kutsulla estääkseen useita säikeitä suorittamasta koodia samanaikaisesti.
Ehto Condition-oliolla: Jos vapaita paikkoja ei ole, säie kutsuu spaceAvailable.await() odottaakseen, kunnes tila vapautuu.
Lukituksen vapautus: Kun säie on vapauttanut tilan cancel()-metodilla, se kutsuu spaceAvailable.signalAll() ilmoittaakseen kaikille odottaville säikeille.
Poikkeusten käsittely: try-finally-lohkojen käyttö varmistaa, että lukko vapautetaan, vaikka poikkeus tapahtuisi.
Lock- ja Condition-rakenteiden käyttö Javassa mahdollistaa joustavamman säikeiden ja synkronoinnin hallinnan kuin perinteinen synchronized-mekanismi. Tämä on erityisen hyödyllistä monimutkaisissa tilanteissa, joissa tarvitaan tarkempaa hallintaa säikeiden ja odotusehtojen suhteen.
Kiitos palautteestasi!