Java Память Модель гарантирует для летучих - PullRequest
4 голосов
/ 22 марта 2020

У меня мало сомнений относительно изменчивой семантики.
Предположим, что есть три потока T1, T2 и T3 и один экземпляр данного класса.

class Foo {
    private int x = 1;
    private int y = 2;
    private int z = 3;
    private volatile int w = 4;
    private volatile int v = 5;

    public void setX(int x) {
        this.x = x;
    }

    public int getX() {
        return this.x;
    }

    (...)
}

Скажем, следующая последовательность операций чтения / происходит запись действий:

1. (T1) foo.getX(); // stored in a local memory of T1
2. (T1) foo.getY(); // stored in a local memory of T1

3. (T2) foo.setX(10);
4. (T2) foo.setY(20);

5. (T3) foo.getY(); // T3 may see 2 or 20, no guarantees if a write action from point 4 is visible to T3
6. (T3) foo.setZ(30);
7. (T3) foo.setW(40);
8. (T3) foo.setV(50);

9. (T1) foo.getW()
10. (T1) foo.getZ()
11. (T1) foo.getY()
12. (T1) foo.getX()

Я знаю, что гарантировано, что T1 в точке 9 увидит значение, установленное в точке 7, и что T1 в точке 10 увидит значение, установленное в точке 6 (если быть точным по крайней мере так же актуально, как это значение).

Но верны ли эти утверждения?

  1. Java Модель памяти гарантирует, что T1 в точке 11 увидит значение по крайней мере, такой же актуальный, какой видит T3 в точке 5 (тот, что из локальной памяти T3 или более актуальный, но даже если в общей памяти есть более актуальное значение, он может быть невидим для T1) .
  2. Нет никаких гарантий того, что видит T1 в точке 12, в частности, нет никаких гарантий, что он видит значение, установленное в точке 3. Более того, если бы было какое-либо действие записи в x перед точкой 1 в любом потоке , T1 в точке 12 мог видеть некоторые несвежие ценность. Если в точке 7 будет какое-либо значение x в локальной памяти T3, JMM гарантирует, что оно будет замечено T1 в точке 12, но при условии, что в точке T3 до точки 7 не было никаких действий записи / чтения, существует таких гарантий нет.
  3. Между точкой 8 и точкой 9 не существует отношения «до и после», потому что это разные изменчивые переменные. Если JVM реализует Java Модель памяти таким образом, она сбрасывает локальную память в общую память при действии чтения энергозависимой переменной и делает недействительной локальную память при действии записи в энергозависимую переменную в виде состояний в статье 1018 * для синхронизированной семантики, то в качестве побочного эффекта между точкой 8 и точкой 9 должно быть отношение «происходит до», но оно не является строго определенным в Java Спецификации языка.
  4. Если в точке 7 и 8 будут действия чтения вместо действий записи, T1 в точке 10 будет по-прежнему видеть значение 30, потому что отношение «происходит до» применяется не только к изменяемой последовательности запись-чтение, но и к операции чтения-чтения, записи-записи и чтения-записи также изменчивые последовательности.

Пожалуйста, подтвердите, что мое понимание верно.

1 Ответ

1 голос
/ 22 марта 2020

Пока ваши операции get / set ТОЛЬКО получают и устанавливают переменную, тогда все ваши предположения будут правильными.

В Java переменные хранятся в памяти. Но компилятор (и среда выполнения) позволяют временно хранить переменную в кэше ЦП, чтобы обеспечить более быстрое чтение и запись во время работы алгоритма или части кода.

Недостатком этого кэширования является то, что когда ядро ​​завершает работу с переменной, оно записывает его обратно в память, как если бы оно было обновлено только один раз. Другие ядра не могут видеть состояние переменной во время ее использования. Что еще хуже, нет гарантии порядка относительно того, КОГДА он будет записан обратно в память.

Устанавливая переменную как Volatile, вы говорите java, что переменная не может быть помещена в ЛЮБОЙ кэши. Чтение или запись в переменную должны происходить в памяти.

Это означает, что Volatile будет выполнять отдельные операции над переменной atomi c. Но это также сделает длительные операции с переменной ОЧЕНЬ МЕДЛЕННЫМ. Так что volatile не является решением для увеличения производительности многопоточного кода.

Примечательно, что операция, требующая более одного чтения или записи, это , а не atomi c. Например, для i ++, который на самом деле равен i = i + 1, можно изменить значение i до завершения записи.

Если вам нужно гарантировать, что операции выполняются атомарно, вы можете использовать блокировку или полутекс (медленный) или Вы можете использовать умную парадигму, такую ​​как Copy-On-Write (COW), чтобы разрешить чтение атомов ati c и запись атомов atom c.

...