0Pricing
Java Academy · Lezione

Problemi comuni

Ordinamento, boxing e pool comune

Problemi comuni è una lezione Java Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Java Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Java Academy include 4 lezioni in totale.

Il parallelismo non è velocità gratuita

Aggiungere parallel() può rendere il codice più lento, non più veloce. Questa lezione tratta le insidie più comuni per aiutarla a evitarle.

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

Insidia: sovraccarico dovuto all'ordine

Le operazioni ordinate come forEachOrdered o limit richiedono coordinamento tra i thread, riducendo i vantaggi del parallelismo.

import java.util.stream.IntStream;

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

unordered può essere utile

Se l'ordine non è importante, chiamare unordered() consente al runtime di evitare il lavoro legato all'ordinamento e di parallelizzare più liberamente.

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

Insidia: boxing nascosto

Usare Stream<Integer> in parallelo moltiplica il costo del boxing tra i thread. Per l'elaborazione numerica parallela preferisca gli stream di valori primitivi.

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

Insidia: il common pool condiviso

Tutti gli stream paralleli condividono un unico ForkJoinPool.commonPool(). Un'attività parallela lunga può lasciare senza risorse le altre attività nella stessa JVM, incluse quelle di altre parti dell'applicazione.

import java.util.concurrent.ForkJoinPool;

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

Isolare il lavoro in un pool personalizzato

Per mantenere il lavoro pesante fuori dal common pool, invii lo stream parallelo all'interno del proprio 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);
    }
}

Insidia: sorgenti difficili da suddividere

LinkedList, Stream.iterate e BufferedReader.lines si suddividono male. Parallelizzarli spesso produce vantaggi minimi o addirittura peggiora le prestazioni.

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

Insidia: lambda con stato

Le lambda che leggono o scrivono uno stato esterno si comportano in modo non deterministico in parallelo. Le mantenga prive di stato per ottenere risultati coerenti.

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

Insidia: findFirst e findAny

findFirst deve rispettare l'ordine, aggiungendo un sovraccarico in parallelo. Se è sufficiente un elemento qualsiasi tra quelli corrispondenti, usi 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);
    }
}

Insidia: operazioni economiche su pochi dati

Con collezioni molto piccole o un lavoro banale, il sovraccarico di suddivisione e unione supera di gran lunga qualsiasi vantaggio. Mantenga l'esecuzione sequenziale.

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

Insidia: unione costosa nei collector

I collector con fasi di unione costose, come quelli che creano grandi strutture ordinate, possono annullare i vantaggi del parallelismo. Le aggregazioni semplici si uniscono a basso costo.

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

Verifica rapida

Perché findFirst può essere più lento di findAny su uno stream parallelo?

Riepilogo

Ha imparato a riconoscere le insidie più comuni del parallelismo:

  • L'ordinamento (forEachOrdered, limit, findFirst) aggiunge un costo di coordinamento; usi unordered o findAny quando possibile.
  • Il boxing moltiplica il costo; preferisca gli stream di valori primitivi.
  • Il common pool condiviso può lasciare senza risorse altre attività; isoli il lavoro pesante in un ForkJoinPool personalizzato.
  • Sorgenti difficili da suddividere, pochi dati e unioni costose compromettono il parallelismo.

Domande Frequenti

La lezione «Problemi comuni» è gratuita?

Sì — il testo completo di «Problemi comuni» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Java Academy, passa a CoddyKit PRO. Il corso Java Academy include 4 lezioni in totale.

Cosa imparerò in «Problemi comuni»?

Ordinamento, boxing e pool comune Eserciti Java Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Java Academy?

Non è richiesta alcuna esperienza precedente. Java Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.

Quanto tempo richiede la lezione «Problemi comuni»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Java Academy?

Sì. Ogni lezione Java Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Creazione di stream paralleli
  2. Quando il parallelismo è utile
  3. Sicurezza dei thread ed effetti collaterali
  4. Problemi comuni
← Torna a Java Academy