Гарантируют ли атомарные переменные видимость памяти?
Небольшой вопрос о видимости памяти.
CodeSample1:
class CustomLock {
private boolean locked = false;
public boolean lock() {
if(!locked) {
locked = true;
return true;
}
return false;
}
}
Этот код склонен к ошибкам в многопоточной среде, во-первых, из-за "if-then-act", которое не является атомарным, и во-вторых, из-за потенциальных проблем с видимостью памяти, где, например, threadA задает для поля значение true, но threadB это позднее желающие прочитать значение поля могут не увидеть этого и все равно увидеть значение false.
Самое простое решение - использовать синхронизированное ключевое слово, как в CodeSample2.
CodeSample2:
class CustomLock {
private boolean locked = false;
public synchronized boolean lock() {
if(!locked) {
locked = true;
return true;
}
return false;
}
}
А что если я захочу использовать атомарную переменную и, например, AtomicBoolean (вопрос относится ко всем атомарным переменным),
CodeSample3:
public static class CustomLock {
private AtomicBoolean locked = new AtomicBoolean(false);
public boolean lock() {
return locked.compareAndSet(false, true);
}
}
Помимо соображений производительности, мы можем видеть, что теперь мы реализовали логику, аналогичную "if-then-act" из CodeSample1, используя AtomicBoolean. На самом деле не имеет значения, что код делает логически, у меня возникает вопрос, что если 2 потока будут вызывать метод lock() в CodeSample3 примерно в одно и то же время, хотя ясно, что любая операция записи в поле теперь будет выполняться атомарно, использование AtomicBoolean также гарантирует видимость памяти?
Извините за длинную историю, просто хотел убедиться, что я выхожу как можно яснее, спасибо, ребята...
2 ответа
Да, в соответствии с Javadocs это гарантирует:
compareAndSet и все другие операции чтения и обновления, такие как getAndIncrement, имеют эффект памяти как чтения, так и записи изменяемых переменных.
у меня возникает вопрос: что, если 2 потока будут вызывать метод lock() в CodeSample3 примерно в одно и то же время, хотя ясно, что любая операция записи в поле теперь будет выполняться атомарно, гарантирует ли использование AtomicBoolean видимость памяти?
За AtomicBoolean
чтобы обрабатывать несколько операций из разных потоков одновременно, это должно гарантировать видимость памяти. Это может сделать гарантию, потому что это оборачивает volatile
поле. Это языковая семантика volatile
Это гарантирует, что барьеры памяти пересекаются, так что несколько потоков видят самое актуальное значение и что любые обновления будут публиковаться в основной памяти.
Кстати, ваш lock(...)
метод должен быть готов tryLock(...)
потому что он может не получить блокировку.