Есть ли мьютекс в 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()


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

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 предоставляет все эти функции и многое другое.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.