Creo que es necesario utilizar AtomicInteger
en el ThreadFactory pero cuando estoy tratando de demostrar a mí mismo, no he logrado duro.
new ThreadFactory() {
private int threadId = 0; <---- AtomicInteger preferred
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true);
t.setName("my-thread-" + (threadId++)); <--- dangerous code
return t;
}
}
Si varias peticiones vienen, la fábrica de hilos va a generar hilos para manejarlos y durante la generación, no podría haber un espacio en el que una condición de carrera podría colarse.
Probé con el siguiente código para demostrar mi teoría, pero que no está sucediendo en absoluto con 2_000 hilos de núcleo.
@Slf4j
public class ThreadFactoryTest {
private ConcurrentHashMap<String, Thread> threadIdThreadMap = new ConcurrentHashMap<>();
private ThreadPoolExecutor myExecutor = new ThreadPoolExecutor(2000, 2000, 30, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100000), new ThreadFactory() {
private int threadId = 0;
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true);
t.setName("my-thread-" + (threadId++));
if (threadIdThreadMap.contains(t.getName())) {
log.error("already existed");
System.err.println(myExecutor);
myExecutor.shutdownNow();
} else threadIdThreadMap.put(t.getName(), t);
return t;
}
}, new ThreadPoolExecutor.AbortPolicy());
@Test
public void testThreadFactory() throws Exception {
for (int i = 0; i < 100; ++i) {
new Thread(() -> runOneHundredJobs()).start();
}
Thread.sleep(1000000);
myExecutor.shutdown();
myExecutor.awaitTermination(100, TimeUnit.MINUTES);
}
private void runOneHundredJobs() {
log.info("{} starting to submit tasks", Thread.currentThread().getName());
for (int i = 0; i < 100; ++i) {
myExecutor.execute(() -> {
while (100 < System.currentTimeMillis()) {
try {
Thread.sleep(1000);
if (Math.random() > 0.99) break;
System.out.println(Thread.currentThread().getName());
System.out.println(myExecutor);
} catch (Exception e) {
}
}
} );
}
}
}
Se parece a una pregunta estúpida ya que siempre se que " es difícil crear la brecha para una condición de carrera multi-hilo ".
Cualquier ayuda / pista se apreciará;)
ACTUALIZAR
Realmente gracias por la ayuda en el camino, y @StephenC @Slaw. Siento que no he entendido bien algunos puntos allí; (
Por lo tanto newThread
se deben implementar en un thread-safe manera y luego en mi caso, la AtomicInteger
se requiere. Y yo quiero tener una cita de StephenC:
La imposibilidad de demostrar una condición de carrera no significa que no existe.
¿Es necesario utilizar AtomicInteger en un ThreadFactory?
Dependerá de cómo se utiliza el objeto de fábrica.
Si proporciona un objeto de fábrica diferente a cada instancia de
ThreadPoolExecutor
entonces los requisitos (reales) de simultaneidad en la fábrica dependerá de cómo el ejecutor utiliza. En ausencia de declaraciones en las javadocs, que tendría que examinar el código fuente. No he comprobado, pero sospecho que la expansión del grupo de subprocesos (incluyendo la llamada anewThread
) ocurrir dentro de un mutex. Si mi sospecha es correcta, entonces este caso de uso no requiere el objeto de fábrica para ser seguro para subprocesos.ACTUALIZACIÓN - Ahora he comprobado, y mi sospecha era incorrecta (para Java 8 y 12). La
newThread
llamada se realiza cuando se crea un nuevoWorker
objeto, y que no se realiza mientras se mantiene un mutex. Por lo tanto, elnewThread
método tiene que ser seguro para subprocesos en este contexto también.Si un objeto de fábrica es compartida con otras cosas (por ejemplo, otro ejecutor) a continuación, estás en lo correcto: sus
newThread
necesidades método a ser seguro para subprocesos.
No he mirado en su código para tratar de mostrar las condiciones de carrera, pero en mi opinión, eso no es la mejor manera de ir sobre esto. inspección de código y el razonamiento es una mejor manera. La imposibilidad de demostrar una condición de carrera no significa que no existe.