Должны ли мы объявлять приватные поля как volatile
, если экземпляры используются в нескольких потоках?
В Эффективная Java ,есть пример, когда код не работает без volatile:
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; }
}
}
}
Приведенный выше код работает, как и ожидалось, без использования volatile:
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
}
}
Я думаю, что из-за общедоступного сеттера компилятор/JVM никогда не должен оптимизировать код, который вызывает getField()
, но в этой статье говорится, что существует некий шаблон «Volatile Bean» (Pattern #4 ), который следует применять для создания безопасных классов изменяемого потока -. Обновление:может быть, эта статья применима только к IBM JVM?
Вопрос в том, :, какая часть JLS явно или неявно говорит, что частные примитивные поля с общедоступными геттерами/сеттерами должны быть объявлены какvolatile
(или им не нужно )?
Извините за длинный вопрос, постарался подробно объяснить проблему. Дайте мне знать, если что-то не ясно. Спасибо.