¿Deberíamos declarar los campos privados como volatile
si las instancias se usan en varios hilos?java: campos privados `volátiles` con getters y setters
En Effective Java, no es un ejemplo en el que el código no funciona sin volátil:
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;
}
}
Las explicaciones dice que
while(!stopRequested)
i++;
está optimizado para algo como esto:
if(!stopRequested)
while(true)
i++;
por lo que no se ven más modificaciones de stopRequested
por el hilo de fondo, por lo que se repite para siempre. (Por cierto, que el código termina sin volatile
en JRE7.)
Consideremos ahora esta clase:
public class Bean {
private boolean field = true;
public boolean getField() {
return field;
}
public void setField(boolean value) {
field = value;
}
}
y un hilo de la siguiente manera:
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; }
}
}
}
El código anterior funciona como se espera sin usar sustancias volátiles :
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
}
}
Creo que debido a la configuración pública, la c ompiler/JVM nunca debe optimizar el código que llama al getField()
, pero this article dice que hay un patrón de "Bean volátil" (Patrón # 4), que se debe aplicar para crear clases mutables de seguridad de subprocesos. Actualización: tal vez ese artículo se aplica solo a IBM JVM?
La pregunta es: ¿qué parte de JLS explícita o implícitamente dice que los campos primitivos privados con getters/setters públicos deben declararse como volatile
(o no tienen que hacerlo)?
Lo siento por una pregunta larga, traté de explicar el problema en detalle. Avísame si algo no está claro. Gracias.
No es que necesite ese campo para cancelar el hilo, podría usar el indicador interrumpido en el hilo. –
@NathanHughes, estas clases son solo ejemplos mínimos, el código real es diferente, no se necesita ninguna interrupción de hilo allí. – khachik