Atomiset Muuttujat
Olemme jo käsitelleet mitä atomisuus tarkoittaa ja millaisia ongelmia se voi aiheuttaa tämän kurssin ensimmäisessä osassa. Tuolloin ratkaisuna käytettiin synchronized-lohkoja tai menetelmiä. Nyt tarkastelemme, kuinka sama tulos voidaan saavuttaa helpommin käyttämällä atomiluokkaa.
Mitä ovat atomiset muuttujat?
Atomiset muuttujat varmistavat, että toiminnot (luku, kirjoitus, inkrementointi) muuttujille suoritetaan atomisesti, eli ne toteutetaan yhtenäisesti ja turvallisesti monisäikeisessä ympäristössä. Tämä takaa, että operaatio suoritetaan kokonaan loppuun ilman, että muut säikeet voivat häiritä sen suoritusta.
Miksi tarvitsemme atomisia muuttujia?
Ilman atomisia muuttujia tai muita synkronointimekanismeja operaatiot kuten inkrementointi (++) voivat olla turvattomia. Esimerkiksi, kun useat säikeet käyttävät samaa muuttujaa samanaikaisesti, päivityksiä voi kadota, mikä johtaa virheellisiin tuloksiin. Atomiset muuttujat ratkaisevat tämän ongelman varmistamalla, että niihin kohdistuvat operaatiot suoritetaan peräkkäin.
Käsittelimme aiemmin tätä ongelmaa, kun inkrementointi jaettiin kolmeen vaiheeseen (luku, inkrementointi, kirjoitus), mutta atomisilla muuttujilla kaikki tapahtuu yhdessä operaatiossa!
Atomisten muuttujien tyypit Javassa
Yleisesti ottaen on olemassa useita atomisia toteutuksia, emmekä käsittele niitä kaikkia tässä, sillä se veisi liikaa aikaa.
Java tarjoaa useita atomisten muuttujien luokkia java.util.concurrent.atomic -paketissa, joista jokainen on suunniteltu käsittelemään tiettyä tietotyyppiä:
AtomicInteger: atomiset operaatiot int-tyypille;AtomicLong: atomiset operaatiot long-tyypille;AtomicBoolean: atomiset operaatiot boolean-tyypille;AtomicReference<V>: atomiset operaatiot olioille (geneerinen tyyppi).
Menetelmät
get() metodi palauttaa muuttujan nykyisen arvon. set(V newValue) metodi asettaa uuden arvon muuttujalle. Toisaalta lazySet(V newValue) on samanlainen kuin set(), mutta se voi viivästyttää arvon päivittämistä, tarjoten järjestetyn päivityksen tietyissä tilanteissa.
Main.java
123456789101112131415161718192021package com.example; import java.util.concurrent.atomic.AtomicReference; public class Main { public static void main(String[] args) { AtomicReference<String> atomicString = new AtomicReference<>("Initial Value"); // Using `get()` to retrieve the current value String value = atomicString.get(); System.out.println("Current Value: " + value); // Using `set()` to update the value atomicString.set("New Value"); System.out.println("Value after set(): " + atomicString.get()); // Using `lazySet()` to update the value atomicString.lazySet("Lazy Set Value"); System.out.println("Value after lazySet(): " + atomicString.get()); } }
compareAndSet(V expect, V update) -metodi päivittää arvon, jos nykyinen arvo vastaa odotettua arvoa. Palauttaa true, jos päivitys oli onnistunut, ja false, jos nykyinen arvo ei vastannut odotettua arvoa. Vastaavasti getAndSet(V newValue) -metodi asettaa uuden arvon ja palauttaa edellisen arvon.
Main.java
123456789101112131415161718192021222324package com.example; import java.util.concurrent.atomic.AtomicReference; public class Main { public static void main(String[] args) { // Initialize an `AtomicReference` with an initial value AtomicReference<String> atomicString = new AtomicReference<>("Initial Value"); // Demonstrate `compareAndSet` boolean success = atomicString.compareAndSet("Initial Value", "Updated Value"); System.out.println("compareAndSet success (expected true): " + success); System.out.println("Current value after compareAndSet: " + atomicString.get()); success = atomicString.compareAndSet("Wrong Value", "Another Update"); System.out.println("compareAndSet success (expected false): " + success); System.out.println("Current value after compareAndSet: " + atomicString.get()); // Demonstrate `getAndSet` String previousValue = atomicString.getAndSet("New Value with getAndSet"); System.out.println("Previous value from getAndSet: " + previousValue); System.out.println("Current value after getAndSet: " + atomicString.get()); } }
getAndIncrement() ja getAndDecrement() metodit kasvattavat tai vähentävät nykyistä arvoa yhdellä ja palauttavat edellisen arvon. Nämä metodit koskevat numeerisia atomimuuttujia kuten AtomicInteger ja AtomicLong. Sen sijaan incrementAndGet() ja decrementAndGet() metodit kasvattavat tai vähentävät nykyistä arvoa yhdellä, mutta palauttavat uuden arvon.
Main.java
123456789101112131415161718192021222324252627282930package com.example; import java.util.concurrent.atomic.AtomicInteger; public class Main { public static void main(String[] args) { // Initialize `AtomicInteger` with initial value AtomicInteger atomicInt = new AtomicInteger(10); // Demonstrate `getAndIncrement()` for `AtomicInteger` int oldValueInt = atomicInt.getAndIncrement(); System.out.println("Value before getAndIncrement(): " + oldValueInt); // Should print 10 System.out.println("Value after getAndIncrement(): " + atomicInt.get()); // Should print 11 // Demonstrate `getAndDecrement()` for `AtomicInteger` int oldValueIntDec = atomicInt.getAndDecrement(); System.out.println("Value before getAndDecrement(): " + oldValueIntDec); // Should print 11 System.out.println("Value after getAndDecrement(): " + atomicInt.get()); // Should print 10 // Demonstrate `incrementAndGet()` for `AtomicInteger` int newValueInt = atomicInt.incrementAndGet(); System.out.println("Value after incrementAndGet(): " + newValueInt); // Should print 11 System.out.println("Current value after incrementAndGet(): " + atomicInt.get()); // Should print 11 // Demonstrate `decrementAndGet()` for `AtomicInteger` int newValueIntDec = atomicInt.decrementAndGet(); System.out.println("Value after decrementAndGet(): " + newValueIntDec); // Should print 10 System.out.println("Current value after decrementAndGet(): " + atomicInt.get()); // Should print 10 } }
getAndAdd(int delta) metodi lisää määritetyn arvon (delta) nykyiseen arvoon ja palauttaa edellisen arvon. Tätä metodia käytetään numeeristen atomimuuttujien kanssa. Toisaalta addAndGet(int delta) metodi lisää myös määritetyn arvon (delta) nykyiseen arvoon, mutta palauttaa uuden arvon.
Main.java
123456789101112131415161718192021222324252627282930package com.example; import java.util.concurrent.atomic.AtomicInteger; public class Main { public static void main(String[] args) { // Initialize `AtomicInteger` with an initial value AtomicInteger atomicInt = new AtomicInteger(50); // Demonstrate `getAndAdd(int delta)` int previousValue = atomicInt.getAndAdd(10); System.out.println("Value before getAndAdd(10): " + previousValue); // Should print 50 System.out.println("Value after getAndAdd(10): " + atomicInt.get()); // Should print 60 // Demonstrate `getAndAdd()` with another delta int previousValue2 = atomicInt.getAndAdd(5); System.out.println("Value before getAndAdd(5): " + previousValue2); // Should print 60 System.out.println("Value after getAndAdd(5): " + atomicInt.get()); // Should print 65 // Demonstrate `addAndGet(int delta)` int newValue = atomicInt.addAndGet(20); System.out.println("Value after addAndGet(20): " + newValue); // Should print 85 System.out.println("Current value after addAndGet(20): " + atomicInt.get()); // Should print 85 // Demonstrate `addAndGet()` with another delta int newValue2 = atomicInt.addAndGet(-15); System.out.println("Value after addAndGet(-15): " + newValue2); // Should print 70 System.out.println("Current value after addAndGet(-15): " + atomicInt.get()); // Should print 70 } }
Esimerkkejä atomisten muuttujien käytöstä
Esimerkki AtomicInteger-luokan käytöstä (AtomicLong, AtomicBoolean toimivat samalla tavalla, joten niille ei ole erillisiä esimerkkejä).
Tehtävä: Toteuttaa laskuri, jota useat säikeet voivat kasvattaa turvallisesti.
Main.java
1234567891011121314151617181920package com.example; import java.util.concurrent.atomic.AtomicInteger; public class Main { private AtomicInteger counter = new AtomicInteger(0); public void increment() { int oldValue = counter.getAndIncrement(); System.out.println(Thread.currentThread().getName() + ": Counter was " + oldValue + ", now " + counter.get()); } public static void main(String[] args) { Main atomicCounter = new Main(); for (int i = 0; i < 5; i++) { new Thread(atomicCounter::increment).start(); } } }
Kuten huomaat, emme käyttäneet tässä synkronointia, koska atominen muuttuja tarjoaa tämän toiminnallisuuden.
Tässä esimerkissä käytetään AtomicInteger-luokkaa laskurin turvalliseen kasvattamiseen. getAndIncrement()-metodi palauttaa ensin muuttujan nykyisen arvon ja kasvattaa sitä sitten yhdellä. Tämä tapahtuu atomisesti, mikä varmistaa, että muuttuja päivittyy oikein.
Esimerkki AtomicReference-luokasta
Tehtävä: Tarjota atominen viittauksen päivitys olioon.
Main.java
123456789101112131415161718192021222324package com.example; import java.util.concurrent.atomic.AtomicReference; public class Main { // `AtomicReference` to safely update and access a shared `String` value private AtomicReference<String> sharedString = new AtomicReference<>("Initial"); public void updateValue(String newValue) { // Atomically sets the new value and gets the old value String oldValue = sharedString.getAndSet(newValue); // Prints the old and new values, along with the thread name System.out.println(Thread.currentThread().getName() + ": Value was " + oldValue + ", now " + sharedString.get()); } public static void main(String[] args) { Main example = new Main(); // Creates and starts 3 threads, each updating the shared value for (int i = 0; i < 3; i++) { new Thread(() -> example.updateValue("Updated by " + Thread.currentThread().getName())).start(); } } }
AtomicReference-luokkaa käytetään tässä atomisesti päivittämään merkkijonoviitteen arvoa. getAndSet()-metodi asettaa uuden arvon atomisesti ja palauttaa edellisen arvon.
Toisin kuin tavalliset muuttujat, jotka vaativat lisä*synkronointia, atomiset muuttujat hyödyntävät matalan tason primitiivejä ylikuorman minimoimiseksi ja suorituskyvyn parantamiseksi. Tämä tekee niistä erityisen sopivia korkean rinnakkaisuuden järjestelmiin.
1. Mikä on atomisten muuttujien etu monisäikeisessä ohjelmoinnissa?
2. Mikä atomisen muuttujan metodi mahdollistaa arvon atomisen muutoksen, jos nykyinen arvo vastaa odotettua arvoa?
3. Mitä set()-metodi takaa atomisissa muuttujissa?
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
Atomiset Muuttujat
Pyyhkäise näyttääksesi valikon
Olemme jo käsitelleet mitä atomisuus tarkoittaa ja millaisia ongelmia se voi aiheuttaa tämän kurssin ensimmäisessä osassa. Tuolloin ratkaisuna käytettiin synchronized-lohkoja tai menetelmiä. Nyt tarkastelemme, kuinka sama tulos voidaan saavuttaa helpommin käyttämällä atomiluokkaa.
Mitä ovat atomiset muuttujat?
Atomiset muuttujat varmistavat, että toiminnot (luku, kirjoitus, inkrementointi) muuttujille suoritetaan atomisesti, eli ne toteutetaan yhtenäisesti ja turvallisesti monisäikeisessä ympäristössä. Tämä takaa, että operaatio suoritetaan kokonaan loppuun ilman, että muut säikeet voivat häiritä sen suoritusta.
Miksi tarvitsemme atomisia muuttujia?
Ilman atomisia muuttujia tai muita synkronointimekanismeja operaatiot kuten inkrementointi (++) voivat olla turvattomia. Esimerkiksi, kun useat säikeet käyttävät samaa muuttujaa samanaikaisesti, päivityksiä voi kadota, mikä johtaa virheellisiin tuloksiin. Atomiset muuttujat ratkaisevat tämän ongelman varmistamalla, että niihin kohdistuvat operaatiot suoritetaan peräkkäin.
Käsittelimme aiemmin tätä ongelmaa, kun inkrementointi jaettiin kolmeen vaiheeseen (luku, inkrementointi, kirjoitus), mutta atomisilla muuttujilla kaikki tapahtuu yhdessä operaatiossa!
Atomisten muuttujien tyypit Javassa
Yleisesti ottaen on olemassa useita atomisia toteutuksia, emmekä käsittele niitä kaikkia tässä, sillä se veisi liikaa aikaa.
Java tarjoaa useita atomisten muuttujien luokkia java.util.concurrent.atomic -paketissa, joista jokainen on suunniteltu käsittelemään tiettyä tietotyyppiä:
AtomicInteger: atomiset operaatiot int-tyypille;AtomicLong: atomiset operaatiot long-tyypille;AtomicBoolean: atomiset operaatiot boolean-tyypille;AtomicReference<V>: atomiset operaatiot olioille (geneerinen tyyppi).
Menetelmät
get() metodi palauttaa muuttujan nykyisen arvon. set(V newValue) metodi asettaa uuden arvon muuttujalle. Toisaalta lazySet(V newValue) on samanlainen kuin set(), mutta se voi viivästyttää arvon päivittämistä, tarjoten järjestetyn päivityksen tietyissä tilanteissa.
Main.java
123456789101112131415161718192021package com.example; import java.util.concurrent.atomic.AtomicReference; public class Main { public static void main(String[] args) { AtomicReference<String> atomicString = new AtomicReference<>("Initial Value"); // Using `get()` to retrieve the current value String value = atomicString.get(); System.out.println("Current Value: " + value); // Using `set()` to update the value atomicString.set("New Value"); System.out.println("Value after set(): " + atomicString.get()); // Using `lazySet()` to update the value atomicString.lazySet("Lazy Set Value"); System.out.println("Value after lazySet(): " + atomicString.get()); } }
compareAndSet(V expect, V update) -metodi päivittää arvon, jos nykyinen arvo vastaa odotettua arvoa. Palauttaa true, jos päivitys oli onnistunut, ja false, jos nykyinen arvo ei vastannut odotettua arvoa. Vastaavasti getAndSet(V newValue) -metodi asettaa uuden arvon ja palauttaa edellisen arvon.
Main.java
123456789101112131415161718192021222324package com.example; import java.util.concurrent.atomic.AtomicReference; public class Main { public static void main(String[] args) { // Initialize an `AtomicReference` with an initial value AtomicReference<String> atomicString = new AtomicReference<>("Initial Value"); // Demonstrate `compareAndSet` boolean success = atomicString.compareAndSet("Initial Value", "Updated Value"); System.out.println("compareAndSet success (expected true): " + success); System.out.println("Current value after compareAndSet: " + atomicString.get()); success = atomicString.compareAndSet("Wrong Value", "Another Update"); System.out.println("compareAndSet success (expected false): " + success); System.out.println("Current value after compareAndSet: " + atomicString.get()); // Demonstrate `getAndSet` String previousValue = atomicString.getAndSet("New Value with getAndSet"); System.out.println("Previous value from getAndSet: " + previousValue); System.out.println("Current value after getAndSet: " + atomicString.get()); } }
getAndIncrement() ja getAndDecrement() metodit kasvattavat tai vähentävät nykyistä arvoa yhdellä ja palauttavat edellisen arvon. Nämä metodit koskevat numeerisia atomimuuttujia kuten AtomicInteger ja AtomicLong. Sen sijaan incrementAndGet() ja decrementAndGet() metodit kasvattavat tai vähentävät nykyistä arvoa yhdellä, mutta palauttavat uuden arvon.
Main.java
123456789101112131415161718192021222324252627282930package com.example; import java.util.concurrent.atomic.AtomicInteger; public class Main { public static void main(String[] args) { // Initialize `AtomicInteger` with initial value AtomicInteger atomicInt = new AtomicInteger(10); // Demonstrate `getAndIncrement()` for `AtomicInteger` int oldValueInt = atomicInt.getAndIncrement(); System.out.println("Value before getAndIncrement(): " + oldValueInt); // Should print 10 System.out.println("Value after getAndIncrement(): " + atomicInt.get()); // Should print 11 // Demonstrate `getAndDecrement()` for `AtomicInteger` int oldValueIntDec = atomicInt.getAndDecrement(); System.out.println("Value before getAndDecrement(): " + oldValueIntDec); // Should print 11 System.out.println("Value after getAndDecrement(): " + atomicInt.get()); // Should print 10 // Demonstrate `incrementAndGet()` for `AtomicInteger` int newValueInt = atomicInt.incrementAndGet(); System.out.println("Value after incrementAndGet(): " + newValueInt); // Should print 11 System.out.println("Current value after incrementAndGet(): " + atomicInt.get()); // Should print 11 // Demonstrate `decrementAndGet()` for `AtomicInteger` int newValueIntDec = atomicInt.decrementAndGet(); System.out.println("Value after decrementAndGet(): " + newValueIntDec); // Should print 10 System.out.println("Current value after decrementAndGet(): " + atomicInt.get()); // Should print 10 } }
getAndAdd(int delta) metodi lisää määritetyn arvon (delta) nykyiseen arvoon ja palauttaa edellisen arvon. Tätä metodia käytetään numeeristen atomimuuttujien kanssa. Toisaalta addAndGet(int delta) metodi lisää myös määritetyn arvon (delta) nykyiseen arvoon, mutta palauttaa uuden arvon.
Main.java
123456789101112131415161718192021222324252627282930package com.example; import java.util.concurrent.atomic.AtomicInteger; public class Main { public static void main(String[] args) { // Initialize `AtomicInteger` with an initial value AtomicInteger atomicInt = new AtomicInteger(50); // Demonstrate `getAndAdd(int delta)` int previousValue = atomicInt.getAndAdd(10); System.out.println("Value before getAndAdd(10): " + previousValue); // Should print 50 System.out.println("Value after getAndAdd(10): " + atomicInt.get()); // Should print 60 // Demonstrate `getAndAdd()` with another delta int previousValue2 = atomicInt.getAndAdd(5); System.out.println("Value before getAndAdd(5): " + previousValue2); // Should print 60 System.out.println("Value after getAndAdd(5): " + atomicInt.get()); // Should print 65 // Demonstrate `addAndGet(int delta)` int newValue = atomicInt.addAndGet(20); System.out.println("Value after addAndGet(20): " + newValue); // Should print 85 System.out.println("Current value after addAndGet(20): " + atomicInt.get()); // Should print 85 // Demonstrate `addAndGet()` with another delta int newValue2 = atomicInt.addAndGet(-15); System.out.println("Value after addAndGet(-15): " + newValue2); // Should print 70 System.out.println("Current value after addAndGet(-15): " + atomicInt.get()); // Should print 70 } }
Esimerkkejä atomisten muuttujien käytöstä
Esimerkki AtomicInteger-luokan käytöstä (AtomicLong, AtomicBoolean toimivat samalla tavalla, joten niille ei ole erillisiä esimerkkejä).
Tehtävä: Toteuttaa laskuri, jota useat säikeet voivat kasvattaa turvallisesti.
Main.java
1234567891011121314151617181920package com.example; import java.util.concurrent.atomic.AtomicInteger; public class Main { private AtomicInteger counter = new AtomicInteger(0); public void increment() { int oldValue = counter.getAndIncrement(); System.out.println(Thread.currentThread().getName() + ": Counter was " + oldValue + ", now " + counter.get()); } public static void main(String[] args) { Main atomicCounter = new Main(); for (int i = 0; i < 5; i++) { new Thread(atomicCounter::increment).start(); } } }
Kuten huomaat, emme käyttäneet tässä synkronointia, koska atominen muuttuja tarjoaa tämän toiminnallisuuden.
Tässä esimerkissä käytetään AtomicInteger-luokkaa laskurin turvalliseen kasvattamiseen. getAndIncrement()-metodi palauttaa ensin muuttujan nykyisen arvon ja kasvattaa sitä sitten yhdellä. Tämä tapahtuu atomisesti, mikä varmistaa, että muuttuja päivittyy oikein.
Esimerkki AtomicReference-luokasta
Tehtävä: Tarjota atominen viittauksen päivitys olioon.
Main.java
123456789101112131415161718192021222324package com.example; import java.util.concurrent.atomic.AtomicReference; public class Main { // `AtomicReference` to safely update and access a shared `String` value private AtomicReference<String> sharedString = new AtomicReference<>("Initial"); public void updateValue(String newValue) { // Atomically sets the new value and gets the old value String oldValue = sharedString.getAndSet(newValue); // Prints the old and new values, along with the thread name System.out.println(Thread.currentThread().getName() + ": Value was " + oldValue + ", now " + sharedString.get()); } public static void main(String[] args) { Main example = new Main(); // Creates and starts 3 threads, each updating the shared value for (int i = 0; i < 3; i++) { new Thread(() -> example.updateValue("Updated by " + Thread.currentThread().getName())).start(); } } }
AtomicReference-luokkaa käytetään tässä atomisesti päivittämään merkkijonoviitteen arvoa. getAndSet()-metodi asettaa uuden arvon atomisesti ja palauttaa edellisen arvon.
Toisin kuin tavalliset muuttujat, jotka vaativat lisä*synkronointia, atomiset muuttujat hyödyntävät matalan tason primitiivejä ylikuorman minimoimiseksi ja suorituskyvyn parantamiseksi. Tämä tekee niistä erityisen sopivia korkean rinnakkaisuuden järjestelmiin.
1. Mikä on atomisten muuttujien etu monisäikeisessä ohjelmoinnissa?
2. Mikä atomisen muuttujan metodi mahdollistaa arvon atomisen muutoksen, jos nykyinen arvo vastaa odotettua arvoa?
3. Mitä set()-metodi takaa atomisissa muuttujissa?
Kiitos palautteestasi!