JavaRush /Java Blog /Random-TW /互斥鎖、監視器和信號量有什麼差別

互斥鎖、監視器和信號量有什麼差別

在 Random-TW 群組發布
你好!在學習 JavaRush 中的多執行緒時,您經常會遇到「互斥體」和「監視器」的概念。現在你能不偷看地回答它們有何不同嗎?:) 互斥量、監視器和信號量之間有什麼區別 - 1如果可以的話,幹得好!如果沒有(而且這種情況經常發生)——也就不足為奇了。「互斥體」和「監視器」的概念確實是相關的。而且,在網路上看外部資源上關於多執行緒的講座和影片時,你還會遇到另一個類似的概念—「信號量」。它的功能也很大程度類似於監視器和互斥體。因此,讓我們理解這三個術語,看幾個例子,最後在我們的頭腦中組織對它們之間有何不同的理解:)

互斥體

互斥體是用於同步執行緒的特殊物件。它「附加」到 Java 中的每個物件 - 你已經知道了:) 無論你使用標準類還是創建自己的類,都沒有關係,並且CatDog所有類的所有物件都有一個互斥體。「mutex」這個名字來自英文“MUTual EXclusion”——“互斥”,這完美地體現了它的目的。正如我們在先前的一講中所說,互斥體的任務就是提供這樣一種機制,使得在某一時刻只有一個執行緒可以存取一個物件。現實生活中互斥體的一個流行的類比是「廁所的例子」。當一個人進入廁所時,他從裡面鎖上門。廁所作為一個可以被多個執行緒存取的物件。廁所門上的鎖是互斥鎖的作用,外面排隊的人是線程的作用。門上的鎖是廁所互斥鎖:它確保一次只有一個人可以進去。 互斥量、監視器和信號量之間有什麼區別 - 2換句話說,一次只有一個執行緒可以處理共享資源。其他執行緒(人)嘗試存取佔用的資源將會失敗。互斥體有幾個重要的特性。 首先,只有兩種狀態是可能的——「空閒」和「忙碌」。這使得更容易理解它的工作原理:可以使用布林變數true/false或二進制數字系統 1/0 來繪製平行線。 其次,國家不能被直接控制。Java 中沒有任何機制可讓您明確取得物件、取得其互斥體並為其指派所需的狀態。換句話說,你不能做這樣的事情:
Object myObject = new Object();
Mutex mutex = myObject.getMutex();
mutex.free();
因此,對象的互斥量無法被釋放。只有Java機器可以直接存取它。程式設計師使用語言工具來處理互斥體。

監視器

監視器是互斥鎖的附加「附加元件」。事實上,監視器是一段對程式設計師「不可見」的程式碼。前面說到互斥鎖,我們舉了一個簡單的例子:
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
       }
   }
}
在標有該單字的程式碼區塊中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上一個範例中的單字)都是監視器。

訊號

當你自己學習多執行緒時,你會遇到的另一個字是「信號量」。讓我們弄清楚它是什麼以及它與監視器和互斥鎖有何不同。信號量是同步資源存取的一種手段。 它的特殊之處在於它在創建同步機制時使用了計數器。 計數器告訴我們有多少執行緒可以同時存取共享資源。 互斥量、監視器和信號量之間有什麼區別 - 3Java 中的信號量由類別表示Semaphore。在建立信號量物件時,我們可以使用以下建構函數:
Semaphore(int permits)
Semaphore(int permits, boolean fair)
我們傳遞給建構函數:
  • int permits— 初始和最大計數器值。即有多少個執行緒可以同時存取一個共享資源;

  • boolean fair- 建立執行緒接收存取的順序。如果fair= true,則按照等待執行緒請求的順序授予存取權限。如果為false,則順序將由執行緒調度程序決定。

使用信號量的典型例子是哲學家午餐問題
互斥量、監視器和信號量之間有什麼區別 - 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()和方法控制其權限計數器。此方法請求從信號量存取資源的權限。如果 counter > 0,則授予權限,且計數器減 1。此方法「釋放」先前授予的權限並將其返回計數器(將信號量的授予計數器加 1)。當我們運行程式時我們會得到什麼?問題解決了嗎?我們的哲學家會在等待輪到他們的時候打架嗎?:) 這是我們收到的控制台輸出: 蘇格拉底坐在桌子旁 柏拉圖坐在 蘇格拉底吃過的桌子旁!他離開了桌子。柏拉圖已經吃過了!他離開桌子 亞里斯多德在桌子旁坐下 畢達哥拉斯在 亞里斯多德吃過的桌子旁坐下!他離開了畢達哥拉斯吃過的桌子!他離開了泰爾斯的桌子,坐在了泰爾斯吃過的 桌子旁邊 !他離開了桌子, 我們成功了!儘管泰勒斯必須獨自用餐,但我認為他並沒有生我們的氣:) 您可能已經注意到互斥體和信號量之間的一些相似之處。 一般來說,它們有相同的目的:同步對某些資源的存取。 唯一的差異是物件的互斥體一次只能由一個執行緒獲取,而信號量則使用執行緒計數器,其中多個執行緒可以同時存取該資源。這不只是巧合的相似之處:) 事實上,互斥鎖是一個單位信號量。也就是說,它是一個計數器初始設定為1的信號量。它也被稱為“二進制信號量”,因為它的計數器只能有2個值——1(“空閒”)和0(“忙”)。就這樣!正如您所看到的,一切都變得不再那麼混亂了:) 現在,如果您想在 Internet 上更詳細地研究多線程主題,那麼您會更容易理解這些概念。下一課見! release()Semaphoreacquire()release()互斥量、監視器和信號量之間有什麼區別 - 5
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION