Typowe pułapki
Kolejność, boxing i wspólna pula
Typowe pułapki to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.
Równoległość nie daje darmowego przyspieszenia
Dodanie parallel() może spowolnić kod zamiast go przyspieszyć. W tej lekcji omówiono typowe pułapki, aby można było ich uniknąć.
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);
}
}Pułapka: narzut związany z kolejnością
Operacje zachowujące kolejność, takie jak forEachOrdered lub limit, wymuszają koordynację między wątkami, zmniejszając korzyści z równoległości.
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
IntStream.rangeClosed(1, 5).parallel()
.forEachOrdered(System.out::println);
}
}unordered może pomóc
Jeśli kolejność nie ma znaczenia, wywołanie unordered() pozwala środowisku uruchomieniowemu pominąć operacje związane z kolejnością i swobodniej wykonywać pracę równolegle.
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);
}
}Pułapka: ukryty boxing
Używanie Stream<Integer> równolegle zwiększa koszt boxingu w wielu wątkach. W przypadku równoległych operacji numerycznych należy preferować strumienie typów prostych.
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);
}
}Pułapka: współdzielona pula common
Wszystkie strumienie równoległe współdzielą jedną pulę ForkJoinPool.commonPool(). Długotrwałe zadanie równoległe może zagłodzić inne zadania w tej samej maszynie JVM, w tym inne części aplikacji.
import java.util.concurrent.ForkJoinPool;
public class Main {
public static void main(String[] args) {
System.out.println(ForkJoinPool.commonPool().getParallelism());
}
}Izolowanie pracy we własnej puli
Aby nie obciążać wspólnej puli ciężkimi zadaniami, należy uruchamiać strumień równoległy we własnej puli 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);
}
}Pułapka: źródła trudne do podziału
LinkedList, Stream.iterate i BufferedReader.lines dzielą się słabo. Ich równoległe wykonywanie często daje niewielką korzyść lub wręcz pogarsza wydajność.
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);
}
}Pułapka: stanowe lambdy
Lambdy, które odczytują lub modyfikują zewnętrzny stan, działają niedeterministycznie w trybie równoległym. Aby uzyskać spójne wyniki, należy zachować ich bezstanowość.
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);
}
}Pułapka: findFirst a findAny
findFirst musi zachować kolejność, co powoduje dodatkowy narzut w trybie równoległym. Jeśli wystarczy dowolny pasujący element, należy użyć 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);
}
}Pułapka: tanie operacje na małych zbiorach danych
W przypadku bardzo małych kolekcji lub trywialnych operacji narzut dzielenia i scalania przewyższa wszelkie korzyści. Należy pozostać przy trybie sekwencyjnym.
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);
}
}Pułapka: kosztowne scalanie w kolektorach
Kolektory z kosztownym etapem scalania, takie jak te budujące duże posortowane struktury, mogą zniwelować korzyści z równoległości. Proste operacje gromadzenia można scalać tanio.
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);
}
}Szybkie sprawdzenie
Dlaczego findFirst może działać wolniej niż findAny na strumieniu równoległym?
Podsumowanie
Poznano typowe pułapki związane z równoległością:
- Zachowywanie kolejności (forEachOrdered, limit, findFirst) zwiększa koszt koordynacji; gdy to możliwe, należy używać
unorderedlubfindAny. - Boxing zwiększa koszt; należy preferować strumienie typów prostych.
- Wspólna pula może zostać zagłodzona; ciężką pracę należy izolować we własnej puli
ForkJoinPool. - Źródła trudne do podziału, małe zbiory danych i kosztowne scalanie osłabiają korzyści z równoległości.
Często zadawane pytania
Czy lekcja „Typowe pułapki” jest bezpłatna?
Tak — pełny tekst „Typowe pułapki” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Typowe pułapki”?
Kolejność, boxing i wspólna pula Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Java Academy?
Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.
Ile czasu zajmuje lekcja „Typowe pułapki”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?
Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Tworzenie strumieni równoległych
- Kiedy równoległość pomaga
- Bezpieczeństwo wątkowe i efekty uboczne
- Typowe pułapki