本人郵箱: <kco1989@qq.com>
歡迎轉載,轉載請註明網址 http://blog.csdn.net/tianshi_kco
github: https://github.com/kco1989/kco
代碼已經所有託管github有須要的同窗自行下載java
這節課,咱們就開始講一下信號量Semaphore
git
Semaphore
:一個可計數的信號量。通常,一個semaphore
信號量是一組許可證。若是必要,那個每次acquire
獲取許可都是阻塞的,直接一個許可證是可用的,並獲取到。每次release
釋放,都會增長一個許可證,潛在的,也會釋放一個阻塞請求。然而。並不是每次許可對象均可以被使用的,這個Semaphore
信號量只保存幾個可用的許可證和相應的操做。github
若是有幾個線程數要訪問幾個共享資源的話,那麼這時候就應該使用信號量。舉例說明:這個有類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()
在當前信號量中獲取一個許可.當前線程會一直阻塞直到有一個可用的許可,或被其餘線程中斷.ide
acquireUninterruptibly()
: 在當前信號量中獲取一個許可.當前線程會一直阻塞直到有一個可用的許可.測試
tryAcquire()
在當前信號量嘗試獲取一個許可,若是有可用,則獲取到這個許可,並當即返回true
,後綴當即返回false
ui
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) { Set<Thread> boyAndGril = 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號 終於洗完澡了...
發現這幾十我的都同時在三間沐浴室裏洗澡,那麼確定有只是一間會出現兩人或兩人以上同時洗澡的狀況.若是浴室夠大,你們都沒有意見,那還好.就是若是肥皂掉了,這個時候,小明
就得考慮要不要彎腰去撿了....
若是以爲個人文章寫的還過得去的話,有錢就捧個錢場,沒錢給我捧我的場(幫我點贊或推薦一下)