Volatile массив - видимость элементов памяти
Рассмотрим фрагмент кода
class A {
private Map<String, Object> taskMap = new HashMap<>();
private volatile Object[] tasksArray ;
// assume this happens on thread1
public void assignTasks() {
synchronized(taskMap){
// put new tasks into map
// reassign values from map as a new array to tasksArray ( for direct random access )
}
}
// assume this is invoked on Thread2
public void action(){
int someindex = <init index to some value within tasksArray.length>;
Object[] localTasksArray = tasksArray;
Object oneTask = localTasksArray[someindex];
// Question : is the above operation safe with respect to memory visibility for Object oneTask ?
// is it possible that oneTask may appear null or in some other state than expected ?
}
}
Вопрос: это операция Object oneTask = localTasksArray[someindex];
безопасно в отношении видимости памяти для объекта oneTask? Возможно ли, что OneTask может показаться нулевым или в каком-либо другом состоянии, чем ожидалось?
Мои мысли таковы:
Возможно, что thread2 может видеть oneTask
как ноль или в каком-то другом состоянии, отличном от ожидаемого. Это потому, что, хотя taskArray
является volatile
и чтение этого массива обеспечит правильную видимость самого массива, но не обеспечит видимость внутреннего состояния объекта oneTask
,
3 ответа
volatile
ключевое слово защищает только поле taskArray
которая является ссылкой на объект []. Всякий раз, когда вы читаете или пишете это поле, оно будет иметь последовательный порядок. Однако это не распространяется ни на массив, на который ссылаются, ни на объекты, на которые ссылается массив.
Скорее всего, вам нужен AtomicReferenceArray.
Насколько я помню (сейчас я исследую, чтобы подтвердить), Java только гарантирует, что сам изменчивый объект сбрасывается в ОЗУ, а не его части (как в записях массива) или подполя объекта (в случае объекты).
Однако - я полагаю, что большинство (если не все) JVM реализуют volatile
доступ в качестве барьера памяти (см. здесь и на странице, на которую даны ссылки Книга рецептов компилятора JSR-133). В качестве барьера памяти это означает, что все предыдущие записи другими потоками будут сброшены из кэша в основную память при доступе, что сделает всю память согласованной в это время.
На это не следует полагаться - если важно, чтобы у вас был полный контроль над тем, что видно, а что нет, вам следует использовать один из Atomic
классы, такие как AtomicReferenceArray
как предложено другими. Они могут даже быть более эффективными, чем volatile
именно по этой причине.
Объявление volatile поля гарантирует, что любая запись этой переменной синхронизируется (и, следовательно, видима) с любым последующим (в соответствии с порядком синхронизации) этого значения. Обратите внимание, что не существует такого понятия, как энергозависимый объект или энергозависимый массив. Есть только изменчивые поля.
Следовательно, в вашем коде нет отношения "до и после" между потоком 1, хранящим объект в массиве, и потоком 2, считывающим его оттуда.