Безопасность потоков в классе 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
}