Fórum Root.cz

Hlavní témata => Vývoj => Téma založeno: Blekova psycholožka 20. 06. 2015, 14:43:44

Název: Java: monitor v různých vláknech
Přispěvatel: Blekova psycholožka 20. 06. 2015, 14:43:44
Ahoj, máme tento kód:

Kód: [Vybrat]
class Q {
int n;
boolean valueSet = false;

synchronized int get() {
while(!valueSet)
try {
wait();
} catch(InterruptedException e) {
System.out.println("Zachycena vyjimka InterruptedException.");
}
System.out.println("Predana hodnota: " + n);
valueSet = false;
notify();
return n;
}

synchronized void put(int n) {
while(valueSet)
try {
wait();
} catch(InterruptedException e) {
System.out.println("Zachycena vyjimka InterruptedException.");
}
this.n = n;
valueSet = true;
notify();
System.out.println("Vlozena hodnota: " + n);
}
}

class Tvurce implements Runnable {
Q q;

Tvurce(Q q) {
this.q = q;
new Thread(this, "Tvurce").start();
}

public void run() {
int i = 0;
while(true) {
q.put(i++);
}
}
}

class Spotrebitel implements Runnable {
Q q;

Spotrebitel(Q q) {
this.q = q;
new Thread(this, "Spotrebitel").start();
}

public void run() {
while(true) {
q.get();
}
}
}

public class TS {

public static void main(String[] args) {
Q q = new Q();
new Tvurce(q);
new Spotrebitel(q);
System.out.println("Program ukoncete stisknutim klaves Ctrl-C.");

}

}

Otázka zní, když nějaké vlákno vleze do synchronized metody, získá tím monitor a ostatní vlákna nemohou volat synchronized metody, dokud to první vlákno monitor nevrátí. No, takže dejme tomu, že nějaké vlákno vleze do metody put(), díky čemuž získá monitor. Poté toto vlákno volá metodu notify(), čímž by měl probudit jiné vlákno, které volalo metodu wait(). Jak může být ale volání notify() úspěšné? Když v tu chvíli to jiné vlákno nemohlo volat metodu wait(), protože nemělo monitor?

Snad je mi rozumět.
Název: Re:Vlákna - Java
Přispěvatel: andy 20. 06. 2015, 15:27:40
Mozno keby si si precital javadoc k wait(), tak by si na to prisiel aj sam ;).
Název: Re:Vlákna - Java
Přispěvatel: Blekova psycholožka 20. 06. 2015, 15:43:54
Javadoc jsem si k těmto metodám přečetl, ale není tam nic, co by mi to vysvětlilo.

Konkrétně k metodě wait() je v javadocu toto: "The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution."

A k metodě notify() toto: "Wakes up a single thread that is waiting on this object's monitor. If any threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation. A thread waits on an object's monitor by calling one of the wait methods."

Z čehož vyplívá má otázka, jak může nějaké vlákno volat v nějaké synchronizované metodě metodu wait(), pokud nemá monitor, monitor vlastní jiné vlákno jenž jej předběhlo, které je v nějaké synchronizované metodě a volá metodu notify().

Název: Re:Vlákna - Java
Přispěvatel: Ne asinek 20. 06. 2015, 15:50:04
Tak pokud k tomu monitoru notifikujes i klavesnici a mys tak by melo.
Název: Re:Vlákna - Java
Přispěvatel: Blekova psycholožka 20. 06. 2015, 16:01:04
Nějak ti nerozumím.
Název: Re:Vlákna - Java
Přispěvatel: Filip Jirsák 20. 06. 2015, 16:37:53
Vlákno 1 získá zámek (monitor) pro this. Následně zavolá wait(), což způsobí, že vlákno 1 ten zámek uvolní a uspí se. Následně vlákno 2 získá zámek pro this, zavolá notify(), tím probudí některé z čekajících vláken, například vlákno 1. Vlákno 1 ale  tu chvíli nedrží zámek od this, drží ho vlákno 2. Vlákno 1 tedy čeká na uvolnění zámku, a teprve když vlákno 2 zámek uvolní, získá ho vlákno 1 (nebo kterékoli jiné vlákno, které na ten zámek čeká - vlákno 1 prostě bude na zámek čekat tak dlouho, dokud ho nedostane).

