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

Flygtige vs. atomvariable i Java

1. Oversigt

I dette selvstudie lærer vi forskellen mellem de flygtige søgeords- og atomklasser og hvilke problemer de løser. For det første er det nødvendigt at vide, hvordan Java håndterer kommunikationen mellem tråde, og hvilke uventede problemer der kan opstå.

Trådsikkerhed er et afgørende emne, der giver et indblik i det interne arbejde i flertrådede applikationer. Vi vil også diskutere løbsforhold, men vi vil ikke gå for dybt ind i dette emne.

2. Samtidighedsproblem

Lad os tage et simpelt eksempel for at se forskellen mellem atomklasser og flygtige søgeord. Forestil dig, at vi forsøger at skabe en tæller, der fungerer i et flertrådsmiljø.

I teorien kan enhver applikationstråd øge denne tællers værdi. Lad os begynde at implementere det med en naiv tilgang og vil tjekke, hvilke problemer der vil opstå:

public class UnsafeCounter {
    
    private int counter;
    
    int getValue() {
        return counter;
    }
    
    void increment() {
        counter++;
    }
}

Dette er en perfekt fungerende tæller, men desværre kun til en enkelt-trådet applikation. Denne tilgang vil lide af synligheds- og synkroniseringsproblemer i et flertrådsmiljø. I store applikationer kan det skabe vanskeligheder at spore fejl og endda korrupte brugernes data.

3. Synlighedsproblem

Et synlighedsproblem er et af problemerne, når du arbejder i en flertrådsapplikation. Synlighedsproblemet er tæt forbundet med Java-hukommelsesmodellen.

I multitrådede applikationer har hver tråd sin cacheversion af delte ressourcer og opdaterer værdierne i eller fra hovedhukommelsen baseret på begivenheder eller en tidsplan.

Trådcachen og hovedhukommelsesværdierne kan variere. Derfor, selvom en tråd opdaterer værdierne i hovedhukommelsen, er disse ændringer ikke øjeblikkeligt synlige for andre tråde. Dette kaldes et synlighedsproblem.

Det flygtige søgeord hjælper os med at løse dette problem ved at omgå caching i en lokal tråd. Således flygtig variabler er synlige for alle tråde, og alle disse tråde vil se den samme værdi. Derfor, når en tråd opdaterer værdien, vil alle tråde se den nye værdi. Vi kan tænke på det som et observatørmønster på lavt niveau og kan omskrive den tidligere implementering:

public class UnsafeVolatileCounter {
    
    private volatile int counter;
    
    public int getValue() {
        return counter;
    }
    
    public void increment() {
        counter++;
    }
}

Eksemplet ovenfor forbedrer tælleren og løser problemet med synlighed. Vi har dog stadig et synkroniseringsproblem, og vores tæller vil ikke fungere korrekt i et multithreaded-miljø.

4. Synkroniseringsproblem

Selvom den er flygtig søgeord hjælper os med synlighed, vi har stadig et andet problem. I vores inkrementeksempel udfører vi to operationer med variablen count. Først læser vi denne variabel og tildeler den derefter en ny værdi. Det betyder, at inkrementoperationen ikke er atomær.

Det, vi står over for her, er en racetilstand . Hver tråd skal læse værdien først, øge den og derefter skrive den tilbage. Problemet vil opstå, når flere tråde begynder at arbejde med værdien, og læser den før en anden skriver den.

På denne måde kan en tråd tilsidesætte resultatet skrevet af en anden tråd. Den synkroniserede søgeord kan løse dette problem. Denne tilgang kan dog skabe en flaskehals, og det er ikke den mest elegante løsning på dette problem.

5. Atomværdier

Atomværdier giver en bedre og mere intuitiv måde at håndtere dette problem på. Deres grænseflade giver os mulighed for at interagere med og opdatere værdier uden et synkroniseringsproblem.

Internt sikrer atomklasser, at stigningen i dette tilfælde vil være en atomoperation. Således kan vi bruge det til at skabe en trådsikker implementering:

public class SafeAtomicCounter {
    private final AtomicInteger counter = new AtomicInteger(0);
    
    public int getValue() {
        return counter.get();
    }
    
    public void increment() {
        counter.incrementAndGet();
    }
}

Vores endelige implementering er trådsikker og kan bruges i en flertrådsapplikation. Det adskiller sig ikke væsentligt fra vores første eksempel, og kun ved at bruge atomklasser kunne vi løse problemer med synlighed og synkronisering i den flertrådede kode.

6. Konklusion

I denne artikel lærte vi, at vi skal være meget forsigtige, når vi arbejder i et multithreading-miljø. Fejlene og problemerne kan være svære at spore og vil sandsynligvis ikke dukke op under fejlretning. Derfor er det vigtigt at vide, hvordan Java håndterer disse situationer.

Det flygtige  søgeord kan hjælpe med synlighedsproblemer og løse problemet med iboende atomoperationer. Indstilling af et flag er et af eksemplerne, hvor flygtige søgeord kan være nyttigt.

Atomvariable hjælper med at håndtere ikke-atomare operationer som inkrement-dekrement eller andre operationer, der skal læse værdien, før du tildeler en ny. Atomværdier er en enkel og bekvem måde at løse synkroniseringsproblemer i vores kode.

Som altid er kildekoden til eksemplerne tilgængelig på GitHub.


Java tag