Безопасность потоков в классе Java

Почему этот класс java не защищен потоком.

class TestClass {  
   private int x;

   int get() {
       return x;
   }

   void set(int x) {
       this.x = x;
   }  
}

Я прочитал, что ключевое слово synchronized необходимо сделать потокобезопасным? В конце концов, это не операции, выполняемые внутри атома?

Ответы

Ответ 1

Хотя само присваивание является атомной операцией, из-за различных реализаций оборудования и компилятора, разные потоки могут видеть разные значения элемента x. I.e, модификация одним потоком может быть невидимой для другого потока из-за какого-то кэширования. Обычно это называется проблемой видимости потока.

Вы можете синхронизировать свой код должным образом либо путем синхронизации на мониторе (используя синхронизированное ключевое слово, либо блокировки java.util.concurrent), либо объявляя x неустойчивым.

Ответ 2

С несколькими процессорами некоторые значения могут кэшироваться процессором и могут не отражать изменения, сделанные другими потоками/процессорами для одних и тех же объектов. Фактически, JVM может быть реализован так, чтобы работать с одним процессором.

Синхронные методы явно требуют спецификации языка для представления барьера памяти и требуют перечитать все переменные экземпляра из памяти.

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

Прочтите "Память и блокировки" в разделе Спецификация языка Java.

Ответ 3

Поскольку поле 'x' не объявлено volatile, JVM не требует, чтобы "x" был видимым для всех других потоков. То есть если один поток постоянно читает значение "x" , а другой поток записывает его, возможно, что поток чтения никогда не "заметит" изменение значения.

Синхронизированное ключевое слово не требуется, но будет работать, поскольку оно создаст необходимый барьер памяти/кеш-флеш, чтобы убедиться, что "x" виден, но использование ключевого слова volatile в этом случае будет более эффективным.

Ответ 4

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

synchronized int getAndSet(int x, boolean set) {
    if (set) this.x = x;
    return this.x;   // param x is for set
}