JavaRush /จาวาบล็อก /Random-TH /อะไรคือความแตกต่างระหว่าง mutex, มอนิเตอร์ และเซมาฟอร์

อะไรคือความแตกต่างระหว่าง mutex, มอนิเตอร์ และเซมาฟอร์

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

มูเท็กซ์

mutex เป็นอ็อบเจ็กต์พิเศษสำหรับการซิงโครไนซ์เธรด มัน “แนบ” กับทุกอ็อบเจ็กต์ใน Java - คุณรู้อยู่แล้วว่า :) มันไม่สำคัญว่าคุณจะใช้คลาสมาตรฐานหรือสร้างคลาสของคุณเอง เช่นCatและDog: ออบเจ็กต์ ทั้งหมดของทุกคลาสจะมี mutex ชื่อ "mutex" มาจากภาษาอังกฤษ "MUTual EXclusion" - "mutual exclusion" และสิ่งนี้สะท้อนถึงวัตถุประสงค์ได้อย่างสมบูรณ์แบบ ดังที่เราได้กล่าวไว้ในการบรรยายครั้งก่อนๆหน้าที่ของ mutex คือการจัดหากลไกดังกล่าวเพื่อให้มีเธรดเดียวเท่านั้นที่สามารถเข้าถึงอ็อบเจ็กต์ได้ในช่วงเวลาหนึ่ง คำเปรียบเทียบยอดนิยมสำหรับ mutex ในชีวิตจริงคือ "ตัวอย่างห้องน้ำ" เมื่อมีคนเข้าห้องน้ำเขาจะล็อคประตูจากด้านใน ห้องน้ำทำหน้าที่เป็นวัตถุที่สามารถเข้าถึงได้โดยใช้หลายเธรด ล็อคประตูห้องน้ำเป็นบทบาทของ mutex และคิวของคนภายนอกเป็นบทบาทของด้าย ตัวล็อคที่ประตูเป็นแบบปิดโถส้วม: ช่วยให้มั่นใจว่าสามารถเข้าไปข้างในได้ครั้งละหนึ่งคนเท่านั้น อะไรคือความแตกต่างระหว่าง mutex, จอภาพ และเซมาฟอร์ - 2กล่าวอีกนัยหนึ่ง ครั้งละหนึ่งเธรดเท่านั้นที่สามารถทำงานกับทรัพยากรที่ใช้ร่วมกันได้ ความพยายามของเธรดอื่น (บุคคล) ในการเข้าถึงทรัพยากรที่ถูกครอบครองจะล้มเหลว mutex มีคุณสมบัติที่สำคัญหลายประการ ประการแรกเป็นไปได้เพียงสองรัฐเท่านั้น - "ว่าง" และ "ไม่ว่าง" ซึ่งช่วยให้เข้าใจวิธีการทำงานได้ง่ายขึ้น: สามารถวาดแนวขนานด้วยตัวแปรบูลีนจริง/เท็จหรือระบบเลขฐานสอง 1/0 ประการที่สองรัฐไม่สามารถควบคุมได้โดยตรง ไม่มีกลไกใน Java ที่จะอนุญาตให้คุณรับอ็อบเจ็กต์ รับ mutex และกำหนดสถานะที่ต้องการให้กับอ็อบเจ็กต์ได้อย่างชัดเจน กล่าวอีกนัยหนึ่ง คุณไม่สามารถดำเนินการบางอย่าง เช่น:
Object myObject = new Object();
Mutex mutex = myObject.getMutex();
mutex.free();
ดังนั้นจึงไม่สามารถปล่อย mutex ของอ็อบเจ็กต์ได้ เฉพาะเครื่อง Java เท่านั้นที่สามารถเข้าถึงได้โดยตรง โปรแกรมเมอร์ทำงานกับ mutexes โดยใช้เครื่องมือภาษา

เฝ้าสังเกต

