Допустим, у нас есть очень простой класс 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 {
закрытый конечный номер;
публичный MyClass (целое число) {
это.число = число;
}
публичный интервал getNumber () {
обратный номер;
}
}
Сделать поле число
изменяемым
.
открытый класс MyClass {
частный изменяемый внутренний номер;
публичный MyClass (целое число) {
это.число = число;
}
публичный интервал getNumber () {
обратный номер;
}
public void setNumber (целое число) {
это.число = число;
}
}
Используйте синхронизированный блок . Классическая версия этого подхода, описанная в главе 4.3.5 Java Concurrency на практике. И самое забавное, что в примере, упомянутом в опечатках к этой книге, есть ошибка.
открытый класс MyClass {
частный внутренний номер;
публичный MyClass (целое число) {
установитьНомер(число);
}
общедоступный синхронизированный int getNumber () {
обратный номер;
}
общедоступный синхронизированный недействительный setNumber (целое число) {
это.число = число;
}
}
Есть еще один факт, который следует добавить в контекст обсуждения.В многопоточной среде JVM может свободно переупорядочивать инструкции за пределами синхронизированного
блока, сохраняя логическую последовательность и отношения "происходит до", заданные JVM. Это может привести к публикации объекта, который еще не создан должным образом, в другой поток.
У меня есть пара вопросов по третьему делу.
Будет ли он эквивалентен следующему фрагменту кода:
public class MyClass {
частный внутренний номер;
публичный MyClass (целое число) {
синхронизировано (это) {
это.число = число;
}
}
общедоступный синхронизированный int getNumber () {
обратный номер;
}
общедоступный синхронизированный недействительный setNumber (целое число) {
это.число = число;
}
}
Будет ли предотвращено изменение порядка в третьем случае или JVM сможет изменить порядок инструкций и, следовательно, опубликовать объект со значением по умолчанию в поле число
?
Если ответ на второй вопрос положительный, то у меня есть еще один вопрос.
открытый класс MyClass {
частный внутренний номер;
публичный MyClass (целое число) {
синхронизированный (новый объект ()) {
это.число = число;
}
}
общедоступный синхронизированный int getNumber () {
обратный номер;
}
общедоступный синхронизированный недействительный setNumber (целое число) {
это.число = число;
}
}
Предполагается, что этот странно выглядящий синхронизированный (новый объект())
предотвращает эффект переупорядочения. Это будет работать?
Для ясности: все эти примеры не имеют никакого практического применения. Мне просто любопытны нюансы многопоточности.