Java >> Java tutorial >  >> Tag >> volatile

AtomicInteger og flygtig

Jeg tror, ​​at Atomic* giver faktisk begge dele atomicitet og flygtighed. Så når du ringer (siger) AtomicInteger.get() , er du garanteret at få det nyeste værdi. Dette er dokumenteret i java.util.concurrent.atomic pakkedokumentation:

Hukommelseseffekterne for adgang og opdateringer af atomics følger generelt reglerne for flygtige stoffer, som angivet i afsnit 17.4 i Java™ Language Specification.

  • get har hukommelseseffekterne ved at læse en flygtig variabel.
  • sæt har hukommelseseffekterne ved at skrive (tildele) en flygtig variabel.
  • lazySet har hukommelseseffekterne ved at skrive (tildele) en flygtig variabel, bortset fra at den tillader genbestillinger med efterfølgende (men ikke tidligere) hukommelseshandlinger, der ikke i sig selv pålægger genbestillingsbegrænsninger med almindelige ikke-flygtige skrivninger. Blandt andre brugskontekster kan> - lazySet gælde, når en reference, der aldrig tilgås igen, nulstilles af hensyn til affaldsindsamling.
  • weakCompareAndSet læser atomisk og betinget skriver en variabel, men opretter ingen sker-før-ordrer, så det giver ingen garantier med hensyn til tidligere eller efterfølgende læsninger og skrivninger af andre variabler end målet for weakCompareAndSet.
  • compareAndSet og alle andre læse-og-opdater-operationer, såsom getAndIncrement, har hukommelseseffekterne ved både at læse og skrive flygtige variable.

Hvis du nu har

volatile AtomicInteger count;

volatile del betyder, at hver tråd vil bruge den seneste AtomicInteger reference, og det faktum, at det er en AtomicInteger betyder, at du også se den seneste værdi for det pågældende objekt.

Det er ikke almindeligt (IME) at have brug for dette - for normalt ville du ikke gentildele count at henvise til et andet objekt. I stedet ville du have:

private final AtomicInteger count = new AtomicInteger();

På det tidspunkt, det faktum, at det er en final variabel betyder, at alle tråde vil beskæftige sig med det samme objekt - og det faktum, at det er en Atomic* objekt betyder, at de vil se den seneste værdi i det pågældende objekt.


Jeg vil sige nej, det er ikke trådsikkert, hvis du definerer trådsikkert som at have det samme resultat under enkelttrådstilstand og multitrådstilstand. I enkelttrådstilstand vil antallet aldrig blive større end 10, men i multitrådstilstand kan det.

Problemet er, at get og incrementAndGet er atomart, men en if er ikke. Husk, at en ikke-atomar operation kan standses til enhver tid. For eksempel:

  1. count = 9 i øjeblikket.
  2. Tråd A kører if(count.get() <10) og får true og stoppede der.
  3. Tråd B kører if(count.get() <10) og får true også, så den kører count.incrementAndGet() og afslutter. Nu count = 10 .
  4. Tråd A genoptages og kører count.incrementAndGet() , nu count = 11 hvilket aldrig vil ske i enkelttrådstilstand.

Hvis du vil gøre det trådsikkert uden at bruge synchronized som er langsommere, prøv denne implementering i stedet:

class A{

final AtomicInteger count;

void someMethod(){
// do something
  if(count.getAndIncrement() <10){
      // safe now
  } else count.getAndDecrement(); // rollback so this thread did nothing to count
}

For at bevare den originale semantik og understøtte flere tråde kan du gøre noget som:

public class A {

    private AtomicInteger count = new AtomicInteger(0);

    public void someMethod() {

        int i = count.get();
        while (i < 10 && !count.compareAndSet(i, i + 1)) {
            i = count.get();
        }

    }

}

Dette undgår, at enhver tråd nogensinde ser antallet nå 10.


Java tag