Допустим, у нас очень простой класс 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;
}
}
Есть еще один факт, который следует добавить в контекст обсуждения. В многопоточной среде 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())
должен предотвратить эффект переупорядочения. Будет ли это работать?
Просто чтобы прояснить, все эти примеры не имеют практического применения. Мне просто любопытны нюансы многопоточности.