Java - wątki

RC
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 12 lat
0

Jako, iż jest to mój pierwszy post na tym forum witam wszystkich programmersów :)

Piszę program na zaliczenie zajęć z Javy i utknąłem, proszę o pomoc
Mam tablicę wątków którym potrzebuję przekazać m.in. dwa inty, robię to przez konstruktor w petli for dla kazdego wątku. Wyglada to tak:

Kopiuj
w[i] = new MyThread(a, b, xp[i], xp[i]+delta, wyn, 1);

w pierwszej iteracji petli for wszystko jest ok. przekazuję xPoczatkowe (xp[i]), xKoncowe (xp[i] + delta) i w debuggerze pokazuje wartosci xp=0, xk = 32. Natomiast podczas kolejnej iteracji zwiekszam xp o 32+1 i probuje przekazac kolejnemu watkowi kolejny zakres t.j. xp = 33 oraz xk = 64. Co się okazuje ? że do drugiego wątku przekazywane są poprawne parametry ALE w pierwszym wątku wartosci xp oraz xk są takie same ! mianowicie zamiast [0 oraz 32] jest [32 oraz 32]. Sadzilem ze typy podstawowe przekazywane sa przez referencję... Dlaczego xp[i] to tablica intów a nie pojedynczy int? bo kombinuję i kombinuję ale dalej nie wychodzi

aha dodam ze jak przekazywalem wartosci "z palca" tj bez petli for czyli:

Kopiuj
w[0] = new MyThread(a, b, 0, 32, wyn, 1);
w[1] = new MyThread(a, b, 33, 65, wyn, 1);
w[2] = new MyThread(a, b, 66, 96, wyn, 1);
//.....

to wszystko dzialalo :) z gory dziekuje za pomoc

a tak wyglada calosc:

Kopiuj
package watki;
import java.util.Random;
public class Watki {
	private static void wypiszTab(int[][] tab, String napis){
		System.out.println(napis + " :\n");
		for(int i = 0; i < tab.length; i++){
			for(int j = 0; j < tab[i].length; j++)
				System.out.print(tab[i][j] + "\t");
			System.out.println();
		}
		System.out.println("\n\n");
	}
	private static void wypiszTab(int[] tab,  String napis){
		System.out.println(napis + " :\n");
		for(int i: tab)
			System.out.println(i);
		System.out.println("\n\n");
	}
    
	public static void main(String[] args) {
		int rozmiar = 90, ileWatkow = 3, x = 0, delta = (rozmiar/ileWatkow)+1;
		MyThread[] w = new MyThread[ileWatkow];
		Thread[] ww = new Thread[ileWatkow];
		int[][] a = new int[rozmiar][10];
		int[] b = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, wyn = new int[rozmiar];
		int[] xp = new int[ileWatkow] ;
		Random generator = new Random();
		for(int i = 0; i < a.length; i++)
			for(int j = 0; j < a[i].length; j++)
				a[i][j] = (generator.nextInt()%10)+10;

		wypiszTab(a, "Tablica A");
		wypiszTab(b, "Wektor B");
		
		for(int i = 0; i < ileWatkow; i++){
			xp[i] = x;
			w[i] = new MyThread(a, b, xp[i], xp[i]+delta, wyn, 1);
			ww[i] = new Thread(w[i]);
			ww[i].start();
			x+=delta+1;
		}
		wypiszTab(wyn, "Wyniki");
    }
}
class MyThread implements Runnable{
	private int[][] a;
	private int[] b, wyn;
	private int xp, xk, nrWatq;
	public MyThread(int[][] a, int[] b, int xpp, int xkk, int[]wyn, int nrWatq){
		this.a = a;
		this.b = b;
		this.xp = xpp;
		this.xk = xkk;
		this.wyn = wyn;
		this.nrWatq = nrWatq;
	}
	public void run(){
		System.out.println("Watek " + nrWatq + " rozpoczal sie");
		for(;xp < xk && xp < a.length; xp++){
			int s = 0;
			for(int i = 0; i < a[xp].length; i++) {
				s+=a[xp][i]*b[i];
			}
			wyn[xp] = s;
			System.out.println("Watek " + nrWatq + " wyliczyl wlasnie wiersz");
			try {
				Thread.sleep((int)(Math.random()*16));
			} catch(InterruptedException e){}
		}
		System.out.println("Watek " + nrWatq + " zakonczyl sie");
	}
}

edytowany 1x, ostatnio: rctr
ŁF
Moderator
  • Rejestracja:ponad 22 lata
  • Ostatnio:dzień
0

no właśnie - "typy podstawowe przekazywane sa przez referencję", czyli wartość takiej zmiennej zmieni się w każdym odwołaniu do niej, również w tych w wątku. skopiuj sobie wartość tej zmiennej zanim przekażesz ją do konstruktora wątku.


RC
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 12 lat
0

Przejezyczylem sie... myslalem ze przez wartosc :) ale okazuje sie ze jednak przez referencję. Ok zabieram sie do kodzenia...

0

ŁK coś ci się pomyliło, przyczyna że w kolejnej itertacji xp = xk w pierszym wątku jest zupełnie inna - ten wątek się wykonał i on modyfikuje xp

ŁF
ŁF, nie ŁK ;-) wiesz, nie jestem programistą java, przeczytałem co sam napisałeś i wyciągnąłem z tego wniosek. a że sam miałem analogiczny problem w C# to pomyślałem, że może uzewnętrznię swoje przemyślenia. a wątek się nie wykonał, tylko zaczął wykonywać, co jednak nie wpływa istotnie na sens Twojej wypowiedzi.
1

Obiekty proste są przekazywane przez wartość

RC
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 12 lat
0
rty napisał(a):

Obiekty proste są przekazywane przez wartość

tez tak wlasnie sadzilem

co do tego czy sie wykonal? watpie... specjalnie wstawilem opoznienie. Oto co program drukuje:

Watek 0 rozpoczal sie
Watek 0 wyliczyl wlasnie wiersz
Watek 2 rozpoczal sie
Watek 2 wyliczyl wlasnie wiersz
Watek 1 rozpoczal sie
Watek 1 wyliczyl wlasnie wiersz
Watek 2 wyliczyl wlasnie wiersz
Watek 2 wyliczyl wlasnie wiersz
Watek 0 wyliczyl wlasnie wiersz
Watek 2 wyliczyl wlasnie wiersz
Watek 1 wyliczyl wlasnie wiersz
Watek 0 wyliczyl wlasnie wiersz
Watek 2 wyliczyl wlasnie wiersz
Watek 0 wyliczyl wlasnie wiersz
Watek 0 wyliczyl wlasnie wiersz
Watek 2 wyliczyl wlasnie wiersz
Watek 1 wyliczyl wlasnie wiersz
Watek 2 wyliczyl wlasnie wiersz
Watek 2 wyliczyl wlasnie wiersz
Watek 0 wyliczyl wlasnie wiersz
Watek 2 zakonczyl sie
Watek 0 wyliczyl wlasnie wiersz
Watek 1 wyliczyl wlasnie wiersz
Watek 0 wyliczyl wlasnie wiersz
Watek 0 wyliczyl wlasnie wiersz
Watek 0 wyliczyl wlasnie wiersz
Watek 1 wyliczyl wlasnie wiersz
Watek 0 wyliczyl wlasnie wiersz
Watek 1 wyliczyl wlasnie wiersz
Watek 1 wyliczyl wlasnie wiersz
Watek 0 zakonczyl sie
Watek 1 wyliczyl wlasnie wiersz
Watek 1 wyliczyl wlasnie wiersz
Watek 1 wyliczyl wlasnie wiersz
Watek 1 wyliczyl wlasnie wiersz
Watek 1 zakonczyl sie

Jak widac zanim ktorys z watkow sie zakonczy wszystkie watki sa uruchomione
Siedzac nad debuggerem wydedukowalem ze zmienna xp jest zmieniana w 41-wszej linijce (i to zarowno wewnatrz petli jak i w obiekcie wątku) zupelnie jakby na te zmienne wskazywala ta sama referencja !:

Kopiuj
x+=delta;

tylko dlaczego!? ja sie pytam :)

cos mi sie jeszcze przypomnialo z tego co czytalem o wątkach. Ponoć wątki posiadają wspólną pamięć, ale to też nie tlumaczy dlaczego zmienne sie zmieniaja, przeciez odwoluję się poprzez this do zmiennej z AKTUALNEGO obiektu... nie rozumiem tego :)

EDIT:
A najśmieszniejsze jest to, że mimo wszystko program wykonuje się poprawnie :)