จอภาพเป็น “ส่วนเสริม” เพิ่มเติมสำหรับ mutex ในความเป็นจริง จอภาพเป็นเพียงส่วนหนึ่งของโค้ด ที่"มองไม่เห็น" สำหรับโปรแกรมเมอร์ เมื่อพูดถึง mutex ก่อนหน้านี้ เราได้ยกตัวอย่างง่ายๆ:
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       //...some logic available to all threads

       synchronized (obj) {

           //logic that is only available to one thread at a time
       }
   }
}
ในบล็อกของโค้ดที่ทำเครื่องหมายด้วยคำว่าmutex synchronizedของอ็อบเจ็กต์ของเราจะถูกบันทึก objโอเค การจับกุมเกิดขึ้น แต่ "กลไกการป้องกัน" บรรลุผลสำเร็จได้อย่างไร? synchronizedเหตุใด กระทู้อื่นจึงไม่สามารถเข้าไปในบล็อกได้เมื่อเห็นคำใดคำ หนึ่ง มันคือจอภาพที่สร้างกลไกการป้องกัน! คอมไพเลอร์จะแปลงคำดังกล่าวsynchronizedเป็นโค้ดพิเศษหลายชิ้น กลับมาที่ตัวอย่างของเราอีกครั้งพร้อมกับวิธีการdoSomething()และเพิ่มเข้าไป:
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       //...some logic available to all threads

       //logic that is only available to one thread at a time
       synchronized (obj) {

           /*выполнить важную работу, при которой доступ к an objectу
           должен быть только у одного потока*/
           obj.someImportantMethod();
       }
   }
}
นี่คือสิ่งที่จะเกิดขึ้น "ภายใต้ประทุน" ของโปรแกรมของเราหลังจากที่คอมไพเลอร์แปลงโค้ดนี้:
public class Main {

   private Object obj = new Object();

   public void doSomething() throws InterruptedException {

       //...some logic available to all threads

       //логика, которая одновременно доступна только для одного потока:

       /*до тех пор, пока мьютекс an object занят -
       любой другой поток (кроме того, который его захватил), спит*/
       while (obj.getMutex().isBusy()) {
           Thread.sleep(1);
       }

       //пометить мьютекс an object How занятый
       obj.getMutex().isBusy() = true;

       /*выполнить важную работу, при которой доступ к an objectу
       должен быть только у одного потока*/
       obj.someImportantMethod();

       //освободить мьютекс an object
       obj.getMutex().isBusy() = false;
   }
}
ตัวอย่างนี้แน่นอนว่าไม่มีอยู่จริง ที่นี่ เมื่อใช้โค้ดที่คล้ายกับ Java เราพยายามสะท้อนถึงสิ่งที่เกิดขึ้นในขณะนี้ภายในเครื่อง Java อย่างไรก็ตาม รหัสเทียมนี้ให้ความเข้าใจที่ดีถึงสิ่งที่เกิดขึ้นจริงกับวัตถุและเธรดภายในบล็อกsynchronizedและวิธีที่คอมไพเลอร์แปลงคำนี้เป็นคำสั่งต่าง ๆ ที่โปรแกรมเมอร์ "มองไม่เห็น" โดยพื้นฐานแล้วจอภาพใน Java จะแสดงโดยใช้คำว่าsynchronized . รหัสทั้งหมดที่ปรากฏแทนคำsynchronizedในตัวอย่างสุดท้ายคือจอภาพ

สัญญาณ

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

  • boolean fair- เพื่อสร้างลำดับที่เธรดจะได้รับการเข้าถึง ถ้าfair= trueให้สิทธิ์การเข้าถึงแก่เธรดที่รอตามลำดับที่พวกเขาร้องขอ หากเป็นเท็จลำดับจะถูกกำหนดโดยตัวกำหนดเวลาเธรด

ตัวอย่างคลาสสิกของการใช้เซมาฟอร์คือปัญหา ของนักปรัชญาในการรับประทานอาหารกลางวัน
อะไรคือความแตกต่างระหว่าง mutex, จอภาพ และเซมาฟอร์ - 4
เราจะลดความซับซ้อนของเงื่อนไขลงเล็กน้อยเพื่อความเข้าใจที่ดีขึ้น ลองนึกภาพเรามีนักปรัชญา 5 คนที่ต้องการอาหารกลางวัน ในเวลาเดียวกัน เรามีโต๊ะหนึ่งตัว และสามารถรองรับคนได้ไม่เกินสองคนในเวลาเดียวกัน หน้าที่ของเราคือการเลี้ยงนักปรัชญาทุกคน ทั้งคู่ไม่ควรหิว และไม่ควร “ขวาง” กันเมื่อพยายามนั่งลงที่โต๊ะ (เราต้องหลีกเลี่ยงการหยุดชะงัก) ชั้นเรียนนักปรัชญาของเราจะมีลักษณะดังนี้:
class Philosopher extends Thread {

   private Semaphore sem;

   // поел ли философ
   private boolean full = false;

   private String name;

   Philosopher(Semaphore sem, String name) {
       this.sem=sem;
       this.name=name;
   }

