สวัสดี! เมื่อคุณศึกษามัลติเธรดบน CodeGym คุณมักพบแนวคิดของ "mutex" และ "monitor" บอกได้ไหมว่าต่างกันอย่างไร? :) ถ้าใช่ ทำได้ดีมาก! ถ้าไม่ (นี่เป็นเรื่องธรรมดาที่สุด) ก็ไม่แปลกใจเลย "Mutex" และ "monitor" เป็นแนวคิดที่เกี่ยวข้องกัน นอกจากนี้ เมื่อคุณอ่านบทเรียนและดูวิดีโอเกี่ยวกับมัลติเธรดบนเว็บไซต์อื่น คุณจะพบกับแนวคิดอื่นที่คล้ายคลึงกัน นั่นคือ "สัญญาณ" นอกจากนี้ยังมีฟังก์ชันที่คล้ายกันมากกับมอนิเตอร์และมิวเท็กซ์ นั่นเป็นเหตุผลที่เราจะตรวจสอบสามคำนี้ เราจะดูตัวอย่างบางส่วนและทำความเข้าใจอย่างถ่องแท้ว่าแนวคิดเหล่านี้แตกต่างกันอย่างไร :)
กล่าวอีกนัยหนึ่งคือ ครั้งละหนึ่งเธรดเท่านั้นที่สามารถทำงานกับทรัพยากรที่ใช้ร่วมกันได้ ความพยายามของเธรดอื่น (บุคคล) เพื่อเข้าถึงทรัพยากรที่ถูกครอบครองจะล้มเหลว mutex มีคุณสมบัติที่สำคัญหลายประการ อย่างแรกเป็นไปได้เพียงสองสถานะ: "ปลดล็อค" และ "ล็อค" สิ่งนี้ช่วยให้เราเข้าใจวิธีการทำงาน: คุณสามารถวาดเส้นขนานกับตัวแปรบูลีน (จริง/เท็จ) หรือเลขฐานสอง (0/1) รัฐไม่สามารถควบคุมได้โดยตรง Java ไม่มีกลไกที่จะให้คุณรับวัตถุ รับ mutex และกำหนดสถานะที่ต้องการอย่างชัดเจน กล่าวอีกนัยหนึ่ง คุณไม่สามารถทำสิ่งต่อไปนี้:
เซมาฟอร์ใน Java แสดงโดยคลาสเซมาฟอร์ เมื่อสร้างวัตถุสัญญาณ เราสามารถใช้ตัวสร้างต่อไปนี้:
เพื่อให้เข้าใจได้ง่าย เราจะลดความซับซ้อนลงเล็กน้อย ลองนึกภาพว่าเรามีนักปรัชญา 5 คนที่ต้องกินข้าวกลางวัน นอกจากนี้เรายังมีโต๊ะหนึ่งตัวที่สามารถรองรับได้พร้อมกันไม่เกินสองคน หน้าที่ของเราคือเลี้ยงนักปรัชญาทุกคน ไม่มีใครควรหิวและไม่มีใคร "ปิดกั้น" ซึ่งกันและกันเมื่อพยายามนั่งลงที่โต๊ะ (เราต้องหลีกเลี่ยงการหยุดชะงัก) นี่คือลักษณะของชั้นเรียนนักปรัชญาของเรา:
ข้อแตกต่างเพียงอย่างเดียวคือ mutex ของออบเจ็กต์สามารถรับได้ทีละเธรดเท่านั้น ในขณะที่ในกรณีของสัญญาณซึ่งใช้ตัวนับเธรด เธรดจำนวนมากสามารถเข้าถึงทรัพยากรพร้อมกันได้ นี่ไม่ใช่แค่เรื่องบังเอิญ :) จริงๆ แล้ว mutex เป็นสัญญาณโดยมีค่าเท่ากับ 1 กล่าวคือเป็นสัญญาณที่สามารถรองรับเธรดเดียวได้ เรียกอีกอย่างว่า "สัญญาณไบนารี" เนื่องจากตัวนับสามารถมีได้เพียง 2 ค่าเท่านั้น — 1 ("ปลดล็อค") และ 0 ("ล็อค") แค่นั้นแหละ! อย่างที่คุณเห็น มันไม่ได้ทำให้สับสนแต่อย่างใด :) ตอนนี้ ถ้าคุณต้องการศึกษามัลติเธรดโดยละเอียดบนอินเทอร์เน็ต คุณจะสำรวจแนวคิดเหล่านี้ได้ง่ายขึ้นเล็กน้อย แล้วพบกันในบทเรียนต่อไป!
มิวเท็กซ์
mutex (หรือล็อค) เป็นกลไกพิเศษสำหรับการซิงโครไนซ์เธรด หนึ่ง "แนบ" กับทุกอ็อบเจ็กต์ใน Java — คุณรู้อยู่แล้วว่า :) ไม่สำคัญว่าคุณจะใช้คลาสมาตรฐานหรือสร้างคลาสของคุณเอง เช่นCat and Dog : all object of all class have a mutex คำว่า "mutex" มาจาก "MUTual EXclusion" ซึ่งอธิบายจุดประสงค์ของมันได้อย่างสมบูรณ์แบบ ดังที่เราได้กล่าวไว้ในบทเรียนก่อนหน้านี้mutex ช่วยให้มั่นใจได้ว่ามีเธรดเข้าถึงวัตถุได้ครั้งละหนึ่งเธรดเท่านั้น ตัวอย่างในชีวิตจริงยอดนิยมของมิวเท็กซ์เกี่ยวข้องกับห้องสุขา เมื่อบุคคลเข้าไปในฉากกั้นห้องน้ำ เขาจะล็อกประตูจากด้านใน ห้องสุขาเป็นเหมือนวัตถุที่สามารถเข้าถึงได้ด้วยด้ายหลายเส้น ล็อคประตูพาร์ทิชันเป็นเหมือน mutex และผู้คนที่อยู่ข้างนอกก็เป็นตัวแทนของเธรด ตัวล็อคประตูเป็นแบบ mutex ของชักโครก ทำให้มั่นใจได้ว่าจะมีเพียงคนเดียวเท่านั้นที่สามารถเข้าไปข้างในได้
Object myObject = new Object();
Mutex mutex = myObject.getMutex();
mutex.free();
ซึ่งหมายความว่าคุณไม่สามารถปล่อย mutex ของวัตถุได้ เฉพาะเครื่อง Java เท่านั้นที่เข้าถึงได้โดยตรง โปรแกรมเมอร์ทำงานกับ mutexes ผ่านเครื่องมือของภาษา
เฝ้าสังเกต
จอภาพเป็น "โครงสร้างเสริม" เพิ่มเติมเหนือ mutex ในความเป็นจริงแล้วจอภาพคือโค้ดก้อนหนึ่งที่ "มองไม่เห็น" สำหรับโปรแกรมเมอร์ เมื่อเราพูดถึง mutexes ก่อนหน้านี้ เราได้ยกตัวอย่างง่ายๆ:
public class Main {
private Object obj = new Object();
public void doSomething() {
// ...some logic, available for all threads
synchronized (obj) {
// Logic available to just one thread at a time
}
}
}
ในบล็อกรหัสที่ทำเครื่องหมายด้วย คีย์เวิร์ด ที่ซิงโครไนซ์ จะมีการรับ mutex ของวัตถุobj ของเรา เยี่ยมมาก เราสามารถรับล็อคได้ แต่ "การป้องกัน" มีให้อย่างไร? เมื่อเราเห็นคำว่าsynchronizedอะไรที่ทำให้เธรดอื่นไม่สามารถเข้าสู่บล็อกได้ การป้องกันมาจากจอภาพ! คอมไพเลอร์แปลง คีย์เวิร์ด ที่ซิง โครไนซ์ เป็นโค้ดพิเศษหลายส่วน กลับมาที่ตัวอย่างของเราอีกครั้งด้วยเมธอดdoSomething() เราจะเพิ่ม:
public class Main {
private Object obj = new Object();
public void doSomething() {
// ...some logic, available for all threads
// Logic available to just one thread at a time
synchronized (obj) {
/* Do important work that requires that the object
be accessed by only one thread */
obj.someImportantMethod();
}
}
}
นี่คือสิ่งที่เกิดขึ้น "ภายใต้ประทุน" หลังจากคอมไพเลอร์แปลงรหัสนี้:
public class Main {
private Object obj = new Object();
public void doSomething() throws InterruptedException {
// ...some logic, available for all threads
// Logic available to just one thread at a time:
/* as long as the object's mutex is busy,
all the other threads (except the one that acquired it) are put to sleep */
while (obj.getMutex().isBusy()) {
Thread.sleep(1);
}
// Mark the object's mutex as busy
obj.getMutex().isBusy() = true;
/* Do important work that requires that the object
be accessed by only one thread */
obj.someImportantMethod();
// Free the object's mutex
obj.getMutex().isBusy() = false;
}
}
แน่นอนว่านี่ไม่ใช่ตัวอย่างจริง ที่นี่ เราใช้โค้ดที่เหมือน Java เพื่ออธิบายสิ่งที่เกิดขึ้นภายในเครื่อง Java ที่กล่าวว่ารหัสหลอกนี้ให้ความเข้าใจที่ยอดเยี่ยมเกี่ยวกับสิ่งที่เกิดขึ้นจริงกับวัตถุและเธรดภายในบล็อกที่ซิงโครไนซ์และวิธีที่คอมไพเลอร์แปลงคำสำคัญนี้เป็นคำสั่งหลายคำสั่งที่โปรแกรมเมอร์ "มองไม่เห็น" โดยทั่วไปJava ใช้ คีย์เวิร์ด ที่ซิงโครไนซ์เพื่อแสดงมอนิเตอร์ รหัสทั้งหมดที่ปรากฏแทนคีย์เวิร์ดที่ซิงโครไนซ์ในตัวอย่างสุดท้ายคือมอนิเตอร์
สัญญาณ
อีกคำหนึ่งที่คุณจะพบในการศึกษาส่วนตัวเกี่ยวกับมัลติเธรดคือ "semaphore" มาดูกันว่านี่คืออะไรและแตกต่างจากจอภาพและ mutex อย่างไร สัญญาณเป็นเครื่องมือสำหรับการซิงโครไนซ์การเข้าถึงทรัพยากรบางอย่าง คุณสมบัติที่โดดเด่นคือใช้ตัวนับเพื่อสร้างกลไกการซิงโครไนซ์ ตัวนับบอกเราว่ามีกี่เธรดที่สามารถเข้าถึงทรัพยากรที่ใช้ร่วมกันได้พร้อมกัน
Semaphore(int permits)
Semaphore(int permits, boolean fair)
เราส่งสิ่งต่อไปนี้ไปยังตัวสร้าง:
- บูลีนแฟร์ — กำหนดลำดับที่เธรดจะได้รับการเข้าถึง หากความยุติธรรมเป็นจริง การเข้าถึงจะได้รับสิทธิ์สำหรับเธรดที่รออยู่ตามลำดับที่พวกเขาร้องขอ หากเป็นเท็จ ลำดับจะถูกกำหนดโดยตัวกำหนดตารางเวลาของเธรด

