De nuevo te traigo un método que te facilitará el trabajo en Java, sobre todo si estás haciendo programación concurrente y debes manejar la sincronización de hilos. El método wait() en Java se creó para que los hilos puedan trabajar de manera coordinada y eficiente, sin interferir entre ellos, claro. Si aún no lo estás usando, déjame decirte que te estás perdiendo de una gran herramienta. Te cuento qué es y cómo usar el método wait() en Java de forma fácil.
¿Qué es wait() en Java?
El método wait() en Java es parte de la clase java.lang.Object y es fundamental en la gestión de la sincronización entre hilos. La idea es que uses este método para poner un hilo en espera hasta que otro hilo lo despierte.
Te lo pongo más fácil todavía, cuando un hilo ejecuta wait(), se suspende y libera el bloqueo del objeto que está utilizando, permitiendo que otros hilos accedan a ese recurso. Solo un hilo podrá reanudar la ejecución del hilo que está en espera, mediante la invocación del método notify() o notifyAll().
¿Cómo funciona wait() en Java?
Para que entiendas bien cómo funciona wait() en Java, necesito que tengas claro el concepto de monitores en Java. Se trata de que cada objeto tiene un monitor asociado, que es un mecanismo de control de acceso a sus recursos compartidos.
Cuando un hilo entra en un bloque sincronizado, obtiene el monitor del objeto. Sin embargo, si ese hilo llama a wait(), se libera el monitor y se coloca en un estado de espera. Entonces, el hilo permanecerá en espera hasta que otro hilo lo despierte utilizando el método notify() o notifyAll().
Te lo dejo por pasos para que lo entiendas más:
- Un hilo adquiere el monitor de un objeto y entra en una sección crítica.
- Si el hilo encuentra que no puede continuar (por ejemplo, porque necesita que otro hilo realice una acción), llama al método wait().
- El hilo se suspende y libera el monitor, permitiendo que otros hilos entren en la sección crítica.
- Otro hilo, que tiene el monitor, puede realizar una acción (como modificar un valor) y luego llama a notify() o notifyAll() para despertar al hilo suspendido.
Tipos de wait() en Java
Estas son las variantes más importantes del método wait() en Java:
- wait(): Pone el hilo en espera hasta que otro hilo lo notifique, sin límite de tiempo.
- wait(long timeout): Pone el hilo en espera durante un tiempo específico, después del cual se reanudará automáticamente, incluso si no ha sido notificado.
- wait(long timeout, int nanos): Similar a la versión anterior, pero con una mayor precisión en los milisegundos.
Ejemplo práctico del uso de wait() en Java
Voy a mostrarte cómo se usa wait() en un entorno de múltiples hilos:
Contexto: imagina que tienes una situación en la que varios hilos compiten por recursos limitados. Un ejemplo clásico es la gestión de una bandeja de café en una oficina, donde un productor (el que llena la bandeja) y un consumidor (el que consume el café) compiten por acceder a los recursos.
Ejemplo de un productor-consumidor con wait():
Entonces, piensa que hay una bandeja de café con un límite de 10 tazas. El productor debe agregar tazas a la bandeja y el consumidor debe tomar tazas. Si la bandeja está llena, el productor debe esperar. Si está vacía, el consumidor debe esperar.
class CoffeeTray {
private int coffeeCount = 0;
private final int MAX_CAPACITY = 10;
public synchronized void addCoffee() throws InterruptedException {
while (coffeeCount >= MAX_CAPACITY) {
wait(); // Espera si la bandeja está llena
}
coffeeCount++;
System.out.println("Un productor añadió una taza de café. Tazas en la bandeja: " + coffeeCount);
notify(); // Notifica al consumidor
}
public synchronized void takeCoffee() throws InterruptedException {
while (coffeeCount == 0) {
wait(); // Espera si la bandeja está vacía
}
coffeeCount--;
System.out.println("Un consumidor tomó una taza de café. Tazas restantes: " + coffeeCount);
notify(); // Notifica al productor
}
}
class Producer implements Runnable {
private CoffeeTray tray;
public Producer(CoffeeTray tray) {
this.tray = tray;
}
@Override
public void run() {
try {
while (true) {
tray.addCoffee();
Thread.sleep(1000); // Simula el tiempo de producción
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
class Consumer implements Runnable {
private CoffeeTray tray;
public Consumer(CoffeeTray tray) {
this.tray = tray;
}
@Override
public void run() {
try {
while (true) {
tray.takeCoffee();
Thread.sleep(1500); // Simula el tiempo de consumo
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public class WaitExample {
public static void main(String[] args) {
CoffeeTray tray = new CoffeeTray();
Thread producerThread = new Thread(new Producer(tray));
Thread consumerThread = new Thread(new Consumer(tray));
producerThread.start();
consumerThread.start();
}
}
¿Cómo funciona este código?
- Bandeja de café (CoffeeTray): Esta clase tiene un contador de tazas (coffeeCount) y un límite de capacidad (MAX_CAPACITY). Los métodos addCoffee() y takeCoffee() están sincronizados para garantizar que no haya conflictos entre los hilos.
- Productor (Producer): El productor agrega tazas a la bandeja, pero espera si la bandeja está llena.
- Consumidor (Consumer): El consumidor toma tazas de la bandeja, pero espera si está vacía.
Si notaste bien, el productor y el consumidor se coordinan mediante wait() y notify() para garantizar que la bandeja de café nunca se llene demasiado ni quede vacía.
Profundiza más en cómo manejar la sincronización de hilos y domina Java a nivel profesional, en el Bootcamp de Java Full Stack de KeepCoding. En este programa aprenderás Java y sus características avanzadas, como la programación concurrente, y estarás listo para construir excelentes y escalables aplicaciones en el mundo real.