Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Lære Atomare Variabler | Højniveausynkroniseringsmekanismer
Multitrådning i Java

bookAtomare Variabler

Vi har allerede dækket hvad atomicitet er, og hvilke problemer det kan forårsage i den første sektion af dette kursus. Dengang håndterede vi problemet ved at bruge synchronized blokke eller metoder. Nu vil vi undersøge, hvordan man kan opnå det samme resultat lettere ved at anvende en atomar klasse.

Hvad er atomare variabler?

Atomare variabler sikrer, at operationer (læsning, skrivning, inkrementering) på variabler udføres atomisk, hvilket betyder, at de udføres sammenhængende og sikkert i et multitrådet miljø. Dette garanterer, at operationen bliver fuldført helt, uden mulighed for indblanding fra andre tråde under dens udførelse.

Hvorfor har vi brug for atomare variabler?

Uden brug af atomare variabler eller andre synchroniseringsmekanismer kan operationer som inkrementering (++) være usikre. For eksempel, når flere tråde får adgang til den samme variabel samtidigt, kan opdateringer gå tabt, hvilket resulterer i forkerte resultater. Atomare variabler løser dette problem ved at sikre, at operationer på dem udføres sekventielt.

Vi har tidligere diskuteret dette problem, da inkrementerings-operationen blev opdelt i tre trin (læs, inkrementer, skriv), men med atomare variabler udføres det hele i én operation!

Typer af atomare variabler i Java

Note
Bemærk

Generelt findes der mange atomare implementeringer, og vi vil ikke dække dem alle her, da det ville tage for lang tid.

Java tilbyder flere atomare variabelklasser i pakken java.util.concurrent.atomic, hvor hver klasse er designet til at håndtere en specifik datatyp:

  • AtomicInteger: til atomare operationer på int;
  • AtomicLong: til atomare operationer på long;
  • AtomicBoolean: til atomare operationer på boolean;
  • AtomicReference<V>: til atomare operationer på objekter (generisk type).

Metoder

Metoden get() returnerer den aktuelle værdi af en variabel. Metoden set(V newValue) angiver en ny værdi for variablen. Omvendt ligner lazySet(V newValue) set(), men kan udsætte opdateringen af værdien og tilbyder en ordnet opdatering i visse situationer.

Main.java

Main.java

copy
123456789101112131415161718192021
package 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()); } }

Metoden compareAndSet(V expect, V update) opdaterer værdien, hvis den nuværende værdi svarer til den forventede værdi. Den returnerer true, hvis opdateringen var vellykket, og false, hvis den nuværende værdi ikke matchede den forventede værdi. Til sammenligning sætter metoden getAndSet(V newValue) en ny værdi og returnerer den forrige værdi.

Main.java

Main.java

copy
123456789101112131415161718192021222324
package 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()); } }

Metoderne getAndIncrement() og getAndDecrement() øger eller mindsker den aktuelle værdi med én og returnerer den forrige værdi. Disse metoder anvendes på numeriske atomare variabler såsom AtomicInteger og AtomicLong. Til sammenligning øger eller mindsker metoderne incrementAndGet() og decrementAndGet() også den aktuelle værdi med én, men returnerer den nye værdi.

Main.java

Main.java

copy
123456789101112131415161718192021222324252627282930
package 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 } }

Metoden getAndAdd(int delta) lægger den angivne værdi (delta) til den aktuelle værdi og returnerer den forrige værdi. Denne metode anvendes med numeriske atomare variabler. Omvendt lægger metoden addAndGet(int delta) også den angivne værdi (delta) til den aktuelle værdi, men returnerer den nye værdi.

Main.java

Main.java

copy
123456789101112131415161718192021222324252627282930
package 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 } }

Eksempler på brug af atomare variabler

Eksempel med AtomicInteger (AtomicLong, AtomicBoolean fungerer på samme måde, så der gives ikke separate eksempler for dem).

Opgave: Implementering af en tæller, der sikkert øges af flere tråde.

Main.java

Main.java

