Concurrencia y subprocesamiento múltiple en Java: de la teoría a la práctica
En el mundo moderno del software, el rendimiento de las aplicaciones depende directamente de la capacidad de utilizar eficientemente los recursos del procesador. La concurrencia y el subprocesamiento múltiple en Java son conceptos clave que permiten ejecutar varias tareas simultáneamente, acelerar el procesamiento de datos y crear interfaces de usuario receptivas. En este artículo, analizaremos los mecanismos principales de subprocesamiento múltiple en Java, aprenderemos a crear y gestionar hilos, y también revisaremos las mejores prácticas para evitar errores comunes.
¿Qué es un hilo y cómo crearlo en Java?
Un hilo (thread) es un proceso ligero que se ejecuta dentro de una misma aplicación. Cada hilo tiene su propia pila de llamadas y puede funcionar independientemente de otros hilos. En Java, existen dos formas principales de crear un hilo.
Forma 1: Herencia de la clase Thread
public class MyThread extends Thread { @Override public void run() { System.out.println("¡Hilo " + getName() + " iniciado!"); }
public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); // Inicio del hilo }}Forma 2: Implementación de la interfaz Runnable
public class MyRunnable implements Runnable { @Override public void run() { System.out.println("Hilo ejecutándose a través de Runnable"); }
public static void main(String[] args) { Thread thread = new Thread(new MyRunnable()); thread.start(); }}Se recomienda usar la segunda forma, ya que no requiere herencia y permite implementar varias interfaces simultáneamente.
Sincronización de hilos: evitando la condición de carrera
Cuando varios hilos acceden simultáneamente a datos compartidos, se produce una condición de carrera (race condition). Para evitarlo, Java proporciona mecanismos de sincronización.
Palabra clave synchronized
Bloquea el acceso a un método o bloque de código para otros hilos hasta que el hilo actual termine su ejecución:
public class Counter { private int count = 0;
public synchronized void increment() { count++; }
public synchronized int getCount() { return count; }}Bloqueos con Lock
Un mecanismo más flexible que synchronized. Permite establecer tiempos de espera e intentar obtener un bloqueo sin bloquearse:
import java.util.concurrent.locks.ReentrantLock;
public class SafeCounter { private final ReentrantLock lock = new ReentrantLock(); private int count = 0;
public void increment() { lock.lock(); try { count++; } finally { lock.unlock(); // Siempre liberamos el bloqueo } }}Pool de hilos y ExecutorService
Crear un nuevo hilo para cada tarea es una operación costosa. Para una gestión eficiente de los hilos, use ExecutorService del paquete java.util.concurrent.
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;
public class ThreadPoolExample { public static void main(String[] args) { // Creamos un pool de 4 hilos ExecutorService executor = Executors.newFixedThreadPool(4);
for (int i = 0; i < 10; i++) { final int taskId = i; executor.submit(() -> { System.out.println("Tarea " + taskId + " ejecutándose en " + Thread.currentThread().getName()); }); }
executor.shutdown(); // Finalizamos el pool después de ejecutar todas las tareas }}Tipos principales de pools:
newFixedThreadPool(n)— número fijo de hilos-
Blogs
Book Recommendations
Leer también
Docker y Swift: contenerización de aplicaciones de nivel avanzado
Cómo crear un sitio web con Python desde cero
Accessibilidad en JavaScript: cómo hacer un sitio web cómodo para todos
GraphQL API con Swift: tutorial práctico paso a paso
Documentación en C++: cómo escribir código que habla por sí mismo