¿Quieres que tus hilos coordinen sus actividades y se sincronicen mejor? Pues tienes que aprender a usar notify() en Java. Este es uno de los métodos más importantes cuando se trata de trabajar con la sincronización de hilos en Java, porque te ayuda a evitar errores y te deja aprovechar al máximo la capacidad de concurrencia en Java.
¿Qué es notify() en Java?
Con el método notify() podrás despertar un hilo que está en estado de espera. Además, pertenece a la clase Object y es muy usado en la programación concurrente en Java. Por lo general se usa junto con el método wait(), y ambos forman una gran pareja para la sincronización de hilos.
- ¿Qué hace wait()? pone un hilo en espera hasta que otra parte del programa indique que puede continuar.
- ¿Qué hace notify() en Java? envía una señal a un hilo en espera para que reanude su ejecución.
Algo importante que tienes que saber es que notify() solo despierta un hilo que esté esperando en el monitor del objeto, lo que significa que si varios hilos están esperando en el mismo objeto, no necesariamente se despertará el primero en la cola. Si necesitas despertar todos los hilos en espera, debes usar notifyAll().
¿Por qué necesitamos notify() en Java?
Si ya has programado en Java tienes conocimiento de que, en muchos programas, los hilos deben coordinar su trabajo para evitar condiciones de carrera y garantizar que los recursos compartidos sean accesibles de manera segura. Entonces, cuando tú usas notify() y wait() estás dejando que los hilos se comuniquen entre sí y gestionen el acceso a los recursos de una mejor manera.
Además, cuando un hilo está esperando un recurso que otro hilo está ocupando, necesita sincronización para asegurarse de que no haya conflictos. Por eso es que necesitamos los métodos wait() y notify().
Funcionamiento de notify() en Java
Como te dije, cuando un hilo se encuentra en un estado de espera (por ejemplo, esperando a que un recurso esté disponible), se puede liberar el control de ese hilo mediante la invocación del método notify(). De modo que, el hilo que estaba esperando recibe una señal para continuar con su ejecución.
Quiero que te quede claro el funcionamiento, entonces te dejo este ejemplo práctico de un escenario de producción y consumo de recursos.
Contexto: Imagina que tienes una clase Bolsa que contiene una lista de productos y que una clase HiloEnvio se encarga de enviar la bolsa cuando esta está llena.
Ejemplo práctico: Sincronización con notify() en Java
Piensa en una aplicación sencilla donde tienes una Bolsa que puede almacenar productos. Solo puede almacenar hasta 5 productos. Cuando la bolsa se llena, se envía automáticamente.
Sería algo como esto:
Clase Bolsa
package com.example;
import java.util.ArrayList;
public class Bolsa {
private ArrayList<Producto> listaProductos = new ArrayList<Producto>();
public void addProducto(Producto producto) {
if (!estaLlena()) {
listaProductos.add(producto);
}
}
public ArrayList<Producto> getListaProductos() {
return listaProductos;
}
public int getSize() {
return listaProductos.size();
}
public boolean estaLlena() {
return listaProductos.size() >= 5;
}
}
Clase Producto
package com.example;
public class Producto {
private String nombre;
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
}
Clase HiloEnvio (El hilo que envía la bolsa cuando está llena)
package com.example;
public class HiloEnvio extends Thread {
private Bolsa bolsa;
public HiloEnvio(Bolsa bolsa) {
super();
this.bolsa = bolsa;
}
@Override
public void run() {
synchronized (bolsa) {
try {
// Si la bolsa no está llena, espera
while (!bolsa.estaLlena()) {
bolsa.wait();
}
System.out.println("Enviando la bolsa con " + bolsa.getSize() + " productos");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Clase Principal
package com.example;
public class Principal {
public static void main(String[] args) {
Bolsa bolsa = new Bolsa();
HiloEnvio hiloEnvio = new HiloEnvio(bolsa);
hiloEnvio.start();
for (int i = 0; i < 10; i++) {
Producto producto = new Producto();
synchronized (bolsa) {
try {
// Cada vez que la bolsa esté llena, notifica al hilo de envío
if (bolsa.estaLlena()) {
bolsa.notify();
}
Thread.sleep(1000); // Simula un tiempo de producción
bolsa.addProducto(producto);
System.out.println("Producto añadido. Total en bolsa: " + bolsa.getSize());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
Te explico el ejemplo
- El hilo principal: Es el que va agregando productos a la bolsa cada segundo.
- El hilo HiloEnvio: Está esperando que la bolsa se llene (usando wait()) para enviar los productos.
- Sincronización: Cuando la bolsa alcanza el límite de 5 productos, el hilo principal llama a notify() para notificar al hilo HiloEnvio que ya puede proceder a enviar la bolsa.
Cuándo debes usar notify() en Java
Notify() en Java suele usarse cuando un hilo espera que otro hilo le notifique para continuar.
Lo más probable es que lo necesites en estos casos:
- Productor-consumidor: Un hilo productor agrega elementos a un buffer y un hilo consumidor los toma. El productor debe notificar al consumidor cuando haya elementos disponibles.
- Productor múltiple: Varios hilos productores pueden estar agregando elementos a un buffer y un hilo consumidor debe esperar a que haya suficientes elementos para procesarlos.
- Sincronización de tareas dependientes: Cuando tienes múltiples tareas que deben ejecutarse en un orden específico, puedes usar notify() en Java para que cada hilo espere la señal del anterior.
He creado un super Bootcamp para que profundices más en la programación concurrente, la sincronización de hilos y otros aspectos avanzados de Java, se llama: el Bootcamp de Java Full Stack de KeepCoding y te proporcionará las habilidades necesarias para dominar estos temas y convertirte en un experto en desarrollo backend. ¡No dejes pasar la oportunidad de aprender con los mejores!