Do czego służy zmienne słowo kluczowe

672

W pracy dzisiaj spotkałem volatilesłowo kluczowe w Javie. Nie znając go zbyt dobrze, znalazłem wyjaśnienie:

Teoria i praktyka Java: zarządzanie zmiennością

Biorąc pod uwagę szczegółowość, w której artykuł wyjaśnia dane słowo kluczowe, czy kiedykolwiek go używałeś, czy widziałeś przypadek, w którym możesz użyć tego słowa kluczowego we właściwy sposób?

Richard
źródło

Odpowiedzi:

742

volatilema semantykę widoczności pamięci. Zasadniczo wartość volatilepola staje się widoczna dla wszystkich czytelników (w szczególności innych wątków) po zakończeniu operacji zapisu. Bez tego volatileczytelnicy mogliby zobaczyć nie zaktualizowaną wartość.

Aby odpowiedzieć na twoje pytanie: Tak, używam volatilezmiennej, aby kontrolować, czy jakiś kod kontynuuje pętlę. Pętla sprawdza volatilewartość i kontynuuje, jeśli jest true. Warunek można ustawić false, wywołując metodę „stop”. Pętla widzi falsei kończy się, gdy testuje wartość po zakończeniu wykonywania metody zatrzymania.

Książka „ Java Concurrency in Practice ”, którą gorąco polecam, daje dobre wyjaśnienie volatile. Ta książka została napisana przez tę samą osobę, która napisała artykuł IBM wymieniony w pytaniu (w rzeczywistości cytuje swoją książkę na dole tego artykułu). Używam tego, volatileco jego artykuł nazywa „flagą statusu wzorca 1”.

Jeśli chcesz dowiedzieć się więcej o tym, jak volatiledziała pod maską, przeczytaj model pamięci Java . Jeśli chcesz wyjść poza ten poziom, sprawdź dobrą książkę o architekturze komputerowej, taką jak Hennessy i Patterson, i poczytaj o spójności i spójności pamięci podręcznej.

Greg Mattes
źródło
118
Ta odpowiedź jest poprawna, ale niepełna. Pomija ważną właściwość, volatilektóra pojawiła się wraz z nowym modelem pamięci Java zdefiniowanym w JSR 133: że gdy wątek czyta volatilezmienną, widzi nie tylko wartość ostatnio zapisaną przez inny wątek, ale także wszystkie inne zapisuje do innych zmiennych, które były widoczne w tym drugim wątku w momencie volatilepisania. Zobacz tę odpowiedź i odniesienie .
Adam Zalcman
46
Dla początkujących poprosiłbym cię o pokazanie kodu (proszę?)
Hungry Blue Dev
6
Artykuł powiązany z pytaniem zawiera przykłady kodu.
Greg Mattes
Myślę, że link „Hennessy & Patterson” jest zepsuty. Link do „modelu pamięci Java” faktycznie prowadzi do specyfikacji języka Java Oracle „Rozdział 17. Wątki i blokady”.
Kris,
2
@fefrei: „natychmiast” to termin potoczny. Oczywiście nie można tego zagwarantować, gdy ani algorytmy synchronizacji wykonania, ani harmonogramu wątków nie są określone. Jedynym sposobem, aby program dowiedział się, czy odczyt niestabilny następuje po danym zapisie lotnym, jest sprawdzenie, czy widziana wartość jest oczekiwaną zapisaną.
Holger,
177

„… Zmienny modyfikator gwarantuje, że każdy wątek odczytujący pole zobaczy ostatnią zapisaną wartość”. - Josh Bloch

Jeśli zastanawiasz się nad użyciem volatile, przeczytaj pakiet, java.util.concurrentktóry dotyczy zachowania atomowego.

Wpis w Wikipedii na temat wzorca Singleton pokazuje niestabilność w użyciu.

