CodeGym/Java kurs/Modul 3/Atomiske operasjoner i Java

Atomiske operasjoner i Java

Tilgjengelig

Forutsetninger for fremveksten av atomoperasjoner

La oss ta en titt på dette eksemplet for å hjelpe deg å forstå hvordan atomoperasjoner fungerer:

public class Counter {
    int count;

    public void increment() {
        count++;
    }
}

Når vi har én tråd, fungerer alt bra, men hvis vi legger til multithreading, får vi feil resultater, og alt fordi inkrementoperasjonen ikke er én operasjon, men tre: en forespørsel om å få gjeldende verditelle, øk den deretter med 1 og skriv igjen tiltelle.

Og når to tråder ønsker å øke en variabel, vil du mest sannsynlig miste data. Det vil si at begge trådene mottar 100, som et resultat vil begge skrive 101 i stedet for den forventede verdien på 102.

Og hvordan løser man det? Du må bruke låser. Det synkroniserte nøkkelordet hjelper til med å løse dette problemet, ved å bruke det gir deg garantien for at én tråd får tilgang til metoden om gangen.

public class SynchronizedCounterWithLock {
    private volatile int count;

    public synchronized void increment() {
        count++;
    }
}

I tillegg må du legge til det flyktige søkeordet , som sikrer riktig synlighet av referanser blant tråder. Vi har gjennomgått arbeidet hans ovenfor.

Men det er fortsatt ulemper. Den største er ytelsen, på det tidspunktet når mange tråder prøver å skaffe en lås og man får en skrivemulighet, vil resten av trådene enten bli blokkert eller suspendert til tråden slippes.

Alle disse prosessene, blokkering, bytte til en annen status er svært kostbare for systemytelsen.

Atomoperasjoner

Algoritmen bruker maskininstruksjoner på lavt nivå som sammenligne-og-bytte (CAS, sammenligne-og-bytte, som sikrer dataintegritet og det er allerede mye forskning på dem).

En typisk CAS-operasjon opererer på tre operander:

  • Minneplass for arbeid (M)
  • Eksisterende forventet verdi (A) for en variabel
  • Ny verdi (B) skal angis

CAS oppdaterer atomisk M til B, men bare hvis verdien til M er den samme som A, ellers blir det ikke gjort noe.

I det første og andre tilfellet returneres verdien til M. Dette lar deg kombinere tre trinn, nemlig å hente verdien, sammenligne verdien og oppdatere den. Og det hele blir til én operasjon på maskinnivå.

I det øyeblikket en flertrådsapplikasjon får tilgang til en variabel og prøver å oppdatere den og CAS blir brukt, vil en av trådene hente den og kunne oppdatere den. Men i motsetning til låser vil andre tråder rett og slett få feil om at de ikke kan oppdatere verdien. Deretter skal de over til videre arbeid, og bytte er helt utelukket i denne typen arbeid.

I dette tilfellet blir logikken vanskeligere på grunn av det faktum at vi må håndtere situasjonen når CAS-operasjonen ikke fungerte vellykket. Vi skal bare modellere koden slik at den ikke går videre før operasjonen lykkes.

Introduksjon til atomtyper

Har du kommet over en situasjon der du må sette opp synkronisering for den enkleste variabelen av typen int ?

Den første måten vi allerede har dekket på er å bruke volatile + synchronized . Men det finnes også spesielle Atomic*-klasser.

Hvis vi bruker CAS, fungerer operasjonene raskere sammenlignet med den første metoden. Og i tillegg har vi spesielle og veldig praktiske metoder for å legge til en verdi og øke og redusere operasjoner.

AtomicBoolean , AtomicInteger , AtomicLong , AtomicIntegerArray , AtomicLongArray er klasser der operasjonene er atomiske. Nedenfor vil vi analysere arbeidet med dem.

AtomicInteger

AtomicInteger -klassen gir operasjoner på en int- verdi som kan leses og skrives atomisk, i tillegg til å gi utvidede atomoperasjoner.

Den har og sette metoder som fungerer som lese- og skrivevariabler.

Det vil si "skjer-før" med enhver påfølgende mottak av den samme variabelen som vi snakket om tidligere. Atomic compareAndSet- metoden har også disse minnekonsistensfunksjonene.

Alle operasjoner som returnerer en ny verdi utføres atomisk:

int addAndGet (int delta) Legger til en bestemt verdi til gjeldende verdi.
boolesk compareAndSet(forventet int, update int) Setter verdien til den gitte oppdaterte verdien hvis gjeldende verdi samsvarer med forventet verdi.
int decrementAndGet() Reduserer gjeldende verdi med én.
int getAndAdd(int delta) Legger den gitte verdien til den gjeldende verdien.
int getAndDecrement() Reduserer gjeldende verdi med én.
int getAndIncrement() Øker gjeldende verdi med én.
int getAndSet(int newValue) Angir den gitte verdien og returnerer den gamle verdien.
int incrementAndGet() Øker gjeldende verdi med én.
lazySet(int newValue) Sett til slutt til den gitte verdien.
boolean weakCompareAndSet(forventet, oppdatering int) Setter verdien til den gitte oppdaterte verdien hvis gjeldende verdi samsvarer med forventet verdi.

Eksempel:

ExecutorService executor = Executors.newFixedThreadPool(5);
IntStream.range(0, 50).forEach(i -> executor.submit(atomicInteger::incrementAndGet));
executor.shutdown();
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.HOURS);

System.out.println(atomicInteger.get()); // prints 50
1
Oppgave
Modul 3,  nivålekse
Låst
Earn a Million!
task4201
1
Oppgave
Modul 3,  nivålekse
Låst
Early Bird Gets the Worm
task4202
Kommentarer
  • Populær
  • Ny
  • Gammel
Du må være pålogget for å legge igjen en kommentar
Denne siden har ingen kommentarer ennå