Ошибка при работе с volatile

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

public class Main{
    public static volatile int count = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    count = count - 1;
                }
            }
        });

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int j = 0; j < 10000; j++) {
                    count = count + 1;
                }
            }
        });

        thread.start();
        thread1.start();

        thread.join();
        thread1.join();

        System.out.println(count);

    }

}

Ответы (1 шт):

Автор решения: Komdosh

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

Инкремент count++ выполняет 3 отдельные операции чтения, сложения, записи:

count = count + 1

Поэтому допустима ситуация (T1 и T2 - потоки):

  • T1: Чтение count (count == 0)
  • T2: Чтение count (count == 0)
  • T1: Запись count + 1 (count == 1)
  • T2: Запись count + 1 (count == 1) -- T2 в контексте хранит, что count == 0, хотя T1 уже произвёл запись
  • T2: Чтение count (count == 1)

Для атомарности используйте AtomicInteger.

→ Ссылка