Ande TURNER
źródło
18
Dlaczego są zarówno słowa kluczowe, jak volatilei synchronizedsłowa kluczowe?
ptkato
5
Artykuł w Wikipedii na temat Wzoru Singletona bardzo się zmienił od tamtego czasu i nie zawiera już wspomnianego volatileprzykładu. Można go znaleźć w zarchiwizowanej wersji .
bskp
1
@ptkato Te dwa słowa kluczowe służą zupełnie innym celom, więc pytanie nie ma większego sensu jako porównanie, chociaż oba są powiązane ze współbieżnością. To tak, jakby mówiąc: „Dlaczego istnieją zarówno voidi publicsłowa kluczowe”.
DavidS,
134

Ważna uwaga na temat volatile:

  1. Synchronizacja w Javie jest możliwe za pomocą słów kluczowych, Java synchronizedi volatilei zamki.
  2. W Javie nie możemy mieć synchronizedzmiennej. Używanie synchronizedsłowa kluczowego ze zmienną jest nielegalne i spowoduje błąd kompilacji. Zamiast używać synchronizedzmiennej w Javie, możesz użyć volatilezmiennej java , która poinstruuje wątki JVM, aby odczytały wartość volatilezmiennej z pamięci głównej i nie buforowały jej lokalnie.
  3. Jeśli zmienna nie jest współużytkowana przez wiele wątków, nie trzeba używać volatilesłowa kluczowego.

źródło

Przykładowe użycie volatile:

public class Singleton {
    private static volatile Singleton _instance; // volatile variable
    public static Singleton getInstance() {
        if (_instance == null) {
            synchronized (Singleton.class) {
                if (_instance == null)
                    _instance = new Singleton();
            }
        }
        return _instance;
    }
}

Instancję tworzymy leniwie w momencie otrzymania pierwszego żądania.

Jeśli nie utworzymy _instancezmiennej, volatileWątek, który tworzy instancję, Singletonnie będzie w stanie komunikować się z drugim wątkiem. Więc jeśli wątek A tworzy instancję Singleton i zaraz po utworzeniu, procesor psuje itp., Wszystkie inne wątki nie będą mogły zobaczyć wartości _instancejako nie zerowej i będą wierzyć, że wciąż jest przypisana zerowa.

Dlaczego to się dzieje? Ponieważ wątki czytnika nie blokują się i dopóki wątek zapisujący nie wyjdzie z synchronizowanego bloku, pamięć nie zostanie zsynchronizowana, a wartość _instancenie zostanie zaktualizowana w pamięci głównej. W przypadku słowa kluczowego Volatile w Javie jest to obsługiwane przez samą Javę i takie aktualizacje będą widoczne dla wszystkich wątków czytelników.

Wniosek : volatilesłowo kluczowe służy również do komunikowania zawartości pamięci między wątkami.

Przykładowe użycie bez lotnych:

public class Singleton{    
    private static Singleton _instance;   //without volatile variable
    public static Singleton getInstance(){   
          if(_instance == null){  
              synchronized(Singleton.class){  
               if(_instance == null) _instance = new Singleton(); 
      } 
     }   
    return _instance;  
    }

Powyższy kod nie jest bezpieczny dla wątków. Mimo że ponownie sprawdza wartość instancji w bloku zsynchronizowanym (ze względu na wydajność), kompilator JIT może zmienić porządek kodu bajtowego w taki sposób, aby odwołanie do instancji zostało ustawione przed zakończeniem wykonywania przez konstruktor. Oznacza to, że metoda getInstance () zwraca obiekt, który mógł nie zostać całkowicie zainicjowany. Aby kod był bezpieczny dla wątków, można użyć słowa kluczowego volatile od Java 5 dla zmiennej instancji. Zmienne oznaczone jako lotne są widoczne tylko dla innych wątków, gdy konstruktor obiektu całkowicie zakończy wykonywanie.
Źródło

wprowadź opis zdjęcia tutaj

volatileużycie w Javie :

Szybkie iteratory są zwykle implementowane przy użyciu volatilelicznika na obiekcie listy.

