Gibt es einen Mutex in Java?


110

Gibt es ein Mutex-Objekt in Java oder eine Möglichkeit, eines zu erstellen? Ich frage, weil ein Semaphor-Objekt, das mit 1 Erlaubnis initialisiert wurde, mir nicht hilft. Denken Sie an diesen Fall:

try {
   semaphore.acquire();
   //do stuff
   semaphore.release();
} catch (Exception e) {
   semaphore.release();
}

Wenn beim ersten Erwerb eine Ausnahme auftritt, erhöht die Freigabe im catch-Block die Berechtigungen, und das Semaphor ist kein binäres Semaphor mehr.

Wird der richtige Weg sein?

try {
   semaphore.acquire();
   //do stuff
} catch (Exception e) {
   //exception stuff
} finally {
   semaphore.release();
}

Wird der obige Code sicherstellen, dass das Semaphor binär ist?


Suchen Sie im javadoc nach java.util.concurrent.locks.AbstractQueuedSynchronizer. Es enthält ein Beispiel für das Schreiben einer Mutex-Klasse. -dbednar
Joe

Haben Sie dieses Verhalten empirisch herausgefunden? Ist die Implementierung so, dass durch Ausführen von release () auf einem Semaphor mit 1 Berechtigung eine zusätzliche Berechtigung hinzugefügt wird, selbst wenn es derzeit eine andere enthält?
Launisch

Antworten:



133

Jedes Objekt in Java kann mithilfe eines synchronizedBlocks als Sperre verwendet werden . Dadurch wird auch automatisch die Sperre aufgehoben, wenn eine Ausnahme auftritt.

Object someObject = ...;

synchronized (someObject) {
  ...
}

Weitere Informationen hierzu finden Sie hier: Eigensperren und Synchronisierung


Sehr hilfreich kaufen wollte ich ein Semaphor verwenden.
Noam Nevo

11
@Noam: Vergleichen Sie einfach den Code mit dem Semaphor und synchronizedSie werden sehen, was besser lesbar und weniger fehleranfällig ist.
Vlad

17
Das synchronisierte Schlüsselwort kann nicht verwendet werden, wenn Sie erwarten, die Sperre in einer anderen Methode (z transaction.begin(); transaction.commit(). B. ) aufzuheben .
Hosam Aly

und es ist nicht objektorientiert. Es ist viel von der Synchronisation auf niedriger Ebene
anshulkatta

Schauen Sie auch in someObject.wait(timeout)und someObject.notify()während Sie den Code dieser Antwort suchen.
Daniel F

25
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


private final Lock _mutex = new ReentrantLock(true);

_mutex.lock();

// your protected code here

_mutex.unlock();

5
Inwiefern ist dies den bereits angebotenen Lösungen überlegen? Wie löst es das Problem, das der ursprüngliche Fragesteller hatte?
Martin

@Martin : "Lock implementations provide more extensive locking operations than can be obtained using synchronized methods and statements.", von: docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/… ... obwohl Sie einen Punkt haben. Argvs Antwort illustriert oder erklärt diese Operationen nicht.
FrustratedWithFormsDesigner

3
Dies ist ein rekursiver Mutex, der mehrere erneute Sperren desselben Threads ermöglicht, was problematisch sein kann. Ein "wahrer" grundlegender Mutex (nicht rekursiv, C ++ - Stil) würde jeweils nur eine Sperre zulassen. Wenn Sie Ihre Zeile in ändern private final ReentrantLock _mutex = ..., können Sie getHoldCount () verwenden , um die Anzahl der erneuten Thread-Sperren zurückzugeben. (Sie können ein anwenden Condition, um dies zu verhindern. Siehe die API .)
EntangledLoops

16

Niemand hat dies klar erwähnt, aber diese Art von Muster ist normalerweise nicht für Semaphoren geeignet. Der Grund dafür ist , dass jeder Thread eine Semaphore freigeben kann, aber Sie wollen in der Regel nur den Besitzer Thread , der ursprünglich zu entsperren können gesperrt werden . Für diesen Anwendungsfall verwenden wir in Java normalerweise ReentrantLocks, die wie folgt erstellt werden können:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

private final Lock lock = new ReentrantLock(true);

Und das übliche Design-Verwendungsmuster ist:

  lock.lock();
  try {
      // do something
  } catch (Exception e) {
      // handle the exception
  } finally {
      lock.unlock();
  }

Hier ist ein Beispiel im Java-Quellcode, in dem Sie dieses Muster in Aktion sehen können.

Wiedereintrittsschlösser haben den zusätzlichen Vorteil, dass sie die Fairness unterstützen.

Verwenden Sie Semaphoren nur, wenn Sie eine Semantik ohne Eigentumsfreigabe benötigen.


5
Eigentlich sollte dies die (einzige) richtige Antwort auf diese Frage sein. Klare Erklärung der Unterschiede zwischen Semaphor und gegenseitiger Ausschlusssperre. Die Verwendung eines Semaphors mit count=1ist keine gegenseitige Ausschlusssperre.
Kaihua

3
Ich bin froh, dass jemand darauf hingewiesen hat. Für den exklusiven Zugriff auf eine Ressource sind Mutexe der richtige Weg. Binäre Semaphoren sind keine Mutexe, Semaphoren sollten eher als Signalmechanismus verwendet werden.
Shivam Tripathi

Rubel: Also ist lockzB ReentrantLockein mutex? Ich bin mir nicht sicher, warum mutexund werde binary semaphoreals dieselbe Einheit angesehen. Semaphorekann von jedem Thread freigegeben werden, so dass möglicherweise kein Schutz garantiert wird critical section. Irgendwelche Gedanken?
CuriousMind

@ Kaihua: Ich stimme mit deinem Gedanken überein. Diese Antwort bringt den entscheidenden Unterschied
CuriousMind

6

Ich denke, Sie sollten versuchen mit:

Während der Semaphor-Initialisierung:

Semaphore semaphore = new Semaphore(1, true);

Und in deinem Runnable Implementation

try 
{
   semaphore.acquire(1);
   // do stuff

} 
catch (Exception e) 
{
// Logging
}
finally
{
   semaphore.release(1);
}

So habe ich es gemacht, aber ich bin mir nicht ganz sicher, ob dies der richtige Weg ist.
Freistehend

1
Laut docs.oracle.com/javase/7/docs/api/java/util/concurrent/… "Es ist nicht erforderlich, dass ein Thread, der eine Erlaubnis freigibt, diese Erlaubnis durch Aufrufen von" erworben "erworben hat. Die korrekte Verwendung eines Semaphors ist durch Programmierkonvention in der Anwendung festgelegt. " Wenn der Erwerb eine Ausnahme auslöst, wird durch die Freigabe in der Endgültigkeit eine Genehmigung fälschlicherweise freigegeben. Andere Beispiele in diesem Thread zeigen den richtigen Ablauf.
Brent K.

3

Ein Fehler im ursprünglichen Beitrag ist der Aufruf von purchase (), der in der try-Schleife festgelegt wurde. Hier ist ein korrekter Ansatz zur Verwendung des "binären" Semaphors (Mutex):

semaphore.acquire();
try {
   //do stuff
} catch (Exception e) {
   //exception stuff
} finally {
   semaphore.release();
}

1

Um sicherzustellen, dass a Semaphorebinär ist, müssen Sie beim Erstellen des Semaphors nur sicherstellen, dass Sie die Anzahl der Genehmigungen als 1 übergeben. Die Javadocs haben etwas mehr Erklärung.


1

Die Sperre jedes Objekts unterscheidet sich kaum vom Mutex / Semaphore-Design. Zum Beispiel gibt es keine Möglichkeit, das Durchlaufen verknüpfter Knoten korrekt zu implementieren, indem die Sperre des vorherigen Knotens aufgehoben und der nächste erfasst wird. Mit Mutex ist es jedoch einfach zu implementieren:

Node p = getHead();
if (p == null || x == null) return false;
p.lock.acquire();  // Prime loop by acquiring first lock.
// If above acquire fails due to interrupt, the method will
//   throw InterruptedException now, so there is no need for
//   further cleanup.
for (;;) {
Node nextp = null;
boolean found;
try { 
 found = x.equals(p.item); 
 if (!found) { 
   nextp = p.next; 
   if (nextp != null) { 
     try {      // Acquire next lock 
                //   while still holding current 
       nextp.lock.acquire(); 
     } 
     catch (InterruptedException ie) { 
      throw ie;    // Note that finally clause will 
                   //   execute before the throw 
     } 
   } 
 } 
}finally {     // release old lock regardless of outcome 
   p.lock.release();
} 

Derzeit gibt es keine solche Klasse java.util.concurrent, aber die Mutext-Implementierung finden Sie hier Mutex.java . In Bezug auf Standardbibliotheken bietet Semaphore all diese Funktionen und vieles mehr.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.