La programación multihilo es una técnica clave en el desarrollo de aplicaciones modernas, ya que permite ejecutar múltiples tareas al mismo tiempo dentro de un programa. Java proporciona un soporte robusto para trabajar con hilos (o threads), lo que facilita la creación de aplicaciones concurrentes. En este artículo exploraremos los conceptos básicos y cómo empezar a trabajar con hilos en Java.
¿Qué es un hilo (thread)?
Un hilo es una unidad de ejecución dentro de un programa. Un programa en Java puede contener múltiples hilos que se ejecutan de manera independiente pero que comparten los mismos recursos, como la memoria y las variables.
Cada aplicación Java tiene al menos un hilo principal, el cual comienza con el método main()
.
Beneficios de usar hilos
- Concurrente: Permite que múltiples tareas se ejecuten al mismo tiempo.
- Mejor rendimiento: Mejora el uso de los recursos del procesador.
- Asíncrono: Ideal para tareas como leer archivos, procesar datos en segundo plano o manejar múltiples usuarios.
Cómo funcionan los hilos en Java
Un hilo tiene un ciclo de vida con los siguientes estados:
- Nuevo (New): El hilo se crea pero no está en ejecución.
- Ejecutable (Runnable): Está listo para ejecutarse, pero espera que el sistema operativo lo programe.
- Ejecutando (Running): El hilo está ejecutándose.
- Bloqueado/Esperando (Blocked/Waiting): El hilo está esperando un recurso o condición para continuar.
- Terminado (Terminated): El hilo ha completado su tarea.
Cómo crear hilos en Java
Método 1: Extender la clase Thread
class MiHilo extends Thread {
@Override
public void run() {
// Código que ejecutará el hilo
System.out.println("Hilo en ejecución: " + Thread.currentThread().getName());
}
}
public class Main {
public static void main(String[] args) {
MiHilo hilo1 = new MiHilo(); // Crear el hilo
hilo1.start(); // Iniciar el hilo
}
}
run()
: Define el código que ejecutará el hilo.start()
: Inicia el hilo y lo pone en el estado «Runnable».
Método 2: Implementar la interfaz Runnable
class MiTarea implements Runnable {
@Override
public void run() {
System.out.println("Tarea en ejecución: " + Thread.currentThread().getName());
}
}
public class Main {
public static void main(String[] args) {
Thread hilo1 = new Thread(new MiTarea()); // Crear el hilo con Runnable
hilo1.start(); // Iniciar el hilo
}
}
Este método es más flexible porque permite que la clase herede de otra además de implementar Runnable
.
Ejemplo: Simulación de tareas concurrentes
Supongamos que queremos simular un sistema donde varios usuarios ejecutan tareas de manera independiente:
class TareaUsuario implements Runnable {
private final String usuario;
public TareaUsuario(String usuario) {
this.usuario = usuario;
}
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("Usuario " + usuario + " ejecutando tarea " + i);
}
}
}
public class Main {
public static void main(String[] args) {
Thread usuario1 = new Thread(new TareaUsuario("Alicia"));
Thread usuario2 = new Thread(new TareaUsuario("Carlos"));
usuario1.start();
usuario2.start();
}
}
Salida esperada:
Usuario Alicia ejecutando tarea 1
Usuario Carlos ejecutando tarea 1
Usuario Alicia ejecutando tarea 2
Usuario Carlos ejecutando tarea 2
...
Manejo de recursos compartidos
Cuando varios hilos comparten recursos, como variables o estructuras de datos, puede haber problemas de concurrencia. Java proporciona el modificador synchronized
para evitar conflictos.
Ejemplo: Contador sincronizado
class Contador {
private int contador = 0;
public synchronized void incrementar() {
contador++;
System.out.println(Thread.currentThread().getName() + " - Contador: " + contador);
}
}
public class Main {
public static void main(String[] args) {
Contador contador = new Contador();
Runnable tarea = contador::incrementar;
Thread hilo1 = new Thread(tarea, "Hilo-1");
Thread hilo2 = new Thread(tarea, "Hilo-2");
hilo1.start();
hilo2.start();
}
}
Consideraciones importantes al trabajar con hilos
- Evitar usar
run()
directamente:- Si llamas a
run()
directamente, el código se ejecutará en el hilo principal y no en un nuevo hilo.
- Si llamas a
- Manejo de excepciones:
- Usa
try-catch
para manejar interrupciones y errores dentro del métodorun()
.
- Usa
- Control del ciclo de vida:
- Siempre usa
start()
para iniciar un hilo.
- Siempre usa
- Sincronización:
- Los recursos compartidos deben protegerse usando
synchronized
para evitar condiciones de carrera.
- Los recursos compartidos deben protegerse usando
Resumen
- Un hilo es una unidad de ejecución que permite realizar tareas concurrentes.
- Puedes crear hilos extendiendo
Thread
o implementandoRunnable
. - Usa
synchronized
para evitar conflictos cuando varios hilos comparten recursos. - Trabajar con hilos requiere planificación para evitar problemas como condiciones de carrera o interbloqueos.
Con estos conceptos básicos, estás listo para comenzar a trabajar con hilos en Java y comprender su importancia en la programación concurrente. Practica con ejemplos simples para consolidar tu aprendizaje. ¡Buena suerte!