   public void run()
   {
       try
       {
           // если философ еще не ел
           if (!full) {
               //Запрашиваем у семафора разрешение на выполнение
               sem.acquire();
               System.out.println (name + " садится за стол");

               // философ ест
               sleep(300);
               full = true;

               System.out.println (name + " поел! Он выходит из-за стола");
               sem.release();

               // философ ушел, освободив место другим
               sleep(300);
           }
       }
       catch(InterruptedException e) {
           System.out.println ("What-то пошло не так!");
       }
   }
}
และนี่คือโค้ดสำหรับรันโปรแกรมของเรา:
public class Main {

   public static void main(String[] args) {

       Semaphore sem = new Semaphore(2);
       new Philosopher(sem,"Сократ").start();
       new Philosopher(sem,"Платон").start();
       new Philosopher(sem,"Аристотель").start();
       new Philosopher(sem,"Фалес").start();
       new Philosopher(sem,"Пифагор").start();
   }
}
เราสร้างเซมาฟอร์โดยนับ 2 เพื่อตอบสนองเงื่อนไขที่นักปรัชญาเพียงสองคนเท่านั้นที่สามารถรับประทานอาหารพร้อมกันได้ นั่นคือมีเพียงสองเธรดเท่านั้นที่สามารถทำงานพร้อมกันได้ เนื่องจากคลาสของเราPhilosopherสืบทอดมาจากThread! คลาสacquire()และวิธีการควบคุมตัวนับการอนุญาต วิธีการร้องขอสิทธิ์ในการเข้าถึงทรัพยากรจากเซมาฟอร์ หากตัวนับ > 0 สิทธิ์จะได้รับและตัวนับจะลดลง 1 วิธีการ"ปล่อย" สิทธิ์ที่ได้รับก่อนหน้านี้และส่งกลับไปยังตัวนับ (เพิ่มตัวนับการให้สิทธิ์ของเซมาฟอร์ด้วย 1) เราจะได้อะไรเมื่อเรารันโปรแกรม? ปัญหาได้รับการแก้ไขแล้วหรือยังนักปรัชญาของเราจะต่อสู้ในขณะที่รอถึงตาพวกเขาหรือไม่? :) นี่คือเอาต์พุตคอนโซลที่เราได้รับ: โสกราตีสนั่งลงที่โต๊ะ เพลโตนั่งลงที่โต๊ะ ที่โสกราตีสกิน! เขาออกจากโต๊ะ เพลโตกินแล้ว! เขาออกจากโต๊ะที่ อริสโตเติลนั่งลงที่โต๊ะที่พีธากอรัสนั่งลงที่โต๊ะที่ อริสโตเติลกิน! เขาออกจากโต๊ะที่พีทาโกรัสกินเข้าไปแล้ว! เขาออกจากโต๊ะ ที่ทาเลสนั่งลงที่โต๊ะ ที่ทาเลสกินเข้าไป! เขาออกจากโต๊ะแล้ว เราสำเร็จแล้ว! และแม้ว่าทาเลสจะต้องรับประทานอาหารคนเดียว ฉันคิดว่าเขาไม่ได้โกรธเรา :) คุณอาจสังเกตเห็นความคล้ายคลึงกันบางอย่างระหว่างมิวเท็กซ์และเซมาฟอร์ โดยทั่วไปมีวัตถุประสงค์เดียวกัน: เพื่อซิงโครไนซ์การเข้าถึงทรัพยากรบางอย่าง ข้อแตกต่างเพียงอย่างเดียวคือ mutex ของอ็อบเจ็กต์สามารถรับได้ครั้งละหนึ่งเธรดเท่านั้น ในขณะที่ในกรณีของเซมาฟอร์ จะใช้ตัวนับเธรด และหลายเธรดสามารถเข้าถึงทรัพยากรได้ในคราวเดียว และ นี่ไม่ใช่แค่ความคล้ายคลึงกันโดยบังเอิญ :) อันที่จริงmutex นั้นเป็นสัญญาณแบบจุดเดียว นั่นคือมันเป็นเซมาฟอร์ที่เริ่มตั้งตัวนับเป็น 1 เรียกอีกอย่างว่า "เซมาฟอร์ไบนารี" เพราะตัวนับสามารถมีค่าได้ 2 ค่าเท่านั้น - 1 (“ว่าง”) และ 0 (“ไม่ว่าง”) นั่นคือทั้งหมด! อย่างที่คุณเห็นทุกอย่างไม่สับสนมากนัก :) หากคุณต้องการศึกษาหัวข้อมัลติเธรดบนอินเทอร์เน็ตโดยละเอียดมากขึ้น คุณจะนำทางแนวคิดได้ง่ายขึ้นเล็กน้อย พบกันใหม่ในบทเรียนหน้า! release()Semaphoreacquire()release()อะไรคือความแตกต่างระหว่าง mutex, จอภาพ และเซมาฟอร์ - 5
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION