Зачем нужно volatile в Java

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

Пример поля с volatile:

Использование volatile — это один из способов обеспечения согласованного доступа к переменной разными потоками. Более подробно о разработке многопоточных приложений в Java и синхронизации между потоками можно прочесть в моей статье про многопоточность в Java.

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

Более того, присвоение значения этой переменной имеет связь happens-before (произошло-до) для последующих чтений из этой переменной для любых потоков, то есть после присвоения нового значения переменной все потоки увидят это новое значение. Дело в том, что Java позволяет потокам в целях производительности сохранять локальные копии переменной для каждого потока, который её использует (например в кешах или регистрах процессора). В таком случае после записи другим потоком нового значения в исходную переменную, первый поток будет видеть свою локальную копию со старым значением. Использование ключевого слова volatile гарантирует, что все потоки всегда будут использовать общее, исходное значение, и они будут видеть изменения этого исходного значения другими потоками сразу же. Аналогично все изменения переменных, произошедшие внутри sychronized-методов и synchronized-блоков, а также блоков с другими блокировками вроде реализаций интерфейса java.util.concurrent.locks.Lock после выхода из блокировки будут гарантировано видны любым другим потокам после взятия блокировки над тем же самым объектом, но если более сложные блокировки не нужны, то можно использовать volatile. Можно даже использовать их совместно — особого вреда, я думаю, не будет.

Переменная volatile  используется в одном из вариантов реализаций паттерна синглетон.

Зачем нужно volatile в Java: 6 комментариев

  1. Многопоточность — зло великое. И простому комьютеру с этим не справится. Два процесса подерутся за ресурсы и компьютер зависнет.
    Как можно убедить процессор не помещать значение в кэш?
    Ведь это он делает автоматическиЙ
    Лишь существам сверхъестественным и могучим возможно за один момент два дела делать.

  2. «Ключевое слово volatile указывается для поля для того, чтобы указать компилятору, что все операции присвоения этой переменной и все операции чтения из неё должны быть атомарными.» — с чего бы это?

    public class Test {
    public static void main(String[] args) {
    for (int i = 0; i < 1_000_000_000; i++) {
    test(i);
    }
    }

    private static void test(int i) {
    A a = new A();
    Thread thread1 = new Thread(a);
    Thread thread2 = new Thread(a);

    thread1.start();
    thread2.start();

    while (thread1.isAlive() || thread2.isAlive()) {

    }
    if (a.getI() != 2) {
    System.out.println("a = "+ a.getI() + " it's wrong " + i);
    }
    }
    }

    class A extends Thread {
    private int i;

    void inc() {
    i++;
    }

    public void run() {
    inc();
    }

    public int getI() {
    return i;
    }
    }

    //a = 1 it's wrong 111

    1. Атомарными для volatile являются операции присваивания и чтения значения. У вас используется постфиксная операция инкремента, которая на самом деле состоит из:
      i = (int) i + 1;
      В случае с volatile никто не гарантирует безопасность таких конструкций. после считывания значения i, а также после прибавления 1, другой поток может запросто успеть записать в исходное i своё значение, которое мы перетрём своим присваиванием значения.

      Но сами операции чтения и присваивания в случае volatile атомарны и всегда работают с исходным значением, обеспечивая связь happens-before.

  3. А сами операции чтения и записи могут быть не атомарны? Инкремент да — по сути несколько операций. А чтение — одна операция? Поток может прерваться в середине чтения и потом продолжить чтение? Не совсем понимаю этот момент.

    1. Чтение и запись для long и double не во всегда будут атомарными, например. Особенно для 32-х разрядных процессоров. Чтение и запись int и float на современных системах атомарны, но volatile лучше все равно написать, думаю (но это не точно).

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *