Có Mutex trong Java không?


110

Có đối tượng Mutex trong java hay cách tạo một đối tượng? Tôi đang hỏi vì một đối tượng Semaphore được khởi tạo với 1 giấy phép không giúp được tôi. Hãy nghĩ đến trường hợp này:

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

nếu một ngoại lệ xảy ra ở lần mua đầu tiên, việc phát hành trong khối catch sẽ tăng giấy phép và semaphore không còn là semaphore nhị phân nữa.

Cách chính xác sẽ là?

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

Đoạn mã trên có đảm bảo rằng semaphore sẽ là nhị phân không?


Hãy xem javadoc cho java.util.concurrent.locks.AbstractQueuedSynchronizer. Nó có một ví dụ về cách viết một lớp Mutex. -dbednar
joe

Bạn có phát hiện ra hành vi này theo kinh nghiệm không? Việc triển khai như vậy có thực hiện việc thực thi release () trên Semaphore 1 giấy phép sẽ thêm một giấy phép bổ sung ngay cả khi nó đang giữ một giấy phép khác, thực sự không?
Whimusical

Câu trả lời:


112

Xem trang này: http://www.oracle.com/technetwork/articles/javase/index-140767.html

Nó có một mô hình hơi khác, đó là (tôi nghĩ) những gì bạn đang tìm kiếm:

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

Trong cách sử dụng này, bạn chỉ gọi release()sau khi thành côngacquire()


133

Bất kỳ đối tượng nào trong Java đều có thể được sử dụng làm khóa bằng cách sử dụng một synchronizedkhối. Thao tác này cũng sẽ tự động giải phóng khóa khi có ngoại lệ.

Object someObject = ...;

synchronized (someObject) {
  ...
}

Bạn có thể đọc thêm về điều này tại đây: Khóa nội tại và đồng bộ hóa


Mua rất hữu ích, tôi muốn sử dụng semaphore.
Noam Nevo

11
@Noam: chỉ cần so sánh mã với semaphore và với synchronized, bạn sẽ thấy cái gì dễ đọc hơn và ít bị lỗi hơn.
Vlad

17
Không thể sử dụng từ khóa được đồng bộ hóa nếu bạn muốn giải phóng khóa bằng một phương pháp khác (ví dụ transaction.begin(); transaction.commit():).
Hosam Aly

và nó không phản đối oriented..its nhiều đồng bộ mức thấp
anshulkatta

Cũng xem xét someObject.wait(timeout)someObject.notify()trong khi bạn đang xem mã của câu trả lời này.
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
Ở điểm nào điều này vượt trội hơn so với các giải pháp đã được cung cấp? Nó giải quyết vấn đề mà người hỏi ban đầu gặp phải như thế nào?
Martin

@Martin:, "Lock implementations provide more extensive locking operations than can be obtained using synchronized methods and statements."từ: docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/… ... mặc dù bạn có lý. Câu trả lời của Argv không minh họa hoặc giải thích các hoạt động này.
FrustratedWithFormsDesigner

3
Đây là một mutex đệ quy sẽ cho phép nhiều lần khóa lại từ cùng một luồng, điều này có thể có vấn đề. Một mutex cơ bản "đúng" (không đệ quy, C ++) sẽ chỉ cho phép một khóa tại một thời điểm. Nếu bạn thay đổi dòng của mình thành private final ReentrantLock _mutex = ..., bạn có thể sử dụng getHoldCount () để trả về số lần khóa lại chuỗi. (Bạn có thể áp dụng một Conditionđể ngăn chặn điều này. Hãy xem API .)
EntangledLoops

16

Không ai đã đề cập rõ ràng về điều này, nhưng loại mô hình này thường không phù hợp với các vòng bán kết. Nguyên nhân là do bất kỳ chủ đề có thể phát hành một semaphore, nhưng bạn thường chỉ muốn thread chủ sở hữuban đầu được khóa để có thể mở khóa . Đối với trường hợp sử dụng này, trong Java, chúng tôi thường sử dụng ReentrantLocks, có thể được tạo như sau:

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

private final Lock lock = new ReentrantLock(true);

Và kiểu thiết kế thông thường được sử dụng là:

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

Đây là một ví dụ trong mã nguồn java nơi bạn có thể thấy mẫu này hoạt động.

Khóa Reentrant có thêm lợi ích là hỗ trợ sự công bằng.

Chỉ sử dụng semaphores nếu bạn cần ngữ nghĩa không thuộc quyền sở hữu-phát hành.


5
Trên thực tế, đây là câu trả lời chính xác (duy nhất) cho câu hỏi này. Giải thích rõ ràng về sự khác biệt giữa semaphore và khóa loại trừ lẫn nhau. Sử dụng semaphore với count=1không phải là một khóa loại trừ lẫn nhau.
Kaihua

3
Rất vui khi ai đó đã chỉ ra. Để có quyền truy cập độc quyền vào một tài nguyên, mutexes là cách nên làm. Semaphores nhị phân không phải là mutex, semaphores nên được sử dụng nhiều hơn như cơ chế báo hiệu.
Shivam Tripathi

Ruble: Vậy lockví dụ ReentrantLocka mutex? Tôi không chắc tại sao mutexbinary semaphoređang được coi là cùng một thực thể. Semaphorecó thể được phát hành bởi bất kỳ luồng nào, vì vậy điều đó có thể không đảm bảo việc bảo vệ critical section. Có suy nghĩ gì không?
CuriousMind

@Kaihua: Tôi đồng cảm với suy nghĩ của bạn. Câu trả lời này mang đến sự khác biệt chính
CuriousMind

6

Tôi nghĩ bạn nên thử với:

Trong khi khởi tạo Semaphore:

Semaphore semaphore = new Semaphore(1, true);

Và trong của bạn Runnable Implementation

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

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

Đây là cách tôi đã làm, nhưng tôi không hoàn toàn chắc chắn liệu đây có phải là cách để thực hiện hay không.
Tách ra

1
Theo docs.oracle.com/javase/7/docs/api/java/util/concurrent/… "Không có yêu cầu rằng một chuỗi phát hành giấy phép phải có được giấy phép đó bằng cách gọi hàm. Cách sử dụng chính xác của semaphore là được thiết lập bởi quy ước lập trình trong ứng dụng. " Nếu lệnh thu thập ném ra một ngoại lệ, bản phát hành cuối cùng sẽ phát hành sai giấy phép. Các ví dụ khác trong chủ đề này hiển thị luồng chính xác.
Brent K.

3

Sai lầm trong bài viết gốc là cuộc gọi get () được đặt bên trong vòng lặp thử. Đây là một cách tiếp cận đúng để sử dụng semaphore "nhị phân" (Mutex):

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

1

Để đảm bảo rằng a Semaphorelà nhị phân, bạn chỉ cần đảm bảo rằng bạn chuyển số lượng giấy phép là 1 khi tạo semaphore. Các Javadocs có lời giải thích thêm một chút.


1

Khóa của mỗi đối tượng hơi khác so với thiết kế Mutex / Semaphore. Ví dụ: không có cách nào để triển khai chính xác việc duyệt qua các nút được liên kết với việc giải phóng khóa của nút trước và nắm bắt nút tiếp theo. Nhưng với mutex, nó rất dễ thực hiện:

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

Hiện tại, không có lớp nào như vậy trong java.util.concurrent, nhưng bạn có thể tìm thấy triển khai Mutext tại đây Mutex.java . Đối với các thư viện tiêu chuẩn, Semaphore cung cấp tất cả các chức năng này và nhiều hơn nữa.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.