Jinak v dnešní době je lepší používat třídy z balíku java.util.concurrent a java.util.concurrent.locks, dává vám to mnohem víc možností.
Název: Re:Vlákna - Java
Přispěvatel: Blekova psycholožka 20. 06. 2015, 16:54:48
Takže když zavolám metodu notify(), tak monitor nemusí nutně dostat to vlákno, které volalo metodu wait(), ale jakékoliv vlákno, které prostě čeká na zámek, vyjma sebe.

Tedy stane-li se situace, kdy vlákno 2 předběhne vlákno 1 a zavolá metodu notify(), tak vlákno 1 se začne provádět bez ohledu na to, zda volalo metodu wait(), důležité je, že vlákno 1 čekalo na zámek.

Chápu to správně?
Název: Re:Vlákna - Java
Přispěvatel: Filip Jirsák 20. 06. 2015, 17:27:32
Ne, když zavoláte notify(), probudí se tím některé vlákno, které předtím na stejném objektu zavolalo wait(). Na zámky to ale nemá žádný vliv, zámek pořád drží vlákno, které zavolalo notify() (protože držení zámku je podmínkou volání notify()) a probuzené vlákno na tento zámek čeká. Přičemž to, že bylo vlákno probuzeno z wait() jej při čekání na zámek nijak neupřednostňuje před jinými vlákny čekajícími na tentýž zámek.

Pokud vlákno 2 předběhne vlákno 1 a zavolá notify() v okamžiku, kdy ještě nikdo nezavolal wait(), proběhne notify() na prázdno (nic neudělá, resp. vyšle zprávu k probuzení, která nikoho nezajímá, takže se zahodí). Vlákno 2 pak dokončí synchronizovaný blok, čímž uvolní zámek a může pokračovat vlákno 1. To získá zámek, začne provádět synchronizovaný blok, vyhodnotí podmínku a zjistí, že hodnota je nastavena, tudíž vůbec nevstoupí do cyklu a nevolá wait(). Rovnou vypíše "Predana hodnota", shodí příznak nastavení hodnoty a informuje případné čekající vlákno.

Na tomhle příkladu je vidět, že wait()/notify() má málo možností. Ten příklad bude fungovat, pokud máte jen dvě vlákna, jedno zapisující a jedno čtecí. Pokud budete mít víc vláken, může nastat třeba případ, že jedno vlákno čte, druhé čeká na čtení a třetí čeká na zápis. První vlákno přečte hodnotu a probudí některé z dalších dvou vláken - klidně to ale může být to druhé čtecí vlákno, které zjistí, že hodnota není nastavená a znovu se uspí. A pak už nikdo další nezavolá znovu notify(), takže ta tři vlákna budou čekat donekonečna. Přitom by stačilo probudit to třetí vlákno, které chce zapsat. Řešením je místo notify() použít notifyAll(), které probudí všechna vlákna. Ta vlákna, která chtějí číst, zjistí že není co, a znovu se uspí, a to zapisovací vlákno zapíše a zase probudí všechna čekající vlákna. Jak je vidět, je to lepší než předchozí případ (nedojde k uváznutí programu), ale není to efektivní, protože zbytečně probouzíte i vlákna, která stejně nebudou moci nic udělat. Ve skutečnosti tam totiž máte dvě podmínky - některá vlákna čekají na to, až bude hodnota nastavená (aby ji mohla přečíst), jiná vlákna čekají, až bude prázdná (aby tam mohla zapsat novou hodnotu). Když použijete zámky z java.util.concurrent.lock, můžete si k jednomu zámku navěsit kolik podmínek chcete a při změně stavu nějaké podmínky probudit jenom jedno vlákno, které čeká na splnění právě téhle podmínky.
Název: Re:Vlákna - Java
Přispěvatel: Blekova psycholožka 20. 06. 2015, 17:37:06
Děkuji za vysvětlení, už to chápu.
Název: Re:Vlákna - Java
Přispěvatel: perceptron 20. 06. 2015, 18:00:04
zjavne ide o nejake zadanie na zapocet

v realne jave programovat wait / notify je na slucku.

na producer consumer napr blockingqueue