Java nació con soporte nativo para hilos y un modelo de memoria definido y documentado.
Esta primera unidad sienta las bases: diferencias entre concurrencia y paralelismo en la JVM, por qué usarlos en aplicaciones modernas y qué rasgos distinguen al modelo de hilos de Java frente a otras plataformas.
Dominar estas reglas evita efectos de visibilidad extraños y te ayuda a elegir las primitivas correctas desde el inicio.
Al final de la sección tendrás criterios prácticos para dimensionar pools y separar trabajo CPU-bound (el cuello está en el procesador) de I/O-bound (el cuello está en entrada/salida).
Thread, usa ExecutorService o CompletableFuture y la JVM los mapea a hilos del sistema operativo.synchronized, volatile o utilidades de java.util.concurrent.Al diseñar APIs que expondrán hilos de usuario recuerda que los hilos del recolector y del JIT también consumen CPU.
Si usas contenedores o funciones serverless, valida cuántos hilos nativos están permitidos para evitar throttling o pausas inesperadas.
parallelStream()) distribuyen trabajo entre núcleos.Divide tus pools: uno optimizado para I/O (muchos hilos) y otro reducido para CPU (hilos cercanos a la cantidad de núcleos).
Para cargas mixtas, usa colas diferenciadas y mide la latencia percibida antes de ajustar el tamaño de los ejecutores.
import java.time.Instant;
import java.util.concurrent.*;
public class EjecutorBasico {
public static void main(String[] args) throws Exception {
ExecutorService pool = Executors.newFixedThreadPool(4);
Callable<String> ioBound = () -> {
TimeUnit.MILLISECONDS.sleep(300); // Simula I/O
return "Listo I/O @ " + Instant.now();
};
Callable<Long> cpuBound = () ->
java.util.stream.LongStream.rangeClosed(1, 5_000_000L).sum();
Future<String> respuestaIo = pool.submit(ioBound);
Future<Long> respuestaCpu = pool.submit(cpuBound);
System.out.println(respuestaIo.get());
System.out.println("Suma CPU: " + respuestaCpu.get());
pool.shutdown();
}
}
El ejemplo usa un ExecutorService fijo para mezclar tareas bloqueantes y cálculo; la JVM reparte los hilos sobre los núcleos disponibles y el programador controla el tamaño del pool.
Thread.yield() o prioridades para influir en el reparto.synchronized, volatile y Lock aseguran que las escrituras sean observables en otros hilos.java.util.concurrent (colas bloqueantes, CountDownLatch, CompletableFuture) reducen errores de bajo nivel y proporcionan garantías de orden.Documenta qué variables requieren volatile o bloqueos y evita exponer referencias mutables sin protección.
Aplica perfiles con jconsole o jcmd para ver cuánto tiempo pasan los hilos en estados bloqueados y ajustar tu estrategia.