edytowany 1x, ostatnio: rctr
Ranides
  • Rejestracja:około 19 lat
  • Ostatnio:ponad 9 lat
  • Postów:892
1

w pierwszym wątku wartosci xp oraz xk są takie same ! mianowicie zamiast [0 oraz 32] jest [32 oraz 32].

mi się wydaje, że nic wątkowi nie jest zmieniane po cichu, ani nikt mu nic przez referencję int'ów nie przesyła... ;)
Tylko sam sobie tak pętlę napisałeś, że Ci się podczas debugowania chyba pomerdało ;) No i niechcący sobie popsułeś numerowanie wątków:

Kopiuj
// no jejku jej - to ci wywala przecież wszystkie instrukcje piszące o tym, co się dzieje ;)
new MyThread(a, b, xp[i], xp[i] + delta, wyn, 1);

// numerek, numerek wątka przekazuj ;-)
new MyThread(a, b, xp[i], xp[i] + delta, wyn, i);

No i ta pętla xp oraz xk...

Kopiuj
for (; xp < xk && xp < a.length; xp++) {

no to chyba wiadomo, że zmiena xp w tym "pierwszym wątku" mutuje bez przerwy. W drugim też. I w kolejnych tak samo. I to raczej oczywiste, że w każdym wątku koniec konców będą wartości:
xp == xk == nrwatku * (delta+1)
co się zresztą zgadza z twoją obserwacją, że wątek zakończył na etapie [31;31]. No tak - no na takim etapie powinien skończyć. Zmiennych mu nikt nie modyfikował. On sam sobie przecież xp regularnie inkrementuje, aż dojdzie do momentu, że xp==xk ;) Jak chcesz wiedzieć, z jakimi argumentami wątki tworzono, to sobie zrób jakiś licznik ;-) A te przekazane int'y to sobie jako final zadeklaruj, żebyś przez pomyłkę ich sobie sam nie modyfikował ;)

Kopiuj
// ...

private final int xp, xk, nrWatq;

// ...

for (int xi=xp; xi < xk && xi < a.length; xi++) {

program zdaje się, że daje dobre wyniki, bo robi to, co chciałeś, żeby robił. Tylko debugowaniem się zagubiłeś ;)

Ranides
czyli w sumie masz odpowiedź taką, jaką udzielił "rty" - tylko trochę łopatologiczniej ;)
airborn
  • Rejestracja:ponad 15 lat
  • Ostatnio:prawie 7 lat
  • Postów:274
1

Wydaje mi się, czy chcesz zrobić w zasadzie to samo czym zajmuje się Fork/Join od Javy 7?

Ranides
gość napisał wyraźnie: uczy się. sugerujesz, że najszybciej nauczy się np rysować, kupując gotowe rysunki ?
airborn
Myślę, że szybciej nauczy się rysować jeżeli weźmie ołówek i zacznie rysować, niż jeżeli zacznie naukę od samodzielnego zrobienia sobie ołówka.
Ranides
pytanie teraz brzmi, czy wyżej wymieniony program, to już rysowanie, czy jeszcze robienie ołówka. IMHO takie rzeczy jak synchronizacja wątków to podstawa. a ćwiczenia polegające na liczeniu czegoś równolegle to w zasadzie oczywista oczywistość. podam Ci ciekawostkę: na żadnym środowisku produkcyjnym w żadnym banku nie mamy zainstalowanego JRE 7. jakbym poprosił o upgrade kilkuset środowisk, działających latami, bo nie umiałbym wątkami zarządzać, to by mnie admini zatłukli ;)
airborn
Oczywistym jest, że podstawy trzeba mieć, ale warto również znać alternatywy. To że Ty nie możesz upgradować środowiska wynika ze specyfiki Twojej pracy - znam ludzi którzy zaczynali rok temu od utrzymywania kodu z Javy 1.4, ale to nie znaczy, że nie warto się generyków uczyć.
Ranides
nie no, w ogólności to strasznie fajnie znać te nowości ;) Ale no kurczak - kilka wątków w tablicy warto ogarnąć "w rękach" pisząc taki projekt. Pisać wielowątkowy serwer aplikacyjny - o, to byłby już overkill ;) Twój post się przyda pewnie, jak już sobie "rctr" ogarnie program - a następny program napisze na Fork/Join, żeby porównać ;)
Ranides
po przemyśleniu - masz upvote do postu za to, że się zrobił punktem wyjściowym do filozoficznej dyskusji :D
airborn
Och, dziękuję ;)
RC
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 12 lat
0

