มี Mutex ใน Java หรือไม่?


111

มีวัตถุ 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.AbstractQueuedSynchronizer มันมีตัวอย่างวิธีการเขียนคลาส Mutex -dbednar
joe

คุณพบพฤติกรรมนี้ในเชิงประจักษ์หรือไม่? การใช้งานเช่นการเรียกใช้ release () บน Semaphore 1-permit จะเพิ่มใบอนุญาตพิเศษแม้ว่าจะถือครองอีกใบในขณะนี้จริงหรือ?
แปลก

คำตอบ:


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) {
  ...
}

คุณสามารถอ่านเพิ่มเติมเกี่ยวกับสิ่งนี้ได้ที่นี่: Intrinsic Locks and Synchronization


การซื้อที่มีประโยชน์มากฉันต้องการใช้สัญญาณ
Noam Nevo

11
@Noam: เพียงแค่เปรียบเทียบโค้ดกับเซมาฟอsynchronizedร์คุณจะเห็นว่าอะไรที่อ่านได้ดีกว่าและมีข้อผิดพลาดน้อยกว่า
Vlad

17
ไม่สามารถใช้คีย์เวิร์ดที่ซิงโครไนซ์ได้หากคุณต้องการคลายล็อกด้วยวิธีการอื่น (เช่นtransaction.begin(); transaction.commit())
Hosam Aly

และไม่ใช่เชิงวัตถุ.. มีการซิงโครไนซ์ระดับต่ำมาก
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

@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
นี่คือ mutex แบบเรียกซ้ำซึ่งจะอนุญาตให้ทำการล็อกซ้ำหลายครั้งจากเธรดเดียวกันซึ่งอาจเป็นปัญหาได้ "จริง", mutex พื้นฐาน (ไม่เรียกซ้ำ, C ++ - สไตล์) จะอนุญาตให้ล็อกได้ครั้งละหนึ่งครั้งเท่านั้น หากคุณเปลี่ยนบรรทัดเป็นprivate final ReentrantLock _mutex = ...คุณสามารถใช้getHoldCount ()เพื่อส่งคืนจำนวนการล็อกเธรดซ้ำ (คุณสามารถใช้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 ที่คุณสามารถเห็นรูปแบบนี้ในการทำงาน

การล็อกกลับมีประโยชน์เพิ่มเติมในการสนับสนุนความเป็นธรรม

ใช้ semaphores เฉพาะในกรณีที่คุณต้องการความหมายแบบไม่เป็นเจ้าของ


5
อันที่จริงนี่ควรเป็นคำตอบที่ถูกต้อง (เท่านั้น) สำหรับคำถามนี้ คำอธิบายที่ชัดเจนเกี่ยวกับความแตกต่างระหว่างสัญญาณและการล็อกการยกเว้นซึ่งกันและกัน การใช้สัญญาณร่วมกับcount=1ไม่ใช่การล็อกการยกเว้นซึ่งกันและกัน
Kaihua

3
ดีใจที่มีคนชี้ให้เห็น สำหรับการเข้าถึงแบบเอกสิทธิ์เฉพาะบุคคลไปยัง mutexes ทรัพยากรเป็นวิธีที่จะไป เซมาโฟเรสไบนารีไม่ใช่ mutex ควรใช้เซมาโฟเรสเป็นกลไกการส่งสัญญาณให้มากขึ้น
Shivam Tripathi

รูเบิล: lockเช่นReentrantLockmutex? ผมไม่แน่ใจว่าทำไมmutexและbinary semaphoreจะถูกนำมาบรรจุให้เป็นบุคคลเดียวกัน Semaphoreสามารถได้รับการปล่อยตัวโดยด้ายใด ๆ critical sectionเพื่อให้อาจจะไม่รับประกันการป้องกัน ความคิดใด ๆ ?
CuriousMind

@ ไคฮัว: ฉันสะท้อนความคิดของคุณ คำตอบนี้นำมาซึ่งความแตกต่างที่สำคัญ
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/… "ไม่มีข้อกำหนดว่าเธรดที่เผยแพร่ใบอนุญาตจะต้องได้รับอนุญาตนั้นโดยการโทรได้รับการใช้เซมาฟอร์ที่ถูกต้องคือ ก่อตั้งขึ้นโดยหลักการเขียนโปรแกรมในแอปพลิเคชัน " หากการได้มามีข้อยกเว้นการเปิดตัวในที่สุดจะออกใบอนุญาตอย่างไม่ถูกต้อง ตัวอย่างอื่น ๆ ในชุดข้อความนี้แสดงขั้นตอนที่ถูกต้อง
Brent K.

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 มีฟังก์ชันทั้งหมดนี้และอื่น ๆ อีกมากมาย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.