2013-05-12 22 views
33

Reentrancy bedeutet, dass Sperren pro Thread und nicht pro Aufruf erworben werden.'Reentrancy' in Java

Da eine intrinsische Sperre von einem Thread gehalten wird, heißt das nicht, dass ein Thread, der einmal ausgeführt wird, einer Aufrufbasis entspricht?

Vielen Dank, es scheint, dass meine: in einem Thread, wenn ich eine Sperre lockA erhalten, wenn Prozessfunktion doA die Funktion aufrufen doB und doB auch eine Sperre lockA benötigen, dann wil es eine reentrancy sein. In Java wird dieses Phänomen pro Thread erfasst, sodass ich keine Deadlocks in Betracht ziehen muss?

+0

Stellen Sie sich vor, ein Verfahren Sperren der Eingabe X-Objekt, und während sie in dieser Methode Sie rufen die gleichen Verfahren (entweder direkt oder über weitere in der Aufrufliste) und Sperrobjekt X wieder. Was geschieht?Wartest du auf die Sperre, die du bereits hältst, oder ist dir bewusst, dass der Thread, der es hält, auch der Thread ist, der es erneut aufruft und es passieren lässt? Dies wird Reentrancy genannt - Sie geben die gleiche Methode ein, mit der Sie bereits früher gearbeitet haben. – Patashu

+1

Ja, synchronisierte Blöcke und Sperren in Java sind reentrant. Sobald der Thread foo über eine Sperrleiste verfügt, kann er sicher Methoden aufrufen, die ebenfalls Sperren sperren, ohne sie zuvor entsperren zu müssen. – Patashu

+0

Sie müssen weiterhin Deadlock berücksichtigen, Deadlock kann auftreten, wenn zwei Threads aufeinander warten. – rubixibuc

Antwort

52

Ablaufinvarianz bedeutet, dass Sperren auf einem Pro-Thread erworben werden und nicht pro-Aufruf-Basis.

Das ist eine irreführende Definition. Es ist wahr (so), aber es fehlt der wahre Punkt.

Reentrancy bedeutet (im Allgemeinen CS/IT Terminologie), dass Sie etwas tun, und während Sie es noch tun, tun Sie es noch einmal. Im Fall von Sperren bedeutet es, Sie so etwas wie diese auf einem einzigen Thread tut:

  1. Acquire eine Sperre auf „foo“.
  2. Etwas tun
  3. Erwerben Sie eine Sperre auf "foo". Beachten Sie, dass wir das zuvor erworbene Schloss nicht freigegeben haben.
  4. ...
  5. Entriegelungsschloss auf "foo"
  6. ...
  7. Entriegelungsschloss auf "foo"

Mit einem einspringenden Verriegelungs-/Schließmechanismus, der Versuch, das gleiche zu erwerben Die Sperre wird erfolgreich ausgeführt und erhöht einen internen Zähler, der zur Sperre gehört. Das Schloss wird nur freigegeben, wenn der aktuelle Schlosshalter es zweimal losgelassen hat.

Hier ist ein Beispiel in Java mit primitiven Objektsperren/Monitore ... die einspringende sind:

Object lock = new Object(); 
... 
synchronized (lock) { 
    ... 
    doSomething(lock, ...) 
    ... 
} 

public void doSomething(Object lock, ...) { 
    synchronized (lock) { 
     ... 
    } 
} 

Die Alternative zu Reentry ist nichtablaufinvarianten sichernd, wo es ein Fehler für einen Thread zu versuchen, wäre um ein Schloss zu erhalten, das es bereits hält.

Der Vorteil der Verwendung von Reentrant-Sperren besteht darin, dass Sie sich keine Gedanken über die Möglichkeit eines Ausfalls machen müssen, weil Sie versehentlich eine Sperre erhalten, die Sie bereits besitzen. Der Nachteil ist, dass Sie nicht davon ausgehen können, dass nichts, das Sie aufrufen, den Status der Variablen ändert, die die Sperre schützen soll. Dies ist jedoch normalerweise kein Problem.Sperren werden im Allgemeinen zum Schutz gegen gleichzeitige Zustandsänderungen verwendet, die von anderen Threads vorgenommen werden.


Also brauche ich nicht Deadlocks in Betracht ziehen?

Ja, Sie tun.

Ein Thread wird nicht gegen sich selbst blockieren (wenn das Schloss wieder einspringt). Sie können jedoch einen Deadlock erhalten, wenn andere Threads eine Sperre für das Objekt haben, das Sie sperren möchten.

+0

Da ein Thread einmal ausgeführt wird, wie könnte ich es dabei noch machen, dann nochmal machen? – znlyj

+1

Siehe den Beispiel-Java-Code, den ich gerade hinzugefügt habe. –

+0

Ja, danke. – znlyj

1

Dies bedeutet nur, sobald ein Thread eine Sperre hat, kann es den gesperrten Codeabschnitt so oft eingeben, wie es nötig ist. Wenn Sie also einen synchronisierten Codeabschnitt wie eine Methode haben, kann nur der Thread, der die Sperre erreicht hat, diese Methode aufrufen, kann diese Methode jedoch beliebig oft aufrufen, einschließlich aller anderen Codes, die von derselben Sperre gehalten werden. Dies ist wichtig, wenn Sie über eine Methode verfügen, die eine andere Methode aufruft und beide von derselben Sperre synchronisiert werden. Wenn dies nicht der Fall war, Der zweite Methodenaufruf würde blockieren. Dies würde auch für rekursive Methodenaufrufe gelten.

public void methodA() 
{ 
    // other code 
    synchronized(this) 
    { 
      methodB(); 
    } 
} 

public void methodB() 
{ 
    // other code 
    syncrhonized(this) 
    { 
      // it can still enter this code  
    } 

} 
13

etwas wie folgt vorstellen:

function A(): 
    lock (X) 
     B() 
    unlock (X) 

function B(): 
    A() 

Jetzt nennen wir A. passiert Folgendes:

  • Wir A eingeben, X
  • Sperren eingeben We B
  • Wir betreten A wieder , X wieder verriegeln

Seit w Der erste Aufruf von A wurde nie beendet, X ist immer noch gesperrt. Dies wird Wiedereintritt genannt - während die Funktion A noch nicht zurückgekehrt ist, wird die Funktion A erneut aufgerufen. Wenn A auf einen globalen, statischen Zustand angewiesen ist, kann dies zu einem "Wiedereintritts-Fehler" führen, bei dem die Funktion erneut ausgeführt wird, bevor der statische Zustand vom Ausgang der Funktion bereinigt wird und die Hälfte der berechneten Werte mit dem Anfang von kollidiert der zweite Anruf.

In diesem Fall laufen wir in eine Sperre, die wir bereits halten. Wenn das Schloss wieder betreten wird, merkt es, dass wir der gleiche Thread sind, der das Schloss bereits hält und uns durchlässt. Andernfalls wird es für immer blockieren - es wird auf eine Sperre warten, die es bereits hält.

In Java, lock und synchronized sind Re-Eingang bewusst - wenn eine Sperre von einem Thread gehalten wird, und der Thread versucht, die gleiche Sperre wieder zu erwerben, ist es erlaubt. Wenn wir also den obigen Pseudocode in Java schreiben würden, würde das nicht blockieren.

7

Java Concurrency in der Praxis Buch Staaten - Reentrancy means that locks are acquired on a per-thread rather than per-invocation basis.

Lassen Sie mich erklären, was es genau bedeutet. Zuallererst sind intrinsische Sperren von Natur aus reentrant. Die Art und Weise, wie die Reentrancy erreicht wird, besteht darin, einen Zähler für die Anzahl der erworbenen Schlösser und den Besitzer des Schlosses aufrechtzuerhalten. Wenn der Zählerstand 0 ist und ihm kein Besitzer zugeordnet ist, wird die Sperre nicht von einem Thread gehalten. Wenn ein Thread die Sperre erwirbt, zeichnet JVM den Besitzer auf und setzt den Zähler auf 1. Wenn derselbe Thread versucht, die Sperre erneut zu übernehmen, wird der Zähler inkrementiert, und wenn der besitzende Thread vorhanden ist, wird der synchronisierte Blockzähler dekrementiert. Wenn der Wert 0 erreicht, wird die Sperre wieder aufgehoben.

Ein einfaches Beispiel wäre -

public class Test { 
    public synchronized void performTest() { 
     //... 
    } 
} 

public class CustomTest extends Test { 
    public synchronized void performTest() { 
     //... 
     super.performTest(); 
    } 
} 

ohne reentrancy gäbe es eine Sackgasse sein.

enter image description here