24、Java并发性和多线程-信号量
以下內(nèi)容轉(zhuǎn)自http://ifeve.com/semaphore/:
Semaphore(信號(hào)量) 是一個(gè)線程同步結(jié)構(gòu),用于在線程間傳遞信號(hào),以避免出現(xiàn)信號(hào)丟失(譯者注:下文會(huì)具體介紹),或者像鎖一樣用于保護(hù)一個(gè)關(guān)鍵區(qū)域。自從5.0開(kāi)始,jdk在java.util.concurrent包里提供了Semaphore的官方實(shí)現(xiàn),因此大家不需要自己去實(shí)現(xiàn)Semaphore。但是還是很有必要去熟悉如何使用Semaphore及其背后的原理
本文的涉及的主題如下:
一、簡(jiǎn)單的Semaphore實(shí)現(xiàn)
下面是一個(gè)信號(hào)量的簡(jiǎn)單實(shí)現(xiàn):
public class Semaphore {private boolean signal = false;public synchronized void take() {this.signal = true;this.notify();}public synchronized void release() throws InterruptedException{while(!this.signal) wait();this.signal = false;}}take方法發(fā)出一個(gè)被存放在Semaphore內(nèi)部的信號(hào),而release方法則等待一個(gè)信號(hào),當(dāng)其接收到信號(hào)后,標(biāo)記位signal被清空,然后該方法終止。
使用這個(gè)semaphore可以避免錯(cuò)失某些信號(hào)通知。用take方法來(lái)代替notify,release方法來(lái)代替wait。如果某線程在調(diào)用release等待之前調(diào)用take方法,那么調(diào)用release方法的線程仍然知道take方法已經(jīng)被某個(gè)線程調(diào)用過(guò)了,因?yàn)樵揝emaphore內(nèi)部保存了take方法發(fā)出的信號(hào)。而wait和notify方法就沒(méi)有這樣的功能。
當(dāng)用semaphore來(lái)產(chǎn)生信號(hào)時(shí),take和release這兩個(gè)方法名看起來(lái)有點(diǎn)奇怪。這兩個(gè)名字來(lái)源于后面把semaphore當(dāng)做鎖的例子,后面會(huì)詳細(xì)介紹這個(gè)例子,在該例子中,take和release這兩個(gè)名字會(huì)變得很合理。
二、使用Semaphore來(lái)產(chǎn)生信號(hào)
下面的例子中,兩個(gè)線程通過(guò)Semaphore發(fā)出的信號(hào)來(lái)通知對(duì)方
Semaphore semaphore = new Semaphore();SendingThread sender = new SendingThread(semaphore);ReceivingThread receiver = new ReceivingThread(semaphore);receiver.start(); sender.start(); public class SendingThread {Semaphore semaphore = null;public SendingThread(Semaphore semaphore){this.semaphore = semaphore;}public void run(){while(true){//do something, then signalthis.semaphore.take();}} } public class RecevingThread {Semaphore semaphore = null;public ReceivingThread(Semaphore semaphore){this.semaphore = semaphore;}public void run(){while(true){this.semaphore.release();//receive signal, then do something... }} }三、可計(jì)數(shù)的Semaphore
上面提到的Semaphore的簡(jiǎn)單實(shí)現(xiàn)并沒(méi)有計(jì)算通過(guò)調(diào)用take方法所產(chǎn)生信號(hào)的數(shù)量。可以把它改造成具有計(jì)數(shù)功能的Semaphore。下面是一個(gè)可計(jì)數(shù)的Semaphore的簡(jiǎn)單實(shí)現(xiàn)。
public class CountingSemaphore {private int signals = 0;public synchronized void take() {this.signals++;this.notify();}public synchronized void release() throws InterruptedException{while(this.signals == 0) wait();this.signals--;}}四、有上限的Semaphore
上面的CountingSemaphore并沒(méi)有限制信號(hào)的數(shù)量。下面的代碼將CountingSemaphore改造成一個(gè)信號(hào)數(shù)量有上限的BoundedSemaphore。
public class BoundedSemaphore {private int signals = 0;private int bound = 0;public BoundedSemaphore(int upperBound){this.bound = upperBound;}public synchronized void take() throws InterruptedException{while(this.signals == bound) wait();this.signals++;this.notify();}public synchronized void release() throws InterruptedException{while(this.signals == 0) wait();this.signals--;this.notify();} }在BoundedSemaphore中,當(dāng)已經(jīng)產(chǎn)生的信號(hào)數(shù)量達(dá)到了上限,take方法將阻塞新的信號(hào)產(chǎn)生請(qǐng)求,直到某個(gè)線程調(diào)用release方法后,被阻塞于take方法的線程才能傳遞自己的信號(hào)。
五、把Semaphore當(dāng)鎖來(lái)使用
當(dāng)信號(hào)量的數(shù)量上限是1時(shí),Semaphore可以被當(dāng)做鎖來(lái)使用。通過(guò)take和release方法來(lái)保護(hù)關(guān)鍵區(qū)域。請(qǐng)看下面的例子:
BoundedSemaphore semaphore = new BoundedSemaphore(1);...semaphore.take();try{//critical section } finally {semaphore.release(); }在前面的例子中,Semaphore被用來(lái)在多個(gè)線程之間傳遞信號(hào),這種情況下,take和release分別被不同的線程調(diào)用。但是在鎖這個(gè)例子中,take和release方法將被同一線程調(diào)用,因?yàn)橹辉试S一個(gè)線程來(lái)獲取信號(hào)(允許進(jìn)入關(guān)鍵區(qū)域的信號(hào)),其它調(diào)用take方法獲取信號(hào)的線程將被阻塞,知道第一個(gè)調(diào)用take方法的線程調(diào)用release方法來(lái)釋放信號(hào)。對(duì)release方法的調(diào)用永遠(yuǎn)不會(huì)被阻塞,這是因?yàn)槿魏我粋€(gè)線程都是先調(diào)用take方法,然后再調(diào)用release。
通過(guò)有上限的Semaphore可以限制進(jìn)入某代碼塊的線程數(shù)量。設(shè)想一下,在上面的例子中,如果BoundedSemaphore?上限設(shè)為5將會(huì)發(fā)生什么?意味著允許5個(gè)線程同時(shí)訪問(wèn)關(guān)鍵區(qū)域,但是你必須保證,這個(gè)5個(gè)線程不會(huì)互相沖突。否則你的應(yīng)用程序?qū)⒉荒苷_\(yùn)行。
必須注意,release方法應(yīng)當(dāng)在finally塊中被執(zhí)行。這樣可以保在關(guān)鍵區(qū)域的代碼拋出異常的情況下,信號(hào)也一定會(huì)被釋放。
==>如有問(wèn)題,請(qǐng)聯(lián)系我:easonjim#163.com,或者下方發(fā)表評(píng)論。<==總結(jié)
以上是生活随笔為你收集整理的24、Java并发性和多线程-信号量的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 驳斥苹果“诊断后门论”,声援扎德尔斯基
- 下一篇: 【模板】割点(割顶)