Скажем, у нас очень простой Java-класс MyClass
.
public class MyClass {
private int number;
public MyClass(int number) {
this.number = number;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
Существует три способа создания потокобезопасного Java-класса, который имеет некоторое состояние:
-
Сделайте это по-настоящему неизменным
public class MyClass { private final int number; public MyClass(int number) { this.number = number; } public int getNumber() { return number; } }
-
Сделать поле
number
volatile
.public class MyClass { private volatile int number; public MyClass(int number) { this.number = number; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } }
-
Используйте блок
synchronized
. Классическая версия этого подхода, описанная в главе 4.3.5 Java Concurrency на практике. И самое смешное в том, что в примере есть ошибка, о которой упоминается в описании этой книги.public class MyClass { private int number; public MyClass(int number) { setNumber(number); } public synchronized int getNumber() { return number; } public synchronized void setNumber(int number) { this.number = number; } }
Есть еще один факт, который следует добавить в контекст обсуждения. В Multhithreaded среде JVM может свободно изменять порядок команд за пределами блока synchronized
, сохраняя логическую последовательность, и происходит - до отношений, определенных JVM. Это может привести к тому, что объект публикации не будет правильно сконструирован еще в другом потоке.
У меня есть несколько вопросов относительно третьего случая.
-
Будет ли это эквивалентно следующему фрагменту кода:
public class MyClass { private int number; public MyClass(int number) { synchronized (this){ this.number = number; } } public synchronized int getNumber() { return number; } public synchronized void setNumber(int number) { this.number = number; } }
-
Будет ли предотвращено переупорядочение в третьем случае или возможно, чтобы JVM изменила порядок надстроек и, следовательно, опубликовала объект со значением по умолчанию в поле
number
? -
Если ответ на второй вопрос да, то у меня есть еще один вопрос.
public class MyClass { private int number; public MyClass(int number) { synchronized (new Object()){ this.number = number; } } public synchronized int getNumber() { return number; } public synchronized void setNumber(int number) { this.number = number; } }
Этот странно выглядящий synchronized (new Object())
должен предотвращать эффект переупорядочения. Будет ли это работать?
Чтобы быть ясными, все эти примеры не имеют практических приложений. Мне просто интересно узнать о нюансах многопоточности.