CodeGym /จาวาบล็อก /สุ่ม /ความแตกต่างระหว่าง Mutex, Monitor และ Semaphore
John Squirrels
ระดับ
San Francisco

ความแตกต่างระหว่าง Mutex, Monitor และ Semaphore

เผยแพร่ในกลุ่ม
สวัสดี! เมื่อคุณศึกษามัลติเธรดบน CodeGym คุณมักพบแนวคิดของ "mutex" และ "monitor" บอกได้ไหมว่าต่างกันอย่างไร? :) ถ้าใช่ ทำได้ดีมาก! ถ้าไม่ (นี่เป็นเรื่องธรรมดาที่สุด) ก็ไม่แปลกใจเลย "Mutex" และ "monitor" เป็นแนวคิดที่เกี่ยวข้องกัน นอกจากนี้ เมื่อคุณอ่านบทเรียนและดูวิดีโอเกี่ยวกับมัลติเธรดบนเว็บไซต์อื่น คุณจะพบกับแนวคิดอื่นที่คล้ายคลึงกัน นั่นคือ "สัญญาณ" นอกจากนี้ยังมีฟังก์ชันที่คล้ายกันมากกับมอนิเตอร์และมิวเท็กซ์ นั่นเป็นเหตุผลที่เราจะตรวจสอบสามคำนี้ เราจะดูตัวอย่างบางส่วนและทำความเข้าใจอย่างถ่องแท้ว่าแนวคิดเหล่านี้แตกต่างกันอย่างไร :)

มิวเท็กซ์

mutex (หรือล็อค) เป็นกลไกพิเศษสำหรับการซิงโครไนซ์เธรด หนึ่ง "แนบ" กับทุกอ็อบเจ็กต์ใน Java — คุณรู้อยู่แล้วว่า :) ไม่สำคัญว่าคุณจะใช้คลาสมาตรฐานหรือสร้างคลาสของคุณเอง เช่นCat and Dog : all object of all class have a mutex คำว่า "mutex" มาจาก "MUTual EXclusion" ซึ่งอธิบายจุดประสงค์ของมันได้อย่างสมบูรณ์แบบ ดังที่เราได้กล่าวไว้ในบทเรียนก่อนหน้านี้mutex ช่วยให้มั่นใจได้ว่ามีเธรดเข้าถึงวัตถุได้ครั้งละหนึ่งเธรดเท่านั้น ตัวอย่างในชีวิตจริงยอดนิยมของมิวเท็กซ์เกี่ยวข้องกับห้องสุขา เมื่อบุคคลเข้าไปในฉากกั้นห้องน้ำ เขาจะล็อกประตูจากด้านใน ห้องสุขาเป็นเหมือนวัตถุที่สามารถเข้าถึงได้ด้วยด้ายหลายเส้น ล็อคประตูพาร์ทิชันเป็นเหมือน mutex และผู้คนที่อยู่ข้างนอกก็เป็นตัวแทนของเธรด ตัวล็อคประตูเป็นแบบ mutex ของชักโครก ทำให้มั่นใจได้ว่าจะมีเพียงคนเดียวเท่านั้นที่สามารถเข้าไปข้างในได้ mutex, จอภาพ และ semaphore ต่างกันอย่างไร  - 2กล่าวอีกนัยหนึ่งคือ ครั้งละหนึ่งเธรดเท่านั้นที่สามารถทำงานกับทรัพยากรที่ใช้ร่วมกันได้ ความพยายามของเธรดอื่น (บุคคล) เพื่อเข้าถึงทรัพยากรที่ถูกครอบครองจะล้มเหลว mutex มีคุณสมบัติที่สำคัญหลายประการ อย่างแรกเป็นไปได้เพียงสองสถานะ: "ปลดล็อค" และ "ล็อค" สิ่งนี้ช่วยให้เราเข้าใจวิธีการทำงาน: คุณสามารถวาดเส้นขนานกับตัวแปรบูลีน (จริง/เท็จ) หรือเลขฐานสอง (0/1) รัฐไม่สามารถควบคุมได้โดยตรง Java ไม่มีกลไกที่จะให้คุณรับวัตถุ รับ 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 อย่างไร สัญญาณเป็นเครื่องมือสำหรับการซิงโครไนซ์การเข้าถึงทรัพยากรบางอย่าง คุณสมบัติที่โดดเด่นคือใช้ตัวนับเพื่อสร้างกลไกการซิงโครไนซ์ ตัวนับบอกเราว่ามีกี่เธรดที่สามารถเข้าถึงทรัพยากรที่ใช้ร่วมกันได้พร้อมกัน mutex, จอภาพ และ semaphore ต่างกันอย่างไร  - 3เซมาฟอร์ใน Java แสดงโดยคลาสเซมาฟอร์ เมื่อสร้างวัตถุสัญญาณ เราสามารถใช้ตัวสร้างต่อไปนี้:

Semaphore(int permits)
Semaphore(int permits, boolean fair)
เราส่งสิ่งต่อไปนี้ไปยังตัวสร้าง:
    int อนุญาต — ค่าเริ่มต้นและค่าสูงสุดของตัวนับ กล่าวอีกนัยหนึ่ง พารามิเตอร์นี้กำหนดจำนวนเธรดที่สามารถเข้าถึงรีซอร์สที่ใช้ร่วมกันได้พร้อมกัน
  • บูลีนแฟร์ — กำหนดลำดับที่เธรดจะได้รับการเข้าถึง หากความยุติธรรมเป็นจริง การเข้าถึงจะได้รับสิทธิ์สำหรับเธรดที่รออยู่ตามลำดับที่พวกเขาร้องขอ หากเป็นเท็จ ลำดับจะถูกกำหนดโดยตัวกำหนดตารางเวลาของเธรด
ตัวอย่างคลาสสิกของการใช้สัญญาณคือปัญหานักปรัชญาการรับประทานอาหาร mutex, จอภาพ และ semaphore ต่างกันอย่างไร  - 4เพื่อให้เข้าใจได้ง่าย เราจะลดความซับซ้อนลงเล็กน้อย ลองนึกภาพว่าเรามีนักปรัชญา 5 คนที่ต้องกินข้าวกลางวัน นอกจากนี้เรายังมีโต๊ะหนึ่งตัวที่สามารถรองรับได้พร้อมกันไม่เกินสองคน หน้าที่ของเราคือเลี้ยงนักปรัชญาทุกคน ไม่มีใครควรหิวและไม่มีใคร "ปิดกั้น" ซึ่งกันและกันเมื่อพยายามนั่งลงที่โต๊ะ (เราต้องหลีกเลี่ยงการหยุดชะงัก) นี่คือลักษณะของชั้นเรียนนักปรัชญาของเรา:

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 แท้จริงแล้ว พวกเขามีภารกิจเดียวกัน นั่นคือการซิงโครไนซ์การเข้าถึงทรัพยากรบางอย่าง mutex, จอภาพ และ semaphore ต่างกันอย่างไร  - 5ข้อแตกต่างเพียงอย่างเดียวคือ mutex ของออบเจ็กต์สามารถรับได้ทีละเธรดเท่านั้น ในขณะที่ในกรณีของสัญญาณซึ่งใช้ตัวนับเธรด เธรดจำนวนมากสามารถเข้าถึงทรัพยากรพร้อมกันได้ นี่ไม่ใช่แค่เรื่องบังเอิญ :) จริงๆ แล้ว mutex เป็นสัญญาณโดยมีค่าเท่ากับ 1 กล่าวคือเป็นสัญญาณที่สามารถรองรับเธรดเดียวได้ เรียกอีกอย่างว่า "สัญญาณไบนารี" เนื่องจากตัวนับสามารถมีได้เพียง 2 ค่าเท่านั้น — 1 ("ปลดล็อค") และ 0 ("ล็อค") แค่นั้นแหละ! อย่างที่คุณเห็น มันไม่ได้ทำให้สับสนแต่อย่างใด :) ตอนนี้ ถ้าคุณต้องการศึกษามัลติเธรดโดยละเอียดบนอินเทอร์เน็ต คุณจะสำรวจแนวคิดเหล่านี้ได้ง่ายขึ้นเล็กน้อย แล้วพบกันในบทเรียนต่อไป!
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION