1、引入信號(hào)量(Semaphore)
信號(hào)量為多線程提供更為強(qiáng)大的控制方法。廣義上說(shuō),信號(hào)量是對(duì)鎖的擴(kuò)展。無(wú)論是內(nèi)部鎖synchronized還是重入鎖ReentrantLock,一次都只允許一個(gè)線程訪問(wèn)一個(gè)資源,而信號(hào)量可以指定多個(gè)線程,同時(shí)訪問(wèn)某一個(gè)資源。
信號(hào)量主要提供了以下構(gòu)造函數(shù):
public Semaphore(int permits)
public Semaphore(int permits, boolean fair)
在構(gòu)造信號(hào)量對(duì)象時(shí),,必須指定信號(hào)量的準(zhǔn)入數(shù),即同時(shí)能申請(qǐng)多少個(gè)許可。當(dāng)每個(gè)線程每次只申請(qǐng)一個(gè)許可時(shí),這就相當(dāng)于制定了同時(shí)有多少個(gè)線程可以訪問(wèn)某一資源。
2、信號(hào)量的主要邏輯方法
public void acquire()
public void acquireUninterruptibly()
public boolean tryAcquire()
public boolean tryAcquire(long timeout, TimeUnit unit)
public void release()
- acquire()方法嘗試獲得一個(gè)準(zhǔn)入的許可。若無(wú)法獲得,則線程會(huì)等待,直到有線程釋放一個(gè)許可或當(dāng)前線程被中斷。
- acquireUninterruptibly()方法和acquire()類似,但不響應(yīng)中斷。
- tryAcquire()嘗試獲得一個(gè)許可,如果成功返回true,失敗則返回false,它不會(huì)進(jìn)行等待,立即返回。
- tryAcquire(long timeout, TimeUnit unit)嘗試在指定的時(shí)間內(nèi)獲得一個(gè)許可。
- release()方法用于在線程訪問(wèn)資源結(jié)束后,釋放一個(gè)許可。以使其他等待許可的線程可以進(jìn)行資源訪問(wèn)。
3、簡(jiǎn)單演示一下Semaphore功能
演示代碼如下:
public class SemaphoreDemo implements Runnable
{
//聲明了一個(gè)包含五個(gè)許可的信號(hào)量。這就意味著同時(shí)可以有5個(gè)線程進(jìn)入臨界區(qū)
final Semaphore semaphore = new Semaphore(5);
@Override
public void run()
{
try
{
semaphore.acquire();
//模擬耗時(shí)操作
Thread.sleep(2000);
System.out.println(Thread.currentThread().getId() + ":done!");
semaphore.release();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
ExecutorService exec = Executors.newFixedThreadPool(20);
final SemaphoreDemo demo = new SemaphoreDemo();
for (int i = 0; i < 20; ++i)
{
exec.submit(demo);
}
}
}
上述代碼中,下面代碼塊為臨界區(qū)管理代碼,程序會(huì)限制執(zhí)行這段代碼的線程數(shù)。申明了一個(gè)包含5個(gè)許可的信號(hào)量,這就意味著同時(shí)可以有5個(gè)線程進(jìn)入下面臨界區(qū)代碼段。申請(qǐng)信號(hào)量使用acquire()操作,在離開(kāi)時(shí),務(wù)必使用release()釋放信號(hào)量,這就和釋放鎖一個(gè)道理。如果不幸發(fā)生了信號(hào)量泄露(申請(qǐng)了但沒(méi)釋放),那么可以進(jìn)入臨界區(qū)的線程數(shù)就會(huì)越來(lái)越少,直到所有的線程均不可訪問(wèn)。在本例中,同時(shí)開(kāi)啟了20個(gè)線程。觀察這段程序的輸出,會(huì)發(fā)現(xiàn)系統(tǒng)以5個(gè)線程為一組,依次輸出帶有線程ID的文本。
//模擬耗時(shí)操作
Thread.sleep(2000);
System.out.println(Thread.currentThread().getId() + ":done!");