1. Qué es un hilo de ejecución (thread)
El hilo como línea de trabajo independiente
En Java (y en programación en general), un hilo de ejecución —es una secuencia independiente de instrucciones que avanza en paralelo con otros hilos dentro de un mismo programa. Imagine una fábrica: cada costurera tiene su mesa de trabajo y su tarea; trabaja de forma independiente, pero todo contribuye al resultado común.
Por defecto, un programa Java arranca con un único hilo —el que ejecuta el método main. Pero nada nos impide crear hilos adicionales para que distintas partes del programa se ejecuten a la vez.
Procesos e hilos: ¿en qué se diferencian?
- Proceso — es una unidad de ejecución «pesada». Cada proceso tiene su propio espacio de memoria, sus variables y sus recursos. Los procesos están completamente aislados entre sí — si uno «se rompe», los demás no se ven afectados.
- Hilo (thread) — es una unidad de ejecución «ligera» dentro de un proceso. Todos los hilos de un mismo proceso comparten memoria y recursos. Esto significa que pueden intercambiar datos con facilidad (y, por desgracia, también pueden estorbarse mutuamente).
Analogía:
Un proceso es como un piso independiente: tiene sus propias paredes y sus propios inquilinos.
Los hilos son los inquilinos dentro de ese mismo piso: cada uno tiene sus tareas, pero la cocina y el baño son comunes.
¿Cómo se ve en Java?
Cuando ejecutas el programa, la JVM crea al menos un hilo —el principal (main). Pero puedes crear nuevos hilos para ejecutar tareas en paralelo.
2. ¿Para qué sirve el multihilo?
Reactividad: la UI no debe «colgarse»
Supongamos que escribes una aplicación gráfica —por ejemplo, un editor de texto—. El usuario pulsa el botón «Guardar» y tú empiezas a escribir el archivo en disco, una operación lenta. Si haces todo en el hilo principal, la ventana de la aplicación «se congelará»: el usuario no podrá pulsar nada, el cursor no se moverá y la interfaz no responderá. Si, en cambio, guardas el archivo en un hilo aparte — la interfaz seguirá siendo ágil e incluso el usuario podrá cambiar de opinión y cerrar la aplicación.
Ejemplo cotidiano:
Abres el navegador y comienzas a descargar un archivo grande. ¡Si el navegador no usara hilos, no podrías ni abrir una pestaña nueva ni desplazarte por la página hasta que terminara la descarga!
Procesamiento de datos en paralelo
Supongamos que tienes una lista de mil archivos que hay que procesar (por ejemplo, recalcular hashes o reemplazar texto). ¿Por qué no procesarlos en paralelo? Cada hilo toma su archivo y trabaja con él de forma independiente, y el trabajo total termina varias veces más rápido.
Ejemplo:
Un servidor atiende peticiones de cientos de clientes. Si lo hiciera en un único hilo, el resto de clientes esperaría su turno eternamente. Con hilos, cada solicitud se procesa de forma independiente.
Aprovechar los procesadores multinúcleo
Los procesadores modernos — no son un único «cerebro», sino todo un equipo (núcleos) que puede trabajar en paralelo. Si tu programa usa un solo hilo, el resto de núcleos se aburren y se ponen a jugar al Buscaminas. Si lanzas varios hilos — todos los núcleos están ocupados y el programa se ejecuta más rápido.
Dato curioso:
Incluso tu teléfono tiene varios núcleos, y los portátiles y servidores — ¡decenas! No aprovecharlos es como comprar un autobús y viajar en él tú solo.
3. Ejemplos de la vida real
| Ámbito | Ejemplo de multihilo |
|---|---|
| Descarga de archivos | Descargar varios archivos a la vez |
| Interfaz de usuario | La aplicación no se «cuelga» al cargar/guardar datos |
| Servidores | Procesar numerosas solicitudes de red en paralelo |
| Videojuegos | Hilos separados para física, gráficos, música e IA |
| Mensajería | Recepción de mensajes, envío de archivos y actualización de la interfaz |
| Procesamiento de vídeo | Procesar fotogramas en paralelo |
Mini analogía:
El cocinero prepara la sopa, al mismo tiempo el horno hornea el pastel y el robot aspirador limpia el suelo — todo ocurre a la vez y la cena está lista más rápido.
4. Posibles dificultades del multihilo
Condición de carrera (race condition)
Cuando varios hilos modifican simultáneamente la misma variable, el resultado puede ser impredecible. Por ejemplo, si dos hilos incrementan a la vez un contador compartido, el valor final puede ser incorrecto. De ello hablaremos con más detalle en una de las próximas lecciones.
Sincronización
Para que los hilos no se estorben, hay que idear maneras de «ponerse de acuerdo» — quién y cuándo puede modificar los datos. A esto se le llama sincronización. Para ello existen palabras clave y construcciones específicas (synchronized, bloqueos, etc.), de las que hablaremos más adelante.
Deadlock (bloqueo mutuo)
A veces los hilos pueden «hacerse tan amigos» que se esperan mutuamente para siempre, y la aplicación se queda colgada. A esto se le llama deadlock — y es uno de los errores más insidiosos en la programación multihilo.
Depuración y pruebas
Los errores en programas multihilo son muy difíciles de atrapar: a veces todo funciona y a veces no. En ocasiones el bug solo aparece en el servidor o en el equipo del usuario, mientras que en el tuyo todo va perfecto. Esto convierte las pruebas y la depuración del código multihilo en una auténtica odisea para el desarrollador.
5. Visión rápida: cómo se ve un programa multihilo
Ejemplo sin hilos:
public class Main {
public static void main(String[] args) {
// Contamos hasta 5
for (int i = 1; i <= 5; i++) {
System.out.println(i);
}
// Mostramos letras
for (char c = 'A'; c <= 'E'; c++) {
System.out.println(c);
}
}
}
La salida siempre es la misma:
1
2
3
4
5
A
B
C
D
E
Ejemplo con hilos:
public class Main {
public static void main(String[] args) {
Thread numbers = new Thread(() -> {
for (int i = 1; i <= 5; i++) {
System.out.println(i);
try {
Thread.sleep(100); // Esperamos un poco
} catch (InterruptedException e) {
// Ignoramos
}
}
});
Thread letters = new Thread(() -> {
for (char c = 'A'; c <= 'E'; c++) {
System.out.println(c);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// Ignoramos
}
}
});
numbers.start();
letters.start();
}
}
La salida aparecerá entremezclada:
1
A
2
B
3
C
4
D
5
E
o, si los hilos «compiten», puede aparecer en otro orden. Lo importante — ambos bucles se ejecutan en paralelo.
6. Matices útiles
Esquema visual: cómo trabajan juntos los hilos
+-------------------+ +-------------------+
| Hilo principal | | Segundo hilo |
+-------------------+ +-------------------+
| 1 | 2 | 3 | 4 | 5 | | A | B | C | D | E |
+-------------------+ +-------------------+
| |
| Ambos trabajan |
| simultáneamente |
+-------------------------+
Dónde usa Java hilos «bajo el capó»
- Recolección de basura (Garbage Collector) — un hilo aparte limpia los objetos no utilizados.
- Entrada/salida (IO) — lectura y escritura de archivos, conexiones de red.
- Servidores y aplicaciones web — cada petición de cliente se maneja en un hilo aparte.
- Temporizadores, planificadores de tareas — ejecución de tareas programadas.
7. Errores típicos de principiantes
Error n.º 1: esperar que los hilos siempre aceleren el programa.
En realidad, si tienes una máquina monoprocesador o has organizado mal el trabajo, el multihilo puede incluso ralentizar la ejecución debido al «lío» y a la sobrecarga de cambiar entre hilos.
Error n.º 2: ignorar los problemas de sincronización.
Muchos piensan: «Solo lanzo dos hilos, ¿qué podría salir mal?» Pero si ambos hilos modifican una misma variable, el resultado puede ser completamente inesperado.
Error n.º 3: usar hilos para todo.
No conviene lanzar un hilo separado para cada nimiedad. Los hilos son un recurso, y su cantidad excesiva puede provocar lentitud e incluso la caída de la aplicación.
Error n.º 4: no gestionar los errores.
Los hilos pueden lanzar excepciones (por ejemplo, al trabajar con archivos o con la red). Si no gestionas esos errores, la aplicación puede finalizar de forma abrupta o «quedarse colgada».
GO TO FULL VERSION