Existe um Mutex em Java?


110

Existe um objeto Mutex em java ou uma maneira de criar um? Estou perguntando porque um objeto Semaphore inicializado com 1 licença não me ajuda. Pense neste caso:

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

se ocorrer uma exceção na primeira aquisição, a liberação no bloco catch aumentará as permissões e o semáforo não será mais um semáforo binário.

Será a maneira correta?

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

O código acima garantirá que o semáforo será binário?


Consulte o javadoc para java.util.concurrent.locks.AbstractQueuedSynchronizer. Ele tem um exemplo de como escrever uma classe Mutex. -dbednar
joe

Você descobriu esse comportamento empiricamente? A implementação é tal que a execução de release () em um Semaphore de 1 licença adiciona uma licença extra, mesmo se estiver segurando outra, na verdade?
Whimusical 01 de

Respostas:



133

Qualquer objeto em Java pode ser usado como um bloqueio usando um synchronizedbloco. Isso também irá automaticamente liberar o bloqueio quando ocorrer uma exceção.

Object someObject = ...;

synchronized (someObject) {
  ...
}

Você pode ler mais sobre isso aqui: Bloqueios intrínsecos e sincronização


Muito útil comprar eu queria usar um semáforo.
Noam Nevo

11
@Noam: basta comparar o código com o semáforo e com o synchronized, você verá o que é mais legível e menos sujeito a erros.
Vlad,

17
A palavra-chave synchronized não pode ser usada se você espera liberar o bloqueio em um método diferente (por exemplo transaction.begin(); transaction.commit()).
Hosam Aly

e não é orientado a objetos
.. é

Além disso, observe someObject.wait(timeout)e someObject.notify()enquanto analisa o código desta resposta.
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
De que forma isso é superior às soluções já fornecidas? Como isso resolve o problema que o autor da pergunta original estava tendo?
Martin

@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/… ... embora você tenha razão . A resposta de Argv não ilustra nem explica essas operações.
FrustratedWithFormsDesigner

3
Este é um mutex recursivo que permitirá múltiplos bloqueios do mesmo thread, o que pode ser problemático. Um mutex básico "verdadeiro" (não recursivo, estilo C ++) permitiria apenas um bloqueio de cada vez. Se você alterar sua linha para private final ReentrantLock _mutex = ..., pode usar getHoldCount () para retornar o número de bloqueios de thread. (Você pode aplicar um Conditionpara evitar isso. Consulte a API .)
EntangledLoops

16

Ninguém mencionou isso claramente, mas esse tipo de padrão geralmente não é adequado para semáforos. O motivo é que qualquer thread pode liberar um semáforo, mas normalmente você só deseja que o thread do proprietário que foi bloqueado originalmente seja desbloqueado . Para este caso de uso, em Java, geralmente usamos ReentrantLocks, que pode ser criado assim:

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

private final Lock lock = new ReentrantLock(true);

E o padrão de design usual de uso é:

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

Aqui está um exemplo no código-fonte java onde você pode ver esse padrão em ação.

Os bloqueios reentrantes têm o benefício adicional de apoiar a justiça.

Use semáforos apenas se precisar de uma semântica de liberação sem propriedade.


5
Na verdade, esta deve ser a (única) resposta correta para esta pergunta. Explicação clara das diferenças entre semáforo e bloqueio de exclusão mútua. Usar um semáforo com count=1não é um bloqueio de exclusão mútua.
Kaihua,

3
Que bom que alguém apontou. Para acesso exclusivo a um recurso, os mutexes são o caminho a percorrer. Os semáforos binários não são mutex, os semáforos devem ser usados ​​mais como mecanismo de sinalização.
Shivam Tripathi

Ruble: Então, lockpor exemplo, é ReentrantLockum mutex? Não sei por que mutexe binary semaphoreestão sendo considerados a mesma entidade. Semaphorepode ser liberado por qualquer thread, de modo que pode não garantir proteção critical section. Alguma ideia?
CuriousMind

@Kaihua: Eu me identifico com seu pensamento. Esta resposta traz a principal diferença
CuriousMind

6

Eu acho que você deveria tentar com:

Durante a inicialização do Semaphore:

Semaphore semaphore = new Semaphore(1, true);

E no seu Runnable Implementation

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

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

Foi assim que fiz, mas não tenho certeza se esse é o caminho a percorrer.
Separado de

1
De acordo com docs.oracle.com/javase/7/docs/api/java/util/concurrent/… "Não há nenhuma exigência de que um thread que libera uma licença deva ter adquirido essa licença chamando adquirir. O uso correto de um semáforo é estabelecido pela convenção de programação no aplicativo. " Se a aquisição lançar uma exceção, a liberação no finalmente liberará incorretamente uma licença. Outros exemplos neste tópico mostram o fluxo correto.
Brent K.

3

O erro na postagem original é a chamada purchase () definida dentro do loop try. Aqui está uma abordagem correta para usar semáforo "binário" (Mutex):

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

1

Para garantir que a Semaphoreseja binário, você só precisa passar o número de licenças como 1 ao criar o semáforo. Os Javadocs têm um pouco mais de explicação.


1

O bloqueio de cada objeto é um pouco diferente do design Mutex / Semaphore. Por exemplo, não há maneira de implementar corretamente o cruzamento de nós vinculados com a liberação do bloqueio do nó anterior e a captura do próximo. Mas com mutex é fácil de implementar:

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

Atualmente, não existe essa classe em java.util.concurrent, mas você pode encontrar a implementação de Mutext aqui Mutex.java . Quanto às bibliotecas padrão, o Semaphore fornece todas essas funcionalidades e muito mais.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.