Javenue logo

Javenue

Программирование на Java

Информационные технологии

Модель памяти Java и атомарность операций (java memory model)

Бывает, что иногда почитываю разные интересные книжки. Вот например, для собственного развития всякие JSR читаю :). Из недавнего - просматривал JSR-133 Java Memory Model and Thread Specification (Модель памяти Java).

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

Рассмотрим такой программный код:

public class Atomicity extends Thread {
    volatile static int i;
    boolean b;

    public void run() {
        while (true) {
            if (b = !b) i++;
            else i--;
        }
    }

    public static void main(String[] args) {
//        new Atomicity().start();
        new Atomicity().start();

        while (true)
            System.out.println(i);
    }
}

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

При запуске на экран будут выводится числа 0 и 1. Естественно, так как значение переменной i в потоке попеременно инкрементируется и декрементируется.

Можно сделать предположение, что если раскомментировать первую строку метода main, то значение i будет принимать максимум 3 разных значения, например 0, 1 и 2.

На самом деле в System.out будет выводится что-то типа такого:

...
472
...
97472
...
115920
...

Так в чем же дело?

А все дело в том, что операции инкремента и декремента не являются атомарными. Происходит последовательно считывание, увеличение/уменшение и далее запись значения.

Исправить ошибку в коде помогает synchronized блок:

    public void run() {
        while (true) {
            synchronized (Atomicity.class) {
                if (b = !b) i++;
                else i--;
            }
        }
    }

Синхронизация по классу Atomicity говорит о том, что в один момент времени в synchronized блоке может находится не больше одного потока. Остальные потоки будут ждать, для того чтобы захватить монитор класса Atomicity. При чем случится это только после того, как активный поток отпустит этот монитор.

После исправления и запуска класса в консоли видим следующее:

0
1
1
1
2
0
...

Атомарность говорит о том, что некоторое действие (или их последовательность) должно происходить "все и сразу". Осутствие синхронизации может привести к катострофическим последствиям. Это далеко не NullPointerException, который можно обнаружить сразу. Программа может работать достаточно долго и визаульно никаких неполадок обнаружено не будет.

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

И еще очень важное замечание - в соответствии с Java Memory Model записи в переменые типа long и double не являются атомарными. Типы long и double являются 64-битными и атомарными считаются записи только в каждую 32-битную часть. Это может привести к тому, что некоторый поток видит одну часть значения обновленной, а вторую еще нет.

Чтобы гарантировать атомарность записи в long и double необходимо объявлять их как volatile.

Кстати, запись ссылки на объект (reference) всегда атомарна, не зависимо от того, имеем мы дело с 32-х или 64-х битной реализацией JVM.

В Java Memory Model рассказано много еще чего интересного, например о видимости (Visibility) и упорядоченности (Ordering). Но это уже совсем другая история.

Надеюсь, статья была вам интересна. В любом случае, жду ваших комментариев.


Комментариев: 3

  Выйти

  * для публикации комментариев нужно  

Евгений:

Ну пусть хоть один комментарий, наконец-то появится)) Спасибо за статью. Помогло понять атомарность и немного улучшило знание многопоточности. Учите Java! - http://javarush.ru/user/reference/ec656a0d-8cf9-4ed5-b376-3894e6c940f1 (регистрируйтесь плз не как анонимы!)

Любовь Моргенштерн:

Я тоже с javarush.ru) Статья полезная и нужная, все так коротко и по теме. Спасибо :)

Данил Суетин:

Особенно полезна и понятна после статейки о структуре памяти (я новичок в программировании и всё сразу понять без азов - трудно)