Na forum 4programmers.net korzystamy z plików cookies. Część z nich jest niezbędna do funkcjonowania
naszego forum, natomiast wykorzystanie pozostałych zależy od Twojej dobrowolnej zgody, którą możesz
wyrazić poniżej. Klikając „Zaakceptuj Wszystkie” zgadzasz się na wykorzystywanie przez nas plików cookies
analitycznych oraz reklamowych, jeżeli nie chcesz udzielić nam swojej zgody kliknij „Tylko niezbędne”.
Możesz także wyrazić swoją zgodę odrębnie dla plików cookies analitycznych lub reklamowych. W tym celu
ustaw odpowiednio pola wyboru i kliknij „Zaakceptuj Zaznaczone”. Więcej informacji o technologii cookie
znajduje się w naszej polityce prywatności.
Przerobiłam sobie jakieś materiały o java8, w tym tez jakaś książkę. W książce były przedstawione parallelStream'y i nawet zachwalali, żeby ich używać.
Na różnych forach jest to jednak odradzane, bo może skutkować tgread starvation.
Jakie macie opinie w tym temacie na podstawie posiadanego doświadczenia? Czy parallelStream faktycznie powinien być zupełnie zbanowany, czy można go używać przy pewnych założeniach?
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ć.
Usunąć wpis?
Tej operacji nie będzie można cofnąć.
Biały Mleczarz
Biały Mleczarz
0
Ogolnie to pamietam, ze byly kiedys jakies problemy ale z tego co pamiętam zostal fixniete. Teraz trzeba uzywac z rozwaga po prostu.
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.
A racja, już wiem o co chodzi ;) Tak rzeczywiście, wątek będzie czekał na zakończenie parrarel stream i dlatego jeśli coś dłużej trwa to jest "zamrożony" wątek z ktorego poszedł ten stream ;)
Edit: chociaż to i tak w sumie jest lepiej jakby wtedy był użyty "normalny" stream, bo też by zamroził wątek z którego poszedł tylko na dłużej ;)
@shagrin:
Napisz mi gdzie tam jest napisane coś o "ubijaniu systemu" i co to w ogóle znaczy?
W artykule jest napisane tylko, że jedne zadania mogą blokować inne i trzeba mieć to na uwadze. ForkJoinPool jest dość niedeterministyczny jeśli chodzi o kolejność wykonywania zadań ponieważ implementuje work stealing, ale niedeterminizm i tak jest czymś powszednim podczas programowania równoległego. Słusznie wskazano, że Parallel Streams w Javie 8 nie dają możliwości wyboru puli wątków.
...ale to opisałem już w poprzednim poście. Moim zdaniem kuleje u ciebie czytanie ze zrozumieniem.
Edit: chociaż to i tak w sumie jest lepiej jakby wtedy był użyty "normalny" stream, bo też by zamroził wątek z którego poszedł tylko na dłużej ;)
No właśnie! Jeśli gdzieś tam mam Thread.sleep(10 lat) to jakiś wątek zostanie zablokowany na 10 lat. Proste i logiczne. Wrzucenie Thread.sleep(10 lat) do wątku z domyślnej puli ForkJoinPool nie sprawi magicznie, że system wybuchnie. Po prostu inny wątek zostanie zablokowany niż przy braku użycia Parallel Streams.
Tak jak napisał @Wibowit a wcześniej pisałem ja, paralell stream używa domyślnego fork join pool, więc jeśli załadujemy tam wykonanie czegoś co jest długie (np. przetworzenie n elementów z których każdy sie blokuje przy przetwarzaniu, a mamy n rdzeni) to cokolwiek innego korzystającego z tego samego fork join pool musi wisieć i czekać aż się coś zwolni.
Zasada jest podobna jak wykonywanie długich operacji w wątku GUI -> nie należy tego robić bo interfejs wisi.
@Wibowit: to to na pewno. Pytanie gdzie w kodzie siedzi że to commonPool, albo inny pool o którym piszę niżej. Tego nie wiem i niechce mi się szukać.
@tdudzik nie rozumiem pytania. Co dokładnie wiem jak działa? Skąd wiem, że paralleStream wykorzystuje ForkJoinFramework? Chociażby z tego taska o którym pisałem przed chwilą.
tdudzik
Skąd wiesz, że parallelStream wykorzystuje jeden wspólny fork join pool, przez co zablokowanie n wątków, gdzie n to ilość rdzeni spowoduje zablokowanie innych parallelStreamów
Jest sobie metoda java.util.concurrent.ForkJoinTask#fork która wykorzystuje ten commonPool, no chyba że zastosowany jest 'hack' z posta poniżej. Z implementacji metody fork widać też wprost czemu ten hack działa.
tdudzik
ok, w takim razie przejrzę kod ForkJoinTask i metod o których mówicie, dzięki. ;)
Tak jak koledzy wspomnieli, domyślnie wykorzystywana jest domyślna pula z ForkJoinPool ALE jest też metoda żeby to zmienić. Traktował bym to trochę bardziej jako hack, no ale cóż:
Kopiuj
ForkJoinPool forkJoinPool = new ForkJoinPool(2);
forkJoinPool.submit(() ->
//parallel task here, for example
IntStream.range(1, 1_000_000).parallel().filter(PrimesPrint::isPrime).collect(toList())
).get();
Można streama wykonać w innej puli i wówczas wykorzysta on tę pulę do pracy
Niby wygląda jak hack, ale by użyć niedomyślnej puli wątków w przypadku async/ await w C# trzeba zrobić coś dość podobnego: https://stackoverflow.com/a/23959475
Kopiuj
var prevCtx = SynchronizationContext.Current;try{
SynchronizationContext.SetSynchronizationContext(newThreadPoolSynchronizationContext());// async operations.}finally{
SynchronizationContext.SetSynchronizationContext(prevCtx);}
Wygląda to nawet bardziej imperatywnie od tego hacka w Javie.
tdudzikfork
widać też wprost czemu ten hack działa.tdudzik