欲窮千里目,更上一層樓。—唐
這句詩的意思是:想看到更遠更廣闊的景物,你就要再上一層樓。想學到更多更深的知識,你就要比原來更努力。
PS: 如果覺得本文有用的話,請幫忙點贊,留言評論支持一下哦,您的支持是我較大的動力!謝謝啦~
Semaphore,計數信號量, 用來控制同時訪問某個特定資源的線程數量 ,需要我們設定它的較大訪問數量。 Semaphore 管理著一組虛擬許可,許可的初始數量可以通過構造函數來指定。在執行操作時可以首先獲取許可,并在使用后釋放許可。如果沒有許可,那么獲取操作將阻塞直到有可用的許可。
Semaphore 可以用于實現一個資源池,也可以將任何一個容器變成一個有界的阻塞容器,他在限制資源訪問量上有很大的用處。
Semaphore 的核心方法
首先,我們先來看它的兩個構造函數。
** * Creates a {@code Semaphore} with the given number of * permits and nonfair fairness setting. *public Semaphore(int permits { sync = new NonfairSync(permits}** * Creates a {@code Semaphore} with the given number of * permits and the given fairness setting. *public Semaphore(int permits, boolean fair { sync = fair ? new FairSync(permits : new NonfairSync(permits}<pre>
參數 permits 表示許可數量,即同時允許多少個線程訪問。參數 fair 表示公平性,即等待越久越先獲取到許可。
其次,再來看一下它獲取和釋放許可的方法,信號量的核心用法就是下面這些。
獲取一個許可public void acquire( throws InterruptedException { }獲取permits個許可public void acquire(int permits throws InterruptedException { } 釋放一個許可public void release( { } 釋放permits個許可public void release(int permits { } 嘗試獲取一個許可,若獲取成功,則立即返回true,若獲取失敗,則立即返回falsepublic boolean tryAcquire( { } 嘗試獲取一個許可,若在指定的時間內獲取成功,則立即返回true,否則則立即返回false public boolean tryAcquire(long timeout, TimeUnit unit throws InterruptedException { } 嘗試獲取permits個許可,若獲取成功,則立即返回true,若獲取失敗,則立即返回falsepublic boolean tryAcquire(int permits { }嘗試獲取permits個許可,若在指定的時間內獲取成功,則立即返回true,否則則立即返回falsepublic boolean tryAcquire(int permits, long timeout, TimeUnit unit throws InterruptedException { }<pre>
使用場景
前面說過,Semaphore 可以用于實現一個資源池。所以,我們用它來實現一個固定數量的消息池,只允許固定數量的線程同時訪問。
這個例子中消息池的數量為 3 個,信號量的許可數量也設置為 3 個,即用 Semaphore 來控制較多同時只能有三個線程使用,其中消息可以循環使用。
如果已有三個線程已經獲取到了消息,那么其他線程獲取消息的時候將會阻塞,直到有線程釋放消息,它才能獲取到。獲取消息和釋放消息通過 Semaphore 的 acqure( 和 release( 方法進行控制,其中 Semaphore 的許可數量不應大于消息池的較大數量。
import java.util.concurrent.Semaphorepublic class SemaphoreTest{表示消息池可用消息只有5個private static final int MAX_POOL_SIZE = 3獲取消息的客戶端的線程數量private static final int CLIENT_SIZE = 6 消息數組,存放所有消息private static Message messages = new Message[MAX_POOL_SIZE] 信號量,許可數量為消息的較大可用數量private static Semaphore semaphore = new Semaphore(MAX_POOL_SIZE初始化消息數組static void init( { for(int i = 0 i < MAX_POOL_SIZE i++ { messages[i] = new Message( }}同步方法,獲取可用的消息static synchronized Message obtain( { Message msg = null for(int i = 0 i < MAX_POOL_SIZE i++ { if(messages[i].getFlag( == false { msg = messages[i] msg.setId(i msg.setFlag(true return msg } } return msg}同步方法,把用完的消息放回消息池static synchronized boolean release(Message msg { if(msg.getFlag( == true { msg.setFlag(false msg.setId(-1 return true } return false}用信號量控制能獲取消息的數目static Message obtainMsg( throws InterruptedException { semaphore.acquire( return obtain(}成功釋放消息的同時釋放信號量static void releaseMsg(Message msg { System.out.print(Thread.currentThread(.getName( + " ***Release msg id*** = "+ msg.getId( + "n" if(release(msg { semaphore.release( }}public static void main(String args { 初始化 init( 創建子線程,獲取消息 for(int i = 0 i < CLIENT_SIZE i++ { new Thread(new Runnable( { @Override public void run( { try { 獲取消息 Message msg = obtainMsg( System.out.print(Thread.currentThread(.getName( + " Obtain msg id = "+ msg.getId( + "n" 假裝耗時操作 Thread.sleep(1000 釋放消息 releaseMsg(msg } catch (InterruptedException e { e.printStackTrace( } }}.start( }}聲明一個消息類static class Message { private int id 表示每個消息的id private boolean flag 表示消息是否可用 public Message( { this.id = -1 this.flag = false } public void setId(int id { this.id = id } public void setFlag(boolean b { this.flag = b } public int getId( { return this.id } public boolean getFlag( { return this.flag }}}<pre>
執行結果:
這里寫圖片描述
從這個結果看,線程 1,線程 2,線程 0 先獲取到消息;接著線程 1 和 2 釋放消息;釋放消息后,那么此時消息池又有兩個空閑消息,所以,線程 3 和線程 5 獲取了消息;
緊接著線程 0 釋放消息,線程 4 立馬獲取了消息。。。
這程序的執行結果和我們預期的流程一樣。 需要注意的點,Semaphore 是線程安全的。在這個例子中,不可能同時有 4 個線程能同時獲取到消息。
注意
既然 Semaphore 是線程安全的,為什么上面兩個方法需要添加同步?
static synchronized boolean release(Message msgstatic synchronized Message obtain(<pre>
這里我們不能混淆概念,Semaphore 的線程安全是指同時只能有三個線程進入,即 acquire( 和 release( 必定線程安全。 然而獲取到許可后的操作不保證線程安全,所以這里加同步鎖是為了確保獲取消息的過程是安全的。
另外一點需要注意,為什么下面兩個方法不需要使用同步鎖?
static void releaseMsg(Message msgstatic Message obtainMsg( throws InterruptedException<pre>
細心的朋友可能已經知道,這里加上同步的話,會產生死鎖。假如此時 acquire( 發生阻塞,那么obtainMsg( 一直持有同步鎖,而 releaseMsg( 的時候必須等待同步鎖的釋放,這時必定陷入死鎖,一直死等,然而沒什么軟用。
這里不需要加同步鎖,是因為我們要確保安全的內容是 獲取許可集 后的數據安全,和釋放許可集之前的數據安全。
本文完結,如果覺得有幫助,請關注我哦,謝謝啦~