1. 程式人生 > >Semaphore的工作原理及例項

Semaphore的工作原理及例項

        Semaphore是一種在多執行緒環境下使用的設施,該設施負責協調各個執行緒,以保證它們能夠正確、合理的使用公共資源的設施,也是作業系統中用於控制程序同步互斥的量。Semaphore是一種計數訊號量,用於管理一組資源,內部是基於AQS的共享模式。它相當於給執行緒規定一個量從而控制允許活動的執行緒數。

1.工作原理

以一個停車場是運作為例。為了簡單起見,假設停車場只有三個車位,一開始三個車位都是空的。這時如果同時來了五輛車,看門人允許其中三輛不受阻礙的進入,然後放下車攔,剩下的車則必須在入口等待,此後來的車也都不得不在入口處等待。這時,有一輛車離開停車場,看門人得知後,開啟車攔,放入一輛,如果又離開兩輛,則又可以放入兩輛,如此往復。這個停車系統中,每輛車就好比一個執行緒,看門人就好比一個訊號量,看門人限制了可以活動的執行緒。假如裡面依然是三個車位,但是看門人改變了規則,要求每次只能停兩輛車,那麼一開始進入兩輛車,後面得等到有車離開才能有車進入,但是得保證最多停兩輛車。對於Semaphore類而言,就如同一個看門人,限制了可活動的執行緒數。

Semaphore主要方法:

Semaphore(int permits):構造方法,建立具有給定許可數的計數訊號量並設定為非公平訊號量。

Semaphore(int permits,boolean fair):構造方法,當fair等於true時,建立具有給定許可數的計數訊號量並設定為公平訊號量。

void acquire():從此訊號量獲取一個許可前執行緒將一直阻塞。相當於一輛車佔了一個車位。

void acquire(int n):從此訊號量獲取給定數目許可,在提供這些許可前一直將執行緒阻塞。比如n=2,就相當於一輛車佔了兩個車位。

void release():釋放一個許可,將其返回給訊號量。就如同車開走返回一個車位。

void release(int n):釋放n個許可。

int availablePermits():當前可用的許可數。

2.例項講解

        接下來舉個例子,就是關於每個人的個人資訊,那麼一個人佔用一個執行緒,並用Semphore類建立物件從而初始化訊號量,控制可活動的執行緒數。具體程式碼如下:

package concurrent;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.LinkedBlockingQueue;
public class SemaphoreDemo {
	private static final Semaphore semaphore=new Semaphore(3);
	private static final ThreadPoolExecutor threadPool=new ThreadPoolExecutor(5,10,60,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
	
	private static class InformationThread extends Thread{
		private final String name;
		private final int age;
		public InformationThread(String name,int age)
		{
			this.name=name;
			this.age=age;
		}
		
		public void run()
		{
			try
			{
				semaphore.acquire();
				System.out.println(Thread.currentThread().getName()+":大家好,我是"+name+"我今年"+age+"歲當前時間為:"+System.currentTimeMillis());
				Thread.sleep(1000);
				System.out.println(name+"要準備釋放許可證了,當前時間為:"+System.currentTimeMillis());
				System.out.println("當前可使用的許可數為:"+semaphore.availablePermits());
				semaphore.release();
				
			}
			catch(InterruptedException e)
			{
				e.printStackTrace();
			}
		}
	}
	public static void main(String[] args)
	{
		String[] name= {"李明","王五","張傑","王強","趙二","李四","張三"};
		int[] age= {26,27,33,45,19,23,41};
		for(int i=0;i<7;i++)
		{
			Thread t1=new InformationThread(name[i],age[i]);
			threadPool.execute(t1);
		}
	}

}

執行上述程式結果如下:

pool-1-thread-3:大家好,我是張傑我今年33歲當前時間為:1520424000186
pool-1-thread-1:大家好,我是李明我今年26歲當前時間為:1520424000186
pool-1-thread-2:大家好,我是王五我今年27歲當前時間為:1520424000186
張傑要準備釋放許可證了,當前時間為:1520424001187
李明要準備釋放許可證了,當前時間為:1520424001187
王五要準備釋放許可證了,當前時間為:1520424001187
當前可使用的許可數為:0
當前可使用的許可數為:0
當前可使用的許可數為:0
pool-1-thread-4:大家好,我是王強我今年45歲當前時間為:1520424001187
pool-1-thread-2:大家好,我是張三我今年41歲當前時間為:1520424001187
pool-1-thread-1:大家好,我是李四我今年23歲當前時間為:1520424001187
李四要準備釋放許可證了,當前時間為:1520424002187
王強要準備釋放許可證了,當前時間為:1520424002187
當前可使用的許可數為:0
張三要準備釋放許可證了,當前時間為:1520424002187
pool-1-thread-5:大家好,我是趙二我今年19歲當前時間為:1520424002187
當前可使用的許可數為:0
當前可使用的許可數為:0
趙二要準備釋放許可證了,當前時間為:1520424003188
當前可使用的許可數為:2

以上是非公平訊號量,將建立Semaphore物件的語句改為如下語句:

private static final Semaphore semaphore=new Semaphore(3,true);

執行程式:

pool-1-thread-2:大家好,我是王五我今年27歲當前時間為:1520424286454
pool-1-thread-3:大家好,我是張傑我今年33歲當前時間為:1520424286454
pool-1-thread-1:大家好,我是李明我今年26歲當前時間為:1520424286454
pool-1-thread-1:李明要準備釋放許可證了,當前時間為:1520424287455
當前可使用的許可數為:0
pool-1-thread-2:王五要準備釋放許可證了,當前時間為:1520424287455
pool-1-thread-3:張傑要準備釋放許可證了,當前時間為:1520424287455
當前可使用的許可數為:0
當前可使用的許可數為:1
pool-1-thread-1:大家好,我是李四我今年23歲當前時間為:1520424287455
pool-1-thread-5:大家好,我是趙二我今年19歲當前時間為:1520424287455
pool-1-thread-4:大家好,我是王強我今年45歲當前時間為:1520424287455
pool-1-thread-4:王強要準備釋放許可證了,當前時間為:1520424288456
當前可使用的許可數為:0
pool-1-thread-1:李四要準備釋放許可證了,當前時間為:1520424288456
pool-1-thread-3:大家好,我是張三我今年41歲當前時間為:1520424288456
pool-1-thread-5:趙二要準備釋放許可證了,當前時間為:1520424288456
當前可使用的許可數為:0
當前可使用的許可數為:0
pool-1-thread-3:張三要準備釋放許可證了,當前時間為:1520424289456
當前可使用的許可數為:2

3.實現單例模式

        將建立訊號量物件語句修改如下:

private static final Semaphore semaphore=new Semaphore(1);

        執行程式,結果如下:

pool-1-thread-1:大家好,我是李明我今年26歲當前時間為:1520424379699
pool-1-thread-1:李明要準備釋放許可證了,當前時間為:1520424380700
當前可使用的許可數為:0
pool-1-thread-2:大家好,我是王五我今年27歲當前時間為:1520424380700
pool-1-thread-2:王五要準備釋放許可證了,當前時間為:1520424381701
當前可使用的許可數為:0
pool-1-thread-3:大家好,我是張傑我今年33歲當前時間為:1520424381701
pool-1-thread-3:張傑要準備釋放許可證了,當前時間為:1520424382702
當前可使用的許可數為:0
pool-1-thread-4:大家好,我是王強我今年45歲當前時間為:1520424382702
pool-1-thread-4:王強要準備釋放許可證了,當前時間為:1520424383702
當前可使用的許可數為:0
pool-1-thread-5:大家好,我是趙二我今年19歲當前時間為:1520424383702
pool-1-thread-5:趙二要準備釋放許可證了,當前時間為:1520424384702
當前可使用的許可數為:0
pool-1-thread-1:大家好,我是李四我今年23歲當前時間為:1520424384702
pool-1-thread-1:李四要準備釋放許可證了,當前時間為:1520424385702
當前可使用的許可數為:0
pool-1-thread-2:大家好,我是張三我今年41歲當前時間為:1520424385702
pool-1-thread-2:張三要準備釋放許可證了,當前時間為:1520424386703
當前可使用的許可數為:0

        如上可知,如果將給定許可數設定為1,就如同一個單例模式,即單個停車位,只有一輛車進,然後這輛車出來後,下一輛車才能進。

4.總結

        Semaphore主要用於控制當前活動執行緒數目,就如同停車場系統一般,而Semaphore則相當於看守的人,用於控制總共允許停車的停車位的個數,而對於每輛車來說就如同一個執行緒,執行緒需要通過acquire()方法獲取許可,而release()釋放許可。如果許可數達到最大活動數,那麼呼叫acquire()之後,便進入等待佇列,等待已獲得許可的執行緒釋放許可,從而使得多執行緒能夠合理的執行。