Ranides masz racje :D zapomnialem ze do licznika petli uzylem nieprzepisanej zmiennej xp :D debugowac zacząłem jak mi to w ogole ruszyc nie chcialo a potem to juz tylko brnąłem zeby tylko znalezc ten "blad". Jakież było moje zdziwienie gdy zorientowałem się że "wątki liczą na złych parametrach" a wyniki wychodzą poprawne :D

Dziekuję!
Tak btw to jest to mnozenie macierzy przez wektor ale do celow testowych uzylem wektora jednostkowego :)

edytowany 1x, ostatnio: rctr
Olamagato
  • Rejestracja:ponad 16 lat
  • Ostatnio:8 dni
  • Lokalizacja:Polska, Warszawa
  • Postów:1058
0

Jeżeli chcesz bawić się wątkami, to zapomnij o większości założeń, które uważasz za pewniki. Klasyczne debugowanie kodu wielowątkowego nie ma wiele sensu bo samo zatrzymanie wątku likwiduje zależności czasowe (które przy wielowątkowości mogą być kluczowe), a wypisanie danych powoduje niejawną synchronizację. Debugowanie takiego kodu jest możliwe, ale potrzeba przy nim mnóstwo wyobraźni - bo musisz sobie wyobrazić ze szczątkowych lub fałszywych danych co się naprawdę dzieje.

Przy przekazywaniu danych musisz zawsze posługiwać się synchronizacją, albo zmiennymi atomowymi (w tym zmiennymi prostymi do 32-bit) z modyfikatorem volatile. Żeby uniknąć mieszania danymi przez różne procesory - co prowadzi do przetwarzania przypadkowego - dane te powinny być niezmienne, czyli mające takie właściwości jak wartości proste przekazywane (zawsze) przez wartość. Tylko wtedy przekazanie referencji nie różni się od przekazania wartości.
Tablice są najbardziej dalekie od niezmienności - są one zaprojektowane do skrajnej zmienności. Dlatego ich używanie w kodzie współbieżnym, to zły lub bardzo zły pomysł. Dlatego powstały kontenery wyspecjalizowane właśnie do współbieżności. Należy je używać zamiast tablic.
Każdy dodatkowy wątek, to kolejna pula stanów, więc jeżeli wątki dodatkowo nawzajem wpływają na swoje stany, to likwiduje się determinizm aplikacji. Powstaje wtedy bardzo zły kod i w praktyce cała koncepcja projektu upada. Rozwijanie kodu takiej jak Twój powyżej powyżej, prowadzi właśnie do takiego zachowania aplikacji.

Kiedy człowiek uczy się od początku programowania, to też nie uczy się najpierw jak zbudować kompilator, tylko używa wysokopoziomowych klocków (takich jak printf) do zrobienia prostych rzeczy. Podobnie jest we współbieżności. Tutaj klocki wysokopoziomowe są dostępne dopiero od Javy 1.5, (i rozwinięte w 1.6/1.7), dlatego warto zaczynać naukę od nich. Natomiast przejście do tworzenia nowych obiektów Thread i przekazywania danych z użyciem synchronizacji można przyrównać bardziej do rozpoczęcia nauki assemblera niż do pogłębiania umiejętności posługiwania się współbieżnością. Tak jak przy "normalnym" programowaniu assembler jest potrzebny głównie do zrozumienia budowy i niuansów kodu wyższego poziomu (lub do polepszenia wydajności), tak i tutaj podobną rolę pełni klasa Thread (a także metody wait, notify(All) oraz słowo synchronize i klasa Lock).

Krótko podsumowując. Przeglądnij sobie drzewko interfejsów Executors, Future i klasy je implementujące (np. FutureTask), wysokopoziomowe klasy do sterowania: CountDownLatch, CyclicBarier, Phaser, Semaphore, kontenery mające nazwy zaczynające się od "Concurrent" oraz mające w nazwie "Blocking". Na koniec dopiero weź się za klasę ForkJoinPool oraz jej klocki RecursiveTask i RecursiveAction bo istnieje ona głównie dla maksymalizacji wydajności.


Jeżeli ktoś komuś coś, ewentualnie nikt nikomu nic, to właściwie po co...?
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)