Threads: wait() und notify()

sh33p

Bekanntes Mitglied
Moin,

habe mal eine Verständnis Frage. Die Methoden wait() und notify() können ja benutzt werden, um eine bestimmte deterministische Abfolge zu erreichen. Ich habe im Inet in einigen Beispielen gesehen, das z.b wait und notify benutzt wurden, wenn ein Schreiber eine Ressource schreiben sollte und ein Leser diese lesen sollte. Hier wurde wait und notify dann so eingesetzt,das wenn z.b das Array, auf das geschrieben wurde, voll ist, gewartet wird bis wieder platz ist(wait() ) und dann die Methode, die schreibt, durch ein notify() des Lesers benachrichtigt wurde, das sozuzusagen wieder platzt ist.
Wie würde ich das z.b in so einer Situation machen:

Java:
public synchronized void set(int i){

this. i = i;

}

public synchronized int get(){
return i;
}

Ich möchte natürlich erreichen, das erst gelesen wurde, wenn set() mit dem setzen fertig ist.Und das set() erst den Wert setzt, wenn get() mit dem lesen fertig ist und das keine deadlocks auftreten.
Aber wie kommunizier ich sozusagen zwischen den 2 Methoden? Hier fehlen mir ja solche Bedingungen wie.. "Wenn das Array voll ist..dann". Könnte man das über ein boolean Flag machen?🙂
 
Entweder über ein flag, oder indem 'i' einen Wert hat, der als "noch nicht gesetzt" gilt, und der abgefragt wird... Etwas mehr Kontext könnte aber ggf hilfreich sein.
 
Java:
public class Test{


private int z;
public Test(int z){
  this.z = z;
}

public synchronized void set(int z){
  this.z = z;
}
public synchronized int get(){
return z;

}
public  synchronized void macheEtwas(Test t){
  int a = t.get();
  t.set(z);
  set(a);
}
}

Sagen wir mal, das ich diese Klasse thread-sicher machen will. Wie sähe dann eine sinnvolle implementierung aus?

>objekt variable boolean flag deklarieren
> in get(), wenn flag = false ist, wait().. usw.
> in set(), wenn flag = false ist, wait().. usw.

?
 
Hm... beschreib' ggf. nochmal genauer, was du meinst: Das setzen und lesen an sich ist durch das synchronized schon "atomar gemacht", d.h. das ist threadsicher. Ich dachte jetzt, es geht darum, auf einen bestimmten Wert zu warten....!?
 
ich wollte vielmehr darauf eingehen,ob die Benutzung der Methode machEtwas() thread-sicher ist.
ist hier eine deterministische abfolge gewährleistet und können hier deadlocks auftreten?

also ich denke:


durch die synchronistation ist eine deterministische abfolge gewährleistet. Bsp:

Thread A erhält den "Schlüssel" des Sperrobjektes, geht in den Monitor und führt set(1) und get() (ist ja 1 aus).
Wenn Thread A fertig ist, wird der Schlüssel freigegeben, Thread B erhält den Schlüssel und führt set(2) und get() (ist ja 2) aus. So dass eine deterministsche Abfolge gewährleistet ist.
Ein Deadlock kann nicht auftreten, da ein Thread ja nicht auf das "frei werden" eine belegten Ressource warten muss.

Seh ich das richtig?
 
Zuletzt bearbeitet:
OK, also geht es gar nicht um wait und notify, sondern nur(?) um die Frage, ob in der macheEtwas ein Deadlock auftreten kann?!

Ich find' sowas schwierig zu beantworten 😳 aber es sieht für mich zumindest für den Fall gefährlich aus, dass es zwei Test-Objekte und zwei Threads gibt, und die beiden Threads jeweils macheEtwas auf einem der Test-Objekte aufrufen wollen, und als Argument jeweils das andere Test-Objekt übergeben werden soll:

Thread A betritt a.macheEtwas(b), hat in diesem Moment den Lock auf a
Thread B betritt b.macheEtwas(a), hat in diesem Moment den Lock auf b
Thread A will a.get() aufrufen - muss auf den Lock warten, den Thread B hat
Thread B will b.get() aufrufen - muss auf den Lock warten, den Thread A hat
-> Deadlock

Falls dieser Fall auftreten kann, sollte man sich etwas anderes überlegen...
 
Objekt A
---------
Thread A ruft
a.macheEtwas b auf()
. Hat in diesem
moment Lock auf A

Objekt B
--------
Thread B ruft b.machetwas(a) auf, und hat in diesem moment lock auf b


Jetzt wird auf Objekt A von Thread A b.get() in macheEtwas aufgerufen
Hier muss Thread A aber auf den Lock warten den Thread B hat

Thread B will a.get() in macheEtwas auf Objekt B aufrufen,muss aber auf
den Lock von Thread A warten


