Existe-t-il un Mutex en Java?


110

Existe-t-il un objet Mutex en java ou un moyen d'en créer un? Je demande parce qu'un objet Semaphore initialisé avec 1 permis ne m'aide pas. Pensez à ce cas:

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

si une exception se produit lors de la première acquisition, la libération dans le bloc catch augmentera les autorisations et le sémaphore n'est plus un sémaphore binaire.

La bonne manière sera-t-elle?

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

Le code ci-dessus garantira-t-il que le sémaphore sera binaire?


Regardez le javadoc pour java.util.concurrent.locks.AbstractQueuedSynchronizer. Il a un exemple de la façon d'écrire une classe Mutex. -dbednar
joe

Avez-vous découvert ce comportement empiriquement? L'implémentation est-elle telle que l'exécution de release () sur un sémaphore à 1 permis ajoute un permis supplémentaire même s'il en détient actuellement un autre, vraiment?
Whimusical

Réponses:



133

Tout objet en Java peut être utilisé comme verrou à l'aide d'un synchronizedbloc. Cela prendra également en charge automatiquement la libération du verrou lorsqu'une exception se produit.

Object someObject = ...;

synchronized (someObject) {
  ...
}

Vous pouvez en savoir plus à ce sujet ici: Verrous et synchronisation intrinsèques


Achat très utile, je voulais utiliser un sémaphore.
Noam Nevo

11
@Noam: comparez simplement le code avec le sémaphore et avec synchronized, vous verrez ce qui est mieux lisible et moins sujet aux erreurs.
Vlad

17
Le mot-clé synchronized ne peut pas être utilisé si vous prévoyez de libérer le verrou dans une méthode différente (par exemple transaction.begin(); transaction.commit()).
Hosam Aly

et ce n'est pas orienté objet ... c'est beaucoup de synchronisation de bas niveau
anshulkatta

Regardez également someObject.wait(timeout)et someObject.notify()pendant que vous regardez le code de cette réponse.
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
En quoi est-ce supérieur aux solutions déjà proposées? Comment résout-il le problème rencontré par le demandeur d'origine?
Martin du

@Martin:, "Lock implementations provide more extensive locking operations than can be obtained using synchronized methods and statements."de: docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/… ... bien que vous ayez un point. La réponse d'Argv n'illustre ni n'explique ces opérations.
FrustratedWithFormsDesigner

3
Il s'agit d'un mutex récursif qui permettra plusieurs re-verrouillages à partir du même thread, ce qui peut être problématique. Un «vrai» mutex basique (non récursif, style C ++) n'autoriserait qu'un seul verrou à la fois. Si vous remplacez votre ligne par private final ReentrantLock _mutex = ..., vous pouvez utiliser getHoldCount () pour renvoyer le nombre de re-verrouillages de threads. (Vous pouvez appliquer un Conditionpour éviter cela. Voir l'API .)
EntangledLoops

16

Personne ne l'a clairement mentionné, mais ce type de modèle n'est généralement pas adapté aux sémaphores. La raison est que tout thread peut libérer un sémaphores, mais vous ne voulez généralement que le thread propriétaire que verrouillé à l' origine pour pouvoir déverrouiller . Pour ce cas d'utilisation, en Java, nous utilisons généralement ReentrantLocks, qui peuvent être créés comme ceci:

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

private final Lock lock = new ReentrantLock(true);

Et le modèle d'utilisation habituel est:

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

Voici un exemple dans le code source java où vous pouvez voir ce modèle en action.

Les verrous réentrants présentent l'avantage supplémentaire de favoriser l'équité.

N'utilisez des sémaphores que si vous avez besoin d'une sémantique sans propriété.


5
En fait, cela devrait être la (seule) bonne réponse à cette question. Explication claire des différences entre sémaphore et verrouillage d'exclusion mutuelle. L'utilisation d'un sémaphore avec count=1n'est pas un verrou d'exclusion mutuelle.
Kaihua

3
Heureux quelqu'un a souligné. Pour un accès exclusif à une ressource, les mutex sont la voie à suivre. Les sémaphores binaires ne sont pas mutex, les sémaphores devraient être davantage utilisés comme mécanisme de signalisation.
Shivam Tripathi

Rouble: Alors , est lockpar exemple ReentrantLockun mutex? Je ne sais pas pourquoi mutexet binary semaphoresont assimilés à la même entité. Semaphorepeut être libéré par n'importe quel thread, ce qui ne garantit pas la protection critical section. Des pensées?
CuriousMind

@Kaihua: Je résonne avec votre pensée. Cette réponse apporte la différence clé
CuriousMind

6

Je pense que vous devriez essayer avec:

Pendant l'initialisation du sémaphore:

Semaphore semaphore = new Semaphore(1, true);

Et dans votre Runnable Implementation

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

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

C'est ainsi que je l'ai fait, mais je ne sais pas si c'est la voie à suivre.
Détaché

1
Selon docs.oracle.com/javase/7/docs/api/java/util/concurrent/… "Il n'est pas nécessaire qu'un thread qui libère un permis ait acquis ce permis en appelant acquiert. L'utilisation correcte d'un sémaphore est établi par convention de programmation dans l'application. " Si l'acquisition lève une exception, la publication dans le finally libèrera de manière incorrecte un permis. D'autres exemples de ce fil montrent le bon déroulement.
Brent K.

3

L'erreur dans le message d'origine est un appel acquise () défini dans la boucle try. Voici une approche correcte pour utiliser le sémaphore "binaire" (Mutex):

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

1

Pour vous assurer que a Semaphoreest binaire, vous devez simplement vous assurer que vous passez le nombre de permis à 1 lors de la création du sémaphore. Les Javadocs ont un peu plus d'explications.


1

Le verrou de chaque objet est peu différent de la conception Mutex / Semaphore. Par exemple, il n'existe aucun moyen d'implémenter correctement la traversée des nœuds liés en libérant le verrou du nœud précédent et en capturant le suivant. Mais avec mutex, il est facile de mettre en œuvre:

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();
} 

Actuellement, il n'existe pas de classe de ce type dans java.util.concurrent, mais vous pouvez trouver l'implémentation de Mutext ici Mutex.java . Quant aux bibliothèques standard, Semaphore fournit toutes ces fonctionnalités et bien plus encore.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.