Apuntes sobre Hilos en Java: De Principiante a Avanzado

Los hilos (o threads) son fundamentales en la programación concurrente. Java ofrece un soporte robusto para trabajar con ellos mediante las clases y métodos en su API. En este artículo, te guiaré paso a paso, desde los conceptos básicos hasta usos más avanzados, para que entiendas cómo trabajar con hilos en Java de manera efectiva.


¿Qué es un hilo?

Un hilo es la unidad más pequeña de procesamiento que puede ejecutarse de forma concurrente dentro de un programa. Cada hilo funciona de manera independiente, pero comparte recursos del programa principal, como la memoria.

Por defecto, todos los programas en Java tienen un hilo principal llamado main, que se inicia automáticamente cuando ejecutas un programa.


Creación de hilos: Nivel básico

En Java, hay dos formas principales de crear y usar hilos:

1. Extender la clase Thread

La clase Thread es la base de los hilos en Java. Puedes crear un hilo extendiendo esta clase y sobrescribiendo el método run().

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();
        hilo1.start(); // Inicia el hilo
    }
}

Notas:

  • El método run() contiene el código que se ejecutará cuando el hilo inicie.
  • El método start() pone al hilo en estado «runnable», listo para ejecutarse.

2. Implementar la interfaz Runnable

Esta es la forma preferida cuando necesitas que tu clase herede de otra clase, ya que Java no permite la herencia múltiple.

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());
        hilo1.start(); // Inicia el hilo
    }
}

Diferencias principales:

  • Thread es una clase, mientras que Runnable es una interfaz.
  • Usar Runnable te da más flexibilidad si necesitas heredar de otra clase.

Sincronización: Evitando conflictos entre hilos

Cuando varios hilos acceden a recursos compartidos, como una variable o una lista, pueden producirse conflictos (condiciones de carrera). Para evitar esto, usamos sincronización.

Uso de synchronized

El modificador synchronized garantiza que solo un hilo pueda ejecutar un bloque de código crítico a la vez.

class Contador {
    private int valor = 0;

    public synchronized void incrementar() {
        valor++;
        System.out.println(Thread.currentThread().getName() + " - Valor: " + valor);
    }
}

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();
    }
}

Comunicación entre hilos

Los hilos pueden necesitar coordinarse entre sí, por ejemplo, cuando uno debe esperar a que otro complete una tarea. Para esto usamos wait(), notify() y notifyAll().

Ejemplo: Productor-Consumidor

class Almacen {
    private int producto = -1;
    private boolean disponible = false;

    public synchronized void producir(int valor) throws InterruptedException {
        while (disponible) {
            wait(); // Espera si ya hay un producto
        }
        producto = valor;
        disponible = true;
        System.out.println("Producido: " + producto);
        notify(); // Notifica al consumidor
    }

    public synchronized int consumir() throws InterruptedException {
        while (!disponible) {
            wait(); // Espera si no hay producto disponible
        }
        disponible = false;
        System.out.println("Consumido: " + producto);
        notify(); // Notifica al productor
        return producto;
    }
}

public class Main {
    public static void main(String[] args) {
        Almacen almacen = new Almacen();

        Thread productor = new Thread(() -> {
            try {
                for (int i = 0; i < 5; i++) {
                    almacen.producir(i);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        Thread consumidor = new Thread(() -> {
            try {
                for (int i = 0; i < 5; i++) {
                    almacen.consumir();
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        productor.start();
        consumidor.start();
    }
}

Uso de Executors: Gestión avanzada de hilos

Cuando necesitas gestionar múltiples hilos, puedes usar el framework ExecutorService. Esto facilita la creación, ejecución y finalización de hilos.

Ejemplo: Pool de hilos

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
    public static void main(String[] args) {
        ExecutorService ejecutor = Executors.newFixedThreadPool(3); // 3 hilos en el pool

        for (int i = 1; i <= 10; i++) {
            int tarea = i;
            ejecutor.execute(() -> {
                System.out.println("Ejecutando tarea " + tarea + " en " + Thread.currentThread().getName());
            });
        }

        ejecutor.shutdown(); // Cierra el pool de hilos
    }
}

Ventajas de ExecutorService:

  • Maneja automáticamente la reutilización de hilos.
  • Facilita la ejecución de tareas concurrentes.
  • Proporciona métodos para controlar el ciclo de vida de los hilos.

Resumen: Hilos en Java

  1. Conceptos básicos:
    • Los hilos permiten ejecutar tareas concurrentemente.
    • Puedes crear hilos extendiendo Thread o implementando Runnable.
  2. Sincronización:
    • Usa synchronized para evitar conflictos al compartir recursos.
    • wait(), notify() y notifyAll() son útiles para coordinar hilos.
  3. Avanzado:
    • ExecutorService simplifica la gestión de múltiples hilos.
    • Herramientas adicionales como Callable, Future y CompletableFuture permiten manejar tareas más complejas.

Con esta guía tienes los fundamentos para trabajar con hilos en Java, desde lo más básico hasta técnicas más avanzadas. ¡Ahora puedes comenzar a implementar tus propios sistemas concurrentes!

Deja un comentario

Información básica sobre protección de datos Ver más

  • Responsable: Tomas Gonzalez.
  • Finalidad:  Moderar los comentarios.
  • Legitimación:  Por consentimiento del interesado.
  • Destinatarios y encargados de tratamiento:  No se ceden o comunican datos a terceros para prestar este servicio.
  • Derechos: Acceder, rectificar y suprimir los datos.
  • Información Adicional: Puede consultar la información detallada en la Política de Privacidad.

error: Content is protected !!

Descubre más desde Tomás González: Formador y Desarrollador Web

Suscríbete ahora para seguir leyendo y obtener acceso al archivo completo.

Seguir leyendo

Este sitio web utiliza cookies, si necesitas más información puedes visitar nuestra política de privacidad    Ver
Privacidad