0Pricing
Java Academy · Lección

Errores comunes

Orden, boxing y el common pool

Errores comunes es una lección gratuita de Java Academy en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Java Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Java Academy incluye 4 lecciones en total.

El paralelismo no acelera todo gratis

Añadir parallel() puede hacer que el código sea más lento, no más rápido. En esta lección se explican las trampas habituales para que pueda evitarlas.

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        long sum = IntStream.rangeClosed(1, 1_000_000).parallel()
            .asLongStream().sum();
        System.out.println(sum);
    }
}

Problema: coste de mantener el orden

Las operaciones ordenadas, como forEachOrdered o limit, obligan a coordinar los hilos, lo que reduce las ventajas del paralelismo.

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        IntStream.rangeClosed(1, 5).parallel()
            .forEachOrdered(System.out::println);
    }
}

unordered puede ayudar

Si el orden no importa, llamar a unordered() permite al entorno de ejecución omitir el trabajo relacionado con el orden y paralelizar con mayor libertad.

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        long count = IntStream.rangeClosed(1, 1_000_000).parallel()
            .unordered()
            .filter(n -> n % 5 == 0)
            .count();
        System.out.println(count);
    }
}

Problema: boxing oculto

Usar Stream<Integer> en paralelo multiplica el coste del boxing entre los hilos. Prefiera los streams de tipos primitivos para el trabajo numérico en paralelo.

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        long sum = IntStream.rangeClosed(1, 1_000_000).parallel().asLongStream().sum();
        System.out.println(sum);
    }
}

Problema: el pool común compartido

Todos los streams paralelos comparten un ForkJoinPool.commonPool(). Una tarea paralela larga puede privar de recursos a otras tareas en la misma JVM, incluidas otras partes de su aplicación.

import java.util.concurrent.ForkJoinPool;

public class Main {
    public static void main(String[] args) {
        System.out.println(ForkJoinPool.commonPool().getParallelism());
    }
}

Aislar el trabajo en un pool personalizado

Para mantener el trabajo pesado fuera del pool común, envíe el stream paralelo dentro de su propio ForkJoinPool.

import java.util.concurrent.ForkJoinPool;
import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) throws Exception {
        ForkJoinPool pool = new ForkJoinPool(2);
        long sum = pool.submit(() ->
            IntStream.rangeClosed(1, 1_000_000).parallel().asLongStream().sum()
        ).get();
        pool.shutdown();
        System.out.println(sum);
    }
}

Problema: fuentes que se dividen mal

LinkedList, Stream.iterate y BufferedReader.lines se dividen mal. Paralelizarlas suele ofrecer pocos beneficios o incluso perjudicar el rendimiento.

import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        long n = Stream.iterate(1, x -> x + 1)
            .limit(100_000)
            .parallel()
            .filter(x -> x % 2 == 0)
            .count();
        System.out.println(n);
    }
}

Problema: lambdas con estado

Las lambdas que leen o escriben en un estado externo se comportan de forma no determinista en paralelo. Manténgalas sin estado para obtener resultados coherentes.

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        int sum = IntStream.rangeClosed(1, 100).parallel()
            .map(n -> n + 1)
            .sum();
        System.out.println(sum);
    }
}

Problema: findFirst frente a findAny

findFirst debe respetar el orden, lo que añade costes en paralelo. Si sirve cualquier elemento coincidente, use findAny.

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        int found = IntStream.rangeClosed(1, 1_000_000).parallel()
            .filter(n -> n % 99991 == 0)
            .findAny()
            .getAsInt();
        System.out.println(found);
    }
}

Problema: operaciones económicas con pocos datos

En colecciones diminutas o con trabajo trivial, el coste de dividir y combinar supera con creces cualquier beneficio. Mantenga la ejecución secuencial.

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Integer> small = List.of(1, 2, 3, 4);
        int sum = small.stream().mapToInt(Integer::intValue).sum();
        System.out.println(sum);
    }
}

Problema: combinación costosa en collectors

Los collectors con pasos de combinación costosos, como los que construyen estructuras ordenadas grandes, pueden anular las ventajas del paralelismo. Las acumulaciones sencillas se combinan de forma económica.

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        long total = IntStream.rangeClosed(1, 1_000_000).parallel()
            .mapToLong(n -> n)
            .sum();
        System.out.println(total);
    }
}

Comprobación rápida

¿Por qué findFirst puede ser más lento que findAny en un stream paralelo?

Resumen

Ha aprendido los problemas habituales del paralelismo:

  • El orden (forEachOrdered, limit, findFirst) añade costes de coordinación; use unordered o findAny cuando sea posible.
  • El boxing multiplica el coste; prefiera los streams de tipos primitivos.
  • El pool común compartido puede quedarse sin recursos; aísle el trabajo pesado en un ForkJoinPool personalizado.
  • Las fuentes que se dividen mal, los datos escasos y las combinaciones costosas reducen las ventajas del paralelismo.

Preguntas frecuentes

¿La lección «Errores comunes» es gratis?

Sí — el texto completo de «Errores comunes» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Java Academy, actualiza a CoddyKit PRO. El curso de Java Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Errores comunes»?

Orden, boxing y el common pool Practicas Java Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Java Academy?

No se requiere experiencia previa. Java Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.

¿Cuánto tiempo toma la lección «Errores comunes»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Java Academy?

Sí. Cada lección de Java Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Creación de streams paralelos
  2. Cuándo ayuda el paralelismo
  3. Seguridad de hilos y efectos secundarios
  4. Errores comunes
← Volver a Java Academy