Должны ли мы объявить частные поля как volatile
, если instanced используются в нескольких потоках?java: `volatile` частные поля с геттерами и сеттерами
В Effective Java, есть пример, когда код не работает без летучий:
import java.util.concurrent.TimeUnit;
// Broken! - How long would you expect this program to run?
public class StopThread {
private static boolean stopRequested; // works, if volatile is here
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread = new Thread(new Runnable() {
public void run() {
int i = 0;
while (!stopRequested)
i++;
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
stopRequested = true;
}
}
Объяснения говорит, что
while(!stopRequested)
i++;
оптимизирован что-то вроде этого:
if(!stopRequested)
while(true)
i++;
поэтому дальнейшие модификации stopRequested
не видны по фоновому потоку, так что он навсегда зацикливается. (Кстати, этот код завершается без volatile
на JRE7.)
Теперь рассмотрим этот класс:
public class Bean {
private boolean field = true;
public boolean getField() {
return field;
}
public void setField(boolean value) {
field = value;
}
}
и нить следующим образом:
public class Worker implements Runnable {
private Bean b;
public Worker(Bean b) {
this.b = b;
}
@Override
public void run() {
while(b.getField()) {
System.err.println("Waiting...");
try { Thread.sleep(1000); }
catch(InterruptedException ie) { return; }
}
}
}
Приведенный выше код работает, как ожидалось, без использования летучих веществ :
public class VolatileTest {
public static void main(String [] args) throws Exception {
Bean b = new Bean();
Thread t = new Thread(new Worker(b));
t.start();
Thread.sleep(3000);
b.setField(false); // stops the child thread
System.err.println("Waiting the child thread to quit");
t.join();
// if the code gets, here the child thread is stopped
// and it really gets, with JRE7, 6 with -server, -client
}
}
Я думаю, что из-за публичного сеттера, c ompiler/JVM никогда не должен оптимизировать код, который вызывает getField()
, но this article говорит, что существует некоторый шаблон «Volatile Bean» (шаблон № 4), который должен применяться для создания изменяемых потокобезопасных классов. Обновление: Возможно, эта статья применима только к IBM JVM?
Вопрос: какая часть JLS явно или неявно говорит, что частные примитивные поля с общедоступными getters/setters должны быть объявлены как volatile
(или им это не обязательно)?
Извините за длинный вопрос, я попытался подробно объяснить проблему. Дайте мне знать, если что-то неясно. Благодарю.
Это не похоже на то, что вам нужно это поле, чтобы отменить поток, вы можете использовать прерванный флаг в потоке. –
@NathanHughes, эти классы - всего лишь минимальные примеры, фактический код отличается, и там не требуется прерывание потока. – khachik