Я пытаюсь решить простую проблему и попадаю в кроличью нору модели памяти Java.
Каков самый простой и/или наиболее эффективный (здесь вызов решения), но свободный от гонки (точно определенный в соответствии с JMM) способ написать класс Java, содержащий нефинальное ссылочное поле, которое инициализируется к ненулевому значению в конструкторе и впоследствии никогда не менялось, так что последующий доступ к этому полю любым другим потоком не может увидеть ненулевое значение?
Сломанный начальный пример:
public class Holder {
private Object value;
public Holder(Object value) {
if (value == null)
throw NullPointerException();
this.value = value;
}
public Object getValue() { // this could return null!
return this.value;
}
}
И, согласно этот пост, пометив поле volatile
даже не работает!
public class Holder {
private volatile Object value;
public Holder(Object value) {
if (value == null)
throw NullPointerException();
this.value = value;
}
public Object getValue() { // this STILL could return null!!
return this.value;
}
}
Это лучшее, что мы можем сделать??
public class Holder {
private Object value;
public Holder(Object value) {
if (value == null)
throw NullPointerException();
synchronized (this) {
this.value = value;
}
}
public synchronized Object getValue() {
return this.value;
}
}
Хорошо, что насчет этого?
public class Holder {
private Object value;
public Holder(Object value) {
if (value == null)
throw NullPointerException();
this.value = value;
synchronized (this) { }
}
public synchronized Object getValue() {
return this.value;
}
}
Дополнительное примечание: связанный вопрос спрашивает, как сделать это без использования каких-либо volatile
или синхронизации, что конечно же невозможно.
AtomicReference
? - person Andy Turner   schedule 09.08.2016@NonNull private Object value;
из Java 8? - person 4castle   schedule 09.08.2016getValue()
в первом примере может вернутьnull
? кроме того, если вы планируете присвоить значение только один раз в конструкторе, а затем просто прочитать его - похоже, вы ищете неизменяемый объект, в таком случае - почему бы не объявить его какfinal
? (Я вижу, что вы написали нефинал только потому, что не предоставили хорошую мотивацию...) - person Nir Alfasi   schedule 09.08.2016getValue()
не может возвращать значение null, если в конструкторе нет выходаthis
, которого нет. В противном случае любой вызовgetValue()
должен следовать за всем конструктором либо в том же потоке, либо в потоке, запущенном тем же потоком после возврата из конструктора, и JLS #17.4;5 гарантирует hb(x,y), если < i>x и y — это последовательные действия в одном потоке. - person user207421   schedule 09.08.2016final
, например, когда вы реализуетеCloneable
и вам нужно глубоко клонировать некоторые поля. - person Archie   schedule 09.08.2016this
была передана ему потоком, вызвавшим конструктор. - person Archie   schedule 09.08.2016this
существует только после возврата конструктора, и действие по передаче ее другому потоку будет составлятьy
в терминах цитаты JLS. - person user207421   schedule 09.08.2016this
можетescape
конструктора передаваться методу, вызываемому внутри конструктора, а это означает, что присвоение полюvalue
также может быть невидимым для другого потока. Но в исходном вопросе такой утечки нет. - person PNS   schedule 09.08.2016Holder
, конструктор возвращается, а затем T1 делает новый объектHolder
видимым для другого потока T2, который затем вызываетget()
; в этом случаеget()
может вернутьnull
(возможно только потому, что поле неfinal
). - person Archie   schedule 09.08.2016