Недавно я нашел этот камень в моей базе кода:
/** This class is used to "publish" changes to a non-volatile variable.
*
* Access to non-volatile and volatile variables cannot be reordered,
* so if you make changes to a non-volatile variable before calling publish,
* they are guaranteed to be visible to a thread which calls syncChanges
*
*/
private static class Publisher {
//This variable may not look like it doing anything, but it really is.
//See the documentaion for this class.
private volatile AtomicInteger sync = new AtomicInteger(0);
void publish() {
sync.incrementAndGet();
}
/**
*
* @return the return value of this function has no meaning.
* You should not make *any* assumptions about it.
*/
int syncChanges() {
return sync.get();
}
}
Это используется как таковое:
Тема 1
float[][] matrix;
matrix[x][y] = n;
publisher.publish();
Тема 2
publisher.syncChanges();
myVar = matrix[x][y];
Тема 1 - поток обновления фона, который работает непрерывно. Тема 2 - рабочий поток HTTP, который не заботится о том, чтобы то, что он читает, каким-либо образом последовательным или атомарным, только то, что записи "в конечном итоге" попадают туда и не теряются в качестве предложений богам concurrency.
Теперь это вызывает все мои предупреждающие звонки. Пользовательский алгоритм concurrency, написанный глубоко внутри несвязанного кода.
К сожалению, исправление кода не является тривиальным. Поддержка Java для параллельных примитивных матриц не является хорошей. Похоже, что самый лучший способ исправить это - использовать ReadWriteLock
, но это, вероятно, будет иметь отрицательные последствия для производительности. Правильность важна, очевидно, но, похоже, я должен доказать, что это неправильно, прежде чем просто вырвать ее из чувствительной к производительности области.
Согласно документации java.util.concurrent, создайте следующие отношения happens-before
:
Каждое действие в потоке происходит - перед каждым действием в этом потоке, которое приходит позже в порядке программы.
Записывается в нестабильное поле - перед каждым последующим чтением этого же поля. Записи и чтения изменчивых полей имеют аналогичные эффекты согласованности памяти как входные и выходящие мониторы, но не влекут за собой блокировку взаимного исключения.
Итак, это звучит так:
- выполняется матрица write-before publish() (правило 1)
- publish() происходит до syncChanges() (правило 2)
- syncChanges() происходит до чтения матрицы (правило 1)
Таким образом, код действительно установил цепочку событий для этой матрицы.
Но я не уверен. concurrency сложно, и я не эксперт в области. Что я пропустил? Действительно ли это безопасно?