La concurrencia en Java se maneja principalmente a través de su API de Hilos (Threads), que permite la ejecución simultánea de múltiples tareas. Esto optimiza el uso de la CPU y mejora el rendimiento general de las aplicaciones.
Conceptos Clave
Procesos vs. Hilos
- Proceso: Unidad de asignación de recursos del sistema operativo.
- Hilo: Unidad de ejecución dentro de un proceso. Un proceso puede contener múltiples hilos.
Estados de un Hilo
Un hilo en Java puede encontrarse en varios estados durante su ciclo de vida:
- NEW: El hilo ha sido creado pero aún no ha sido iniciado.
- RUNNABLE: El hilo está listo para ejecutarse o está en ejecución.
- BLOCKED: El hilo está esperando para adquirir un bloqueo de monitor (semaforo).
- WAITING: El hilo está esperando indefinidamente la intervención de otro hilo.
- TIMED_WAITING: El hilo está esperando por un período de tiempo específico.
- TERMINATED: El hilo ha finalizado su ejecución.
Transiciones de Estado
Las transiciones entre los estados de un hilo son provocadas por diversas acciones:
Métodos Comunes
1. Control de Ciclo de Vida
| Método | Clase/Interfaz | Descripción | Consideraciones |
|---|---|---|---|
start() |
Thread | Inicia la ejecución del hilo, pasándolo al estado RUNNABLE. | Solo puede llamarse una vez. Una segunda llamada lanza IllegalThreadStateException. |
run() |
Thread/Runnable | Contiene el código que el hilo ejecutará. | Llamar run() directamente no inicia un nuevo hilo; se ejecuta en el hilo actual. |
sleep(long millis) |
Thread | Pausa la ejecución del hilo actual durante el tiempo especificado. | No libera los bloqueos (locks) que el hilo posea. |
yield() |
Thread | Sugiere al planificador que ceda el tiempo de CPU. | Es solo una sugerencia; no hay garentía de que el hilo ceda el control. |
interrupt() |
Thread | Intenta interrumpir el hilo. | Establece una bandera de interrupción. Debe ser manejado explícitamente por el hilo (usando isInterrupted()). |
isInterrupted() |
Thread | Verifica si el hilo ha sido interrumpido. | No modifica el estado de interrupción. |
interrupted() |
Thread | Verifica y borra el estado de interrupción del hilo actual. | Método estático; afecta al hilo que lo llama. |
2. Métodos de Sincronización
| Método | Clase/Interfaz | Descripción | Consideraciones |
|---|---|---|---|
wait() |
Object | Libera el bloqueo del monitor y pone al hilo en estado WAITING. | Debe ser invocado dentro de un bloque synchronized. |
notify() |
Object | Despierta a un hilo arbitrario que esté esperando en el monitor de este objeto. | Debe ser invocado dentro de un bloque synchronized. |
notifyAll() |
Object | Despierta a todos los hilos que estén esperando en el monitor de este objeto. | Debe ser invocado dentro de un bloque synchronized. |
join() |
Thread | Espera a que el hilo de destino termine su ejecución. | Implementado internamente usando wait(). |
join(long millis) |
Thread | Espera a que el hilo de destino termine o hasta que transcurra el tiempo especificado. | Continúa la ejecución si el tiempo expira. |
Formas de Crear Hilos
1. Extender la Clase Thread
Define una nueva clase que herede de Thread y sobrescribe el método run().
class MiHilo extends Thread {
@Override
public void run() {
System.out.println("Hilo ejecutándose...");
}
}
// Uso:
MiHilo hilo = new MiHilo();
hilo.start();
2. Implementar la Interfaz Runnable
Crea una clase que implemente Runnable y proporciona la lógica en el método run().
class MiRunnable implements Runnable {
@Override
public void run() {
System.out.println("Hilo ejecutándose...");
}
}
// Uso:
Thread hilo = new Thread(new MiRunnable());
hilo.start();
3. Implementar la Interfaz Callable (Retorno de Valor)
Similar a Runnable, pero el método call() puede devolver un resultado y lanzar excepciones.
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
class MiCallable implements Callable<string> {
@Override
public String call() throws Exception {
// Simula una tarea que devuelve un resultado
return "Resultado de la tarea";
}
}
// Uso con Thread:
FutureTask<string> futureTask = new FutureTask<>(new MiCallable());
Thread hilo = new Thread(futureTask);
hilo.start();
try {
String resultado1 = futureTask.get(); // Espera y obtiene el resultado
System.out.println("Resultado 1: " + resultado1);
} catch (Exception e) {
e.printStackTrace();
}
// Uso con ExecutorService (Pool de Hilos):
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<string> future = executor.submit(new MiCallable());
try {
String resultado2 = future.get(); // Espera y obtiene el resultado
System.out.println("Resultado 2: " + resultado2);
} catch (Exception e) {
e.printStackTrace();
} finally {
executor.shutdown(); // Cierra el pool de hilos
}
</string></string></string>
4. Pools de Hilos (Executor Framework)
Java proporciona el framework ExecutorService para gestionar pools de hilos de manera eficiente. Se discutirá más a fondo en secciones posteriores.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
// Ejemplo de definición de tareas (se asume que existen RunnableTask y CallableTask)
// class RunnableTask implements Runnable { ... }
// class CallableTask implements Callable<string> { ... }
// Crear un pool de hilos con 10 hilos
ExecutorService executor = Executors.newFixedThreadPool(10);
// Enviar tareas para ejecución
// executor.execute(new RunnableTask());
// Future<string> future = executor.submit(new CallableTask());
// Al finalizar, cerrar el pool de hilos
// executor.shutdown();
</string></string>