class Philosopher extends Thread {
private Semaphore sem;
// Did the philosopher eat?
private boolean full = false;
private String name;
Philosopher(Semaphore sem, String name) {
this.sem=sem;
this.name=name;
}
public void run()
{
try
{
// If the philosopher has not eaten
if (!full) {
// Ask the semaphore for permission to run
sem.acquire();
System.out.println(name + " takes a seat at the table");
// The philosopher eats
sleep(300);
full = true;
System.out.println(name + " has eaten! He leaves the table");
sem.release();
// The philosopher leaves, making room for others
sleep(300);
}
}
catch(InterruptedException e) {
System.out.println("Something went wrong!");
}
}
}
และนี่คือรหัสเพื่อเรียกใช้โปรแกรมของเรา:
public class Main {
public static void main(String[] args) {
Semaphore sem = new Semaphore(2);
new Philosopher(sem, "Socrates").start();
new Philosopher(sem,"Plato").start();
new Philosopher(sem,"Aristotle").start();
new Philosopher(sem, "Thales").start();
new Philosopher(sem, "Pythagoras").start();
}
}
เราสร้างสัญญาณที่มีการตั้งค่าเคาน์เตอร์เป็น 2 เพื่อให้เป็นไปตามเงื่อนไข: นักปรัชญาสองคนเท่านั้นที่สามารถกินได้ในเวลาเดียวกัน นั่นคือ มีเพียงสองเธรดเท่านั้นที่สามารถรันพร้อมกันได้ เนื่องจากคลาสPhilosopher ของเราสืบทอด เธรด ! วิธี การรับ ()และปล่อย ()ของ คลาส Semaphoreควบคุมตัวนับการเข้าถึง วิธีการรับ () ขอสัญญาณสำหรับการเข้าถึงทรัพยากร ถ้าตัวนับคือ >0 แสดงว่าให้สิทธิ์เข้าถึงและตัวนับจะลดลง 1 ตัวrelease()เมธอด "ปล่อย" การเข้าถึงที่ได้รับก่อนหน้านี้ ส่งคืนไปยังตัวนับ (เพิ่มตัวนับการเข้าถึงของสัญญาณโดย 1) เราได้อะไรเมื่อเรารันโปรแกรม? ปัญหาได้รับการแก้ไขหรือไม่? นักปรัชญาของเราจะไม่ต่อสู้ในขณะที่พวกเขารอถึงตาของพวกเขาหรือ? :) นี่คือเอาต์พุตคอนโซลที่เราได้รับ:
Socrates takes a seat at the table
Plato takes a seat at the table
Socrates has eaten! He leaves the table
Plato has eaten! He leaves the table
Aristotle takes a seat at the table
Pythagoras takes a seat at the table
Aristotle has eaten! He leaves the table
Pythagoras has eaten! He leaves the table
Thales takes a seat at the table
Thales has eaten! He leaves the table
เราทำได้! และแม้ว่าทาเลสต้องรับประทานอาหารคนเดียว ฉันไม่คิดว่าเราได้ทำให้เขาขุ่นเคืองใจ :) คุณอาจสังเกตเห็นความคล้ายคลึงกันบางอย่างระหว่าง mutex และ semaphore แท้จริงแล้ว พวกเขามีภารกิจเดียวกัน นั่นคือการซิงโครไนซ์การเข้าถึงทรัพยากรบางอย่าง 
GO TO FULL VERSION