De los errores se aprende, sí, y yo aprendí bastante cuando trabajé en una aplicación donde varios hilos intentaban acceder a un mismo recurso, casi me vuelvo loco. Era como ver a personas queriendo entrar al mismo tiempo a una sala con capacidad limitada, causando errores y bloqueos a cada rato. Gracias a java.util.concurrent.Semaphore fue que pude controlar todo ese tráfico y me aseguré de que todo fluyera sin problemas. Prepárate para aprender a usar esta herramienta y terminar con esos problemas de concurrencia que suelen agobiarnos a muchos. También podrás limitar el acceso a recursos compartidos, mejor dicho, Semaphore le aportará bastante a tus proyectos.
¿Qué es java.util.concurrent.Semaphore?
Creo que su nombre ya te da una idea. Un semáforo es un mecanismo de sincronización que permite controlar cuántos hilos pueden acceder a un recurso al mismo tiempo. En esencia, funciona como un contador que controla la cantidad de permisos disponibles para ejecutar tareas en una sección crítica o acceder a recursos limitados.
Esto es lo que puedes hacer con java.util.concurrent.Semaphore :
- Limitar el acceso concurrente a recursos compartidos, como bases de datos, archivos o impresoras.
- Prevenir conflictos en entornos multihilo asegurándote de que no haya más hilos activos de los permitidos en un momento dado.
Cómo funciona java.util.concurrent.Semaphore
Crear un semáforo
Para usar un semáforo, primero debes especificar el número de permisos disponibles. Por ejemplo, si quieres que solo tres hilos puedan acceder a un recurso a la vez, puedes crear el semáforo así:
Semaphore semaphore = new Semaphore(3);
Solicitar un permiso
Un hilo debe llamar a acquire() para obtener un permiso antes de ingresar a la sección crítica. Si no hay permisos disponibles, el hilo se bloqueará hasta que uno sea liberado.
semaphore.acquire();
Liberar un permiso
Cuando el hilo termina de usar el recurso, llama a release() para devolver el permiso al semáforo y permitir que otros hilos accedan.
semaphore.release();
Ejemplos prácticos para aprender a usar java.util.concurrent.Semaphore
Controlando el acceso a una base de datos
Imagina una aplicación donde varios hilos realizan consultas a una base de datos, pero solo se permiten dos conexiones simultáneas.
import java.util.concurrent.Semaphore;
public class DatabaseAccess {
private static final Semaphore semaphore = new Semaphore(2);
public static void main(String[] args) {
Runnable queryTask = () -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " está accediendo a la base de datos...");
Thread.sleep(2000); // Simula una consulta
System.out.println(Thread.currentThread().getName() + " ha terminado su consulta.");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
};
for (int i = 1; i <= 5; i++) {
new Thread(queryTask, "Hilo-" + i).start();
}
}
}
En este caso, solo dos hilos pueden acceder a la base de datos a la vez. Los demás esperan su turno. Además, cada hilo libera su permiso una vez que termina, permitiendo que otro hilo lo tome.
Simulando el uso de una impresora
Supongamos que tienes tres impresoras compartidas y varios usuarios que necesitan imprimir documentos. ¿Qué harías en este caso? Te muestro una solución:
import java.util.concurrent.Semaphore;
public class PrinterQueue {
private static final Semaphore printerSemaphore = new Semaphore(3);
public static void main(String[] args) {
Runnable printTask = (document) -> {
try {
printerSemaphore.acquire();
System.out.println(Thread.currentThread().getName() + " está usando la impresora...");
Thread.sleep((int) (Math.random() * 3000));
System.out.println(Thread.currentThread().getName() + " terminó de imprimir.");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
printerSemaphore.release();
}
};
for (int i = 1; i <= 6; i++) {
new Thread(() -> printTask.run("Documento-" + i), "Usuario-" + i).start();
}
}
}
Puedes ver que solo tres hilos (usuarios) pueden usar la impresora a la vez. Fuera de eso, una vez que un usuario termina, otro puede acceder a la impresora.
Diferencias entre semáforos y candados
Aunque tanto los semáforos como los candados (Locks) son herramientas de control de concurrencia, tienen diferencias importantes que no puedes dejar pasar:
Característica | Semáforo | Candado |
---|---|---|
Permisos múltiples | Puede manejar varios permisos a la vez. | Solo permite uno a la vez. |
Propiedad | No tiene un dueño específico. | Asociado al hilo que lo adquiere. |
Liberación del recurso | Cualquier hilo puede liberar el permiso. | Solo el hilo que lo adquirió. |
¿Te diste cuenta? java.util.concurrent.Semaphore es como un semáforo en una carretera con carriles limitados: gestiona el tráfico (hilos) y asegura que no haya conflictos al acceder a recursos compartidos. Aprender a usar esta herramienta puede marcar una gran diferencia en el rendimiento y estabilidad de tus aplicaciones multihilo.
Sabemos que si estás aquí es porque la programación es tu pasión, así que tenemos algo para decirte: Apúntate al Bootcamp de Java Full Stack de KeepCoding. Te daremos las claves para destacar en el sector IT y construir aplicaciones complejas y eficientes. ¡El momento es ahora!