¿Hay algún problema con la seguridad del hilo de este código java? Los subprocesos 1-10 agregan números a través de sample.add(), y los subprocesos 11-20 llaman a removeAndDouble() e imprimen los resultados en stdout. Recuerdo que, en el fondo de mi mente, alguien dijo que asignar un elemento de la misma manera que tengo en removeAndDouble() usándolo fuera del bloque sincronizado puede no ser seguro para subprocesos. Que el compilador puede optimizar las instrucciones para que ocurran fuera de secuencia. ¿Es ese el caso aquí? ¿Mi método removeAndDouble() es inseguro?Asignar un objeto a un campo definido fuera de un bloque sincronizado: ¿es seguro para subprocesos?
¿Hay algo más erróneo desde una perspectiva de concurrencia con este código? Estoy tratando de obtener una mejor comprensión de la concurrencia y el modelo de memoria con java (1.6 en adelante).
import java.util.*;
import java.util.concurrent.*;
public class Sample {
private final List<Integer> list = new ArrayList<Integer>();
public void add(Integer o) {
synchronized (list) {
list.add(o);
list.notify();
}
}
public void waitUntilEmpty() {
synchronized (list) {
while (!list.isEmpty()) {
try {
list.wait(10000);
} catch (InterruptedException ex) { }
}
}
}
public void waitUntilNotEmpty() {
synchronized (list) {
while (list.isEmpty()) {
try {
list.wait(10000);
} catch (InterruptedException ex) { }
}
}
}
public Integer removeAndDouble() {
// item declared outside synchronized block
Integer item;
synchronized (list) {
waitUntilNotEmpty();
item = list.remove(0);
}
// Would this ever be anything but that from list.remove(0)?
return Integer.valueOf(item.intValue() * 2);
}
public static void main(String[] args) {
final Sample sample = new Sample();
for (int i = 0; i < 10; i++) {
Thread t = new Thread() {
public void run() {
while (true) {
System.out.println(getName()+" Found: " + sample.removeAndDouble());
}
}
};
t.setName("Consumer-"+i);
t.setDaemon(true);
t.start();
}
final ExecutorService producers = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
final int j = i * 10000;
Thread t = new Thread() {
public void run() {
for (int c = 0; c < 1000; c++) {
sample.add(j + c);
}
}
};
t.setName("Producer-"+i);
t.setDaemon(false);
producers.execute(t);
}
producers.shutdown();
try {
producers.awaitTermination(600, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
sample.waitUntilEmpty();
System.out.println("Done.");
}
}
Buena llamada en 'notifyAll'. –
Sí, es bueno invocar notifyAll() y el tiempo de espera agotado. – Mike