Чи є Mutex на Java?


110

Чи є об'єкт Mutex в Java або спосіб створити його? Я прошу, тому що об'єкт Semaphore, ініціалізований з 1 дозволом, не допомагає мені. Подумайте про цей випадок:

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

якщо виняток трапляється при першому придбанні, вивільнення в блоці вилову збільшить дозволи, і семафор більше не буде бінарним семафором.

Чи буде правильний шлях?

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

Чи забезпечить вищезазначений код, що семафор буде двійковим?


Подивіться на javadoc для java.util.concurrent.locks.Ab абстрактQueuedSynchronizer. У ній є приклад того, як написати клас Mutex. -dbednar
joe

Ви дізналися цю поведінку емпірично? Чи реалізація така, що виконання release () на 1-дозволі Semaphore додає додатковий дозвіл, навіть якщо він зараз має інший?
Примхливий

Відповіді:


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


133

Будь-який об’єкт у Java може використовуватися як блокування за допомогою synchronizedблоку. Це також автоматично забезпечить звільнення блокування, коли станеться виняток.

Object someObject = ...;

synchronized (someObject) {
  ...
}

Більше про це можна прочитати тут: Внутрішні блокування та синхронізація


Дуже корисно купити я хотів використати семафор.
Ноам Нево

11
@Noam: просто порівняйте код із семафором і з synchronized, ви побачите, що краще читати та менш схильне до помилок.
Влад

17
Синхронізоване ключове слово не можна використовувати, якщо ви очікуєте звільнення блокування іншим методом (наприклад transaction.begin(); transaction.commit()).
Хосам Алі

і його не орієнтована на об'єкти .. це багато що на низькому рівні синхронізації
anshulkatta

Також вивчіть, someObject.wait(timeout)і someObject.notify()поки ви дивитесь коду цієї відповіді.
Даніель Ф

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/… ... хоча у вас є точка. Відповідь Аргва не ілюструє та не пояснює ці операції.
FrustratedWithFormsDesigner

3
Це рекурсивний мютекс, який дозволить отримати кілька повторних блокувань з однієї теми, що може бути проблематично. "Справжній" базовий mutex (нерекурсивний, 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
Радий, що хтось вказав. Для ексклюзивного доступу до ресурсів файли - це шлях. Бінарні семафори не є мутексними, семафори повинні використовуватися більше як сигнальний механізм.
Shivam Tripathi

Рубль: Так , lockнаприклад , ? Я не впевнений, чому і як нас вважають однаковою сутністю. може бути відпущена будь-якою ниткою, так що це не може гарантувати охорону . Будь-які думки? ReentrantLockmutexmutexbinary semaphoreSemaphorecritical section
CuriousMind

@Kaihua: Я резоную з вашою думкою. Ця відповідь приносить ключову різницю
CuriousMind

6

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

Під час ініціалізації Semaphore:

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/… "Немає вимоги, щоб нитка, яка випускає дозвіл, повинна придбати цей дозвіл, зателефонувавши на придбання. Правильне використання семафору: встановлена ​​конвенцією програмування в додатку ". Якщо придбання кидає виняток, нарешті випуск буде неправильно видавати дозвіл. Інші приклади цієї теми показують правильний потік.
Брент К.

3

Помилка в оригінальній публікації - це набір виклику набору (), який знаходиться у циклі спробу. Ось правильний підхід до використання "бінарного" семафору (Mutex):

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

1

Щоб переконатися, що a Semaphoreє двійковим, вам просто потрібно переконатися, що ви перетворюєте кількість дозволів як 1 під час створення семафору. У Javadocs є трохи більше пояснень.


1

Кожен замок об'єкта мало відрізняється від дизайну Mutex / Semaphore. Наприклад, немає способу правильно здійснити проходження пов'язаних вузлів, звільнивши блокування попереднього вузла та захопивши наступний. Але з mutex це легко реалізувати:

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.