Есть ли мьютекс в Java?

111

Есть ли объект Mutex в Java или способ его создать? Я спрашиваю, потому что объект Semaphore, инициализированный с 1 разрешением, мне не помогает. Подумайте об этом случае:

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

если исключение происходит при первом запросе, освобождение в блоке catch увеличит разрешения, и семафор больше не является двоичным семафором.

Будет ли правильный путь?

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

Будет ли приведенный выше код гарантировать, что семафор будет двоичным?

Ноам Нево
источник
Посмотрите javadoc для java.util.concurrent.locks.AbstractQueuedSynchronizer. В нем есть пример того, как написать класс Mutex. -dbednar
джо
Вы выяснили это поведение эмпирически? Действительно ли реализация такова, что выполнение release () на семафоре с 1 разрешением добавляет дополнительное разрешение, даже если в настоящее время он содержит другое?
Причудливый

Ответы:

112

См. Эту страницу: http://www.oracle.com/technetwork/articles/javase/index-140767.html

У него немного другой шаблон, который (я думаю) то, что вы ищете:

try {
  mutex.acquire();
  try {
    // do something
  } finally {
    mutex.release();
  }
} catch(InterruptedException ie) {
  // ...
}

В этом случае вы звоните только release()после успешногоacquire()

Payne
источник
134

Любой объект в Java можно использовать в качестве блокировки с помощью synchronizedблока. Это также автоматически позаботится о снятии блокировки при возникновении исключения.

Object someObject = ...;

synchronized (someObject) {
  ...
}

Вы можете узнать больше об этом здесь: Внутренние блокировки и синхронизация

касабланка
источник
Очень полезная покупка, я хотел использовать семафор.
Ноам Нево
11
@Noam: просто сравните код с семафором, и synchronizedвы увидите, что лучше читается и менее подвержено ошибкам.
Влад
17
Ключевое слово synchronized нельзя использовать, если вы ожидаете снять блокировку другим способом (например transaction.begin(); transaction.commit()).
Хосам Али
и это не объектно-ориентированное ... это большая часть синхронизации низкого уровня
anshulkatta
Также изучите someObject.wait(timeout)и someObject.notify()пока смотрите код этого ответа.
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();
Argv
источник
5
Чем это превосходит уже имеющиеся решения? Как это решает проблему, с которой столкнулся первоначальный спрашивающий?
Мартин
@Martin:, "Lock implementations provide more extensive locking operations than can be obtained using synchronized methods and statements."от: docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ ... ... хотя вы правы . Ответ Argv не иллюстрирует и не объясняет эти операции.
FrustratedWithFormsDesigner
3
Это рекурсивный мьютекс, который позволяет выполнять многократные повторные блокировки из одного и того же потока, что может быть проблематичным. «Истинный» базовый мьютекс (нерекурсивный, в стиле C ++) допускает только одну блокировку за раз. Если вы измените свою строку на private final ReentrantLock _mutex = ..., вы можете использовать getHoldCount (), чтобы вернуть количество повторных блокировок потока. (Вы можете применить a, Conditionчтобы предотвратить это. См. API .)
EntangledLoops
16

Никто не упомянул об этом четко, но такой шаблон обычно не подходит для семафоров. Причина в том, что любой поток может освободить семафор, но обычно вам нужен только поток-владелец, который изначально заблокирован, чтобы иметь возможность разблокировать . Для этого варианта использования в Java мы обычно используем ReentrantLocks, которые можно создать следующим образом:

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

private final Lock lock = new ReentrantLock(true);

И обычный шаблон использования дизайна:

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

Вот пример исходного кода Java, где вы можете увидеть этот шаблон в действии.

У повторных блокировок есть дополнительное преимущество в виде поддержки справедливости.

Используйте семафоры только в том случае, если вам нужна семантика без права собственности.

рубль
источник
5
На самом деле это должен быть (единственный) правильный ответ на этот вопрос. Четкое объяснение различий между семафором и блокировкой взаимного исключения. Использование семафора с count=1не является блокировкой взаимного исключения.
Кайхуа
3
Рад, что кто-то указал. Для монопольного доступа к ресурсу можно использовать мьютексы. Двоичные семафоры не являются мьютексами, семафоры следует использовать больше как сигнальный механизм.
Шивам Трипати
Рубль: Так , lockнапример , ? Я не знаю , почему и принимаются приравнены быть тем же лицом. может быть выпущен любым потоком, поэтому это не может гарантировать защиту . Есть предположения? ReentrantLockmutexmutexbinary semaphoreSemaphorecritical section
CuriousMind
@Kaihua: Я согласен с твоей мыслью. Этот ответ приносит ключевое отличие
CuriousMind
6

Я думаю, вам стоит попробовать:

При инициализации семафора:

Semaphore semaphore = new Semaphore(1, true);

И в твоем Runnable Implementation

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

} 
catch (Exception e) 
{
// Logging
}
finally
{
   semaphore.release(1);
}
Саши Кант
источник
Вот как я это сделал, но я не совсем уверен, что это правильный путь.
Отсоединен
1
Согласно docs.oracle.com/javase/7/docs/api/java/util/concurrent/… «Нет требования, чтобы поток, который выпускает разрешение, должен был получить это разрешение, вызвав получение. Правильное использование семафора установленный соглашением о программировании в приложении ". Если получение выдает исключение, релиз в finally неправильно освобождает разрешение. Другие примеры в этом потоке показывают правильный поток.
Брент К.
3

Ошибка в исходном посте - это вызов функции Accele (), установленный внутри цикла try. Вот правильный подход к использованию «двоичного» семафора (Mutex):

semaphore.acquire();
try {
   //do stuff
} catch (Exception e) {
   //exception stuff
} finally {
   semaphore.release();
}
улыбка
источник
1

Чтобы гарантировать, что a Semaphoreявляется двоичным, вам просто нужно убедиться, что вы передаете количество разрешений как 1 при создании семафора. В Javadocs есть немного больше объяснений.

Санджай Т. Шарма
источник
1

Блокировка каждого объекта мало отличается от дизайна мьютексов / семафоров. Например, нет способа правильно реализовать обход связанных узлов с снятием блокировки предыдущего узла и захватом следующего. Но с мьютексом легко реализовать:

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

В настоящее время такого класса нет java.util.concurrent, но вы можете найти реализацию Mutext здесь Mutex.java . Что касается стандартных библиотек, Semaphore предоставляет все эти функции и многое другое.

Погребнюк
источник