Binary Semaphore 和 Reentrant Lock 傻傻分不清!
1. 引言
在本教程中,我們將探討二進(jìn)制信號(hào)量(Binary Semaphore)和可重入鎖(Reentrant Lock)。另外,我們會(huì)將它們相互比較,看看哪一個(gè)最適合常見(jiàn)情況。
2. 什么是二進(jìn)制信號(hào)量
二進(jìn)制信號(hào)量在單個(gè)資源的訪問(wèn)上提供信令機(jī)制。換句話說(shuō),二進(jìn)制信號(hào)量提供了一種互斥機(jī)制,一次只允許一個(gè)線程訪問(wèn)一個(gè)關(guān)鍵部分。它只保留一個(gè)通行證,因此二進(jìn)制信號(hào)量只有兩種狀態(tài):可用(count=1) 和不可用(count=0)。
我們使用Java中的 Semaphore 類(lèi)來(lái)討論一個(gè)簡(jiǎn)單的二進(jìn)制信號(hào)量的實(shí)現(xiàn) :
- Semaphore binarySemaphore = new Semaphore(1);
- try {
- binarySemaphore.acquire();
- assertEquals(0, binarySemaphore.availablePermits());
- } catch (InterruptedException e) {
- e.printStackTrace();
- } finally {
- binarySemaphore.release();
- assertEquals(1, binarySemaphore.availablePermits());
- }
在這里,我們可以觀察到,acquire方法將可用許可減少了一個(gè)。類(lèi)似地,release方法將可用許可增加1。
另外,Semaphore 類(lèi)提供了 fairness 參數(shù)。當(dāng)設(shè)置為true時(shí),fairness 參數(shù)確保請(qǐng)求線程獲取許可的順序(基于它們的等待時(shí)間):
- Semaphore binarySemaphore = new Semaphore(1, true);
3. 什么是重入鎖?
可重入鎖是一種互斥機(jī)制,允許線程在沒(méi)有死鎖的情況下(多次)重入資源上的鎖。
進(jìn)入鎖的線程每次增加一個(gè)持有計(jì)數(shù)。類(lèi)似地,請(qǐng)求解鎖時(shí)持有計(jì)數(shù)減少。因此,資源被鎖定,直到計(jì)數(shù)器返回到零。例如,讓我們看一個(gè)使用Java中 ReentrantLock 類(lèi)的簡(jiǎn)單實(shí)現(xiàn):
- ReentrantLock reentrantLock = new ReentrantLock();
- try {
- reentrantLock.lock();
- assertEquals(1, reentrantLock.getHoldCount());
- assertEquals(true, reentrantLock.isLocked());
- } finally {
- reentrantLock.unlock();
- assertEquals(0, reentrantLock.getHoldCount());
- assertEquals(false, reentrantLock.isLocked());
- }
這里,lock方法將持有計(jì)數(shù)增加1,并鎖定資源。類(lèi)似地,unlock方法減少持有計(jì)數(shù),如果持有計(jì)數(shù)為零,則解鎖資源。當(dāng)線程重新進(jìn)入鎖時(shí),它必須請(qǐng)求相同次數(shù)的解鎖以釋放資源:
- reentrantLock.lock();
- reentrantLock.lock();
- assertEquals(2, reentrantLock.getHoldCount());
- assertEquals(true, reentrantLock.isLocked());
- reentrantLock.unlock();
- assertEquals(1, reentrantLock.getHoldCount());
- assertEquals(true, reentrantLock.isLocked());
- reentrantLock.unlock();
- assertEquals(0, reentrantLock.getHoldCount());
- assertEquals(false, reentrantLock.isLocked());
與Semaphore類(lèi)類(lèi)似,ReentrantLock類(lèi)也支持 fairness 參數(shù):
- ReentrantLock reentrantLock = new ReentrantLock(true);
4. 二進(jìn)制信號(hào)量與重入鎖
4.1. 機(jī)制
二進(jìn)制信號(hào)量是一種信令機(jī)制,而可重入鎖是一種鎖定機(jī)制。
4.2.所有權(quán)
沒(méi)有線程是二進(jìn)制信號(hào)量的所有者。但是,成功鎖定資源的最后一個(gè)線程是可重入鎖的所有者。
4.3. 本質(zhì)
二進(jìn)制信號(hào)量本質(zhì)上是不可重入的,這意味著同一個(gè)線程不能重新獲取關(guān)鍵部分,否則會(huì)導(dǎo)致死鎖。另一方面,可重入鎖本質(zhì)上允許同一線程多次重入鎖。
4.4. 靈活性
二進(jìn)制信號(hào)量通過(guò)允許鎖定機(jī)制和死鎖恢復(fù)的自定義實(shí)現(xiàn),提供了更高級(jí)別的同步機(jī)制。因此,它為開(kāi)發(fā)人員提供了更多的控制。然而,可重入鎖則是一種低級(jí)同步,具有固定的鎖機(jī)制。
4.5. 可修改性
二進(jìn)制信號(hào)量支持 wait 和 signal(在Java的Semaphore類(lèi)中獲取和釋放)等操作,以允許任何進(jìn)程修改可用的許可證。另一方面,只有鎖定/解鎖資源的同一線程才能修改可重入鎖。
4.6. 死鎖恢復(fù)
二進(jìn)制信號(hào)量提供了一種非所有權(quán)釋放機(jī)制。因此,任何線程都可以釋放二進(jìn)制信號(hào)量的死鎖恢復(fù)許可。
相反,在重入鎖的情況下很難實(shí)現(xiàn)死鎖恢復(fù)。例如,如果可重入鎖的所有者線程進(jìn)入睡眠或無(wú)限等待狀態(tài),就不可能釋放資源,從而導(dǎo)致死鎖情況。
5. 總結(jié)
在這篇短文中,我們探討了二進(jìn)制信號(hào)量和可重入鎖。
首先,我們討論了二進(jìn)制信號(hào)量和可重入鎖的基本定義,以及Java中的基本實(shí)現(xiàn)。然后,我們根據(jù)機(jī)制、所有權(quán)和靈活性等參數(shù)對(duì)它們進(jìn)行了比較。
我們可以肯定地得出結(jié)論二進(jìn)制信號(hào)量為互斥提供了一種基于非所有權(quán)的信令機(jī)制。同時(shí),它還可以進(jìn)一步擴(kuò)展,以提供鎖定功能和容易的死鎖恢復(fù)。
另一方面,可重入鎖提供了具有基于所有者的鎖定功能的可重入互斥,作為簡(jiǎn)單的互斥鎖非常有用。
本文轉(zhuǎn)載自微信公眾號(hào)「鍋外的大佬」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系鍋外的大佬公眾號(hào)。