Kiedy używać AtomicReference w Javie?

Kiedy używamy AtomicReference?

Czy potrzebne jest tworzenie obiektów we wszystkich programach wielowątkowych?

Podaj prosty przykład użycia Atomikreferencji.

Author: Ravindra babu, 2010-10-19

8 answers

Odniesienia atomowe powinny być używane w ustawieniach, w których należy wykonać proste odniesienia atomowe (tj. thread safe, nietrywialne) operacje na odniesienia, dla których synchronizacja oparta na monitorze nie jest odpowiednia. Załóżmy, że chcesz sprawdzić, czy określone pole jest ustawione tylko wtedy, gdy stan obiektu pozostaje taki, jak ostatnio sprawdzany:

AtomicReference<Object> cache = new AtomicReference<Object>();

Object cachedValue = new Object();
cache.set(cachedValue);

//... time passes ...
Object cachedValueToUpdate = cache.get();
//... do some work to transform cachedValueToUpdate into a new version
Object newValue = someFunctionOfOld(cachedValueToUpdate);
boolean success = cache.compareAndSet(cachedValue,cachedValueToUpdate);

Ze względu na semantykę odniesienia atomowego, można to zrobić nawet jeśli obiekt {[1] } jest współdzielony między wątkami, bez użycia synchronized. W Generale, lepiej używać synchronizatorów lub frameworka java.util.concurrent, niż nagiego Atomic*, chyba że wiesz, co robisz.

Dwa doskonałe odniesienia do martwego drzewa, które wprowadzą cię w ten temat:

Zauważ, że (nie wiem czy to zawsze była prawda) Referencja przypisanie (tzn. =) samo w sobie jest atomowe (aktualizacja prymitywnych typów 64-bitowych, takich jaklong lub double może nie być atomowa, ale aktualizacja referencji jest zawsze atomowa, nawet jeśli jest 64-bitowa) bez jawnego użyciaAtomic*.
Zobacz specyfikacja języka Java 3ED, sekcja 17.7.

 158
Author: andersoj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-03 11:56:34

Odniesienie atomowe jest idealne do użycia, gdy trzeba udostępnić I zmienić stan niezmiennego obiektu między wieloma wątkami. To bardzo gęsta wypowiedź, więc trochę ją rozbiję.

Po pierwsze, obiekt niezmienny to obiekt, który nie ulega zmianie po zbudowaniu. Często metody obiektu niezmiennego zwracają nowe instancje tej samej klasy. Niektóre przykłady obejmują klasy owijania Long i Double, a także String, aby wymienić tylko kilka. (Według do programowania współbieżności na JVM obiekty niezmienne są krytyczną częścią współczesnej współbieżności).

Dalej, Dlaczego Atomikreferencja jest lepsza niż obiekt zmienny do dzielenia tej wspólnej wartości. Prosty przykład kodu pokaże różnicę.

volatile String sharedValue;
static final Object lock=new Object();
void modifyString(){
  synchronized(lock){
    sharedValue=sharedValue+"something to add";
  }
}

Za każdym razem, gdy chcesz zmodyfikować ciąg znaków odwołujący się do tego pola lotnego na podstawie jego bieżącej wartości, najpierw musisz uzyskać blokadę tego obiektu. Zapobiega to przedostawaniu się innych wątków w międzyczasie oraz zmiana wartości w środku Nowego ciągu. Następnie, gdy wątek wznawia się, można zatrzasnąć pracę drugiego wątku. Ale szczerze mówiąc, ten kod będzie działał, wygląda na czysty i uszczęśliwiłby większość ludzi.

Mały problem. Jest powolny. Zwłaszcza jeśli istnieje wiele sprzeczności tego obiektu blokady. To dlatego, że większość blokad wymaga wywołania systemu operacyjnego, a Twój wątek zostanie zablokowany i zostanie przełączony kontekstowo z procesora, aby zrobić miejsce dla innych procesy.

Inną opcją jest użycie AtomicRefrence.

public static AtomicReference<String> shared = new AtomicReference<>();
String init="Inital Value";
shared.set(init);
//now we will modify that value
boolean success=false;
while(!success){
  String prevValue=shared.get();
  // do all the work you need to
  String newValue=shared.get()+"lets add something";
  // Compare and set
  success=shared.compareAndSet(prevValue,newValue);
}
Dlaczego tak jest lepiej? Szczerze mówiąc, ten kod jest trochę mniej czysty niż wcześniej. Ale jest coś naprawdę ważnego, co dzieje się pod maską w AtomicRefrence, a mianowicie porównaj i zamień. Jest to pojedyncza Instrukcja procesora, a nie wywołanie systemu operacyjnego, które powoduje przełączenie. Jest to pojedyncza instrukcja na procesorze. A ponieważ nie ma zamków, nie ma przełącznika kontekstowego w przypadku, gdy blokada dostaje ćwiczenia, które oszczędzają jeszcze więcej czasu!

Haczyk jest, dla AtomicReferences, to nie używa .wywołanie equals (), ale zamiast tego porównanie wartości oczekiwanej==. Upewnij się więc, że oczekiwany jest rzeczywistym obiektem zwróconym z get in the loop.

 67
Author: Erik Helleren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-17 13:29:09

Oto przykład użycia AtomicReference:

Rozważ tę klasę, która działa jako zakres liczb i używa indywidualnych zmiennych AtmomicInteger do utrzymania dolnej i górnej granicy liczb.

public class NumberRange {
    // INVARIANT: lower <= upper
    private final AtomicInteger lower = new AtomicInteger(0);
    private final AtomicInteger upper = new AtomicInteger(0);

    public void setLower(int i) {
        // Warning -- unsafe check-then-act
        if (i > upper.get())
            throw new IllegalArgumentException(
                    "can't set lower to " + i + " > upper");
        lower.set(i);
    }

    public void setUpper(int i) {
        // Warning -- unsafe check-then-act
        if (i < lower.get())
            throw new IllegalArgumentException(
                    "can't set upper to " + i + " < lower");
        upper.set(i);
    }

    public boolean isInRange(int i) {
        return (i >= lower.get() && i <= upper.get());
    }
}

Zarówno setLower, jak i setUpper są sekwencjami check-then-act, ale nie używają wystarczającego blokowania, aby uczynić je atomowymi. Jeśli zakres liczb zostanie utrzymany (0, 10), a jeden wątek wywoła setLower(5), podczas gdy inny wątek wywoła setUpper(4), z pewnym pechowym czasem oba przejdą kontrole w zostaną zastosowane setery i obie modyfikacje. Rezultatem jest to, że zakres zawiera teraz (5, 4)nieprawidłowy stan. Tak więc, podczas gdy podstawowe Atomicintegery są bezpieczne dla wątków, Klasa composite nie jest. Można to naprawić za pomocą AtomicReference zamiast używania pojedynczych Atomicintegerów dla górnych i dolnych granic.

public class CasNumberRange {
    //Immutable
    private static class IntPair {
        final int lower;  // Invariant: lower <= upper
        final int upper;
        ...
    }
    private final AtomicReference<IntPair> values =
        new AtomicReference<IntPair>(new IntPair(0, 0));

    public int getLower() { return values.get().lower; }
    public int getUpper() { return values.get().upper; }

    public void setLower(int i) {
        while (true) {
            IntPair oldv = values.get();
            if (i > oldv.upper)
                throw new IllegalArgumentException(
                   "Can't set lower to " + i + " > upper");
            IntPair newv = new IntPair(i, oldv.upper);
            if (values.compareAndSet(oldv, newv))
                return;
        }
    }
    // similarly for setUpper
}
 23
Author: Binita Bharati,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-29 10:00:20

Można użyć AtomicReference przy stosowaniu blokad optymistycznych. Masz obiekt współdzielony i chcesz go zmienić z więcej niż 1 wątku.

  1. możesz utworzyć kopię współdzielonego obiektu
  2. Modyfikuj obiekt współdzielony
  3. musisz sprawdzić, czy obiekt współdzielony jest nadal taki sam jak poprzednio - jeśli tak, to zaktualizuj z odniesieniem do zmodyfikowanej kopii.

Ponieważ inny wątek mógł go zmodyfikować i / może modyfikować między tymi 2 krokami. Musisz to zrobić w operacja atomowa. tutaj AtomicReference może pomóc

 18
Author: HamoriZ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-12 12:30:42

Nie będę dużo mówił. Już moi szanowani koledzy Dali swój cenny wkład. pełny działający kod na ostatnim blogu powinien usunąć wszelkie nieporozumienia. Film opowiada o małym programie w wielowątkowym scenariuszu.

Niektóre ważne elementarne fakty są następujące. 1 > różne wątki mogą występować tylko na przykład i statyczne zmienne członkowskie w przestrzeni sterty. 2> Lotny odczyt lub zapis są całkowicie atomowe i serializowane / dzieje się przed i to tylko z pamięci. Mówiąc to mam na myśli, że każdy odczyt będzie następował po poprzednim zapisie w pamięci. I każdy zapis będzie następował po poprzednim odczycie z pamięci. Tak więc każdy wątek pracujący z zmienną będzie zawsze widział najbardziej aktualną wartość. AtomicReference używa tej właściwości lotnej.

Poniżej znajdują się niektóre z kodu źródłowego AtomicReference. Atomikreferencja odnosi się do odniesienia do obiektu. To odniesienie jest zmienną składową zmienną zmienną atomicreference jako poniżej.

private volatile V value;

Get() po prostu zwraca ostatnią wartość zmiennej (tak jak robi to "happens before").

public final V get()

Poniżej znajduje się najważniejsza metoda Atomikreferencji.

public final boolean  compareAndSet(V expect, V update) {
        return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}

Metoda compareAndSet(expect,update) wywołuje metodę compareAndSwapObject() niebezpiecznej klasy Javy. To wywołanie metody unsafe wywołuje wywołanie natywne, które wywołuje pojedynczą instrukcję do procesora. "expect " I" update " każda Referencja obiekt.

Wtedy i tylko wtedy, gdy zmienna składowa instancji AtomicReference " value "odnosi się do tego samego obiektu, do którego odnosi się" expect"," update "jest teraz przypisane do tej zmiennej instancji i zwracane jest" true". W przeciwnym razie zwracana jest wartość false. Wszystko odbywa się atomicznie. Żaden inny wątek nie może przechwycić pomiędzy. Ponieważ jest to operacja pojedynczego procesora (Magia nowoczesnej architektury komputera), często jest szybsza niż użycie zsynchronizowanego bloku. Ale pamiętaj, że gdy wiele zmienne muszą być aktualizowane atomicznie, Atomikreferencja nie pomoże.

Chciałbym dodać pełnowartościowy kod, który można uruchomić w eclipse. Oczyściłoby to wiele nieporozumień. Tutaj 22 użytkowników (wątki mit) stara się zarezerwować 20 miejsc. Poniżej znajduje się fragment kodu, po którym następuje pełny kod.

Fragment kodu, w którym 22 użytkowników próbuje zarezerwować 20 miejsc.

for (int i = 0; i < 20; i++) {// 20 seats
            seats.add(new AtomicReference<Integer>());
        }
        Thread[] ths = new Thread[22];// 22 users
        for (int i = 0; i < ths.length; i++) {
            ths[i] = new MyTh(seats, i);
            ths[i].start();
        }

Poniżej znajduje się Pełny kod uruchamiający.

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

public class Solution {

    static List<AtomicReference<Integer>> seats;// Movie seats numbered as per
                                                // list index

    public static void main(String[] args) throws InterruptedException {
        // TODO Auto-generated method stub
        seats = new ArrayList<>();
        for (int i = 0; i < 20; i++) {// 20 seats
            seats.add(new AtomicReference<Integer>());
        }
        Thread[] ths = new Thread[22];// 22 users
        for (int i = 0; i < ths.length; i++) {
            ths[i] = new MyTh(seats, i);
            ths[i].start();
        }
        for (Thread t : ths) {
            t.join();
        }
        for (AtomicReference<Integer> seat : seats) {
            System.out.print(" " + seat.get());
        }
    }

    /**
     * id is the id of the user
     * 
     * @author sankbane
     *
     */
    static class MyTh extends Thread {// each thread is a user
        static AtomicInteger full = new AtomicInteger(0);
        List<AtomicReference<Integer>> l;//seats
        int id;//id of the users
        int seats;

        public MyTh(List<AtomicReference<Integer>> list, int userId) {
            l = list;
            this.id = userId;
            seats = list.size();
        }

        @Override
        public void run() {
            boolean reserved = false;
            try {
                while (!reserved && full.get() < seats) {
                    Thread.sleep(50);
                    int r = ThreadLocalRandom.current().nextInt(0, seats);// excludes
                                                                            // seats
                                                                            //
                    AtomicReference<Integer> el = l.get(r);
                    reserved = el.compareAndSet(null, id);// null means no user
                                                            // has reserved this
                                                            // seat
                    if (reserved)
                        full.getAndIncrement();
                }
                if (!reserved && full.get() == seats)
                    System.out.println("user " + id + " did not get a seat");
            } catch (InterruptedException ie) {
                // log it
            }
        }
    }

}    
 3
Author: sankar banerjee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-31 06:41:55

Kiedy używamy AtomicReference?

AtomicReference jest elastycznym sposobem na aktualizację wartości zmiennej atomicznie bez użycia synchronizacji.

AtomicReference obsługa bezpiecznego programowania wątków bez blokady na pojedynczych zmiennych.

Istnieje wiele sposobów osiągnięcia bezpieczeństwa wątku przy wysokim poziomie równoczesnym API. Zmienna atomowa jest jedną z wielu opcji.

Lock obiekty obsługują idiomy blokujące, które upraszczają wiele aplikacje współbieżne.

Executors definiowanie interfejsu API wysokiego poziomu do uruchamiania i zarządzania wątkami. Implementacje executora dostarczane przez Javę.util.współbieżne zarządzanie pulą wątków nadaje się do zastosowań na dużą skalę.

współbieżne Kolekcje ułatwiają zarządzanie dużymi zbiorami danych i mogą znacznie zmniejszyć potrzebę synchronizacji.

zmienne atomowe mają funkcje, które minimalizują synchronizację i pomagają uniknąć pamięci błędy konsystencji.

Podaj prosty przykład użycia Atomikreferencji.

Przykładowy kod z AtomicReference:

String initialReference = "value 1";

AtomicReference<String> someRef =
    new AtomicReference<String>(initialReference);

String newReference = "value 2";
boolean exchanged = someRef.compareAndSet(initialReference, newReference);
System.out.println("exchanged: " + exchanged);

Czy potrzebne jest tworzenie obiektów we wszystkich programach wielowątkowych?

Nie musisz używać AtomicReference we wszystkich programach wielowątkowych.

Jeśli chcesz chronić pojedynczą zmienną, użyj AtomicReference. Jeśli chcesz zabezpieczyć blok kodu, użyj innych konstrukcji, takich jak Lock /synchronized itd.

 2
Author: Ravindra babu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-07 07:40:04

Oto bardzo prosty przypadek użycia i nie ma nic wspólnego z bezpieczeństwem wątku.

Aby współdzielić obiekt pomiędzy wywołaniami lambda, {[1] } jest opcją :

public void doSomethingUsingLambdas() {

    AtomicReference<YourObject> yourObjectRef = new AtomicReference<>();

    soSomethingThatTakesALambda(() -> {
        yourObjectRef.set(youObject);
    });

    soSomethingElseThatTakesALambda(() -> {
        YourObject yourObject = yourObjectRef.get();
    });
}

Nie mówię, że jest to dobry projekt (to tylko trywialny przykład), ale jeśli masz przypadek, w którym musisz dzielić obiekt między wywołania lambda, AtomicReference jest opcją.

W rzeczywistości możesz użyć dowolnego obiektu, który zawiera odniesienie, nawet kolekcji, która ma tylko jeden element. Jednak AtomicReference idealnie pasuje.

 0
Author: Benny Bottema,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-23 09:00:22

Innym prostym przykładem jest modyfikacja bezpiecznego wątku w obiekcie sesji.

public PlayerScore getHighScore() {
    ServletContext ctx = getServletConfig().getServletContext();
    AtomicReference<PlayerScore> holder 
        = (AtomicReference<PlayerScore>) ctx.getAttribute("highScore");
    return holder.get();
}

public void updateHighScore(PlayerScore newScore) {
    ServletContext ctx = getServletConfig().getServletContext();
    AtomicReference<PlayerScore> holder 
        = (AtomicReference<PlayerScore>) ctx.getAttribute("highScore");
    while (true) {
        HighScore old = holder.get();
        if (old.score >= newScore.score)
            break;
        else if (holder.compareAndSet(old, newScore))
            break;
    } 
}

Źródło: http://www.ibm.com/developerworks/library/j-jtp09238/index.html

 -1
Author: Dherik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-15 13:02:43