Чи є 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.