Paralell stream należy używać rozumiejąc ja on działa. Konkretnie rozumiejąc że bierze domyślny fork-join pool! Więc jeśli wrzucisz tam coś dużego do przetwarzania to możesz powiesić aplikacje, bo wszystkie wątki będą mielić.
Mógłbyś rozwinąć? Fork join pool to pula wątków odseparowana od np głównego wątku, Event Dispatch Thread, wątków GC czy masy innych wątków. System operacyjny przełącza się między wątkami nawet jeżeli jest ich więcej niż rdzeni procesora (w praktyce jednocześnie działających wątków w systemie jest o rzędy wielkości więcej niż logicznych rdzeni).
Blokowanie puli wątków jest generalnie niezwiązane z Parallel Streams z Javy. Jeśli zrobię sobie Executors.newFixedThreadPool(5) i wrzucą tam 5 Runnable, które sobie śpią przez 10 lat to właśnie przyblokowałem pulę wątków na te 10 lat. Parallel Streams nie jest jedyną abstrakcją korzystającą z pul wątków - praktycznie wszystkie abstrakcje na równoległe wykonywanie zadań korzystają z pul wątków (włączając w to Future, async/ await i inne wynalazki).
Wadą Parallel Streams w obecnej implementacji (Java 8) jest to, że nie da się podać innej puli wątków niż domyślna (i wspólna dla wszystkich wywołań Parallel Streams), a domyśla pula ma stały rozmiar (ZTCW równy ilości logicznych rdzeni). Z tego względu najlepiej jest unikać używania Parallel Streams w przypadku zadań, gdzie trzeba czekać na dane wejściowe, czyli np wczytywania danych z sieci czy bazy danych. W takich przypadkach lepiej użyć np CompletableFuture.
Z drugiej strony istnieje coś takiego jak ForkJoinPool.ManagedBlocker i to ma sprawić, że potencjalnie zostanie stworzony ekstra wątek dla blokującego wywołania. Jak to działa to nie sprawdzałem.
PS:
W Javce już dawno nie programuję, więc możliwe, że niektóre rzeczy działają inaczej niż sobie wyobrażam.