CodeGym /Blog Java /Ngẫu nhiên /Sự khác biệt giữa Mutex, Monitor và Semaphore

Sự khác biệt giữa Mutex, Monitor và Semaphore

Xuất bản trong nhóm
CHÀO! Khi học đa luồng trên CodeGym, bạn thường gặp khái niệm "mutex" và "monitor". Nếu không nhìn trộm, bạn có thể nói chúng khác nhau như thế nào không? :) Nếu có, làm tốt lắm! Nếu không (điều này là phổ biến nhất), điều đó không có gì ngạc nhiên. "Mutex" và "monitor" thực sự là những khái niệm liên quan. Ngoài ra, khi bạn đọc các bài học và xem video về đa luồng trên các trang web khác, bạn sẽ bắt gặp một khái niệm tương tự khác: "semaphore". Nó cũng có chức năng rất giống với màn hình và mutexes. Đó là lý do tại sao chúng tôi sẽ điều tra ba thuật ngữ này. Chúng ta sẽ xem xét một vài ví dụ và hiểu rõ ràng về sự khác biệt của các khái niệm này với nhau :)

Mutex

Mutex (hoặc khóa) là một cơ chế đặc biệt để đồng bộ hóa các luồng. Một cái được "gắn liền" với mọi đối tượng trong Java — bạn đã biết điều đó rồi :) Không thành vấn đề nếu bạn sử dụng các lớp tiêu chuẩn hoặc tạo các lớp của riêng bạn, ví dụ: Cat and Dog : tất cả các đối tượng của tất cả các lớp đều có một mutex . Thuật ngữ "mutex" xuất phát từ "MUTual LOẠI TRỪ", mô tả hoàn hảo mục đích của nó. Như chúng ta đã nói trong một trong những bài học trước, một mutex giúp đảm bảo rằng chỉ một luồng tại một thời điểm có quyền truy cập vào đối tượng. Một ví dụ thực tế phổ biến về một mutex liên quan đến nhà vệ sinh. Khi một người vào vách ngăn nhà vệ sinh, anh ta sẽ khóa cửa từ bên trong. Nhà vệ sinh giống như một đối tượng có thể được truy cập bằng nhiều luồng. Ổ khóa trên cửa vách ngăn giống như một thanh trượt, và dòng người bên ngoài tượng trưng cho các sợi chỉ. Ổ khóa trên cửa là công tắc của nhà vệ sinh: nó đảm bảo rằng chỉ một người có thể vào bên trong. Sự khác biệt giữa một mutex, một màn hình và một semaphore là gì?  - 2Nói cách khác, tại một thời điểm chỉ có một luồng có thể hoạt động với các tài nguyên được chia sẻ. Nỗ lực của các chủ đề khác (người) để có quyền truy cập vào các tài nguyên bị chiếm đóng sẽ không thành công. Một mutex có một số tính năng quan trọng. Đầu tiên , chỉ có thể có hai trạng thái: "đã mở khóa" và "đã khóa". Điều này giúp chúng tôi hiểu cách thức hoạt động của nó: bạn có thể vẽ các điểm tương đồng với các biến Boolean (đúng/sai) hoặc số nhị phân (0/1). , nhà nước không thể kiểm soát trực tiếp. Java không có cơ chế nào cho phép bạn lấy một đối tượng một cách rõ ràng, lấy mutex của nó và gán trạng thái mong muốn. Nói cách khác, bạn không thể làm điều gì đó như:

Object myObject = new Object();
Mutex mutex = myObject.getMutex();
mutex.free();
Điều này có nghĩa là bạn không thể giải phóng mutex của đối tượng. Chỉ máy Java mới có quyền truy cập trực tiếp vào nó. Lập trình viên làm việc với mutexes thông qua các công cụ của ngôn ngữ.

Màn hình

Màn hình là một "cấu trúc thượng tầng" bổ sung trên một mutex. Trên thực tế, màn hình là một đoạn mã "vô hình" đối với lập trình viên. Khi chúng tôi nói về mutexes trước đó, chúng tôi đã đưa ra một ví dụ đơn giản:

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
       }
   }
}
Trong khối mã được đánh dấu bằng từ khóa được đồng bộ hóa , mutex của đối tượng obj của chúng tôi được lấy. Tuyệt vời, chúng ta có thể có được khóa, nhưng "sự bảo vệ" được cung cấp chính xác như thế nào? Khi chúng ta thấy từ được đồng bộ hóa , điều gì ngăn các luồng khác vào khối? Sự bảo vệ đến từ một màn hình! Trình biên dịch chuyển đổi từ khóa được đồng bộ hóa thành một số đoạn mã đặc biệt. Một lần nữa, hãy quay lại ví dụ của chúng ta với phương thức doSomething() . Chúng tôi sẽ thêm vào nó:

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();
       }
   }
}
Đây là những gì xảy ra "dưới mui xe" sau khi trình biên dịch chuyển đổi mã này:

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;
   }
}
Tất nhiên, đây không phải là một ví dụ thực tế. Ở đây, chúng tôi đã sử dụng mã giống như Java để mô tả những gì xảy ra bên trong máy Java. Điều đó nói rằng, mã giả này mang lại sự hiểu biết tuyệt vời về những gì thực sự xảy ra với đối tượng và các luồng bên trong khối được đồng bộ hóa và cách trình biên dịch chuyển đổi từ khóa này thành một số câu lệnh "vô hình" đối với người lập trình. Về cơ bản, Java sử dụng từ khóa được đồng bộ hóa để đại diện cho màn hình . Tất cả mã xuất hiện thay vì từ khóa được đồng bộ hóa trong ví dụ trước là màn hình.

