Вы когда-нибудь использовали ключевое слово volatile в Java?

java multithreading concurrency keyword volatile

219322 просмотра

22 ответа

4279 Репутация автора

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

Теория и практика Java: управление волатильностью

Учитывая детали, в которых эта статья объясняет ключевое слово, о котором вы говорите, вы когда-нибудь использовали его или вы когда-нибудь видели случай, когда вы могли бы использовать это ключевое слово правильным образом?

Автор: Richard Источник Размещён: 20.09.2008 12:41

Ответы (22)


156 плюса

3447 Репутация автора

«… Модификатор volatile гарантирует, что любой поток, который читает поле, увидит последнее записанное значение». - Джош Блох

Если вы думаете об использовании volatile, прочитайте о пакете, java.util.concurrentкоторый имеет дело с атомарным поведением.

Сообщение Википедии о паттерне Singleton показывает изменчивость в использовании.

Автор: Ande TURNER Размещён: 20.09.2008 12:59

3 плюса

21198 Репутация автора

Абсолютно да. (И не только в Java, но и в C #.) Бывают моменты, когда вам нужно получить или установить значение, которое гарантированно будет атомарной операцией на вашей данной платформе, например, int или boolean, но не требует накладные расходы на блокировку резьбы. Ключевое слово volatile позволяет вам убедиться, что при чтении значения вы получаете текущее значение, а не кэшированное значение, которое просто устарело при записи в другой поток.

Автор: dgvid Размещён: 20.09.2008 01:59

666 плюса

20256 Репутация автора

Решение

volatileимеет семантику для видимости памяти. По сути, значение volatileполя становится видимым для всех читателей (в частности, для других потоков) после завершения операции записи в него. Без volatile, читатели могли видеть не обновленную ценность.

Чтобы ответить на ваш вопрос: Да, я использую volatileпеременную, чтобы контролировать, продолжает ли какой-либо код цикл. Цикл проверяет volatileзначение и продолжается, если оно есть true. Условие может быть установлено falseпутем вызова метода «stop». Цикл видит falseи завершает работу, когда проверяет значение после завершения выполнения метода stop.

Книга « Параллелизм Java на практике », которую я очень рекомендую, дает хорошее объяснение volatile. Эта книга написана тем же человеком, который написал статью IBM, на которую есть ссылка в этом вопросе (на самом деле, он цитирует свою книгу в нижней части этой статьи). Я использую volatileто, что в его статье называется «флаг статуса шаблона 1».

Если вы хотите узнать больше о том, как volatileработает тайник, ознакомьтесь с моделью памяти Java . Если вы хотите выйти за рамки этого уровня, посмотрите хорошую книгу по компьютерной архитектуре, такую ​​как Hennessy & Patterson, и прочитайте о согласованности и согласованности кэша.

Автор: Greg Mattes Размещён: 20.09.2008 02:09

30 плюса

35657 Репутация автора

Одним из распространенных примеров использования volatileявляется использование volatile booleanпеременной в качестве флага для завершения потока. Если вы создали поток и хотите иметь возможность безопасно прервать его из другого потока, вы можете периодически проверять флаг. Чтобы остановить это, установите флаг в true. Установив флаг volatile, вы можете убедиться, что поток, который его проверяет, увидит, что он установлен, в следующий раз, когда он проверяет его, даже не используя synchronizedблок.

Автор: Dave L. Размещён: 20.09.2008 04:00

12 плюса

11792 Репутация автора

Да, volatile необходимо использовать всякий раз, когда вы хотите, чтобы изменяемая переменная была доступна нескольким потокам. Это не очень распространенный вариант использования, потому что обычно вам нужно выполнить более одной атомарной операции (например, проверить состояние переменной перед ее изменением), и в этом случае вместо этого вы будете использовать синхронизированный блок.

Автор: ykaganovich Размещён: 20.09.2008 04:26

5 плюса

232 Репутация автора

Вам нужно будет использовать ключевое слово «volatile» или «синхронизированный» и любые другие инструменты и методы управления параллелизмом, которые могут быть в вашем распоряжении, если вы разрабатываете многопоточное приложение. Примером такого приложения являются настольные приложения.

Если вы разрабатываете приложение, которое будет развернуто на сервере приложений (Tomcat, JBoss AS, Glassfish и т. Д.), Вам не нужно обрабатывать управление параллелизмом самостоятельно, поскольку оно уже решено сервером приложений. На самом деле, если я правильно помню, стандарт Java EE запрещает какой-либо контроль параллелизма в сервлетах и ​​EJB-компонентах, поскольку он является частью уровня «инфраструктуры», который, как вы предполагали, был освобожден от его обработки. Вы можете управлять параллелизмом только в таком приложении, если реализуете одноэлементные объекты. Это даже уже решено, если вы связываете свои компоненты, используя frameworkd, как Spring.

Таким образом, в большинстве случаев разработки Java, когда приложение является веб-приложением и использует IoC-фреймворк, такой как Spring или EJB, вам не нужно использовать 'volatile'.

Автор: Rudi Adianto Размещён: 20.09.2008 07:14

4 плюса

5172 Репутация автора

Да, я использую его довольно часто - это может быть очень полезно для многопоточного кода. Статья, на которую вы указали, хорошая. Хотя следует помнить о двух важных вещах:

  1. Вы должны использовать volatile, только если полностью понимаете, что он делает и чем он отличается от синхронизированного. Во многих ситуациях volatile, на первый взгляд, представляется более простой и более производительной альтернативой синхронизированным, когда зачастую лучшее понимание volatile проясняет, что синхронизированный является единственным вариантом, который будет работать.
  2. volatile на самом деле не работает во многих старых JVM, хотя синхронизируется и работает. Я помню, как видел документ, который ссылался на различные уровни поддержки в разных JVM, но, к сожалению, я не могу найти его сейчас. Обязательно посмотрите, используете ли вы Java pre 1.5 или если у вас нет контроля над JVM, на которых будет работать ваша программа.
Автор: MB. Размещён: 20.09.2008 11:07

48 плюса

18601 Репутация автора

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

Обратите внимание, что нет необходимости в синхронизации

Автор: Pyrolistical Размещён: 24.09.2008 10:29

5 плюса

701 Репутация автора

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);
            }
        }
    }
Автор: fatih tekin Размещён: 02.03.2013 09:07

9 плюса

41910 Репутация автора

ИМО два важных сценария, кроме остановки потока, в котором используется ключевое слово volatile,

  1. Механизм блокировки с двойной проверкой . Часто используется в дизайне шаблона Singleton. В этом то singleton object needs to be declared volatile.
  2. Ложные пробуждения . Поток может иногда просыпаться от ожидающего вызова, даже если не было отправлено уведомление. Такое поведение называется бестолковым пробуждением. Этому можно противостоять, используя условную переменную (логический флаг). Поместите вызов wait () в цикл while, пока флаг имеет значение true. Таким образом, если поток выходит из режима ожидания по каким-либо причинам, отличным от notify / notifyall, тогда он обнаруживает, что флаг все еще имеет значение true и, следовательно, вызовы ожидают снова. Перед вызовом уведомить установите этот флаг в true. В этом случае boolean flag is declared as volatile.
Автор: Aniket Thakur Размещён: 25.02.2014 09:17

3 плюса

148 Репутация автора

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

Только переменная-член может быть изменчивой или переходной.

Автор: tstuber Размещён: 11.08.2014 04:27

12 плюса

131 Репутация автора

Никто не упомянул обработку операций чтения и записи для длинных и двойных переменных. Чтение и запись являются атомарными операциями для ссылочных переменных и большинства примитивных переменных, за исключением типов переменных long и double, которые должны использовать ключевое слово volatile для атомарных операций. @ссылка на сайт

Автор: Donatello Boccaforno Размещён: 11.02.2015 02:51

0 плюса

863 Репутация автора

Летучие переменная модифицируется асинхронно одновременно запущенных потоков в приложении Java. Нельзя иметь локальную копию переменной, которая отличается от значения, которое в настоящее время хранится в «основной» памяти. По сути, переменная, объявленная volatile, должна синхронизировать свои данные во всех потоках, чтобы при каждом обращении к переменной или ее обновлении в любом потоке все остальные потоки сразу видели одно и то же значение. Конечно, вполне вероятно, что переменные переменные имеют более высокий уровень доступа и обновления, чем «простые» переменные, поскольку потоки могут иметь собственную копию данных для большей эффективности.

Когда поле объявляется как volatile, компилятор и среда выполнения уведомляются о том, что эта переменная является общей и что операции с ней не должны переупорядочиваться вместе с другими операциями памяти. Переменные переменной не кэшируются в регистрах или в кэшах, где они скрыты от других. процессоры, поэтому чтение изменчивой переменной всегда возвращает самую последнюю запись любым потоком.

для справки, обратитесь к этому http://techno-terminal.blogspot.in/2015/11/what-are-volatile-variables.html

Автор: satish Размещён: 17.11.2015 09:36

94 плюса

30409 Репутация автора

Важный момент о 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поток, который создает экземпляр, не сможет связаться с другим потоком. Таким образом, если поток A создает экземпляр Singleton и сразу после создания процессор испортит и т. Д., Все остальные потоки не смогут увидеть значение _instanceкак не нулевое, и они будут считать, что ему по-прежнему назначено нулевое значение.

Почему это происходит? Поскольку потоки считывателя не блокируются, и пока поток записывающего устройства не выйдет из синхронизированного блока, память не будет синхронизирована и значение _instanceне будет обновлено в основной памяти. С ключевым словом Volatile в Java это обрабатывается самой Java, и такие обновления будут видны всем потокам читателей.

Вывод : volatileключевое слово также используется для передачи содержимого памяти между потоками.

Пример использования без 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 () возвращает объект, который, возможно, не был полностью инициализирован. Чтобы сделать код потокобезопасным, ключевое слово volatile может использоваться начиная с Java 5 для переменной экземпляра. Переменные, помеченные как volatile, становятся видимыми для других потоков только после того, как конструктор объекта полностью завершит свое выполнение.
Источник

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

volatileиспользование в Java :

Отказоустойчивые итераторы обычно реализуются с использованием volatileсчетчика объекта списка.

  • Когда список обновляется, счетчик увеличивается.
  • Когда Iteratorсоздается, текущее значение счетчика внедряется в Iteratorобъект.
  • Когда Iteratorоперация выполняется, метод сравнивает два значения счетчика и выдает a, ConcurrentModificationExceptionесли они различны.

Реализация отказоустойчивых итераторов обычно легковесна. Они обычно полагаются на свойства структур данных реализации конкретного списка. Там нет общей картины.

Автор: Premraj Размещён: 18.12.2015 09:52

0 плюса

852 Репутация автора

Ключ volatile при использовании с переменной гарантирует, что потоки, читающие эту переменную, увидят одно и то же значение. Теперь, если у вас есть несколько потоков, читающих и записывающих переменную, сделать переменную volatile будет недостаточно, и данные будут повреждены. Потоки изображений прочитали одно и то же значение, но каждый из них сделал несколько изменений (скажем, увеличил счетчик), при обратной записи в память нарушается целостность данных. Поэтому необходимо сделать переменную синхронизированной (возможны разные способы)

Если изменения выполняются одним потоком, а остальным нужно просто прочитать это значение, то подходит volatile.

Автор: Java Main Размещён: 24.12.2015 04:41

1 плюс

29750 Репутация автора

На странице документации оракула возникает необходимость в энергозависимой переменной для решения проблем с согласованностью памяти:

Использование энергозависимых переменных снижает риск ошибок согласованности памяти, потому что любая запись в энергозависимую переменную устанавливает отношение «происходит до» с последующим чтением этой же переменной.

Это означает, что изменения в volatileпеременной всегда видны другим потокам. Это также означает, что когда поток читает изменчивую переменную, он видит не только последнее изменение volatile, но и побочные эффекты кода, который привел к изменению.

Как объяснено в Peter Parkerответе, в отсутствие volatileмодификатора стек каждого потока может иметь свою собственную копию переменной. Делая переменную as volatile, проблемы согласованности памяти были исправлены.

Взгляните на страницу с обучением jenkov для лучшего понимания.

Взгляните на связанный вопрос SE для получения более подробной информации о volatile и вариантах использования volatile:

Разница между изменчивым и синхронизированным в Java

Один практический пример использования:

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

Автор: Ravindra babu Размещён: 09.05.2016 01:24

2 плюса

381 Репутация автора

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

Автор: Neha Vari Размещён: 08.07.2016 11:08

2 плюса

2284 Репутация автора

Существует два различных использования ключевого слова volatile.

  1. Запрещает JVM читать значения из регистра (предполагается, что это кэш) и принудительно считывает его значение из памяти.
  2. Снижает риск ошибок согласованности памяти.

Запрещает JVM читать значения в регистре и принудительно считывать их значения из памяти.

Флаг занятости используются , чтобы предотвратить поток от продолжения в то время как устройство занято , и флаг не защищен замком:

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

