摘要:在每個(gè)線程獲取之前,必須先從信號(hào)量獲取許可。注意,因?yàn)橥瑫r(shí)可能發(fā)生取消,所以返回并不保證有其他線程等待獲取許可。該值僅是估計(jì)的數(shù)字,因?yàn)樵诖朔椒ū闅v內(nèi)部數(shù)據(jù)結(jié)構(gòu)的同時(shí),線程的數(shù)目可能動(dòng)態(tài)地變化。
引言本人郵箱:
歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明網(wǎng)址 http://blog.csdn.net/tianshi_kco
github: https://github.com/kco1989/kco
代碼已經(jīng)全部托管github有需要的同學(xué)自行下載
這節(jié)課,我們就開始講一下信號(hào)量Semaphore
理論Semaphore:一個(gè)可計(jì)數(shù)的信號(hào)量。一般,一個(gè)semaphore 信號(hào)量是一組許可證。如果必要,那個(gè)每次acquire獲取許可都是阻塞的,直接一個(gè)許可證是可用的,并獲取到。每次release釋放,都會(huì)增加一個(gè)許可證,潛在的,也會(huì)釋放一個(gè)阻塞請(qǐng)求。然而。并非每次許可對(duì)象都可以被使用的,這個(gè)Semaphore信號(hào)量只保存幾個(gè)可用的許可證和相應(yīng)的操作。
如果有幾個(gè)線程數(shù)要訪問幾個(gè)共享資源的話,那么這時(shí)候就應(yīng)該使用信號(hào)量。舉例說明:這個(gè)有類Pool類,它就使用信號(hào)量在控制多線程去訪問那么幾個(gè)有限items。
class Pool { private static final int MAX_AVAILABLE = 100; private final Semaphore available = new Semaphore(MAX_AVAILABLE, true); public Object getItem() throws InterruptedException { available.acquire(); return getNextAvailableItem(); } public void putItem(Object x) { if (markAsUnused(x)) available.release(); } // Not a particularly efficient data structure; just for demo protected Object[] items = ... whatever kinds of items being managed protected boolean[] used = new boolean[MAX_AVAILABLE]; protected synchronized Object getNextAvailableItem() { for (int i = 0; i < MAX_AVAILABLE; ++i) { if (!used[i]) { used[i] = true; return items[i]; } } return null; // not reached } protected synchronized boolean markAsUnused(Object item) { for (int i = 0; i < MAX_AVAILABLE; ++i) { if (item == items[i]) { if (used[i]) { used[i] = false; return true; } else return false; } } return false; } }
在每個(gè)線程獲取item之前,必須先從信號(hào)量獲取許可。保證這個(gè)item對(duì)用戶來說是可以使用的。當(dāng)線程結(jié)束使用item時(shí),并讓item返回item池,這信號(hào)量會(huì)釋放這個(gè)許可,之后允許使用線程可以獲取到這個(gè)item。必須要注意的,在程序中,在獲取許可和釋放許可的死胡同并沒有使用同步鎖,信號(hào)量封裝了限制對(duì)池的訪問所需的同步,與維護(hù)池本身的一致性所需的任何同步。
Semaphore(int permits): 創(chuàng)建一個(gè)指定數(shù)量的許可的信號(hào)量
Semaphore(int permits, boolean fair) 創(chuàng)建一個(gè)指定數(shù)量的許可,并保證每個(gè)線程都是公平的,當(dāng)fair為true時(shí),信號(hào)量會(huì)安裝先進(jìn)先出的原則來獲取許可.
acquire() 在當(dāng)前信號(hào)量中獲取一個(gè)許可.當(dāng)前線程會(huì)一直阻塞直到有一個(gè)可用的許可,或被其他線程中斷.
acquireUninterruptibly(): 在當(dāng)前信號(hào)量中獲取一個(gè)許可.當(dāng)前線程會(huì)一直阻塞直到有一個(gè)可用的許可.
tryAcquire() 在當(dāng)前信號(hào)量嘗試獲取一個(gè)許可,如果有可用,則獲取到這個(gè)許可,并立即返回true,后綴立即返回false
tryAcquire 在當(dāng)前信號(hào)量獲取一個(gè)許可,當(dāng)前線程會(huì)一直阻塞直到有一個(gè)可用的許可.或指定時(shí)間超時(shí)了,或被其他線程中斷.
release() 釋放一個(gè)許可,把讓它返回到這個(gè)信號(hào)量中.
acquire(int permits) 請(qǐng)求指定數(shù)量的許可,如果有足夠的許可可用,那么當(dāng)前線程會(huì)立刻返回,如果許可不足,則當(dāng)前會(huì)一直等待,直到被其他線程中斷,或獲取到足夠的許可.
acquireUninterruptibly(int permits) 請(qǐng)求指定數(shù)量的許可,如果有足夠的許可可用,那么當(dāng)前線程會(huì)立刻返回,如果許可不足,則當(dāng)前會(huì)一直等待,直到獲取到足夠的許可.
tryAcquire(int permits) 在當(dāng)前信號(hào)量嘗試獲取指定數(shù)量的許可,如果有可用,則獲取到這個(gè)許可,并立即返回true,后綴立即返回false
tryAcquire(int permits, long timeout, TimeUnit unit) 在指定的超時(shí)時(shí)間,當(dāng)前信號(hào)量嘗試獲取指定數(shù)量的許可,如果有可用,則獲取到這個(gè)許可,并立即返回true,后綴立即返回false
release(int permits) 釋放指定數(shù)量的許可
availablePermits() 返回當(dāng)前信號(hào)量還有幾個(gè)可用的許可
drainPermits() 請(qǐng)求并立即返回當(dāng)前信號(hào)量可用的全部許可
reducePermits(int reduction) 根據(jù)指定的縮減量減小可用許可的數(shù)目。此方法在使用信號(hào)量來跟蹤那些變?yōu)椴豢捎觅Y源的子類中很有用。此方法不同于 acquire,在許可變?yōu)榭捎玫倪^程中,它不會(huì)阻塞等待。
isFair() 返回當(dāng)前的信號(hào)量時(shí)候是公平的
hasQueuedThreads() 查詢是否有線程正在等待獲取。注意,因?yàn)橥瑫r(shí)可能發(fā)生取消,所以返回 true 并不保證有其他線程等待獲取許可。此方法主要用于監(jiān)視系統(tǒng)狀態(tài)。
getQueueLength() 返回正在等待獲取的線程的估計(jì)數(shù)目。該值僅是估計(jì)的數(shù)字,因?yàn)樵诖朔椒ū闅v內(nèi)部數(shù)據(jù)結(jié)構(gòu)的同時(shí),線程的數(shù)目可能動(dòng)態(tài)地變化。此方法用于監(jiān)視系統(tǒng)狀態(tài),不用于同步控制。
getQueuedThreads() 返回一個(gè) collection,包含可能等待獲取的線程。因?yàn)樵跇?gòu)造此結(jié)果的同時(shí)實(shí)際的線程 set 可能動(dòng)態(tài)地變化,所以返回的 collection 僅是盡力的估計(jì)值。所返回 collection 中的元素沒有特定的順序。此方法用于加快子類的構(gòu)造速度,提供更多的監(jiān)視設(shè)施。
例子看了前面那么方法的介紹,恐怕你想吐的的心都有了吧?還是讓我們回歸輕松愉快的例子來吧.這里我們還是繼續(xù)舉小明和小紅談人生和理想的例子.之前他們?cè)谂P室里談了好幾百毫秒的人生和理想.頓時(shí)都感覺身疲憊,感覺身體好像被掏空了一樣.所以這里他們都想洗一個(gè)熱水澡,但是沐浴室只有三間,那就搶吧..ok,開始編程...
首先,先編寫一個(gè)沐浴室ShowerRoom
public class ShowerRoom { private static final int MAX_SIZE = 3; Semaphore semaphore = new Semaphore(MAX_SIZE); public void bathe(String name){ try { semaphore.acquire(); System.out.println(Thread.currentThread().getName() + " 洗唰唰啊..洗唰唰... "); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }finally { System.out.println(Thread.currentThread().getName() + " 終于洗完澡了..."); semaphore.release(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
然后編寫讓小明和小紅去洗澡操作 BoyAndGril
public class BoyAndGril implements Runnable{ ShowerRoom showerRoom; public BoyAndGril(ShowerRoom showerRoom) { this.showerRoom = showerRoom; } @Override public void run() { String name = Thread.currentThread().getName(); showerRoom.bathe(name); } }
最后,測試一下
public class TestMain { public static void main(String[] args) { SetboyAndGril = new HashSet<>(); ShowerRoom showerRoom = new ShowerRoom(); for (int i = 0; i < 10; i ++){ boyAndGril.add(new Thread(new BoyAndGril(showerRoom), "小明" + i + "號(hào)")); } for (int i = 0; i < 10; i ++){ boyAndGril.add(new Thread(new BoyAndGril(showerRoom), "小紅" + i + "號(hào)")); } for (Thread thread : boyAndGril){ thread.start(); } } }
運(yùn)行一下結(jié)果
小紅3號(hào) 洗唰唰啊..洗唰唰... 小紅6號(hào) 洗唰唰啊..洗唰唰... 小明0號(hào) 洗唰唰啊..洗唰唰... 小紅3號(hào) 終于洗完澡了... 小紅2號(hào) 洗唰唰啊..洗唰唰... 小紅6號(hào) 終于洗完澡了... 小明2號(hào) 洗唰唰啊..洗唰唰... 小明0號(hào) 終于洗完澡了... 小紅1號(hào) 洗唰唰啊..洗唰唰... 小紅2號(hào) 終于洗完澡了... 小明5號(hào) 洗唰唰啊..洗唰唰... 小明2號(hào) 終于洗完澡了... 小明7號(hào) 洗唰唰啊..洗唰唰... 小紅1號(hào) 終于洗完澡了... 小紅0號(hào) 洗唰唰啊..洗唰唰... 小明5號(hào) 終于洗完澡了... 小明7號(hào) 終于洗完澡了... 小明4號(hào) 洗唰唰啊..洗唰唰... 小紅4號(hào) 洗唰唰啊..洗唰唰... 小紅0號(hào) 終于洗完澡了... 小明3號(hào) 洗唰唰啊..洗唰唰... 小明4號(hào) 終于洗完澡了... 小明9號(hào) 洗唰唰啊..洗唰唰... 小紅4號(hào) 終于洗完澡了... 小紅7號(hào) 洗唰唰啊..洗唰唰... 小明3號(hào) 終于洗完澡了... 小紅5號(hào) 洗唰唰啊..洗唰唰... 小紅5號(hào) 終于洗完澡了... 小紅9號(hào) 洗唰唰啊..洗唰唰... 小紅7號(hào) 終于洗完澡了... 小明6號(hào) 洗唰唰啊..洗唰唰... 小明9號(hào) 終于洗完澡了... 小明1號(hào) 洗唰唰啊..洗唰唰... 小紅9號(hào) 終于洗完澡了... 小紅8號(hào) 洗唰唰啊..洗唰唰... 小明1號(hào) 終于洗完澡了... 小明8號(hào) 洗唰唰啊..洗唰唰... 小明6號(hào) 終于洗完澡了... 小紅8號(hào) 終于洗完澡了... 小明8號(hào) 終于洗完澡了...
ok,運(yùn)行正常,程序中不會(huì)發(fā)生四個(gè)人以及四個(gè)以上的人在同時(shí)洗澡的情況.
如果有人覺得這個(gè)好像也沒有使用什么共享資源啊,沒有上面那個(gè)例子的item pool,那行,那把有關(guān)semaphore的代碼注釋掉,再運(yùn)行一下.
小紅3號(hào) 洗唰唰啊..洗唰唰... 小紅6號(hào) 洗唰唰啊..洗唰唰... 小明0號(hào) 洗唰唰啊..洗唰唰... 小紅2號(hào) 洗唰唰啊..洗唰唰... 小明2號(hào) 洗唰唰啊..洗唰唰... 小紅1號(hào) 洗唰唰啊..洗唰唰... 小明5號(hào) 洗唰唰啊..洗唰唰... 小明7號(hào) 洗唰唰啊..洗唰唰... 小紅0號(hào) 洗唰唰啊..洗唰唰... 小明4號(hào) 洗唰唰啊..洗唰唰... 小紅4號(hào) 洗唰唰啊..洗唰唰... 小明3號(hào) 洗唰唰啊..洗唰唰... 小明9號(hào) 洗唰唰啊..洗唰唰... 小紅7號(hào) 洗唰唰啊..洗唰唰... 小紅5號(hào) 洗唰唰啊..洗唰唰... 小紅9號(hào) 洗唰唰啊..洗唰唰... 小明6號(hào) 洗唰唰啊..洗唰唰... 小明1號(hào) 洗唰唰啊..洗唰唰... 小紅8號(hào) 洗唰唰啊..洗唰唰... 小明8號(hào) 洗唰唰啊..洗唰唰... 小紅3號(hào) 終于洗完澡了... 小紅2號(hào) 終于洗完澡了... 小明2號(hào) 終于洗完澡了... 小紅6號(hào) 終于洗完澡了... 小明0號(hào) 終于洗完澡了... 小明5號(hào) 終于洗完澡了... 小紅0號(hào) 終于洗完澡了... 小明7號(hào) 終于洗完澡了... 小紅1號(hào) 終于洗完澡了... 小明4號(hào) 終于洗完澡了... 小紅4號(hào) 終于洗完澡了... 小明3號(hào) 終于洗完澡了... 小明9號(hào) 終于洗完澡了... 小紅8號(hào) 終于洗完澡了... 小紅5號(hào) 終于洗完澡了... 小紅9號(hào) 終于洗完澡了... 小明6號(hào) 終于洗完澡了... 小明1號(hào) 終于洗完澡了... 小紅7號(hào) 終于洗完澡了... 小明8號(hào) 終于洗完澡了...
發(fā)現(xiàn)這幾十個(gè)人都同時(shí)在三間沐浴室里洗澡,那么肯定有只是一間會(huì)出現(xiàn)兩人或兩人以上同時(shí)洗澡的情況.如果浴室夠大,大家都沒有意見,那還好.就是如果肥皂掉了,這個(gè)時(shí)候,小明就得考慮要不要彎腰去撿了....
打賞如果覺得我的文章寫的還過得去的話,有錢就捧個(gè)錢場,沒錢給我捧個(gè)人場(幫我點(diǎn)贊或推薦一下)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/69898.html
摘要:多線程工具箱之前言這一篇談一下信號(hào)量。信息信息信息信息信息信息信息信息信息信息信息小結(jié)適用于多線程請(qǐng)求數(shù)量資源的場景,但無法解決單多個(gè)線程對(duì)同一資源訪問的競爭性訪問。在后面我們?cè)谖覀兊亩嗑€程工具箱里面陸續(xù)會(huì)提到。 Java多線程工具箱之Semaphore 前言 這一篇談一下Semaphore:信號(hào)量。 將Semaphore類比為為信號(hào)燈,被繼承Runable的線程類比為列車:理解信號(hào)量...
摘要:當(dāng)線程使用完共享資源后,可以歸還許可,以供其它需要的線程使用。所以,并不會(huì)阻塞調(diào)用線程。立即減少指定數(shù)目的可用許可數(shù)。方法用于將可用許可數(shù)清零,并返回清零前的許可數(shù)六的類接口聲明類聲明構(gòu)造器接口聲明 showImg(https://segmentfault.com/img/bVbfdnC?w=1920&h=1200); 本文首發(fā)于一世流云的專欄:https://segmentfault...
摘要:線程啟動(dòng)規(guī)則對(duì)象的方法先行發(fā)生于此線程的每一個(gè)動(dòng)作。所以局部變量是不被多個(gè)線程所共享的,也就不會(huì)出現(xiàn)并發(fā)問題。通過獲取到數(shù)據(jù),放入當(dāng)前線程處理完之后將當(dāng)前線程中的信息移除。主線程必須在啟動(dòng)其他線程后立即調(diào)用方法。 一、線程安全性 定義:當(dāng)多個(gè)線程訪問某個(gè)類時(shí),不管運(yùn)行時(shí)環(huán)境采用何種調(diào)度方式,或者這些線程將如何交替執(zhí)行,并且在主調(diào)代碼中不需要任何額外的同步或協(xié)同,這個(gè)類都能表現(xiàn)出正確的行...
摘要:倒計(jì)時(shí)鎖,線程中調(diào)用使進(jìn)程進(jìn)入阻塞狀態(tài),當(dāng)達(dá)成指定次數(shù)后通過繼續(xù)執(zhí)行每個(gè)線程中剩余的內(nèi)容。實(shí)現(xiàn)分階段的的功能測試代碼拿客網(wǎng)站群三產(chǎn)創(chuàng)建于年月日。 同步器 為每種特定的同步問題提供了解決方案 Semaphore Semaphore【信號(hào)標(biāo);旗語】,通過計(jì)數(shù)器控制對(duì)共享資源的訪問。 測試類: package concurrent; import concurrent.th...
摘要:基礎(chǔ)問題的的性能及原理之區(qū)別詳解備忘筆記深入理解流水線抽象關(guān)鍵字修飾符知識(shí)點(diǎn)總結(jié)必看篇中的關(guān)鍵字解析回調(diào)機(jī)制解讀抽象類與三大特征時(shí)間和時(shí)間戳的相互轉(zhuǎn)換為什么要使用內(nèi)部類對(duì)象鎖和類鎖的區(qū)別,,優(yōu)缺點(diǎn)及比較提高篇八詳解內(nèi)部類單例模式和 Java基礎(chǔ)問題 String的+的性能及原理 java之yield(),sleep(),wait()區(qū)別詳解-備忘筆記 深入理解Java Stream流水...
閱讀 732·2021-11-24 10:30
閱讀 1267·2021-09-24 09:48
閱讀 3083·2021-09-24 09:47
閱讀 3602·2019-08-29 17:11
閱讀 2885·2019-08-29 15:38
閱讀 2280·2019-08-29 11:03
閱讀 3606·2019-08-26 12:15
閱讀 1018·2019-08-26 10:45