Dadurch blockieren sich beide und es entsteht ein Deadlock.
Der entsteht aber nur, weil wir für jedes Objekt 1 Monitor
haben, also in dem Fall 2 Monitore. Meintest du das?
 
Zuletzt bearbeitet:
Also ich glaube du meinst diese Situation

Es wird diese Klasse als Thread gestartet bzw. es werden 2 Threads
dieser Klasse gestartet. In einer RunMethode existieren 2 Objekte der Klasse Test. Objekt a und b.
In der Run-Methode würde dann a.doit(b) und b.doit(a) aufgerufen.
Wir haben also 2 Threads und 2 Objekte. Da die doit Methode synchronized ist, wird um jede Instanz
der Klasse Test( Also um das Objekt a und b) ein Monitor gebaut.
Kommt jetzt Thread1 in den Monitor von Objekt b so ruft er a.doit(b) auf. Das heisst Thread
1 besitzt gerade das Lock von Objekt a. Kommt anschließend Thread 2 in den Monitor von Objekt B, so ruft er
b.doit(a) auf.Das heisst Thread 2 besitzt gerade das Lock von Objekt B.
Innerhalb von doit() versucht nun Thread 1 b.get() aufzurufen, muss aber warten mit dem Aufruf bis
B frei ist.
Innerhalb von doit() versucht nun Thread 2 a.get() aufzurufen, muss aber warten mit dem Aufruf bis
A frei ist.Das führt zu einem Deadlock
 
Also ich glaube du meinst diese Situation
...
Mit ein paar Löschungen...

...Thread1 ... ruft ... a.doit(b) auf. ...Thread 1 besitzt gerade das Lock von Objekt a.
...Thread2 ... ruft ... b.doit(a) auf. ...Thread 2 besitzt gerade das Lock von Objekt b.
Innerhalb von doit() versucht nun Thread 1 b.get() aufzurufen, muss aber warten mit dem Aufruf bis B frei ist.
Innerhalb von doit() versucht nun Thread 2 a.get() aufzurufen, muss aber warten mit dem Aufruf bis A frei ist.
Das führt zu einem Deadlock

Sieht das ziemlich nach dem aus, was ich gepostet hatte 🙂

Achtung, Halbwissen:
Ich meine sogar mal irgendwo gelesen zu haben (war das nicht bei FindBugs oder PMD?) dass man synchronized-Methoden "vermeiden" sollte, und die Synchronisation am besten komplett innerhalb der Klasse, und in bezug auf ein privates "monitor-objekt" gemacht werden sollte, weil man dadurch verhindern kann, dass jemand von außen auf das Objekt synchonisiert, und durch sowas wie
Code:
synchronized (dasObjekt) { dasObjekt.machWas(); ... }
deadlocks erzeugt, die nicht entstehen müßten, wenn man die synchronisation komplett weggekapselt hätte. Aber ... wie konsequent und systematisch man das (sinnvoll) umsetzen könnte, weiß ich nicht...
 
d.h man sollte eventuell blöcke verwenden zum synchronisieren und das Schlüssel Objekt
private deklarieren?
Bei Methoden kann ich ja das Objekt (this) nicht private deklarieren..


aber um nochmal kurz zu dem obigen Beispiel zu kommen.
Hier kommt es nur zu einem Problem,weil 2 Instanzen und damit 2 Monitore bestehen ?!
 
Zuletzt bearbeitet:
Nochmal, das hatte ich nur irgendwo mal gelesen - richtig systematisch umgesetzt oder als echte "Coding Guideline" habe ich es noch nicht gesehen, aber es klang zumindest plausibel und vernünftig.

Sowas wie
Code:
public synchronized void foo()
{
   // Code
}
ist gleichbedeutend mit
Code:
public void foo()
{
    synchronized (this)
    {
       // Code
    }
}
... aber das meintest du wohl nicht? :bahnhof:


Wie auch immer: Wenn es nur einen monitor gibt, kann es grundsätzlich nicht zu einem Deadlock kommen. Ein Deadlock entsteht (etwas vereinfacht und pauschalisiert) bei Mustern der Form
Code:
void methodX()
{
    synchronized(a) 
    {
        synchronized(b) 
        {
            // Code
        }
    }
}

void methodY()
{
    synchronized(b) 
    {
        synchronized(a) 
        {
            // Code
        }
    }
}
also bei "verschachtelten und verschränkten" synchronisationen: Ein Thread hat den lock auf a, und braucht den auf b, um weiterzumachen, und ein anderer Thread hat den Lock auf b, und braucht den auf a, um weiterzumachen. Leider ist das nicht immer so "offentlichtlich" - wie auch in deinem Beispiel, wo das Problem ja NUR auftritt, wenn a.doit(b) und b.doit(a) "gleichzeitig" von zwei Threads aufgerufen werden. Bei a.doit(b) und b.doit(xxx) würde es (soweit ich das jetzt im Kopf überfliege) kein Problem geben.
 

Zurück
Oben