Поток тестирования продолжится, когда другой поток отключит флаг занятости :

busy = 0;

Однако, поскольку в потоке тестирования часто осуществляется доступ к занятым объектам, JVM может оптимизировать тест, поместив значение занятости в регистр, а затем протестировать содержимое регистра без чтения значения занятости в памяти перед каждым тестом. Поток тестирования никогда не увидит изменения занятости, а другой поток только изменит значение занятости в памяти, что приведет к взаимоблокировке. Объявление флага занятости как volatile заставляет его значение считываться перед каждым тестом.

Снижает риск ошибок согласованности памяти.

Использование энергозависимых переменных снижает риск ошибок согласованности памяти , поскольку любая запись в энергозависимую переменную устанавливает отношение «происходит до» с последующим чтением этой же переменной. Это означает, что изменения в изменчивой переменной всегда видны другим потокам.

Техника чтения, записи без ошибок согласованности памяти называется атомарным действием .

Атомное действие - это то, что эффективно происходит одновременно. Атомное действие не может остановиться в середине: оно либо происходит полностью, либо вообще не происходит. Никакие побочные эффекты атомного действия не видны, пока действие не завершено.

Ниже приведены действия, которые можно указать как атомарные:

  • Чтение и запись являются атомарными для ссылочных переменных и для большинства примитивных переменных (все типы, кроме long и double).
  • Чтение и запись являются атомарными для всех переменных, объявленных как volatile (включая длинные и двойные переменные).

Ура!

Автор: dheeran Размещён: 02.03.2017 08:42

11 плюса

5606 Репутация автора

Переменная, объявленная с volatileключевым словом, имеет два основных качества, которые делают ее особенной.

  1. Если у нас есть переменная volatile, она не может быть кэширована в кеш-память компьютера (микропроцессора) никаким потоком. Доступ всегда происходил из основной памяти.

  2. Если есть операция записи, выполняющая изменчивую переменную, и внезапно запрашивается операция чтения , гарантируется, что операция записи будет завершена до операции чтения .

Два вышеуказанных качества выводят, что

  • Все потоки, читающие переменную volatile, определенно прочитают последнее значение. Потому что никакое кэшированное значение не может его загрязнить. А также запрос на чтение будет предоставлен только после завершения текущей операции записи.

А с другой стороны,

  • Если мы продолжим исследование № 2, о котором я упоминал, мы увидим, что volatileключевое слово - это идеальный способ поддерживать совместно используемую переменную, которая имеет «n» число потоков чтения и только один поток записи для доступа к ней. Как только мы добавим volatileключевое слово, все готово. Никаких других накладных расходов на безопасность потоков.

Conversly,

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

Автор: Supun Wijerathne Размещён: 04.03.2018 06:24

1 плюс

41 Репутация автора

Летучий делает следующее.

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

Автор: sankar banerjee Размещён: 31.05.2018 06:23

0 плюса

3844 Репутация автора

Мне нравится объяснение Дженкова :

Ключевое слово volatile для Java используется для пометки переменной Java как «хранящейся в основной памяти». Точнее, это означает, что каждое чтение энергозависимой переменной будет считываться из основной памяти компьютера, а не из кэша ЦП, и что каждая запись в энергозависимую переменную будет записываться в основную память, а не только в кеш ЦП. ,

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

Это расширенная гарантия видимости, так называемая гарантия «происходит раньше».

Производительность Соображения изменчивы

Чтение и запись изменчивых переменных приводит к тому, что переменная читается или записывается в основную память. Чтение и запись в основную память обходится дороже, чем доступ к кэшу процессора. Доступ к переменным переменным также предотвращает переупорядочение команд, что является обычной техникой повышения производительности. Таким образом, вы должны использовать изменчивые переменные только тогда, когда вам действительно необходимо обеспечить видимость переменных.

Автор: yoAlex5 Размещён: 16.06.2018 02:16

0 плюса

5669 Репутация автора

Ниже приведен очень простой код для демонстрации требования 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не используется: вы никогда не увидите сообщение « Stopped on: xxx » даже после « Stopping on: xxx », и программа продолжит работу.

Stopping on: 1895303906650500

При volatileиспользовании: вы увидите « Остановлено: ххх » немедленно.

Stopping on: 1895285647980000
324565439> Stopped on: 1895285648087300
Автор: manikanta Размещён: 14.02.2019 05:48
32x32