Multithreading en Java

Conceptos de Procesos e Hilos

La mayoría de los sistemas operativos soportan el modelo de procesos. Una tarea generalmente se asocia a un proceso. Las características principales de un proceso incluyen:

  • Ser independientes con recursos dedicados.
  • Poseer un ciclo de vida y estados específicos.
  • Permitir la ejecución concurrente en un procesador único sin interferencias mutuas.

Los hilos representan la unidad más pequeña de ejecución para la CPU. Cada proceso puede contener varios hilos, y todo hilo debe pertenecer a un proceso. Los hilos tienen su propia pila de ejecución, pero no controlan recursos del sistema. La CPU procesa un hilo a la vez; la ilusión de paralelismo surge del rápido cambio de contexto. La gestión y programación de los hilos recae en el proceso padre.

Estados del Ciclo de Vida de Hilos en Java

Los hilos en Java atraviesan cinco estados fundamentales:

  • Nuevo (New): Se crea el objeto de hilo, como Thread h = new Tarea();
  • Listo (Runnable): Al invocar el método start() del hilo, por ejemplo h.start();, el hilo queda preparado para ejecución, esperando asignación de la CPU.
  • En Ejecución (Running): Cuando la CPU selecciona un hilo listo, este comienza su ejecución. Solo los hilos en estado listo pueden transicionar a este estado.
  • Bloqueado (Blocked): Un hilo en ejecución puede suspenderse temporalmente y perder el acceso a la CPU. Las causas incluyen:
    • Bloqueo por espera: Al ejecutar el método wait().
    • Bloqueo por sincronización: Al no obtener un bloqueo synchronized porque otro hilo lo retiene.
    • Otros bloqueos: Originados por sleep(), join() o solicitudes de E/S. El hilo regresa a estado listo al completar la operación.
  • Terminado (Dead): El hilo finaliza su método run() normalmente o por una excepción.

Nota: Los métodos wait(), notify() y notifyAll() pertenecen a la clase Object.

Métodos para Crear y Ejecutar Hilos en Java

1. Extendiendo la Clase Thread

Definir una subclase de Thread y sobrescribir el método run() con la lógica del hilo. Crear instancias de esta clase y ejecutar start().

public class TrabajoA extends Thread {
    @Override
    public void run() {
        for (int iteracion = 0; iteracion < 1000; iteracion++) {
            System.out.println(getName() + " paso: " + iteracion);
        }
    }

    public static void main(String[] args) {
        new TrabajoA().start();
        new TrabajoA().start();
    }
}

2. Implementando la Interfaz Runnable

Crear una clase que implemente Runnable, definir run(), y pasar instancias de esta clase al constructor de Thread.

public class TrabajoB implements Runnable {
    @Override
    public void run() {
        for (int indice = 0; indice < 1000; indice++) {
            System.out.println(Thread.currentThread().getName() + " valor: " + indice);
        }
    }

    public static void main(String[] args) {
        TrabajoB tarea = new TrabajoB();
        new Thread(tarea).start();
        new Thread(tarea).start();
    }
}

3. Utilizando Callable y Future

Implementar Callable con un tipo de retorno, encapsularlo en FutureTask, y luego iniciar un hilo. El valor de retorno se obtiene mediante get().

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class TrabajoC implements Callable<integer> {
    @Override
    public Integer call() throws Exception {
        int acumulado = 0;
        for (; acumulado < 1000; acumulado++) {
            System.out.println(Thread.currentThread().getName() + " conteo: " + acumulado);
        }
        return acumulado;
    }

    public static void main(String[] args) {
        TrabajoC callable = new TrabajoC();
        FutureTask<integer> future = new FutureTask<>(callable);
        new Thread(future).start();
        try {
            Thread.sleep(5000);
            System.out.println("Resultado: " + future.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}</integer></integer>

Aunque extender Thread es directo, la herencia única en Java limita su extensibilidad, por lo que se recomienda implementar Runnable o Callable para mayor flexibilidad. Siempre se deben iniciar hilos con start(); invocar run() directamente no crea un nuevo hilo.

Etiquetas: java hilos thread Runnable Callable

Publicado el 6-21 19:55