2015-09-25 5 views
14

Ich bin nicht damit einverstanden, in Java Sprache müssen wir das Schloss halten, bevor wir eine Bedingung erwarten, um zufrieden zu sein.Warum Lock condition erwarten muss das Schloss halten

zum Beispiel int java Monitor lock:

synchronized(lock){ 
    System.out.println("before lock ..."); 
    lock.wait(); 
    System.out.println("after lock ..."); 
} 

oder die cucurrent utils.

Lock lock = new ReentrantLock(); 
Condition cond = lock.newCondition(); 

lock.lock(); 
try{ 
    System.out.println("before condition ..."); 
    cond.await(); 
    System.out.println("after condition ..."); 
}catch(Exception e){ 
    e.printStackTrace(); 
}finally{ 
    lock.unlock(); 
} 

also, warum können wir nicht warten, ohne das Schloss zu halten?

Wenn das nur wegen Java, andere Sprache funktioniert anders?

Ich hoffe, Sie können den Grund nach dem Design erklären, aber nicht nur für JAVA-SPEC-Definition.

+2

Java wird im Kontext einer virtuellen Maschine ausgeführt und das Verhalten der virtuellen Maschine wird angegeben. –

+0

also, das ist wegen SPEC? – Chinaxing

+2

Nun, natürlich ist es wegen der Spezifikation. Komische Frage. – EJP

Antwort

10

Stellen Sie sich vor, Sie haben etwas, auf das ein Thread warten muss. Vielleicht haben Sie eine Warteschlange und ein Thread muss warten, bis etwas in der Warteschlange ist, damit es es verarbeiten kann. Die Warteschlange muss Thread-sicher sein, daher muss sie durch eine Sperre geschützt werden. Sie könnten den folgenden Code schreiben:

  1. Erwerben Sie das Schloss.
  2. Überprüfen Sie, ob die Warteschlange leer ist.
  3. Wenn die Warteschlange leer ist, warten Sie, bis das Objekt in die Warteschlange gestellt wurde.

Hoppla, das wird nicht funktionieren. Wir halten die Sperre in der Warteschlange, also wie kann ein anderer Thread etwas darauf platzieren? Versuchen wir es erneut:

  1. Erwerben Sie das Schloss.
  2. Überprüfen Sie, ob die Warteschlange leer ist.
  3. Wenn die Warteschlange leer ist, lassen Sie die Sperre los und warten Sie, bis das Objekt in die Warteschlange gestellt wurde.

Hoppla, jetzt haben wir noch ein Problem. Was passiert, wenn nach der Freigabe der Sperre, aber bevor wir darauf warten, dass etwas in die Warteschlange gestellt wird, etwas in die Warteschlange gestellt wird? In diesem Fall werden wir auf etwas warten, das bereits passiert ist.

Zustandsvariablen existieren, um dieses genaue Problem zu lösen. Sie haben eine atomare Operation zum Entsperren und Warten, die dieses Fenster schließt.

Warten Sie also auf das Schloss, denn sonst gäbe es keine Möglichkeit, sicherzustellen, dass Sie nicht auf etwas warten, das bereits passiert ist. Sie müssen das Schloss halten, um zu verhindern, dass ein anderer Thread mit Ihrer Wartezeit rast.

+0

In Java, Zustand oder object.wait nur nach Benachrichtigung oder Signal aufwachen. Also, deine Erklärung ist richtig. Ich denke, wenn wir die Semantik der Bedingung so lassen können: wenn warten/warten, prüfen Sie ihr Weck-Bit (welches durch notify/signal gesetzt wurde), wenn das wahr ist, beenden Sie einfach warten/erwarten (anstatt warten zu halten). und für den Benachrichtigungs-/Signal-Thread setze einfach das Bedingungsaufwachungsbit, wenn notify/signal ist, was bedeutet, dass die Bedingung erfüllt ist. Wenn das Design so ist, wird das Schloss nicht benötigt. – Chinaxing

+1

@Chinaxing Nein, tu das nicht. Bedenken Sie: Zwei Threads sind auf der Bedingungsvariablen blockiert. Ein Thread setzt ein Objekt in die Warteschlange, signalisiert die Zustandsvariable und weckt einen Thread. Ein Thread platziert ein Objekt in der Warteschlange, signalisiert die Zustandsvariable und weckt den anderen Thread. Der erste aufgeweckte Thread verarbeitet das erste Element in der Warteschlange. Bevor der zweite Thread ausgeführt werden kann, verarbeitet der erste aufgeweckte Thread den zweiten Eintrag in der Warteschlange. Nun wird der zweite Thread ausgeführt, die Bedingungsvariable wurde signalisiert, aber die Warteschlange ist leer. –

