resueltos - sincronizacion de hilos en java
¿Cómo hacer que un hilo Java espere la salida de otro hilo? (13)
Esto aplica a todos los idiomas:
Desea tener un modelo de evento / escucha. Usted crea un oyente para esperar un evento en particular. El evento se crearía (o se señalaría) en su hilo de trabajo. Esto bloqueará el hilo hasta que se reciba la señal en lugar de sondear constantemente para ver si se cumple una condición, como la solución que tiene actualmente.
Su situación es una de las causas más comunes de interbloqueos: asegúrese de señalar el otro hilo, independientemente de los errores que puedan haber ocurrido. Ejemplo: si su aplicación arroja una excepción, y nunca llama al método para indicarle a la otra que las cosas se han completado. Esto lo hará para que el otro hilo nunca ''se despierte''.
Sugiero que analice los conceptos de uso de eventos y manejadores de eventos para comprender mejor este paradigma antes de implementar su caso.
Alternativamente, puede usar una llamada a función de bloqueo utilizando un mutex, lo que hará que el hilo espere a que el recurso sea libre. Para hacer esto, necesita una buena sincronización de hilos, como por ejemplo:
Thread-A Locks lock-a
Run thread-B
Thread-B waits for lock-a
Thread-A unlocks lock-a (causing Thread-B to continue)
Thread-A waits for lock-b
Thread-B completes and unlocks lock-b
Estoy haciendo una aplicación Java con una aplicación-lógica-hilo y una base de datos-acceso-hilo. Ambos persisten durante toda la vida útil de la aplicación y ambos deben ejecutarse al mismo tiempo (uno habla con el servidor, otro habla con el usuario, cuando la aplicación está completamente iniciada, necesito que ambos trabajen).
Sin embargo, al inicio, necesito asegurarme de que inicialmente el subproceso de la aplicación espere hasta que el subproceso db esté listo (actualmente determinado por sondeo de un método personalizado dbthread.isReady()
). No me importaría si el hilo de la aplicación bloquea hasta que el hilo db esté listo.
Thread.join()
no parece una solución: el subproceso de base de datos solo se cierra al cerrar la aplicación.
while (!dbthread.isReady()) {}
funciona, pero el ciclo vacío consume muchos ciclos de procesador.
¿Alguna otra idea? Gracias.
La interfaz Future del paquete java.lang.concurrent
está diseñada para proporcionar acceso a los resultados calculados en otro hilo.
Eche un vistazo a FutureTask y ExecutorService por una forma ya hecha de hacer este tipo de cosas.
Recomiendo leer Java Concurrency In Practice para cualquiera que esté interesado en concurrencia y multihilo. Obviamente se concentra en Java, pero también hay mucha carne para cualquiera que trabaje en otros idiomas.
Muchas respuestas correctas pero sin un ejemplo simple. Aquí hay una manera fácil y sencilla de usar CountDownLatch
:
//inside your currentThread.. lets call it Thread_Main
//1
final CountDownLatch latch = new CountDownLatch(1);
//2
// launch thread#2
new Thread(new Runnable() {
@Override
public void run() {
//4
//do your logic here in thread#2
//then release the lock
//5
latch.countDown();
}
}).start();
try {
//3 this method will block the thread of latch untill its released later from thread#2
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//6
// You reach here after latch.countDown() is called from thread#2
Pruebe la clase java.util.concurrent
paquete java.util.concurrent
, que proporciona mecanismos de sincronización de mayor nivel, que son mucho menos propensos a errores que cualquiera de las cosas de bajo nivel.
Puede hacerlo utilizando un objeto de Exchanger compartido entre los dos hilos:
private Exchanger<String> myDataExchanger = new Exchanger<String>();
// Wait for thread''s output
String data;
try {
data = myDataExchanger.exchange("");
} catch (InterruptedException e1) {
// Handle Exceptions
}
Y en el segundo hilo:
try {
myDataExchanger.exchange(data)
} catch (InterruptedException e) {
}
Como han dicho otros, no tome este código alegre y simple de copiar y pegar. Primero lee algo.
Puede leer desde una cola de bloqueo en un hilo y escribir en otro hilo.
Realmente te recomiendo que vayas a través de un tutorial como Java Concurrency de Sun antes de comenzar en el mundo mágico de multihilo.
También hay una serie de buenos libros (google para "Programación concurrente en Java", "Concurrencia de Java en la práctica".
Para llegar a tu respuesta:
En su código que debe esperar al dbThread
, debe tener algo como esto:
//do some work
synchronized(objectYouNeedToLockOn){
while (!dbThread.isReady()){
objectYouNeedToLockOn.wait();
}
}
//continue with work after dbThread is ready
En el dbThread
tu dbThread
, deberías hacer algo como esto:
//do db work
synchronized(objectYouNeedToLockOn){
//set ready flag to true (so isReady returns true)
ready = true;
objectYouNeedToLockOn.notifyAll();
}
//end thread run method here
El objectYouNeedToLockOn
que estoy usando en estos ejemplos es preferiblemente el objeto que necesita manipular al mismo tiempo de cada hilo, o podría crear un Object
separado para ese fin (no recomendaría que los métodos mismos se sincronicen):
private final Object lock = new Object();
//now use lock in your synchronized blocks
Para comprender mejor:
Hay otras formas (a veces mejores) de hacer lo anterior, por ejemplo, con CountdownLatches
, etc. Desde Java 5 hay muchas clases de concurrencia ingeniosas en el paquete java.util.concurrent
y subpaquetes. Realmente necesita encontrar material en línea para conocer la concurrencia u obtener un buen libro.
Requisito ::
- Para esperar la ejecución del siguiente hilo hasta que termine el anterior.
- El siguiente subproceso no debe comenzar hasta que se detenga el hilo anterior, independientemente del consumo de tiempo.
- Debe ser simple y fácil de usar.
Responder ::
@Ver java.util.concurrent.Future.get () doc.
future.get () Espera si es necesario para que se complete el cálculo, y luego recupera su resultado.
¡¡Trabajo hecho!! Ver ejemplo a continuación
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.Test;
public class ThreadTest {
public void print(String m) {
System.out.println(m);
}
public class One implements Callable<Integer> {
public Integer call() throws Exception {
print("One...");
Thread.sleep(6000);
print("One!!");
return 100;
}
}
public class Two implements Callable<String> {
public String call() throws Exception {
print("Two...");
Thread.sleep(1000);
print("Two!!");
return "Done";
}
}
public class Three implements Callable<Boolean> {
public Boolean call() throws Exception {
print("Three...");
Thread.sleep(2000);
print("Three!!");
return true;
}
}
/**
* @See java.util.concurrent.Future.get() doc
* <p>
* Waits if necessary for the computation to complete, and then
* retrieves its result.
*/
@Test
public void poolRun() throws InterruptedException, ExecutionException {
int n = 3;
// Build a fixed number of thread pool
ExecutorService pool = Executors.newFixedThreadPool(n);
// Wait until One finishes it''s task.
pool.submit(new One()).get();
// Wait until Two finishes it''s task.
pool.submit(new Two()).get();
// Wait until Three finishes it''s task.
pool.submit(new Three()).get();
pool.shutdown();
}
}
Salida de este programa ::
One...
One!!
Two...
Two!!
Three...
Three!!
Puedes ver que tarda 6 segundos antes de terminar su tarea que es mayor que otro hilo. Entonces Future.get () espera hasta que la tarea finalice.
Si no usa future.get (), no espera terminar y ejecuta el consumo de tiempo basado.
Buena suerte con concurrencia de Java.
Si quiere algo rápido y sucio, puede simplemente agregar una llamada Thread.sleep () dentro de su ciclo while. Si la biblioteca de la base de datos es algo que no puede cambiar, entonces realmente no hay otra solución fácil. Sondear la base de datos hasta que esté listo con un período de espera no matará el rendimiento.
while (!dbthread.isReady()) {
Thread.sleep(250);
}
Difícilmente se puede llamar código elegante, pero hace el trabajo.
En caso de que pueda modificar el código de la base de datos, entonces es mejor usar un mutex como se propone en otras respuestas.
Use un CountDownLatch con un contador de 1.
CountDownLatch latch = new CountDownLatch(1);
Ahora en el hilo de la aplicación do-
latch.await();
En el hilo de db, después de que hayas terminado, hazlo
latch.countDown();
Ya que
-
join()
ha sido descartado - ya ha usado CountDownLatch y
- Future.get() ya ha sido propuesto por otros expertos,
Puedes considerar otras alternativas:
invokeAll desde
ExecutorService
invokeAll(Collection<? extends Callable<T>> tasks)
Ejecuta las tareas dadas, devolviendo una lista de futuros que mantienen su estado y resultados cuando todo se completa.
ForkJoinPool o newWorkStealingPool de los
Executors
(desde la versión Java 8)Crea un grupo de subprocesos de robo de trabajo utilizando todos los procesadores disponibles como su nivel de paralelismo de destino.
public class ThreadEvent {
private final Object lock = new Object();
public void signal() {
synchronized (lock) {
lock.notify();
}
}
public void await() throws InterruptedException {
synchronized (lock) {
lock.wait();
}
}
}
Usa esta clase así:
Crear un ThreadEvent:
ThreadEvent resultsReady = new ThreadEvent();
En el método, esto está esperando resultados:
resultsReady.await();
Y en el método que crea los resultados después de que se hayan creado todos los resultados:
resultsReady.signal();
EDITAR:
(Perdón por editar esta publicación, pero este código tiene una muy mala condición de carrera y no tengo la reputación suficiente para comentar)
Solo puede usar esto si está 100% seguro de que se llama a la señal () después de aguardar (). Esta es la única razón por la cual no puede usar objetos Java como, por ejemplo, eventos de Windows.
Si el código se ejecuta en este orden:
Thread 1: resultsReady.signal();
Thread 2: resultsReady.await();
entonces el hilo 2 esperará por siempre . Esto se debe a que Object.notify () solo activa uno de los subprocesos actualmente en ejecución. Un hilo esperando más tarde no se despierta. Esto es muy diferente de cómo espero que funcionen los eventos, donde un evento se señala hasta que a) espera o b) se reinicia explícitamente.
Nota: la mayoría de las veces, debe usar notifyAll (), pero esto no es relevante para el problema anterior de "esperar para siempre".