  • Gdy lista jest aktualizowana, licznik jest zwiększany.
  • Po utworzeniu an Iteratorbieżąca wartość licznika jest osadzana w Iteratorobiekcie.
  • Gdy Iteratorwykonywana jest operacja, metoda porównuje dwie wartości licznika i wyrzuca a, ConcurrentModificationExceptionjeśli są one różne.

Implementacja iteratorów odpornych na awarie jest zwykle niewielka. Zazwyczaj polegają na właściwościach struktur danych implementacji określonej listy. Nie ma ogólnego wzorca.

Premraj
źródło
2
„Szybkie i niezawodne iteratory są zwykle wdrażane przy użyciu zmiennego
Vsevolod Golovanov
czy podwójne sprawdzanie _instance jest bezpieczne? myślałem, że nie są bezpieczne nawet przy lotnych
Dexters
„który poinstruuje wątki JVM, aby odczytywały wartość zmiennej lotnej z pamięci głównej i nie buforowały jej lokalnie”. dobry punkt
Humoyun Ahmad,
Dla bezpieczeństwa wątków można również iść private static final Singleton _instance;.
Chris311,
53

volatile jest bardzo przydatny do zatrzymywania wątków.

Nie dlatego, że powinieneś pisać własne wątki, Java 1.6 ma wiele fajnych pul wątków. Ale jeśli jesteś pewien, że potrzebujesz wątku, musisz wiedzieć, jak go zatrzymać.

Wzór, którego używam do wątków, to:

public class Foo extends Thread {

  private volatile boolean close = false;

  public void run() {
    while(!close) {
      // do work
    }
  }
  public void close() {
    close = true;
    // interrupt here if needed
  }
}

W powyższym segmencie kodu odczyt wątku closew pętli while różni się od tego, który wywołuje close(). Bez lotnego wątek prowadzący pętlę może nigdy nie zobaczyć zmiany zamykania.

Zauważ, że nie ma potrzeby synchronizacji

Pirolistyczny
źródło
2
Zastanawiam się, dlaczego to jest nawet konieczne. Czy nie jest to konieczne tylko wtedy, gdy inne wątki muszą reagować na zmianę statusu tego wątku w taki sposób, że zagrożona jest synchronizacja wątków?
Jori
27
@Jori, potrzebujesz zmiennej, ponieważ odczyt wątku close w pętli while różni się od tego, który wywołuje close (). Bez lotnego wątek prowadzący pętlę może nigdy nie zobaczyć zmiany zamykania.
Pirolistyczny
czy powiedziałbyś, że istnieje korzyść między zatrzymaniem takiego wątku lub użyciem metod Thread # interrupt () i Thread # isInterrupted ()?
Ricardo Belchior,
2
@Pyrolistic - Czy zaobserwowałeś wątek, który nigdy nie widział zmiany w praktyce? Czy możesz rozszerzyć przykład, aby niezawodnie wywołać ten problem? Jestem ciekawy, ponieważ wiem, że użyłem (i widziałem, jak inni używają) kodu, który jest w zasadzie identyczny z przykładem, ale bez volatilesłowa kluczowego, i zawsze wydaje się działać dobrze.
aroth
2
@aroth: w dzisiejszych maszynach JVM można zaobserwować, że w praktyce, nawet w najprostszych przykładach, nie można jednak w wiarygodny sposób odtworzyć tego zachowania. Przy bardziej złożonych aplikacjach czasami masz inne działania z gwarancjami widoczności pamięci w kodzie, które sprawiają, że działa, co jest szczególnie niebezpieczne, ponieważ nie wiesz, dlaczego to działa, a prosta, pozornie niezwiązana zmiana w kodzie może uszkodzić Twój wniosek…
Holger
31

Jednym z powszechnych przykładów użycia volatilejest użycie volatile booleanzmiennej jako flagi do zakończenia wątku. Jeśli rozpocząłeś wątek i chcesz móc bezpiecznie przerwać go z innego wątku, możesz okresowo sprawdzać flagę. Aby go zatrzymać, ustaw flagę na true. Tworząc flagę volatile, możesz upewnić się, że sprawdzający ją wątek zobaczy, że został ustawiony przy następnym sprawdzeniu, nawet bez użycia synchronizedbloku.

Dave L.
źródło
27

Zmienna zadeklarowana za pomocą volatilesłowa kluczowego ma dwie główne cechy, dzięki którym jest wyjątkowa.

  1. Jeśli mamy zmienną zmienną, nie można jej buforować do pamięci podręcznej komputera (mikroprocesora) żadnym wątkiem. Dostęp zawsze odbywał się z pamięci głównej.

  2. Jeśli operacja zapisu przebiega na zmiennej zmiennej i nagle żądana jest operacja odczytu , gwarantuje się, że operacja zapisu zostanie zakończona przed operacją odczytu .

Dwie powyższe cechy to dedukują

  • Wszystkie wątki czytające zmienną zmienną na pewno odczytają najnowszą wartość. Ponieważ żadna wartość z pamięci podręcznej nie może jej skazić. Również żądanie odczytu zostanie spełnione dopiero po zakończeniu bieżącej operacji zapisu.

Z drugiej strony

  • Jeśli zbadamy dalej punkt 2 , o którym wspomniałem, zobaczymy, że volatilesłowo kluczowe jest idealnym sposobem na utrzymanie wspólnej zmiennej, która ma „n” liczbę wątków czytnika i tylko jeden wątek zapisujący, aby uzyskać do niej dostęp. Po dodaniu volatilesłowa kluczowego jest to gotowe. Żadnych innych kosztów związanych z bezpieczeństwem nici.

Odwrotnie,

Nie możemy używać volatilesłowa kluczowego wyłącznie w celu zaspokojenia wspólnej zmiennej, która ma dostęp do więcej niż jednego wątku pisującego .

Supun Wijerathne
źródło
3
To wyjaśnia różnicę między lotną i zsynchronizowaną.
ajay
13

Nikt nie wspominał o traktowaniu operacji odczytu i zapisu dla typu długiej i podwójnej zmiennej. Odczyty i zapisy są operacjami atomowymi dla zmiennych referencyjnych i większości prymitywnych zmiennych, z wyjątkiem typów długich i podwójnych zmiennych, które muszą używać zmiennego słowa kluczowego, aby być operacjami atomowymi. @połączyć

Donatello Boccaforno
źródło
Aby było jeszcze jaśniej, NIE ma POTRZEBY ustawiania zmiennej lotnej boolowskiej, ponieważ odczyt i zapis boolean JUŻ JUŻ atomowy.
Kai Wang
2
@KaiWang nie musisz używać lotnych na boolach do celów atomowości. Ale z pewnością możesz to zrobić ze względu na widoczność. Czy to chciałeś powiedzieć?
SusanW,
12

Tak, zmienna musi być używana, gdy chcesz, aby zmienna zmienna była dostępna dla wielu wątków. Nie jest to bardzo częsty przypadek użycia, ponieważ zazwyczaj musisz wykonać więcej niż jedną operację atomową (np. Sprawdź stan zmiennej przed jej modyfikacją), w którym to przypadku zamiast tego użyjesz zsynchronizowanego bloku.

Jaganaganowicz
źródło
10

Moim zdaniem dwa ważne scenariusze inne niż zatrzymanie wątku, w których używane jest niestabilne słowo kluczowe:

  1. Podwójnie sprawdzony mechanizm blokujący . Często używany w wzorze projektowym Singleton. W tym przypadku obiekt singletonowy musi zostać uznany za lotny .
  2. Sztuczne budzenie . Wątek może czasem budzić się z połączenia oczekującego, nawet jeśli nie zostało wysłane powiadomienie. To zachowanie nazywa się fałszywym budzeniem. Można temu przeciwdziałać za pomocą zmiennej warunkowej (flaga logiczna). Umieść wywołanie wait () w pętli while, o ile flaga jest prawdziwa. Więc jeśli wątek budzi się z połączenia oczekującego z powodów innych niż Notify / NotifyAll, to napotyka flagę jest nadal prawdą, a zatem połączenia czekają ponownie. Przed wywołaniem powiadom powiadom ustaw tę flagę na true. W takim przypadku flaga logiczna jest deklarowana jako niestabilna .
Aniket Thakur
źródło
Cała sekcja # 2 wydaje się bardzo zagubiona, łączy zagubione powiadomienia, fałszywe budzenie i problemy z widocznością pamięci. Również jeśli wszystkie zastosowania flagi są zsynchronizowane, zmienna jest zbędna. Myślę, że rozumiem, ale fałszywe budzenie nie jest właściwym terminem. Proszę o wyjaśnienie.
Nathan Hughes,
5

Będziesz musiał użyć słowa kluczowego „niestabilnego” lub „zsynchronizowanego” oraz wszelkich innych narzędzi i technik kontroli współbieżności, które możesz mieć do dyspozycji, jeśli tworzysz aplikację wielowątkową. Przykładem takiej aplikacji są aplikacje komputerowe.

Jeśli tworzysz aplikację, która zostałaby wdrożona na serwerze aplikacji (Tomcat, JBoss AS, Glassfish itp.), Nie musisz samodzielnie obsługiwać kontroli współbieżności, ponieważ jest ona już adresowana przez serwer aplikacji. W rzeczywistości, jeśli dobrze zapamiętałem, standard Java EE zabrania jakiejkolwiek kontroli współbieżności w serwletach i EJB, ponieważ jest on częścią warstwy „infrastruktury”, którą powinieneś być wolny od obsługi. Kontrolę współbieżności wykonujesz w takiej aplikacji tylko wtedy, gdy implementujesz obiekty singletonowe. Ten problem został rozwiązany nawet w przypadku łączenia elementów za pomocą frameworkd, takich jak Spring.

Tak więc w większości przypadków programowania Java, w których aplikacja jest aplikacją internetową i używa frameworka IoC, takiego jak Spring lub EJB, nie trzeba używać „niestabilności”.

Rudi Adianto
źródło
5

volatilegwarantuje tylko, że wszystkie wątki, nawet same, rosną. Na przykład: licznik widzi tę samą powierzchnię zmiennej w tym samym czasie. Nie jest używany zamiast zsynchronizowanego, atomowego lub innego, całkowicie synchronizuje odczyty. Proszę nie porównywać go z innymi słowami kluczowymi Java. Jak pokazano w poniższym przykładzie, operacje na zmiennych lotnych są również atomowe, nie udaje im się to od razu.

package io.netty.example.telnet;

import java.util.ArrayList;
import java.util.List;

public class Main {

    public static volatile  int a = 0;
    public static void main(String args[]) throws InterruptedException{

        List<Thread> list = new  ArrayList<Thread>();
        for(int i = 0 ; i<11 ;i++){
            list.add(new Pojo());
        }

        for (Thread thread : list) {
            thread.start();
        }

        Thread.sleep(20000);
        System.out.println(a);
    }
}
class Pojo extends Thread{
    int a = 10001;
    public void run() {
        while(a-->0){
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Main.a++;
            System.out.println("a = "+Main.a);
        }
    }
}

Nawet niestabilne lub nie, wyniki zawsze będą się różnić. Ale jeśli użyjesz AtomicInteger, jak poniżej, wyniki będą zawsze takie same. To samo dotyczy również synchronizacji.

    package io.netty.example.telnet;

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

    public class Main {

        public static volatile  AtomicInteger a = new AtomicInteger(0);
        public static void main(String args[]) throws InterruptedException{

            List<Thread> list = new  ArrayList<Thread>();
            for(int i = 0 ; i<11 ;i++){
                list.add(new Pojo());
            }

            for (Thread thread : list) {
                thread.start();
            }

            Thread.sleep(20000);
            System.out.println(a.get());

        }
    }
    class Pojo extends Thread{
        int a = 10001;
        public void run() {
            while(a-->0){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Main.a.incrementAndGet();
                System.out.println("a = "+Main.a);
            }
        }
    }
fatih tekin
źródło
4

Tak, używam go dość często - może być bardzo przydatny w przypadku kodu wielowątkowego. Artykuł, który wskazałeś, jest dobry. Chociaż należy pamiętać o dwóch ważnych sprawach:

  1. Powinieneś używać lotnego tylko wtedy, gdy całkowicie rozumiesz, co on robi i czym różni się od synchronizacji. W wielu sytuacjach zmienność wydaje się na powierzchni być prostszą, bardziej wydajną alternatywą dla synchronizacji, gdy często lepsze zrozumienie zmienności wyjaśnia, że ​​synchronizacja jest jedyną opcją, która by działała.
  2. volatile w rzeczywistości nie działa w wielu starszych JVM, chociaż działa zsynchronizowana. Pamiętam dokument, który odwoływał się do różnych poziomów wsparcia w różnych maszynach JVM, ale niestety nie mogę go teraz znaleźć. Zdecydowanie spójrz na to, jeśli używasz Java w wersji 1.5 lub jeśli nie masz kontroli nad maszynami JVM, na których będzie działał Twój program.
MB.
źródło
4

Każdy wątek uzyskujący dostęp do zmiennego pola odczyta jego bieżącą wartość przed kontynuowaniem, zamiast (potencjalnie) przy użyciu buforowanej wartości.

Tylko zmienna składowa może być lotna lub przejściowa.

tstuber
źródło
3

Zdecydowanie tak. (I to nie tylko w Javie, ale także w języku C #). Są chwile, kiedy trzeba uzyskać lub ustawić wartość, która na pewno gwarantuje działanie atomowe na danej platformie, na przykład int lub boolean, ale nie wymaga nadwyżka blokady gwintu. Zmienne słowo kluczowe pozwala upewnić się, że po odczytaniu wartości otrzymujesz aktualną wartość, a nie buforowaną wartość, która właśnie stała się nieaktualna przez zapis w innym wątku.

dgvid
źródło
3

Istnieją dwa różne zastosowania niestabilnego słowa kluczowego.

  1. Zapobiega odczytywaniu przez JVM wartości z rejestru (przyjęcie jako pamięć podręczna) i wymusza odczytanie jej wartości z pamięci.
  2. Zmniejsza ryzyko błędów niespójności pamięci.

Uniemożliwia JVM odczytywanie wartości w rejestrze i wymusza odczytanie jego wartości z pamięci.

Zajęty flaga jest stosowany w celu zapobiegania dalszemu wątku, kiedy urządzenie jest zajęte, a flaga nie jest zabezpieczone przez blokadę:

while (busy) {
    /* do something else */
}

Wątek testowy będzie kontynuowany, gdy inny wątek wyłączy flagę zajętości :

busy = 0;

Ponieważ jednak zajęty jest często uzyskiwany w wątku testowym, JVM może zoptymalizować test poprzez umieszczenie wartości zajętości w rejestrze, a następnie przetestować zawartość rejestru bez odczytywania wartości zajętości w pamięci przed każdym testem. Wątek testowy nigdy nie zobaczy zmiany zajętości, a drugi wątek zmieni tylko wartość zajętości w pamięci, co spowoduje zakleszczenie. Deklaracja flagi zajętości jako lotnej wymusza odczytanie jej wartości przed każdym testem.

Zmniejsza ryzyko błędów spójności pamięci.

Zastosowanie zmiennych niestabilnych zmniejsza ryzyko błędów spójności pamięci , ponieważ każdy zapis do zmiennej niestabilnej ustanawia relację „dzieje się przed” z kolejnymi odczytami tej samej zmiennej. Oznacza to, że zmiany zmiennej lotnej są zawsze widoczne dla innych wątków.

Technika czytania, pisania bez błędów spójności pamięci nazywa się działaniem atomowym .

Działanie atomowe to takie, które skutecznie dzieje się jednocześnie. Działanie atomowe nie może zatrzymać się w środku: albo dzieje się całkowicie, albo wcale. Żadne skutki uboczne akcji atomowej nie są widoczne, dopóki akcja nie zostanie zakończona.

Poniżej znajdują się działania, które możesz określić, które są atomowe:

  • Odczyty i zapisy są atomowe dla zmiennych odniesienia i większości pierwotnych zmiennych (wszystkie typy oprócz długich i podwójnych).
  • Odczyty i zapisy są atomowe dla wszystkich zmiennych uznanych za zmienne (w tym zmiennych długich i podwójnych).

Twoje zdrowie!

Mohanraj Balasubramaniam
źródło
3

volatilemówi dla programisty, że wartość zawsze będzie aktualna. Problem polega na tym, że wartość można zapisać w różnych typach pamięci sprzętowej. Mogą to być na przykład rejestry procesora, pamięć podręczna procesora, pamięć RAM ... Rejestry СPU i pamięć podręczna procesora należą do procesora i nie mogą współużytkować danych w przeciwieństwie do pamięci RAM, która jest ratowana w środowisku wielowątkowym

wprowadź opis zdjęcia tutaj

volatileHasło mówi, że zmienna będzie odczytywane i zapisywane z / do pamięci RAM bezpośrednio . Ma pewien ślad obliczeniowy

Java 5rozszerzone volatilepoprzez wsparcie [O firmie happens-before]

Zapis do zmiennego pola następuje - przed każdym kolejnym odczytem tego pola.

volatileHasło nie leczy się race conditionsytuację, kiedy kilka wątków można napisać kilka wartości jednocześnie. Odpowiedzią jest synchronizedsłowo kluczowe [Informacje]

W rezultacie jest to bezpieczne tylko wtedy, gdy jeden wątek pisze, a inne po prostu odczytują volatilewartość

lotny vs zsynchronizowany

yoAlex5
źródło
2

Volatile wykonuje następujące czynności.

1> Odczytywanie i zapisywanie zmiennych lotnych przez różne wątki są zawsze z pamięci, a nie z własnej pamięci podręcznej wątku lub rejestru procesora. Dlatego każdy wątek zawsze dotyczy najnowszej wartości. 2> Gdy 2 różne wątki działają z tą samą instancją lub zmiennymi statycznymi w stercie, działania innych mogą być postrzegane jako nieuporządkowane. Zobacz blog Jeremy'ego Mansona na ten temat. Ale niestabilność pomaga tutaj.

Poniższy w pełni działający kod pokazuje, jak wiele wątków może być wykonywanych w predefiniowanej kolejności i drukowanych wydrukach bez użycia synchronizowanego słowa kluczowego.

thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3
thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3
thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3

Aby to osiągnąć, możemy użyć następującego pełnego kodu uruchomionego.

public class Solution {
    static volatile int counter = 0;
    static int print = 0;
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Thread[] ths = new Thread[4];
        for (int i = 0; i < ths.length; i++) {
            ths[i] = new Thread(new MyRunnable(i, ths.length));
            ths[i].start();
        }
    }
    static class MyRunnable implements Runnable {
        final int thID;
        final int total;
        public MyRunnable(int id, int total) {
            thID = id;
            this.total = total;
        }
        @Override
        public void run() {
            // TODO Auto-generated method stub
            while (true) {
                if (thID == counter) {
                    System.out.println("thread " + thID + " prints " + print);
                    print++;
                    if (print == total)
                        print = 0;
                    counter++;
                    if (counter == total)
                        counter = 0;
                } else {
                    try {
                        Thread.sleep(30);
                    } catch (InterruptedException e) {
                        // log it
                    }
                }
            }
        }
    }
}

Poniższy link do github zawiera plik readme, który daje właściwe wyjaśnienie. https://github.com/sankar4git/volatile_thread_ordering

sankar banerjee
źródło
1

Na stronie dokumentacji wyroczni pojawia się potrzeba zmiennej zmiennej, aby naprawić problemy z spójnością pamięci:

Zastosowanie zmiennych niestabilnych zmniejsza ryzyko błędów spójności pamięci, ponieważ każdy zapis do zmiennej niestabilnej ustanawia relację przed zdarzeniem z kolejnymi odczytami tej samej zmiennej.

Oznacza to, że zmiany w volatilezmiennej są zawsze widoczne dla innych wątków. Oznacza to również, że gdy wątek odczytuje zmienną zmienną, widzi nie tylko ostatnią zmianę w volatile, ale także skutki uboczne kodu, który doprowadził do zmiany.

Jak wyjaśniono w Peter Parkerodpowiedzi, przy braku volatilemodyfikatora stos każdego wątku może mieć własną kopię zmiennej. Dokonując zmiennej jakovolatile naprawiono problemy ze spójnością pamięci.

Zajrzyj na stronę samouczka z jenkovem dla lepszego zrozumienia.

Spójrz na powiązane pytanie SE, aby uzyskać więcej informacji na temat niestabilnych i przypadków użycia, aby używać niestabilnych:

Różnica między zmienną i zsynchronizowaną w Javie

Jeden praktyczny przypadek użycia:

Masz wiele wątków, które trzeba wydrukować aktualny czas w określonym formacie, na przykład: java.text.SimpleDateFormat("HH-mm-ss"). Yon może mieć jedną klasę, która konwertuje aktualny czas SimpleDateFormati aktualizuje zmienną co sekundę. Wszystkie inne wątki mogą po prostu używać tej zmiennej zmiennej do drukowania bieżącego czasu w plikach dziennika.

Ravindra babu
źródło
1

Zmienne lotne to lekka synchronizacja. Gdy wymagana jest widoczność najnowszych danych wśród wszystkich wątków, a atomowość może być zagrożona, w takich sytuacjach należy preferować Zmienne Zmienne. Odczytywanie zmiennych zmiennych zawsze zwraca ostatni zapis wykonany przez dowolny wątek, ponieważ nie są one buforowane w rejestrach ani w pamięciach podręcznych, w których inne procesory nie widzą. Volatile jest bez blokady. Używam lotnych, gdy scenariusz spełnia kryteria, jak wspomniano powyżej.

Neha Vari
źródło
-1

Klucz niestabilny, gdy jest używany ze zmienną, zapewni, że wątki czytające tę zmienną zobaczą tę samą wartość. Teraz, jeśli masz wiele wątków odczytujących i zapisujących zmienną, zmienność zmiennej nie będzie wystarczająca, a dane zostaną uszkodzone. Wątki obrazów odczytały tę samą wartość, ale każdy z nich wykonał pewne operacje (powiedzmy, że zwiększył licznik), podczas zapisywania w pamięci naruszana jest integralność danych. Dlatego konieczne jest zsynchronizowanie zmiennego (możliwe są różne sposoby)

Jeśli zmiany są wykonywane przez 1 wątek, a pozostałe muszą tylko odczytać tę wartość, niestabilność będzie odpowiednia.

Java Main
źródło
-1

Zmienna niestabilna jest zasadniczo używana do natychmiastowej aktualizacji (opróżniania) w głównej linii współdzielonej pamięci podręcznej po jej aktualizacji, dzięki czemu zmiany są natychmiast odzwierciedlane we wszystkich wątkach roboczych.

Niyaz Ahamad
źródło
-2

Poniżej znajduje się bardzo prosty kod, który demonstruje wymaganie volatilezmiennej, która jest używana do kontrolowania wykonywania wątku z innego wątku (jest to jeden scenariusz, w którym volatilejest wymagany).

// Code to prove importance of 'volatile' when state of one thread is being mutated from another thread.
// Try running this class with and without 'volatile' for 'state' property of Task class.
public class VolatileTest {
    public static void main(String[] a) throws Exception {
        Task task = new Task();
        new Thread(task).start();

        Thread.sleep(500);
        long stoppedOn = System.nanoTime();

        task.stop(); // -----> do this to stop the thread

        System.out.println("Stopping on: " + stoppedOn);
    }
}

class Task implements Runnable {
    // Try running with and without 'volatile' here
    private volatile boolean state = true;
    private int i = 0;

    public void stop() {
        state = false;
    } 

    @Override
    public void run() {
        while(state) {
            i++;
        }
        System.out.println(i + "> Stopped on: " + System.nanoTime());
    }
}

Gdy volatilenie jest używany: nigdy nie zobaczysz komunikatu „ Zatrzymano w: xxx ” nawet po „ Zatrzymanie w: xxx ”, a program będzie nadal działać.

Stopping on: 1895303906650500

Po volatileużyciu: natychmiast zobaczysz komunikat „ Zatrzymano: xxx ”.

Stopping on: 1895285647980000
324565439> Stopped on: 1895285648087300

Demo: https://repl.it/repls/SilverAgonizingObjectcode

manikanta
źródło
Do downvoter: Spróbuj wyjaśnić, dlaczego downvote? Jeśli to nie prawda, przynajmniej nauczę się, co jest nie tak. Dodałem ten sam komentarz dwa razy, ale nie wiem, kto usuwa raz za razem
manikanta