copy
1234567891011121314151617181920
package 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(); } } }

Som du kan se, har vi ikke brugt nogen synkronisering her, da atomvariablen selv leverer denne funktion.

Dette eksempel anvender AtomicInteger til sikkert at øge tælleren. Metoden getAndIncrement() returnerer først den aktuelle værdi af variablen og øger derefter værdien med én. Dette sker atomisk, hvilket sikrer, at variablen opdateres korrekt.

Eksempel med AtomicReference

Opgave: Giv atomisk opdatering af en reference til et objekt.

Main.java

Main.java

copy
123456789101112131415161718192021222324
package 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 bruges her til atomisk at opdatere værdien af en strengreference. Metoden getAndSet() sætter atomisk den nye værdi og returnerer den tidligere værdi.

Note
Bemærk

I modsætning til almindelige variabler, som kræver yderligere synkronisering, anvender atomare variabler lavniveau-primitiver for at minimere overhead og forbedre ydelsen. Dette gør dem særligt velegnede til systemer med høj samtidighed.

1. Hvad er fordelen ved at bruge atomare variabler i multitrådede programmer?

2. Hvilken metode for atomare variabler muliggør en atomar ændring af værdien, hvis den nuværende værdi er den samme som den forventede værdi?

3. Hvad garanterer set()-metoden i atomare variabler?

question mark

Hvad er fordelen ved at bruge atomare variabler i multitrådede programmer?

Select the correct answer

question mark

Hvilken metode for atomare variabler muliggør en atomar ændring af værdien, hvis den nuværende værdi er den samme som den forventede værdi?

Select the correct answer

question mark

Hvad garanterer set()-metoden i atomare variabler?

Select the correct answer

Var alt klart?

Hvordan kan vi forbedre det?

Tak for dine kommentarer!

Sektion 3. Kapitel 5

Spørg AI

expand

Spørg AI

ChatGPT

Spørg om hvad som helst eller prøv et af de foreslåede spørgsmål for at starte vores chat

Suggested prompts:

Can you explain how atomic variables differ from using synchronized blocks?

What are some real-world scenarios where atomic variables are especially useful?

Could you provide a simple code example using AtomicInteger?

Awesome!

Completion rate improved to 3.33

bookAtomare Variabler

Stryg for at vise menuen

Vi har allerede dækket hvad atomicitet er, og hvilke problemer det kan forårsage i den første sektion af dette kursus. Dengang håndterede vi problemet ved at bruge synchronized blokke eller metoder. Nu vil vi undersøge, hvordan man kan opnå det samme resultat lettere ved at anvende en atomar klasse.

Hvad er atomare variabler?

Atomare variabler sikrer, at operationer (læsning, skrivning, inkrementering) på variabler udføres atomisk, hvilket betyder, at de udføres sammenhængende og sikkert i et multitrådet miljø. Dette garanterer, at operationen bliver fuldført helt, uden mulighed for indblanding fra andre tråde under dens udførelse.

Hvorfor har vi brug for atomare variabler?

Uden brug af atomare variabler eller andre synchroniseringsmekanismer kan operationer som inkrementering (++) være usikre. For eksempel, når flere tråde får adgang til den samme variabel samtidigt, kan opdateringer gå tabt, hvilket resulterer i forkerte resultater. Atomare variabler løser dette problem ved at sikre, at operationer på dem udføres sekventielt.

Vi har tidligere diskuteret dette problem, da inkrementerings-operationen blev opdelt i tre trin (læs, inkrementer, skriv), men med atomare variabler udføres det hele i én operation!

Typer af atomare variabler i Java

Note
Bemærk

Generelt findes der mange atomare implementeringer, og vi vil ikke dække dem alle her, da det ville tage for lang tid.

Java tilbyder flere atomare variabelklasser i pakken java.util.concurrent.atomic, hvor hver klasse er designet til at håndtere en specifik datatyp:

  • AtomicInteger: til atomare operationer på int;
  • AtomicLong: til atomare operationer på long;
  • AtomicBoolean: til atomare operationer på boolean;
  • AtomicReference<V>: til atomare operationer på objekter (generisk type).

