Java8 i parallelStream

0

Hej,

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?

0

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ć.

0

Ogolnie to pamietam, ze byly kiedys jakies problemy ale z tego co pamiętam zostal fixniete. Teraz trzeba uzywac z rozwaga po prostu.

0

To wszystko jest jasne, ale nie bardzo rozumiem, gdzie w takim razie jest sens?

0
Shalom napisał(a):

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.

0

Sens jest wtedy gdy operacje trwają długo, np. ja bym użył do kopiowania plików. Chociaż po tym co napisał @Shalom nie jestem pewien

0
scibi92 napisał(a):

Sens jest wtedy gdy operacje trwają długo, np. ja bym użył do kopiowania plików. Chociaż po tym co napisał @Shalom nie jestem pewien

No właśnie przy długotrwałych operacjach można ubić system podobno

0

podobno

W jaki sposób? Może zamiast bawić się w głuchy telefon w końcu ktoś zademonstruje problem?

0

Chociazby przykład z tego artykułu: https://dzone.com/articles/think-twice-using-java-8

0

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

0

@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.

0

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.

0
Shalom napisał(a):

to cokolwiek innego korzystającego z tego samego fork join pool musi wisieć i czekać aż się coś zwolni.

A co innego może korzystać z tego samego fork join pool?

2

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óż:

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

0

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

var prevCtx = SynchronizationContext.Current; 
try 
{
    SynchronizationContext.SetSynchronizationContext(new ThreadPoolSynchronizationContext()); 

    // async operations.
} 
finally 
{  
    SynchronizationContext.SetSynchronizationContext(prevCtx); 
} 

Wygląda to nawet bardziej imperatywnie od tego hacka w Javie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1