0

Einfache Antwort ist, weil Sie andernfalls IllegalMonitorStateException erhalten, die in Object.wait javadoc angegeben ist. Intern verwendet die Synchronisierung in Java die zugrundeliegende OS-Mechanisierung. Es ist also nicht nur Java.

+0

Ja, aber das OP möchte wissen, warum Sie die IllegalMonitorStateException erhalten. –

1

Siehe das Dokument für Condition.

Eine Bedingung ist wie ein Warte-Pool oder eine Wait-Menge eines Objekts und ersetzt die Verwendung der Object-Monitor-Methoden (wait, notify und notifyAll). Bedingungen ermöglichen es einem Thread, die Ausführung zu unterbrechen ("warten"), bis er von einem anderen Thread benachrichtigt wird, dass eine Zustandsbedingung nun wahr sein kann. Eine Condition-Instanz ist intrinsisch an eine Sperre gebunden, genau wie die Object-Monitor-Methoden erfordern, dass die Sperre des gemeinsam genutzten Objekts auf "Warten" oder "Benachrichtigen" wartet. Vor dem Aufruf von wait() für eine Bedingung muss der Thread das Lock-Objekt gesperrt haben, das zum Erzeugen der Bedingung verwendet wird. Wenn die Methode await() aufgerufen wird, wird die mit der Bedingung verknüpfte Sperre freigegeben.

1

Wenn der Thread nur auf ein Signal warten würde, um fortzufahren, gibt es andere Mechanismen dafür. Vermutlich gibt es einen Zustand, der durch das Schloss geschützt ist, auf das der Thread wartet, um bearbeitet zu werden und eine Bedingung zu erfüllen. Um diesen Zustand ordnungsgemäß zu schützen, sollte der Thread die Sperre vor und nach dem Warten auf den Zustand haben, so dass es sinnvoll ist, die Erfassung des Schlosses zu verlangen.

8

Nun, worauf warten wir? Wir warten darauf, dass eine Bedingung wahr wird. Ein anderer Thread wird die Bedingung bestätigen und die wartenden Threads benachrichtigen.

Bevor Sie wait eingeben, müssen wir prüfen, ob die Bedingung falsch ist. diese Prüfung und die Wartezeit müssen atomare sein, d. h. unter dem gleichen Schloss. Andernfalls, wenn wir die Wartezeit eingeben, während die Bedingung bereits erfüllt ist, werden wir wahrscheinlich niemals aufwachen.

Deshalb ist es notwendig, , dass die Sperre bereits vor dem Aufruf erworben wird wait()

synchronized(lock) 
{ 
    if(!condition) 
     lock.wait(); 

Wenn wait() automatisch und im Hintergrund erwirbt Schloss, ein viele von Bugs unentdeckt gehen.


Beim Aufwachen aus wait(), müssen wir den Zustand wieder überprüfen - keine Garantie gibt es, dass die Bedingung hier wahr werden müssen (für viele Gründe - falsche Wakeup, Timeout, Unterbrechung, mehrere Kellner, mehrere Bedingungen)

synchronized(lock) 
{ 
    if(!condition) 
     lock.wait(); 
    if(!condition) // check again 
     ... 

Wenn die Bedingung immer noch falsch ist, warten wir wieder. Daher ist das typische Muster

while(!condition) 
     lock.wait(); 

Aber es gibt auch Fälle, in denen wir nicht warten wollen.


Könnte es jemals legitime Anwendungsfälle geben, in denen Nacktwarte/Notifikation Sinn macht?

synchronized(lock){ lock.wait(); } 

Sicher; Eine Anwendung kann mit naked wait/notify mit klar definiertem Verhalten erstellt werden; es kann argumentiert werden, dass dies das gewünschte Verhalten ist; und das ist die beste Implementierung für dieses Verhalten.

Dies ist jedoch nicht das typische Nutzungsmuster, und es gibt keinen Grund, dies im API-Design zu berücksichtigen.

+0

Nun, selbst mit einer "nackten Wartezeit" haben wir das Problem, dass das 'wait' nur zurückkommt, wenn ein anderer Thread' notify' * aufruft, nachdem * die 'wait' gestartet hat. Aber ohne dass beide Threads für dasselbe Objekt "synchronisiert" sind, gibt es nicht einmal eine Ordnungsbeziehung. Es ist ausdrücklich darauf hinzuweisen, dass nicht nur keine Garantie dafür gegeben werden kann, dass die Bedingung erfüllt ist, es gibt auch keine Garantie, dass die Bedingung zwischen der Benachrichtigung und dem Aufwecken nicht wieder falsch geworden ist. – Holger