Java8 i parallelStream

Java8 i parallelStream
shagrin
  • Rejestracja:prawie 17 lat
  • Ostatnio:ponad 6 lat
  • Lokalizacja:Norwegia, Stavanger
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?


Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
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ć.


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
0

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

shagrin
  • Rejestracja:prawie 17 lat
  • Ostatnio:ponad 6 lat
  • Lokalizacja:Norwegia, Stavanger
0

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


Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około 23 godziny
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.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
S9
  • Rejestracja:ponad 10 lat
  • Ostatnio:5 miesięcy
  • Lokalizacja:Warszawa
  • Postów:3573
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


"w haśle <młody dynamiczny zespół> nie chodzi o to ile masz lat tylko jak często zmienia się skład"
edytowany 1x, ostatnio: scibi92
shagrin
  • Rejestracja:prawie 17 lat
  • Ostatnio:ponad 6 lat
  • Lokalizacja:Norwegia, Stavanger
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


Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około 23 godziny
0

podobno

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


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
shagrin
  • Rejestracja:prawie 17 lat
  • Ostatnio:ponad 6 lat
  • Lokalizacja:Norwegia, Stavanger
0

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


S9
  • Rejestracja:ponad 10 lat
  • Ostatnio:5 miesięcy
  • Lokalizacja:Warszawa
  • Postów:3573
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 ;)


"w haśle <młody dynamiczny zespół> nie chodzi o to ile masz lat tylko jak często zmienia się skład"
edytowany 1x, ostatnio: scibi92
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około 23 godziny
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.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 2x, ostatnio: Wibowit
Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
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.


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
TD
  • Rejestracja:ponad 10 lat
  • Ostatnio:ponad 4 lata
  • Postów:380
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?

Zobacz pozostałe 4 komentarze
Wibowit
Moim zdaniem ta wspólna pula to java.util.concurrent.ForkJoinPool#commonPool
airborn
@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ą.
TD
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
Wibowit
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.
TD
ok, w takim razie przejrzę kod ForkJoinTask i metod o których mówicie, dzięki. ;)
airborn
  • Rejestracja:ponad 15 lat
  • Ostatnio:prawie 7 lat
  • Postów:274
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óż:

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

Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około 23 godziny
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

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


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 1x, ostatnio: Wibowit
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)