đèn hiệu

Một từ khác mà bạn sẽ gặp trong nghiên cứu cá nhân về đa luồng là "semaphore". Hãy tìm hiểu xem đây là gì và nó khác với màn hình và mutex như thế nào. Semaphore là một công cụ để đồng bộ hóa quyền truy cập vào một số tài nguyên. Tính năng đặc biệt của nó là nó sử dụng bộ đếm để tạo cơ chế đồng bộ hóa. Bộ đếm cho chúng ta biết có bao nhiêu luồng có thể truy cập đồng thời vào tài nguyên được chia sẻ. Sự khác biệt giữa một mutex, một màn hình và một semaphore là gì?  - 3Semaphore trong Java được đại diện bởi lớp Semaphore . Khi tạo các đối tượng semaphore, chúng ta có thể sử dụng các hàm tạo sau:

Semaphore(int permits)
Semaphore(int permits, boolean fair)
Chúng tôi chuyển phần sau cho hàm tạo:
    giấy phép int - giá trị ban đầu và giá trị tối đa của bộ đếm. Nói cách khác, tham số này xác định có bao nhiêu luồng có thể truy cập đồng thời vào tài nguyên được chia sẻ;
  • boolean fair — thiết lập thứ tự các luồng sẽ có quyền truy cập. Nếu công bằng là đúng, thì quyền truy cập sẽ được cấp cho các chuỗi đang chờ theo thứ tự mà chúng yêu cầu. Nếu nó sai, thì thứ tự được xác định bởi bộ lập lịch luồng.
Một ví dụ cổ điển về việc sử dụng semaphore là vấn đề triết gia ăn uống. Sự khác biệt giữa một mutex, một màn hình và một semaphore là gì?  - 4Để dễ hiểu, chúng tôi sẽ đơn giản hóa nó một chút. Hãy tưởng tượng rằng chúng ta có 5 triết gia cần ăn trưa. Ngoài ra, chúng tôi có một bàn có thể chứa không quá hai người cùng một lúc. Nhiệm vụ của chúng tôi là nuôi tất cả các triết gia. Không ai trong số họ nên đói, và không ai trong số họ nên "cản" nhau khi cố gắng ngồi vào bàn (chúng ta phải tránh bế tắc). Đây là lớp triết học của chúng ta sẽ như thế nào:

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!");
       }
   }
}
Và đây là mã để chạy chương trình của chúng tôi:

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();
   }
}
Chúng tôi đã tạo một semaphore có bộ đếm được đặt thành 2 để đáp ứng điều kiện: chỉ có hai triết gia có thể ăn cùng một lúc. Tức là chỉ có hai luồng có thể chạy cùng một lúc, bởi vì lớp Philosopher của chúng ta kế thừa Thread ! Các phương thức thu thập ()giải phóng () của lớp Semaphore kiểm soát bộ đếm truy cập của nó. Phương thức thu thập () yêu cầu semaphore truy cập vào tài nguyên. Nếu bộ đếm >0, thì quyền truy cập được cấp và bộ đếm giảm đi 1. Bản phát hành()phương thức "giải phóng" quyền truy cập được cấp trước đó, trả lại quyền truy cập (tăng bộ đếm truy cập của semaphore lên 1). Chúng ta nhận được gì khi chạy chương trình? Vấn đề có được giải quyết không? Các triết gia của chúng ta sẽ không chiến đấu trong khi chờ đến lượt mình sao? :) Đây là đầu ra giao diện điều khiển mà chúng tôi nhận được:

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 
Chúng ta làm được rồi! Và mặc dù Thales phải dùng bữa một mình, tôi không nghĩ chúng ta đã xúc phạm anh ấy :) Bạn có thể nhận thấy một số điểm tương đồng giữa một mutex và một semaphore. Thật vậy, chúng có cùng một nhiệm vụ: đồng bộ hóa quyền truy cập vào một số tài nguyên. Sự khác biệt giữa một mutex, một màn hình và một semaphore là gì?  - 5Sự khác biệt duy nhất là một mutex của một đối tượng chỉ có thể được lấy bởi một luồng tại một thời điểm, trong khi trong trường hợp semaphore, sử dụng bộ đếm luồng, một số luồng có thể truy cập tài nguyên đồng thời. Đây không chỉ là sự trùng hợp ngẫu nhiên :) Một mutex thực sự là một semaphorevới số lượng là 1. Nói cách khác, đó là một semaphore có thể chứa một luồng đơn lẻ. Nó còn được gọi là "semaphore nhị phân" vì bộ đếm của nó chỉ có thể có 2 giá trị — 1 ("đã mở khóa") và 0 ("đã khóa"). Đó là nó! Như bạn có thể thấy, nó không quá khó hiểu :) Bây giờ, nếu bạn muốn nghiên cứu chi tiết hơn về đa luồng trên Internet, bạn sẽ dễ dàng điều hướng các khái niệm này hơn một chút. Hẹn gặp lại các bạn trong các bài học tiếp theo!
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION