Для чого корисне непостійне ключове слово


672

На роботі сьогодні я натрапив на volatileключове слово на Java. Не дуже знайомий з цим, я знайшов таке пояснення:

Теорія та практика Java: Управління мінливістю

З огляду на деталі, в яких ця стаття пояснює питання, про яке йдеться, ви коли-небудь користуєтесь ним, чи ви могли коли-небудь побачити випадок, коли ви могли використовувати це ключове слово правильно?

Відповіді:


742

volatileмає семантику для наочності пам’яті. В основному значення volatileполя стає видимим для всіх читачів (зокрема інших потоків) після завершення операції запису на ньому. Без цього volatileчитачі могли побачити якесь неоновлене значення.

Щоб відповісти на ваше запитання: Так, я використовую volatileзмінну, щоб контролювати, чи продовжує якийсь код цикл. Цикл тестує volatileзначення і продовжує, якщо воно є true. Умову можна встановити false, викликавши метод "стоп". Цикл бачить falseі припиняється, коли він перевіряє значення після завершення методу зупинки.

Книга "Конкурс Java на практиці ", яку я дуже рекомендую, дає хороші пояснення volatile. Ця книга написана тією ж особою, яка написала статтю IBM, на яку йдеться у запитанні (насправді він цитує свою книгу внизу цієї статті). Моє використання - volatileце те, що його стаття називає "прапором статусу 1".

Якщо ви хочете дізнатися більше про те, як volatileпрацює під кришкою, прочитайте про модель пам'яті Java . Якщо ви хочете вийти за рамки цього рівня, ознайомтеся з хорошою книжкою архітектури комп’ютера, на кшталт Hennessy & Patterson, і прочитайте про послідовність кешу та послідовність кешу.


118
Ця відповідь правильна, але неповна. Це унеможливлює важливу властивість volatileцієї функції з новою моделлю пам'яті Java, визначеною в JSR 133: що, коли нитка читає volatileзмінну, вона бачить не тільки значення, останнє, записане до неї іншим потоком, а й усі інші записи в інші змінні, які були помітні в тій іншій нитці під час volatileнаписання. Дивіться цю відповідь та цю посилання .
Адам Зальцман

46
Для початківців я просив би продемонструвати якийсь код (будь ласка?)
Hungry Blue Dev

6
У статті, пов'язаній із запитанням, є приклади коду.
Грег Маттес

Я думаю, посилання "Hennessy & Patterson" порушена. А посилання на "модель пам'яті Java" насправді призводить до специфікації мови Java Oracle "Розділ 17. Нитки та замки".
Кріс

2
@fefrei: "негайно" - це розмовний термін. Звичайно, це не може бути гарантовано, коли жоден, алгоритми планування виконання та алгоритми планування потоку фактично не вказані. Єдиний спосіб, щоб програма з'ясувала, чи не змінюється читання після певного запису, - перевірити, чи є бачене значення очікуваним записаним.
Холгер

177

"... мінливий модифікатор гарантує, що будь-який потік, який читає поле, побачить останнє записане значення". - Джош Блох

Якщо ви думаєте про використання volatile, прочитайте на упаковці, java.util.concurrentяка стосується атомної поведінки.

Публікація у Вікіпедії на одиночному шаблоні демонструє нестабільність у використанні.


18
Чому існують volatileі synchronizedключові слова?
ptkato

5
Стаття Вікіпедії про Singleton Pattern сильно змінилася з тих пір і більше не містить вказаного volatileприкладу. Його можна знайти в архівованій версії .
bskp

1
@ptkato Ці два ключові слова служать зовсім іншим цілям, тому питання не має великого сенсу в якості порівняння, хоча вони обоє пов'язані з одночасністю. Це як сказати "Чому існують voidі publicключові слова, і ключові слова".
DavidS

134

Важливий момент про volatile:

  1. Синхронізація в Java можна за допомогою Java ключових слів synchronizedі volatileі замки.
  2. У Java ми не можемо мати synchronizedзмінну. Використання synchronizedключового слова зі змінною є незаконним і призведе до помилки компіляції. Замість використання synchronizedзмінної в Java, ви можете використовувати volatileзмінну java , яка доручить потокам JVM зчитувати значення volatileзмінної з основної пам'яті та не кешувати її локально.
  3. Якщо змінна не поділяється між декількома потоками, тоді не потрібно використовувати volatileключове слово.

джерело

Приклад використання 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;
    }
}

Ми створюємо екземпляр ліниво під час першого запиту.

Якщо ми не зробимо _instanceзмінну, volatileто Нитка, яка створює екземпляр Singleton, не зможе передавати інший потік. Отже, якщо Thread A створює екземпляр Singleton і відразу після створення CPU псується тощо, всі інші потоки не зможуть побачити значення _instanceяк недійсне, і вони вважатимуть, що це все-таки присвоєно нулю.

Чому це відбувається? Оскільки потоки читачів не роблять жодного блокування і поки потік запису не вийде із синхронізованого блоку, пам'ять не буде синхронізована, і значення _instanceне буде оновлено в основній пам'яті. З ключовим словом Volatile на Java цим керує сама Java, і такі оновлення будуть видимі в усіх потоках читачів.

Висновок : volatileключове слово також використовується для передачі вмісту пам'яті між потоками.

Приклад використання без летких:

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;  
    }

Код, наведений вище, не є безпечним для потоків. Хоча він ще раз перевіряє значення екземпляра в синхронізованому блоці (з міркувань продуктивності), компілятор JIT може переставити байт-код таким чином, що посилання на екземпляр встановлюється до того, як конструктор закінчить його виконання. Це означає, що метод getInstance () повертає об'єкт, який, можливо, не був ініціалізований повністю. Щоб зробити нитку коду безпечною, ключове слово мінливе може використовуватися з Java 5 для змінної екземпляра. Змінні, позначені як мінливі, стають видимими лише для інших потоків після того, як конструктор об'єкта закінчив його виконання повністю.
Джерело

введіть тут опис зображення

volatileвикористання на Java :

Ітератори виходу з ладу, як правило, реалізуються за допомогою volatileлічильника на об'єкті списку.

  • Коли список оновлюється, лічильник збільшується.
  • Коли Iteratorстворюється, поточне значення лічильника вбудовується в Iteratorоб'єкт.
  • Коли Iteratorвиконується операція, метод порівнює два значення лічильника і кидає a, ConcurrentModificationExceptionякщо вони різні.

Запровадження ітераторів безвідмовної роботи, як правило, мало важить. Вони, як правило, покладаються на властивості структур даних конкретної реалізації списку. Загальної закономірності немає.


2
"Ітератори, що виходять з ладу, зазвичай реалізуються за допомогою енергонезалежного лічильника" - це вже не так, занадто дорого: bugs.java.com/bugdatabase/view_bug.do?bug_id=6625725
Всеволод Голованов

чи безпечна подвійна перевірка на предмет _in substance? Я думав, що вони не є безпечними навіть при нестабільних
Dexters

"який дасть вказівку потокам JVM зчитувати значення мінливої ​​змінної з основної пам'яті і не кешувати її локально." хороший момент
Гумоюн Ахмад

Для безпеки потоку можна пойти і з ним private static final Singleton _instance;.
Chris311

53

volatile дуже корисно зупиняти нитки.

Не те, що вам слід писати власні теми, у Java 1.6 є багато приємних пулів потоків. Але якщо ви впевнені, що вам потрібна нитка, вам потрібно знати, як її зупинити.

Я використовую шаблон для ниток:

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
  }
}

У наведеному вище кодовому сегменті зчитування потоку closeв циклі while відрізняється від того, що викликає close(). Без енергонезалежності нитка, що працює в циклі, може ніколи не побачити зміни, щоб закрити.

Зверніть увагу, як немає потреби в синхронізації


2
Цікаво, чому це навіть потрібно. Хіба це не потрібно лише тоді, коли інші потоки повинні реагувати на зміну стану цього потоку таким чином, щоб синхронізація потоків загрожувала?
Джорі

27
@Jori, вам потрібні мінливі, оскільки зчитування потоку в циклі while відрізняється від того, що викликає close (). Без енергонезалежності нитка, що працює в циклі, може ніколи не побачити зміни, щоб закрити.
Піролістичний

Ви б сказали, що є перевага між зупинкою такої нитки або використанням методів Thread # interrupt () та Thread # isInterrupt ()?
Рікардо Бельхіор

2
@Pyrolistic - Ви спостерігали, як нитка ніколи не бачила змін на практиці? Або ви можете розширити приклад, щоб надійно викликати цю проблему? Мені цікаво, тому що я знаю, що я використовував (і бачив інших, хто використовує) код, який в основному ідентичний прикладу, але без volatileключового слова, і він, здається, завжди працює добре.
aroth

2
@aroth: у сучасних JVM ви можете помітити, що на практиці навіть з найпростішими прикладами, однак, ви не можете надійно відтворити таку поведінку. З більш складними програмами іноді у вас є інші дії з гарантіями видимості пам’яті всередині вас кодом, які змушують його працювати, що особливо небезпечно, оскільки ви не знаєте, чому це працює, а проста, очевидно, незв'язана зміна коду може зламати ваш заявка…
Holger

31

Один поширений приклад використання volatile- використовувати volatile booleanзмінну як прапор для завершення потоку. Якщо ви запустили потік, і хочете безпечно перервати її з іншої нитки, ви можете змусити періодично перевіряти прапор. Щоб зупинити це, встановіть прапор значення true. Склавши прапор volatile, ви можете переконатися, що нитка, яка його перевіряє, побачить, що він був встановлений наступного разу, коли він перевіряє його, навіть не використовуючи synchronizedблок.


27

Змінна, оголошена volatileключовим словом, має дві основні якості, які роблять її особливою.

  1. Якщо у нас є летюча змінна, вона не може бути кешована в кеш-пам'ять комп'ютера (мікропроцесора) жодним потоком. Доступ завжди відбувався з основної пам'яті.

  2. Якщо відбувається операція запису на змінній змінній, і раптом буде запрошена операція читання , гарантується, що операція запису буде завершена до операції зчитування .

Дві вищезгадані якості випливають із цього

  • Усі потоки, що читають мінливу змінну, безумовно, прочитають останнє значення. Тому що жодне кешоване значення не може його забруднити. А також запит на читання буде надано лише після завершення поточної операції запису.

А з іншого боку,

  • Якщо ми додатково дослідимо згадуване мною №2 , ми можемо побачити, що volatileключове слово є ідеальним способом підтримувати загальну змінну, яка має 'n' кількість читацьких потоків і лише один потік авторів для доступу до неї. Як тільки ми додамо volatileключове слово, це робиться. Ніяких інших накладних витрат щодо безпеки ниток.

Зрозуміло,

Ми не можемо використовувати volatileключове слово виключно, щоб задовольнити спільну змінну, яка має більше ніж один потік авторів, що має доступ до неї .


3
Цим пояснюється різниця між летючими та синхронізованими.
Аджай

13

Ніхто не згадував обробку операції читання і запису для довгого та подвійного змінних типів. Читання і запис - це атомні операції для опорних змінних і для більшості примітивних змінних, за винятком довгих і подвійних типів змінних, які повинні використовувати ключове слово, що змінюється, щоб бути атомними операціями. @link


Щоб зробити це ще більш зрозумілим, НЕ ПОТРІБНО встановлювати булеву мінливу, тому що читання та запис булевих ВИНАГО атомно.
Кай Ван

2
@KaiWang вам не потрібно використовувати летючі на булевих для цілей атомності. Але ви, безсумнівно, можете з видимості. Це ви хотіли сказати?
SusanW

12

Так, до енергонезалежних потрібно використовувати, коли ви хочете, щоб до змінної змінної було доступно декілька потоків. Це не дуже поширений випадок використання, оскільки, як правило, потрібно виконати більше однієї атомної операції (наприклад, перевірити стан змінної, перш ніж змінювати її), і в цьому випадку ви замість цього використаєте синхронізований блок.


10

На мою думку, два важливі сценарії, окрім зупинки потоку, у якому використовується нестабільне ключове слово, є:

  1. Двічі перевірений замикаючий механізм . Використовується часто в шаблоні дизайну Singleton. У цьому однотонний об'єкт потрібно визнати мінливим .
  2. Хибні пробудження . Нитка може прокинутися від дзвінка в очікуванні, навіть якщо жодного сповіщення не було здійснено. Така поведінка називається помилковим пробудженням. Цьому можна протистояти, використовуючи умовну змінну (булева прапор). Покладіть виклик wait () у цикл часу, поки прапор справжній. Отже, якщо потік прокидається з виклику очікування з будь-яких причин, окрім Notify / NotifyAll, тоді він зустрічається з прапором, як і раніше, і тому дзвінки чекають ще раз. До виклику сповіщення встановіть цей прапор істинним. У цьому випадку булевий прапор оголошується мінливим .

Весь розділ №2 здається дуже заплутаним, він заплутує втрачені сповіщення, помилкові пробудження та проблеми з видимістю пам’яті. Крім того, якщо всі звички прапора синхронізовані, тоді непостійний є зайвим. Я думаю, що я розумію, але помилкове пробудження не є правильним терміном. Будь ласка, поясніть.
Натан Х'юз

5

Вам потрібно буде використовувати ключове слово "летюче" або "синхронізоване" та будь-які інші інструменти та методи контролю сумісності, які можуть бути у вас в розпорядженні, якщо ви розробляєте багатопотокове додаток. Прикладом такого застосування є настільні програми.

Якщо ви розробляєте програму, яка буде розгорнута на сервер додатків (Tomcat, JBoss AS, Glassfish тощо), вам не доведеться самостійно керувати контролем одночасності, як це вже вирішено на сервері додатків. Насправді, якщо я правильно пам’ятав, стандарт Java EE забороняє будь-який контроль сумісності в сервлетах та EJB, оскільки він є частиною «інфраструктурного» шару, який ви повинні звільнити від обробки. Ви керуєте одночасністю в такому додатку, лише якщо реалізуєте одиночні об'єкти. Це навіть вирішено, якщо ви в'яжете свої компоненти за допомогою framed, як Spring.

Таким чином, у більшості випадків розробки Java, де програма є веб-додатком та використовує IoC-фреймворк, наприклад Spring або EJB, вам не потрібно використовувати "летючі".


5

volatileлише гарантує, що всі нитки, навіть самі, збільшуються. Наприклад: лічильник одночасно бачить те саме обличчя змінної. Він не використовується замість синхронізованого або атомного чи іншого матеріалу, він повністю робить читання синхронізованими. Будь ласка, не порівнюйте його з іншими ключовими словами java. Як показано в наведеному нижче прикладі, операції з змінними змінними також є атомними, і вони провалюються або успішно працюють відразу.

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);
        }
    }
}

Навіть якщо ви ставите нестабільні чи ні, результати завжди будуть відрізнятися. Але якщо ви використовуєте AtomicInteger, як показано нижче, результати завжди будуть однаковими. Це те ж саме і з синхронізованими.

    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);
            }
        }
    }

4

Так, я використовую його досить багато - це може бути дуже корисно для багатопотокового коду. Стаття, на яку ви вказали, є хорошою. Хоча слід пам’ятати дві важливі речі:

  1. Ви повинні використовувати летючі лише тоді, коли ви повністю розумієте, що це робить, і чим він відрізняється від синхронізованого. У багатьох ситуаціях летючі на поверхні виявляються більш простою альтернативою синхронізованим, коли часто краще розуміння летучих дає зрозуміти, що синхронізація - це єдиний варіант, який може працювати.
  2. непостійний насправді не працює у багатьох старих JVM, хоча синхронізований. Я пам’ятаю, що бачив документ, який посилався на різні рівні підтримки в різних JVM, але, на жаль, зараз не можу його знайти. Однозначно вивчіть це, якщо ви використовуєте Java pre 1.5 або якщо у вас немає контролю над JVM, над якими буде працювати ваша програма.

4

Кожен потік, що звертається до летючого поля, прочитає своє поточне значення перед продовженням, замість (потенційно) використовуючи кешоване значення.

Тільки змінна члена може бути мінливою або перехідною.


3

Абсолютно, так. (І не тільки в Java, але і в C #.) Бувають випадки, коли вам потрібно отримати або встановити значення, яке гарантовано є атомною операцією на вашій даній платформі, наприклад, int або boolean, але не вимагає накладні фіксації різьби. Ефірне ключове слово дозволяє вам переконатися, що при читанні значення ви отримуєте поточне значення, а не кешоване значення, яке щойно застаріло записом у інший потік.


3

Існує два різні способи використання енергонезалежного ключового слова.

  1. Заважає JVM читати значення з реєстру (вважати кеш) і змушує його значення читати з пам'яті.
  2. Знижує ризик помилок у несумісності.

Заважає JVM читати значення в регістрі і змушує його зчитувати значення з пам'яті.

Прапор зайнятості використовуються , щоб запобігти потік від продовження в той час як пристрій зайнятий , і прапор не захищений замком:

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

Тестовий потік продовжиться, коли інший потік вимкне прапор зайнято :

busy = 0;

Однак, оскільки до тестового потоку часто звертаються до зайнятості, JVM може оптимізувати тест, розміщуючи значення зайнятого в реєстрі, а потім протестувати вміст регістра, не читаючи значення зайнятої пам'яті перед кожним тестом. Тестовий потік ніколи не побачить зміни зайнятості, а інший потік змінить би лише значення зайнятої пам’яті, що призведе до тупикової ситуації. Оголошення прапора зайнято нестабільним змушує його значення читати перед кожним тестом.

Знижує ризик помилок узгодженості пам'яті.

Використання змінних змінних зменшує ризик помилок узгодженості пам’яті , оскільки будь-яке записування на змінну змінну встановлює зв’язок «відбувається раніше» з подальшими зчитуваннями тієї самої змінної. Це означає, що зміни летючої змінної завжди помітні для інших потоків.

Техніка читання, письма без помилок послідовності пам’яті називається атомною дією .

Атомна дія - це те, що ефективно відбувається відразу все. Атомна дія не може зупинитися в середині: вона або відбувається повністю, або зовсім не відбувається. Ніяких побічних ефектів атомної дії не видно, поки дія не буде завершена.

Нижче наведені дії, які ви можете вказати, що є атомними:

  • Читання та записи є атомарними для опорних змінних та для більшості примітивних змінних (усі типи, крім довгих та подвійних).
  • Читання та записи є атомними для всіх змінних, оголошених мінливими (включаючи довгі та подвійні змінні).

Ура!


3

volatileкаже для програміста, що значення завжди буде актуальним. Проблема полягає в тому, що значення можна зберегти на різних типах апаратної пам'яті. Наприклад, це можуть бути регістри процесора, кеш процесора, оперативна пам'ять ... Реєстри CPU та кеш процесора належать CPU і не можуть обмінюватися даними на відміну від оперативної пам'яті, яка виручає в багатопотоковій середовищі.

введіть тут опис зображення

volatileКлючове слово говорить про те , що змінна буде зчитувати і записувати з / в RAM пам'яті безпосередньо . Він має деякий слід розрахунку

Java 5розширено volatileпідтримкою happens-before[Про нас]

Запис у мінливе поле відбувається перед кожним наступним читанням цього поля.

volatileКлючове слово не лікує в race conditionситуацію , коли кілька потоків можуть написати кілька значень одночасно. Відповідь - synchronizedключове слово [Про]

В результаті це безпека лише тоді, коли один потік пише, а інші просто читають volatileзначення

непостійний проти синхронізований


2

Летючий робить наступне.

1> Читання та запис змінних змінних різними потоками завжди з пам'яті, а не з власного кешу або реєстру процесора. Тож кожен потік завжди має справу з останнім значенням. 2> Коли 2 різні потоки працюють з одним і тим самим екземпляром або статичними змінними в купі, можна побачити дії інших як не в порядку. Дивіться про це блог Джеремі Менсона. Але летючі тут допомагають.

Наступний повністю запущений код показує, як ряд потоків може виконуватись у заздалегідь визначеному порядку та виводити друк без використання синхронізованого ключового слова.

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

Для цього ми можемо використовувати наступний повноцінний код запуску.

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
                    }
                }
            }
        }
    }
}

Наведене нижче посилання github має readme, що дає належне пояснення. https://github.com/sankar4git/volatile_thread_ordering


1

На сторінці документації Oracle виникає необхідність у мінливій змінній, щоб виправити проблеми сумісності пам'яті:

Використання змінних змінних зменшує ризик помилок узгодженості пам'яті, оскільки будь-яке записування на змінну змінну встановлює зв'язок "буває до" з наступними зчитуваннями цієї ж змінної.

Це означає, що зміни volatileзмінної завжди помітні для інших потоків. Це також означає, що, коли нитка читає мінливу змінну, вона бачить не лише останню зміну на volatile, але й побічні ефекти коду, що призвели до зміни.

Як пояснено у Peter Parkerвідповіді, за відсутності volatileмодифікатора стек кожного потоку може мати свою копію змінної. Здійснюючи змінну як volatile, виправлені питання щодо сумісності пам'яті.

Перегляньте сторінку навчальних посібників Jenkov для кращого розуміння.

Перегляньте відповідне питання SE для отримання додаткових відомостей про непостійні та випадки використання для використання летючого:

Різниця між летючими та синхронізованими в Java

Один практичний випадок використання:

У вас є багато потоків, які необхідно надрукувати поточний час в певному форматі, наприклад: java.text.SimpleDateFormat("HH-mm-ss"). У Yon може бути один клас, який перетворює поточний час у SimpleDateFormatта оновлює змінну за кожну секунду. Усі інші потоки можуть просто використовувати цю мінливу змінну для друку поточного часу у файлах журналу.


1

Летючі змінні - це легка синхронізація. Коли видимість останніх даних серед усіх потоків є необхідною, а атомність може бути порушена, в таких ситуаціях слід віддавати перевагу змінним змінним. Читання на змінних змінних завжди повертає останню запис, виконану будь-яким потоком, оскільки вони не кешовані ні в регістрах, ні в кешах, де інші процесори не можуть бачити. Летючий без замка. Я використовую мінливі, коли сценарій відповідає критеріям, зазначеним вище.


-1

Летючий ключ, коли використовується зі змінною, гарантує, що потоки, що читають цю змінну, побачать те саме значення. Тепер, якщо у вас є декілька потоків для читання та запису до змінної, зробити змінну мінливою буде недостатньо, а дані будуть пошкоджені. Нитки зображень читають однакове значення, але кожен з них здійснив певні шаги (скажімо, збільшений лічильник), при записі назад у пам'ять порушується цілісність даних. Ось чому необхідно зробити змінну синхронізованою (можливі різні способи)

Якщо зміни виконуються 1 ниткою, а іншим потрібно просто прочитати це значення, летючий буде підходящим.


-1

Летлива змінна в основному використовується для миттєвого оновлення (флеш) в основній спільній кеш-лінії після її оновлення, так що зміни відображаються на всіх робочих потоках негайно.


-2

Нижче представлений дуже простий код для демонстрації вимоги volatileзмінної, яка використовується для управління виконанням теми з іншого потоку (це один сценарій, де volatileце потрібно).

// 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());
    }
}

Якщо volatileне використовується: ви ніколи не побачите повідомлення " Зупинено на: xxx ", навіть після " Зупинка на: xxx ", і програма продовжує працювати.

Stopping on: 1895303906650500

При volatileвикористанні: ви відразу побачите " Зупинено на: ххх ".

Stopping on: 1895285647980000
324565439> Stopped on: 1895285648087300

Демо: https://repl.it/repls/SilverAgoisingObjectcode


Керівникові: Потрібно пояснити, чому саме плут? Якщо це неправда, принаймні я дізнаюся, що не так. Я додав цей самий коментар двічі, але не знаю, хто видаляє знову і знову
manikanta

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.