Metoder

Metoden get() returnerer den aktuelle værdi af en variabel. Metoden set(V newValue) angiver en ny værdi for variablen. Omvendt ligner lazySet(V newValue) set(), men kan udsætte opdateringen af værdien og tilbyder en ordnet opdatering i visse situationer.

Main.java

Main.java

copy
123456789101112131415161718192021
package 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()); } }

Metoden compareAndSet(V expect, V update) opdaterer værdien, hvis den nuværende værdi svarer til den forventede værdi. Den returnerer true, hvis opdateringen var vellykket, og false, hvis den nuværende værdi ikke matchede den forventede værdi. Til sammenligning sætter metoden getAndSet(V newValue) en ny værdi og returnerer den forrige værdi.

Main.java

Main.java

copy
123456789101112131415161718192021222324
package 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()); } }

Metoderne getAndIncrement() og getAndDecrement() øger eller mindsker den aktuelle værdi med én og returnerer den forrige værdi. Disse metoder anvendes på numeriske atomare variabler såsom AtomicInteger og AtomicLong. Til sammenligning øger eller mindsker metoderne incrementAndGet() og decrementAndGet() også den aktuelle værdi med én, men returnerer den nye værdi.

Main.java

Main.java

copy
123456789101112131415161718192021222324252627282930
package 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 } }

Metoden getAndAdd(int delta) lægger den angivne værdi (delta) til den aktuelle værdi og returnerer den forrige værdi. Denne metode anvendes med numeriske atomare variabler. Omvendt lægger metoden addAndGet(int delta) også den angivne værdi (delta) til den aktuelle værdi, men returnerer den nye værdi.

Main.java

Main.java

copy
123456789101112131415161718192021222324252627282930
package 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 } }

Eksempler på brug af atomare variabler

Eksempel med AtomicInteger (AtomicLong, AtomicBoolean fungerer på samme måde, så der gives ikke separate eksempler for dem).

Opgave: Implementering af en tæller, der sikkert øges af flere tråde.

Main.java

Main.java

copy
1234567891011121314151617181920
package 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(); } } }

Som du kan se, har vi ikke brugt nogen synkronisering her, da atomvariablen selv leverer denne funktion.

Dette eksempel anvender AtomicInteger til sikkert at øge tælleren. Metoden getAndIncrement() returnerer først den aktuelle værdi af variablen og øger derefter værdien med én. Dette sker atomisk, hvilket sikrer, at variablen opdateres korrekt.

Eksempel med AtomicReference

Opgave: Giv atomisk opdatering af en reference til et objekt.

Main.java

Main.java

copy
123456789101112131415161718192021222324
package 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 bruges her til atomisk at opdatere værdien af en strengreference. Metoden getAndSet() sætter atomisk den nye værdi og returnerer den tidligere værdi.

Note
Bemærk

I modsætning til almindelige variabler, som kræver yderligere synkronisering, anvender atomare variabler lavniveau-primitiver for at minimere overhead og forbedre ydelsen. Dette gør dem særligt velegnede til systemer med høj samtidighed.

1. Hvad er fordelen ved at bruge atomare variabler i multitrådede programmer?

2. Hvilken metode for atomare variabler muliggør en atomar ændring af værdien, hvis den nuværende værdi er den samme som den forventede værdi?

3. Hvad garanterer set()-metoden i atomare variabler?

question mark

Hvad er fordelen ved at bruge atomare variabler i multitrådede programmer?

Select the correct answer

question mark

Hvilken metode for atomare variabler muliggør en atomar ændring af værdien, hvis den nuværende værdi er den samme som den forventede værdi?

Select the correct answer

question mark

Hvad garanterer set()-metoden i atomare variabler?

Select the correct answer

Var alt klart?

Hvordan kan vi forbedre det?

Tak for dine kommentarer!

Sektion 3. Kapitel 5
some-alt