Concurrencia y multihilo en Java: técnicas avanzadas

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Concurrencia y multihilo en Java: nivel avanzado

\n\n

El multihilo en Java es uno de los temas más potentes y a la vez complejos. A nivel inicial usamos synchronized y Thread, pero para construir sistemas eficientes y escalables se requiere una comprensión más profunda. En este artículo analizaremos mecanismos avanzados de concurrencia: pools de hilos, ForkJoinPool, programación asíncrona con CompletableFuture, ajuste fino de bloqueos y trabajo con memoria. El material está orientado a desarrolladores que ya conocen los fundamentos del multihilo.

\n\n

1. Trabajo avanzado con pools de hilos: ExecutorService y configuración

\n\n

El uso de ExecutorService es el estándar en el desarrollo industrial. Sin embargo, el estándar Executors.newFixedThreadPool(10) no siempre es adecuado. Veamos la configuración detallada de ThreadPoolExecutor.

\n\n
import java.util.concurrent.*;\n\npublic class CustomThreadPool {\n    public static void main(String[] args) {\n        int corePoolSize = 4;\n        int maxPoolSize = 8;\n        long keepAliveTime = 30;\n        TimeUnit unit = TimeUnit.SECONDS;\n        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(100);\n\n        ThreadPoolExecutor executor = new ThreadPoolExecutor(\n                corePoolSize,\n                maxPoolSize,\n                keepAliveTime,\n                unit,\n                workQueue,\n                new ThreadPoolExecutor.CallerRunsPolicy() // política de saturación\n        );\n\n        // Monitoreo del estado del pool\n        executor.submit(() -> {\n            System.out.println("Hilos activos: " + executor.getActiveCount());\n            System.out.println("Tareas completadas: " + executor.getCompletedTaskCount());\n        });\n\n        executor.shutdown();\n    }\n}
\n\n

Parámetros clave:

\n
    \n
  • corePoolSize — número mínimo de hilos que se mantienen incluso sin tareas.
  • \n
  • maxPoolSize — número máximo de hilos.
  • \n
  • keepAliveTime — tiempo de vida de los hilos sobrantes.
  • \n
  • workQueue — cola de tareas. ArrayBlockingQueue con tamaño limitado evita el desbordamiento de memoria.
  • \n
  • Política de saturación — qué hacer si la cola está llena y todos los hilos están ocupados. CallerRunsPolicy ralentiza el hilo que llama.
  • \n
\n\n

Para tareas largas de I/O es mejor aumentar corePoolSize, para tareas CPU-bound hay que basarse en Runtime.getRuntime().availableProcessors().

\n\n

2. ForkJoinPool y tareas recursivas

\n\n

ForkJoinPool es un pool especializado para tareas que se pueden dividir en subtareas (divide y vencerás). Utiliza eficientemente el robo de trabajo (work stealing).

\n\n

Ejemplo: cálculo de la suma de elementos de un array de forma recursiva.

\n\n
import java.util.concurrent.RecursiveTask;\nimport java.util.concurrent.ForkJoinPool;\n\npublic class SumTask extends RecursiveTask<Long> {\n    private static final int THRESHOLD = 1000;\n    private final int[] array;\n    private final int start;\n    private final int end;\n\n    public SumTask(int[] array, int start, int end) {\n        this.array = array;\n        this.start = start;\n        this.end = end;\n    }\n\n    @Override\n    protected Long compute() {\n        if (end - start <= THRESHOLD) {\n            long sum = 0;\n            for (int i = start; i < end; i++) {\n                sum += array[i];\n            }\n            return sum;\n        }\n        int mid = (start + end) / 2;\n        SumTask leftTask = new SumTask(array, start, mid);\n        SumTask rightTask = new SumTask(array, mid, end);\n\n        leftTask.fork(); // ejecución asíncrona\n        long rightResult = rightTask.compute(); // calculamos                

Blogs

Book Recommendations