Java Semaphore และ Mutex คืออะไร – Java Concurrency MultiThread อธิบายด้วย Example
เผยแพร่แล้ว: 2015-03-12Java Concurrency เป็นหัวข้อที่กว้างมาก มีบทช่วยสอนและตัวอย่างหลายร้อยรายการให้ใช้งาน เมื่อก่อนฉันได้เขียนบทช่วยสอนเกี่ยวกับ Run Multiple Threads พร้อมกันใน Java และ Synchronized Blocks ประเภทต่างๆ
ในบทช่วยสอนนี้เราจะพูดถึง:
- คำอธิบายของ Mutex
- คำอธิบายของ Semaphore
- สองตัวอย่างพร้อมรายละเอียด
มาเริ่มกันเลย
Let's keep this in mind
ขณะที่อ่านคำอธิบายด้านล่าง:
- ยกตัวอย่างนักช้อปและลูกค้า
- นักช้อปกำลังยืมแล็ปท็อป
- ลูกค้าสามารถมาและใช้แล็ปท็อปได้ – ลูกค้าต้องการรหัสเพื่อใช้แล็ปท็อป
- หลังการใช้งาน – ลูกค้าสามารถคืนโน้ตบุ๊กให้นักช้อปได้
Mutex คืออะไร (เพียง 1 เธรด):
นักช้อปมีกุญแจสำหรับแล็ปท็อป ลูกค้ารายหนึ่งสามารถมีกุญแจได้ - ยืมแล็ปท็อป - ในขณะนั้น เมื่องานเสร็จสิ้น นักช้อปจะมอบ (ฟรี) กุญแจให้กับลูกค้ารายถัดไปในคิว
Official Definition
:
“โดยทั่วไป Mutex จะใช้เพื่อทำให้เป็นอนุกรมในการเข้าถึงส่วนของ re-entrant code
ที่ cannot be executed concurrently
ได้มากกว่าหนึ่งเธรด ออบเจ็กต์ mutex อนุญาตเพียงหนึ่งเธรดในส่วนที่ถูกควบคุม บังคับให้เธรดอื่นที่พยายามเข้าถึงส่วนนั้นเพื่อรอจนกว่าเธรดแรกจะออกจากส่วนนั้น”
กล่าวอีกนัยหนึ่ง: Mutex = Mutually Exclusive Semaphore
Semaphore คืออะไร (N เธรดที่ระบุ):
สมมติว่าตอนนี้ Shopper มีแล็ปท็อปที่เหมือนกัน 3 เครื่องและปุ่มที่เหมือนกัน 3 ปุ่ม สัญญาณคือจำนวนของ free identical Laptop keys
จำนวนสัญญาณ - จำนวนปุ่ม - ถูกตั้งค่าเป็น 3 เมื่อเริ่มต้น (แล็ปท็อปทั้งสามเครื่องว่าง) จากนั้นค่าการนับจะลดลงเมื่อลูกค้าเข้ามา ถ้าแล็ปท็อปทั้งหมดถูกใช้งาน กล่าวคือไม่มีคีย์ว่างเหลือสำหรับ แล็ปท็อป จำนวนสัญญาณเท่ากับ 0 ตอนนี้ เมื่อลูกค้าคนใดคนหนึ่งส่งคืนแล็ปท็อป สัญญาณจะเพิ่มขึ้นเป็น 1 (รหัสฟรีหนึ่งรหัส) และมอบให้กับลูกค้ารายถัดไปในคิว
Official Definition
: “สัญญาณจะจำกัดจำนวนผู้ใช้ทรัพยากรที่ใช้ร่วมกันพร้อมกันสูงสุดจำนวนสูงสุด เธรดสามารถร้องขอการเข้าถึงทรัพยากร (ลดสัญญาณ) และสามารถส่งสัญญาณว่าพวกเขาใช้ทรัพยากรเสร็จแล้ว (เพิ่มสัญญาณ)”
ต้องอ่านอีกเรื่อง: Lazy Creation of Singleton ThreadSafe Instance
ตัวอย่างที่ 1: (คำอธิบายด้านล่าง)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
package crunchify . com . tutorial ; import java . util . LinkedList ; import java . util . concurrent . Semaphore ; /** * @author Crunchify.com * */ public class CrunchifySemaphoreMutexTutorial { static Object crunchifyLock = new Object ( ) ; static LinkedList <String> crunchifyList = new LinkedList <String> ( ) ; // Semaphore maintains a set of permits. // Each acquire blocks if necessary until a permit is available, and then takes it. // Each release adds a permit, potentially releasing a blocking acquirer. static Semaphore semaphore = new Semaphore ( 0 ) ; static Semaphore mutex = new Semaphore ( 1 ) ; // I'll producing new Integer every time static class CrunchifyProducer extends Thread { public void run ( ) { int counter = 1 ; try { while ( true ) { String threadName = Thread . currentThread ( ) . getName ( ) + counter ++ ; mutex . acquire ( ) ; crunchifyList . add ( threadName ) ; System . out . println ( "Producer is prdoucing new value: " + threadName ) ; mutex . release ( ) ; // release lock semaphore . release ( ) ; Thread . sleep ( 500 ) ; } } catch ( Exception x ) { x . printStackTrace ( ) ; } } } // I'll be consuming Integer every stime static class CrunchifyConsumer extends Thread { String consumerName ; public CrunchifyConsumer ( String name ) { this . consumerName = name ; } public void run ( ) { try { while ( true ) { // acquire lock. Acquires the given number of permits from this semaphore, blocking until all are // available // process stops here until producer releases the lock semaphore . acquire ( ) ; // Acquires a permit from this semaphore, blocking until one is available mutex . acquire ( ) ; String result = "" ; for ( String value : crunchifyList ) { result = value + "," ; } System . out . println ( consumerName + " consumes value: " + result + "crunchifyList.size(): " + crunchifyList . size ( ) + "\n" ) ; mutex . release ( ) ; } } catch ( Exception e ) { e . printStackTrace ( ) ; } } } public static void main ( String [ ] args ) { new CrunchifyProducer ( ) . start ( ) ; new CrunchifyConsumer ( "Crunchify" ) . start ( ) ; new CrunchifyConsumer ( "Google" ) . start ( ) ; new CrunchifyConsumer ( "Yahoo" ) . start ( ) ; } } |
ในบทช่วยสอนด้านบน CrunchifySemaphoreMutexTutorial.java
เมื่อ CrunchifyProducer
เพิ่ม threadName
ให้กับวัตถุ crunchifyList
linkedList มันสามารถส่งสัญญาณสัญญาณ
จากนั้น CrunchifyConsumer
สามารถพยายามรับสัญญาณ ดังนั้นพวกเขาจะรอจนกว่า CrunchifyProducer จะส่งสัญญาณว่ามีการเพิ่ม threadID เมื่อส่งสัญญาณข้อมูลเพิ่มเติม ผู้บริโภครายหนึ่งจะถูกปลุกและจะรู้ว่าสามารถอ่านวัตถุ crunchifyList ได้ สามารถอ่านรายการแล้วกลับไปพยายามหาสัญญาณ
หากในเวลานั้นผู้ผลิตได้เขียนแพ็คเก็ตอื่นที่มีสัญญาณอีกครั้งและผู้บริโภคคนใดคนหนึ่งก็จะอ่านแพ็คเก็ตอื่นต่อไป...
กล่าวอีกนัยหนึ่ง:
1 2 3 |
CrunchifyProducer : Add an object o List - Semaphore . release ( 1 ) CrunchifyConsumer x N ) - Semaphore . acquire ( 1 ) - Read an object from List |

ผลลัพธ์:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Producer is prdoucing new value : Thread - 01 Crunchify consumes value : Thread - 01 , crunchifyList . size ( ) : 1 Producer is prdoucing new value : Thread - 02 Google consumes value : Thread - 02 , crunchifyList . size ( ) : 2 Producer is prdoucing new value : Thread - 03 Yahoo consumes value : Thread - 03 , crunchifyList . size ( ) : 3 Producer is prdoucing new value : Thread - 04 Crunchify consumes value : Thread - 04 , crunchifyList . size ( ) : 4 . . . . . . . . . . . . . . . |
วิธีป้องกันสภาพการแข่งขัน:
What if you have multiple Consumers?
ในบทช่วยสอน Java ด้านบน ผู้บริโภค (ไม่ใช่ผู้ผลิต) ควรล็อกบัฟเฟอร์เมื่ออ่านแพ็กเก็ต (แต่ไม่ใช่เมื่อได้รับสัญญาณ) เพื่อป้องกันสภาวะการแข่งขัน ในตัวอย่างด้านล่าง โปรดิวเซอร์ล็อกรายการด้วยเนื่องจากทุกอย่างอยู่ใน JVM เดียวกัน
ตัวอย่าง-2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
package crunchify . com . tutorial ; import java . util . concurrent . Semaphore ; /** * @author Crunchify.com * */ public class CrunchifyJavaSemaphoreTutorial { private static final int MAX_CONCURRENT_THREADS = 2 ; private final Semaphore crunchifyAdminLOCK = new Semaphore ( MAX_CONCURRENT_THREADS , true ) ; public void crunchifyStartTest ( ) { for ( int i = 0 ; i < 10 ; i ++ ) { CrunchifyPerson person = new CrunchifyPerson ( ) ; person . start ( ) ; } } public class CrunchifyPerson extends Thread { @Override public void run ( ) { try { // Acquire Lock crunchifyAdminLOCK . acquire ( ) ; } catch ( InterruptedException e ) { System . out . println ( "received InterruptedException" ) ; return ; } System . out . println ( "Thread " + this . getId ( ) + " start using Crunchify's car - Acquire()" ) ; try { sleep ( 1000 ) ; } catch ( Exception e ) { } finally { // Release Lock crunchifyAdminLOCK . release ( ) ; } System . out . println ( "Thread " + this . getId ( ) + " stops using Crunchify's car - Release()\n" ) ; } } public static void main ( String [ ] args ) { CrunchifyJavaSemaphoreTutorial test = new CrunchifyJavaSemaphoreTutorial ( ) ; test . crunchifyStartTest ( ) ; } } |
ผลลัพธ์:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
Thread 11 start using Crunchify 's car - Acquire() Thread 10 start using Crunchify' s car - Acquire ( ) Thread 10 stops using Crunchify 's car - Release() Thread 12 start using Crunchify' s car - Acquire ( ) Thread 13 start using Crunchify 's car - Acquire() Thread 11 stops using Crunchify' s car - Release ( ) Thread 13 stops using Crunchify 's car - Release() Thread 15 start using Crunchify' s car - Acquire ( ) Thread 14 start using Crunchify 's car - Acquire() Thread 12 stops using Crunchify' s car - Release ( ) Thread 14 stops using Crunchify 's car - Release() Thread 16 start using Crunchify' s car - Acquire ( ) Thread 15 stops using Crunchify 's car - Release() Thread 17 start using Crunchify' s car - Acquire ( ) Thread 17 stops using Crunchify 's car - Release() Thread 18 start using Crunchify' s car - Acquire ( ) Thread 19 start using Crunchify 's car - Acquire() Thread 16 stops using Crunchify' s car - Release ( ) Thread 18 stops using Crunchify 's car - Release() Thread 19 stops using Crunchify' s car - Release ( ) |