摘要:在每個線程獲取之前,必須先從信號量獲取許可。注意,因為同時可能發生取消,所以返回并不保證有其他線程等待獲取許可。該值僅是估計的數字,因為在此方法遍歷內部數據結構的同時,線程的數目可能動態地變化。
引言本人郵箱:
歡迎轉載,轉載請注明網址 http://blog.csdn.net/tianshi_kco
github: https://github.com/kco1989/kco
代碼已經全部托管github有需要的同學自行下載
這節課,我們就開始講一下信號量Semaphore
理論Semaphore:一個可計數的信號量。一般,一個semaphore 信號量是一組許可證。如果必要,那個每次acquire獲取許可都是阻塞的,直接一個許可證是可用的,并獲取到。每次release釋放,都會增加一個許可證,潛在的,也會釋放一個阻塞請求。然而。并非每次許可對象都可以被使用的,這個Semaphore信號量只保存幾個可用的許可證和相應的操作。
如果有幾個線程數要訪問幾個共享資源的話,那么這時候就應該使用信號量。舉例說明:這個有類Pool類,它就使用信號量在控制多線程去訪問那么幾個有限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; } }
在每個線程獲取item之前,必須先從信號量獲取許可。保證這個item對用戶來說是可以使用的。當線程結束使用item時,并讓item返回item池,這信號量會釋放這個許可,之后允許使用線程可以獲取到這個item。必須要注意的,在程序中,在獲取許可和釋放許可的死胡同并沒有使用同步鎖,信號量封裝了限制對池的訪問所需的同步,與維護池本身的一致性所需的任何同步。
Semaphore(int permits): 創建一個指定數量的許可的信號量
Semaphore(int permits, boolean fair) 創建一個指定數量的許可,并保證每個線程都是公平的,當fair為true時,信號量會安裝先進先出的原則來獲取許可.
acquire() 在當前信號量中獲取一個許可.當前線程會一直阻塞直到有一個可用的許可,或被其他線程中斷.
acquireUninterruptibly(): 在當前信號量中獲取一個許可.當前線程會一直阻塞直到有一個可用的許可.
tryAcquire() 在當前信號量嘗試獲取一個許可,如果有可用,則獲取到這個許可,并立即返回true,后綴立即返回false
tryAcquire 在當前信號量獲取一個許可,當前線程會一直阻塞直到有一個可用的許可.或指定時間超時了,或被其他線程中斷.
release() 釋放一個許可,把讓它返回到這個信號量中.
acquire(int permits) 請求指定數量的許可,如果有足夠的許可可用,那么當前線程會立刻返回,如果許可不足,則當前會一直等待,直到被其他線程中斷,或獲取到足夠的許可.
acquireUninterruptibly(int permits) 請求指定數量的許可,如果有足夠的許可可用,那么當前線程會立刻返回,如果許可不足,則當前會一直等待,直到獲取到足夠的許可.
tryAcquire(int permits) 在當前信號量嘗試獲取指定數量的許可,如果有可用,則獲取到這個許可,并立即返回true,后綴立即返回false
tryAcquire(int permits, long timeout, TimeUnit unit) 在指定的超時時間,當前信號量嘗試獲取指定數量的許可,如果有可用,則獲取到這個許可,并立即返回true,后綴立即返回false
release(int permits) 釋放指定數量的許可
availablePermits() 返回當前信號量還有幾個可用的許可
drainPermits() 請求并立即返回當前信號量可用的全部許可
reducePermits(int reduction) 根據指定的縮減量減小可用許可的數目。此方法在使用信號量來跟蹤那些變為不可用資源的子類中很有用。此方法不同于 acquire,在許可變為可用的過程中,它不會阻塞等待。
isFair() 返回當前的信號量時候是公平的
hasQueuedThreads() 查詢是否有線程正在等待獲取。注意,因為同時可能發生取消,所以返回 true 并不保證有其他線程等待獲取許可。此方法主要用于監視系統狀態。
getQueueLength() 返回正在等待獲取的線程的估計數目。該值僅是估計的數字,因為在此方法遍歷內部數據結構的同時,線程的數目可能動態地變化。此方法用于監視系統狀態,不用于同步控制。
getQueuedThreads() 返回一個 collection,包含可能等待獲取的線程。因為在構造此結果的同時實際的線程 set 可能動態地變化,所以返回的 collection 僅是盡力的估計值。所返回 collection 中的元素沒有特定的順序。此方法用于加快子類的構造速度,提供更多的監視設施。
例子看了前面那么方法的介紹,恐怕你想吐的的心都有了吧?還是讓我們回歸輕松愉快的例子來吧.這里我們還是繼續舉小明和小紅談人生和理想的例子.之前他們在臥室里談了好幾百毫秒的人生和理想.頓時都感覺身疲憊,感覺身體好像被掏空了一樣.所以這里他們都想洗一個熱水澡,但是沐浴室只有三間,那就搶吧..ok,開始編程...
首先,先編寫一個沐浴室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 + "號")); } for (int i = 0; i < 10; i ++){ boyAndGril.add(new Thread(new BoyAndGril(showerRoom), "小紅" + i + "號")); } for (Thread thread : boyAndGril){ thread.start(); } } }
運行一下結果
小紅3號 洗唰唰啊..洗唰唰... 小紅6號 洗唰唰啊..洗唰唰... 小明0號 洗唰唰啊..洗唰唰... 小紅3號 終于洗完澡了... 小紅2號 洗唰唰啊..洗唰唰... 小紅6號 終于洗完澡了... 小明2號 洗唰唰啊..洗唰唰... 小明0號 終于洗完澡了... 小紅1號 洗唰唰啊..洗唰唰... 小紅2號 終于洗完澡了... 小明5號 洗唰唰啊..洗唰唰... 小明2號 終于洗完澡了... 小明7號 洗唰唰啊..洗唰唰... 小紅1號 終于洗完澡了... 小紅0號 洗唰唰啊..洗唰唰... 小明5號 終于洗完澡了... 小明7號 終于洗完澡了... 小明4號 洗唰唰啊..洗唰唰... 小紅4號 洗唰唰啊..洗唰唰... 小紅0號 終于洗完澡了... 小明3號 洗唰唰啊..洗唰唰... 小明4號 終于洗完澡了... 小明9號 洗唰唰啊..洗唰唰... 小紅4號 終于洗完澡了... 小紅7號 洗唰唰啊..洗唰唰... 小明3號 終于洗完澡了... 小紅5號 洗唰唰啊..洗唰唰... 小紅5號 終于洗完澡了... 小紅9號 洗唰唰啊..洗唰唰... 小紅7號 終于洗完澡了... 小明6號 洗唰唰啊..洗唰唰... 小明9號 終于洗完澡了... 小明1號 洗唰唰啊..洗唰唰... 小紅9號 終于洗完澡了... 小紅8號 洗唰唰啊..洗唰唰... 小明1號 終于洗完澡了... 小明8號 洗唰唰啊..洗唰唰... 小明6號 終于洗完澡了... 小紅8號 終于洗完澡了... 小明8號 終于洗完澡了...
ok,運行正常,程序中不會發生四個人以及四個以上的人在同時洗澡的情況.
如果有人覺得這個好像也沒有使用什么共享資源啊,沒有上面那個例子的item pool,那行,那把有關semaphore的代碼注釋掉,再運行一下.
小紅3號 洗唰唰啊..洗唰唰... 小紅6號 洗唰唰啊..洗唰唰... 小明0號 洗唰唰啊..洗唰唰... 小紅2號 洗唰唰啊..洗唰唰... 小明2號 洗唰唰啊..洗唰唰... 小紅1號 洗唰唰啊..洗唰唰... 小明5號 洗唰唰啊..洗唰唰... 小明7號 洗唰唰啊..洗唰唰... 小紅0號 洗唰唰啊..洗唰唰... 小明4號 洗唰唰啊..洗唰唰... 小紅4號 洗唰唰啊..洗唰唰... 小明3號 洗唰唰啊..洗唰唰... 小明9號 洗唰唰啊..洗唰唰... 小紅7號 洗唰唰啊..洗唰唰... 小紅5號 洗唰唰啊..洗唰唰... 小紅9號 洗唰唰啊..洗唰唰... 小明6號 洗唰唰啊..洗唰唰... 小明1號 洗唰唰啊..洗唰唰... 小紅8號 洗唰唰啊..洗唰唰... 小明8號 洗唰唰啊..洗唰唰... 小紅3號 終于洗完澡了... 小紅2號 終于洗完澡了... 小明2號 終于洗完澡了... 小紅6號 終于洗完澡了... 小明0號 終于洗完澡了... 小明5號 終于洗完澡了... 小紅0號 終于洗完澡了... 小明7號 終于洗完澡了... 小紅1號 終于洗完澡了... 小明4號 終于洗完澡了... 小紅4號 終于洗完澡了... 小明3號 終于洗完澡了... 小明9號 終于洗完澡了... 小紅8號 終于洗完澡了... 小紅5號 終于洗完澡了... 小紅9號 終于洗完澡了... 小明6號 終于洗完澡了... 小明1號 終于洗完澡了... 小紅7號 終于洗完澡了... 小明8號 終于洗完澡了...
發現這幾十個人都同時在三間沐浴室里洗澡,那么肯定有只是一間會出現兩人或兩人以上同時洗澡的情況.如果浴室夠大,大家都沒有意見,那還好.就是如果肥皂掉了,這個時候,小明就得考慮要不要彎腰去撿了....
打賞如果覺得我的文章寫的還過得去的話,有錢就捧個錢場,沒錢給我捧個人場(幫我點贊或推薦一下)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/69898.html
摘要:多線程工具箱之前言這一篇談一下信號量。信息信息信息信息信息信息信息信息信息信息信息小結適用于多線程請求數量資源的場景,但無法解決單多個線程對同一資源訪問的競爭性訪問。在后面我們在我們的多線程工具箱里面陸續會提到。 Java多線程工具箱之Semaphore 前言 這一篇談一下Semaphore:信號量。 將Semaphore類比為為信號燈,被繼承Runable的線程類比為列車:理解信號量...
摘要:當線程使用完共享資源后,可以歸還許可,以供其它需要的線程使用。所以,并不會阻塞調用線程。立即減少指定數目的可用許可數。方法用于將可用許可數清零,并返回清零前的許可數六的類接口聲明類聲明構造器接口聲明 showImg(https://segmentfault.com/img/bVbfdnC?w=1920&h=1200); 本文首發于一世流云的專欄:https://segmentfault...
摘要:線程啟動規則對象的方法先行發生于此線程的每一個動作。所以局部變量是不被多個線程所共享的,也就不會出現并發問題。通過獲取到數據,放入當前線程處理完之后將當前線程中的信息移除。主線程必須在啟動其他線程后立即調用方法。 一、線程安全性 定義:當多個線程訪問某個類時,不管運行時環境采用何種調度方式,或者這些線程將如何交替執行,并且在主調代碼中不需要任何額外的同步或協同,這個類都能表現出正確的行...
摘要:倒計時鎖,線程中調用使進程進入阻塞狀態,當達成指定次數后通過繼續執行每個線程中剩余的內容。實現分階段的的功能測試代碼拿客網站群三產創建于年月日。 同步器 為每種特定的同步問題提供了解決方案 Semaphore Semaphore【信號標;旗語】,通過計數器控制對共享資源的訪問。 測試類: package concurrent; import concurrent.th...
摘要:基礎問題的的性能及原理之區別詳解備忘筆記深入理解流水線抽象關鍵字修飾符知識點總結必看篇中的關鍵字解析回調機制解讀抽象類與三大特征時間和時間戳的相互轉換為什么要使用內部類對象鎖和類鎖的區別,,優缺點及比較提高篇八詳解內部類單例模式和 Java基礎問題 String的+的性能及原理 java之yield(),sleep(),wait()區別詳解-備忘筆記 深入理解Java Stream流水...
閱讀 720·2021-11-24 10:30
閱讀 1254·2021-09-24 09:48
閱讀 3074·2021-09-24 09:47
閱讀 3588·2019-08-29 17:11
閱讀 2875·2019-08-29 15:38
閱讀 2270·2019-08-29 11:03
閱讀 3594·2019-08-26 12:15
閱讀 1008·2019-08-26 10:45