Apakah ada Mutex di Jawa?

110

Apakah ada objek Mutex di java atau cara membuatnya? Saya bertanya karena objek Semaphore yang diinisialisasi dengan 1 izin tidak membantu saya. Pikirkan kasus ini:

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

jika pengecualian terjadi pada perolehan pertama, rilis di blok catch akan meningkatkan izin, dan semaphore tidak lagi menjadi semaphore biner.

Akankah cara yang benar?

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

Akankah kode di atas memastikan bahwa semaphore akan menjadi biner?

Noam Nevo
sumber
Lihat javadoc untuk java.util.concurrent.locks.AbstractQueuedSynchronizer. Ini memiliki contoh bagaimana menulis kelas Mutex. -dbednar
joe
Apakah Anda mengetahui perilaku ini secara empiris? Apakah implementasinya seperti mengeksekusi rilis () pada 1-izin Semaphore menambahkan izin ekstra meskipun saat ini memegang izin lain, benarkah?
Aneh

Jawaban:

112

Lihat halaman ini: http://www.oracle.com/technetwork/articles/javase/index-140767.html

Ini memiliki pola yang sedikit berbeda yang (menurut saya) apa yang Anda cari:

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

Dalam penggunaan ini, Anda hanya menelepon release()setelah berhasilacquire()

payne
sumber
133

Objek apa pun di Java dapat digunakan sebagai kunci menggunakan synchronizedblok. Ini juga akan secara otomatis menangani pelepasan kunci ketika pengecualian terjadi.

Object someObject = ...;

synchronized (someObject) {
  ...
}

Anda dapat membaca lebih lanjut tentang ini di sini: Kunci Intrinsik dan Sinkronisasi

casablanca
sumber
Sangat membantu membeli saya ingin menggunakan semaphore.
Noam Nevo
11
@Noam: cukup bandingkan kode dengan semaphore dan dengan synchronized, Anda akan melihat mana yang lebih mudah dibaca dan tidak terlalu rawan kesalahan.
Vlad
17
Kata kunci tersinkronisasi tidak dapat digunakan jika Anda ingin membuka kunci dengan metode yang berbeda (mis transaction.begin(); transaction.commit().).
Hosam Aly
dan tidak berorientasi objek .. banyak sinkronisasi tingkat rendah
anshulkatta
Juga lihat ke dalam someObject.wait(timeout)dan someObject.notify()saat Anda melihat kode jawaban ini.
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
sumber
5
Dalam hal apa ini lebih unggul daripada solusi yang sudah disediakan? Bagaimana cara memecahkan masalah yang dialami penanya asli?
Martin
@ Martin: "Lock implementations provide more extensive locking operations than can be obtained using synchronized methods and statements.", dari: docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/... ... meskipun Anda ada benarnya. Jawaban Argv tidak menggambarkan atau menjelaskan operasi ini.
FrustratedWithFormsDesigner
3
Ini adalah mutex rekursif yang akan memungkinkan beberapa penguncian ulang dari utas yang sama, yang bisa menjadi masalah. Sebuah mutex dasar yang "benar" (non-rekursif, C ++ - style) hanya akan memungkinkan satu kunci pada satu waktu. Jika Anda mengubah baris menjadi private final ReentrantLock _mutex = ..., Anda dapat menggunakan getHoldCount () untuk mengembalikan jumlah utas yang dikunci ulang. (Anda dapat menerapkan a Conditionuntuk mencegah hal ini. Lihat API .)
EntangledLoops
16

Tidak ada yang secara jelas menyebutkan ini, tetapi pola semacam ini biasanya tidak cocok untuk semaphore. Alasannya adalah bahwa utas apa pun dapat melepaskan semaphore, tetapi Anda biasanya hanya ingin utas pemilik yang awalnya terkunci dapat membuka kunci . Untuk kasus penggunaan ini, di Java, kami biasanya menggunakan ReentrantLocks, yang dapat dibuat seperti ini:

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

private final Lock lock = new ReentrantLock(true);

Dan pola desain penggunaan yang biasa adalah:

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

Berikut adalah contoh dalam kode sumber java di mana Anda dapat melihat pola ini beraksi.

Kunci reentrant memiliki manfaat tambahan untuk mendukung keadilan.

Gunakan semaphore hanya jika Anda membutuhkan semantik non-kepemilikan-rilis.

rubel
sumber
5
Sebenarnya ini harus menjadi satu-satunya jawaban yang benar untuk pertanyaan ini. Penjelasan yang jelas tentang perbedaan antara semaphore dan mutual exclusion lock. Menggunakan semaphore dengan count=1bukanlah kunci pengecualian timbal balik.
Kaihua
3
Senang seseorang menunjukkannya. Untuk akses eksklusif ke mutex sumber daya adalah cara yang harus ditempuh. Semaphore biner bukanlah mutex, semaphore harus digunakan lebih sebagai mekanisme pensinyalan.
Shivam Tripathi
Rubel: Jadi, lockmisalnya, ReentrantLocka mutex? Saya tidak yakin mengapa mutexdan binary semaphoredianggap sebagai entitas yang sama. Semaphoredapat dilepaskan oleh utas apa pun, jadi itu mungkin tidak menjamin penjagaan critical section. Ada pemikiran?
CuriousMind
@Aihua: Saya selaras dengan pemikiran Anda. Jawaban ini membawa perbedaan utama
CuriousMind
6

Saya pikir Anda harus mencoba dengan:

Sedangkan inisialisasi Semaphore:

Semaphore semaphore = new Semaphore(1, true);

Dan di Runnable Implementation

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

} 
catch (Exception e) 
{
// Logging
}
finally
{
   semaphore.release(1);
}
Sashi Kant
sumber
Beginilah cara saya melakukannya, tetapi saya tidak yakin apakah ini cara yang harus dilakukan.
Dilepas
1
Menurut docs.oracle.com/javase/7/docs/api/java/util/concurrent/… "Tidak ada persyaratan bahwa utas yang melepaskan izin harus memperoleh izin itu dengan menelepon memperoleh. Penggunaan semaphore yang benar adalah ditetapkan oleh konvensi pemrograman dalam aplikasi. " Jika gain melontarkan pengecualian, rilis pada akhirnya akan salah melepaskan izin. Contoh lain di utas ini menunjukkan aliran yang benar.
Brent K.
3

Kesalahan dalam posting asli adalah panggilan gain () yang diatur di dalam loop percobaan. Berikut adalah pendekatan yang benar untuk menggunakan semaphore "biner" (Mutex):

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

Untuk memastikan bahwa a Semaphoreadalah biner, Anda hanya perlu memastikan bahwa Anda memasukkan jumlah izin sebagai 1 saat membuat semaphore. The Javadocs memiliki sedikit lebih penjelasan.

Sanjay T. Sharma
sumber
1

Setiap kunci objek sedikit berbeda dari desain Mutex / Semaphore. Misalnya, tidak ada cara untuk mengimplementasikan melintasi node tertaut dengan benar dengan melepaskan kunci node sebelumnya dan menangkap yang berikutnya. Tetapi dengan mutex mudah diimplementasikan:

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

Saat ini, tidak ada kelas seperti itu di java.util.concurrent, tetapi Anda dapat menemukan implementasi Mutext di sini Mutex.java . Adapun pustaka standar, Semaphore menyediakan semua fungsi ini dan banyak lagi.

pogrebniuk
sumber