1. 程式人生 > >JAVA併發程式設計——Semaphore(訊號量)

JAVA併發程式設計——Semaphore(訊號量)

控制併發執行緒數的Semaphore

Semaphore(訊號量)是用來控制同時訪問特定資源的執行緒數量,它通過協調各個執行緒,以保證合理的使用公共資源。應用場景Semaphore可以用於做流量控制,特別是公用資源有限的應用場景,比如資料庫連線。假如有一個需求,幾十個執行緒併發地儲存到資料庫中,而資料庫的連線數只有10個,這時我們必須控制只有10個執行緒同時獲取資料庫連線儲存資料,否則會報錯無法獲取資料庫連線。這個時候,就可以使用Semaphore來做流量控制。

Semaphore的構造方法**Semaphore(int permits)**接受一個整型的數字,表示可用的許可證數量。Semaphore的用法也很簡單,首先執行緒使用Semaphore的acquire()方法獲取一個許可證,如果沒有許可證,則阻塞。使用完之後呼叫release()方法歸還許可證。還可以用tryAcquire()方法嘗試獲取許可證。

Semaphore還提供一些其他方法,具體如下:

intavailablePermits():返回此訊號量中當前可用的許可證數。

intgetQueueLength():返回正在等待獲取許可證的執行緒數。

booleanhasQueuedThreads():是否有執行緒正在等待獲取許可證。

void reducePermits(int reduction):減少reduction個許可證,是個protected方法。

Collection getQueuedThreads():返回所有等待獲取許可證的執行緒集合,是個protected方法。

例項程式碼

下面看一個資料庫連線池的例子。演示Semaphore用法,一個數據庫連線池的實現。

實現一個數據庫連線,平庸實現,不做任何操作

public class SqlConnectImpl implements Connection{
	
	/*拿一個數據庫連線*/
    public static final Connection fetchConnection(){
        return new SqlConnectImpl();
    }
//實現介面方法,但不做任操作
}

自定義資料連線池

public class DBPoolSemaphore {

	//資料庫連線的數量
	private final static int POOL_SIZE = 10;
	//兩個指示器,分別表示池子還有可用連線和已用連線
	private final Semaphore useful,useless;
	//存放資料庫連線的容器
	private static LinkedList<Connection> pool = new LinkedList<>();
	//初始化池
	static {
        for (int i = 0; i < POOL_SIZE; i++) {
            pool.addLast(SqlConnectImpl.fetchConnection());
        }
	}
	//初始化許可證
	public DBPoolSemaphore() {
		this.useful = new Semaphore(10);
		this.useless = new Semaphore(0);
	}
	
	/*歸還連線*/
	public void returnConnect(Connection connection) throws InterruptedException {
		if(connection!=null) {
			System.out.println("當前有"+useful.getQueueLength()+"個執行緒等待資料庫連線!!"
					+"可用連線數:"+useful.availablePermits());
			//已用連接獲取許可證
			useless.acquire();
			synchronized (pool) {
				pool.addLast(connection);
			}
			//可用連線
			useful.release();
		}
	}
	
	/*從池子拿連線*/
	public Connection takeConnect() throws InterruptedException {
		useful.acquire();
		Connection connection;
		synchronized (pool) {
			//從頭上拿一個連線並返回
			connection = pool.removeFirst();
		}
		useless.release();
		return connection;
	}
	
}

測試資料庫連線池

public class AppTest {

	private static DBPoolSemaphore dbPool = new DBPoolSemaphore();

	//拿資料庫連線的執行緒
	private static class BusiThread extends Thread{
		@Override
		public void run() {
			Random r = new Random();//讓每個執行緒持有連線的時間不一樣
			long start = System.currentTimeMillis();
			try {
				Connection connect = dbPool.takeConnect();
				System.out.println("Thread_"+Thread.currentThread().getId()
						+"_獲取資料庫連線共耗時【"+(System.currentTimeMillis()-start)+"】ms.");
				SleepTools.ms(100+r.nextInt(100));//模擬業務操作,執行緒持有連線查詢資料
				System.out.println("查詢資料完成,歸還連線!");
				dbPool.returnConnect(connect);
			} catch (InterruptedException e) {
			}
		}
	}
	
	public static void main(String[] args) {
        for (int i = 0; i < 50; i++) {
            Thread thread = new BusiThread();
            thread.start();
        }
	}
	
}

結果如下:

Thread_19_獲取資料庫連線共耗時【0】ms.
Thread_23_獲取資料庫連線共耗時【0】ms.
Thread_12_獲取資料庫連線共耗時【0】ms.
Thread_14_獲取資料庫連線共耗時【0】ms.
Thread_18_獲取資料庫連線共耗時【0】ms.
Thread_20_獲取資料庫連線共耗時【0】ms.
Thread_16_獲取資料庫連線共耗時【0】ms.
Thread_17_獲取資料庫連線共耗時【0】ms.
Thread_13_獲取資料庫連線共耗時【0】ms.
Thread_15_獲取資料庫連線共耗時【0】ms.
查詢資料完成,歸還連線!
當前有40個執行緒等待資料庫連線!!可用連線數:0
Thread_22_獲取資料庫連線共耗時【154】ms.
查詢資料完成,歸還連線!
當前有39個執行緒等待資料庫連線!!可用連線數:0
Thread_21_獲取資料庫連線共耗時【163】ms.
查詢資料完成,歸還連線!
當前有38個執行緒等待資料庫連線!!可用連線數:0
Thread_25_獲取資料庫連線共耗時【193】ms.
查詢資料完成,歸還連線!
當前有37個執行緒等待資料庫連線!!可用連線數:0
Thread_24_獲取資料庫連線共耗時【195】ms.
查詢資料完成,歸還連線!
當前有36個執行緒等待資料庫連線!!可用連線數:0
Thread_26_獲取資料庫連線共耗時【198】ms.
查詢資料完成,歸還連線!
當前有35個執行緒等待資料庫連線!!可用連線數:0
Thread_27_獲取資料庫連線共耗時【210】ms.
查詢資料完成,歸還連線!
當前有34個執行緒等待資料庫連線!!可用連線數:0
Thread_28_獲取資料庫連線共耗時【211】ms.
查詢資料完成,歸還連線!
當前有33個執行緒等待資料庫連線!!可用連線數:0
Thread_30_獲取資料庫連線共耗時【225】ms.
查詢資料完成,歸還連線!
當前有32個執行緒等待資料庫連線!!可用連線數:0
Thread_31_獲取資料庫連線共耗時【237】ms.
查詢資料完成,歸還連線!
當前有31個執行緒等待資料庫連線!!可用連線數:0
Thread_32_獲取資料庫連線共耗時【247】ms.
查詢資料完成,歸還連線!
當前有30個執行緒等待資料庫連線!!可用連線數:0
Thread_34_獲取資料庫連線共耗時【276】ms.
查詢資料完成,歸還連線!
當前有29個執行緒等待資料庫連線!!可用連線數:0
Thread_33_獲取資料庫連線共耗時【316】ms.
查詢資料完成,歸還連線!
當前有28個執行緒等待資料庫連線!!可用連線數:0
Thread_36_獲取資料庫連線共耗時【335】ms.
查詢資料完成,歸還連線!
當前有27個執行緒等待資料庫連線!!可用連線數:0
Thread_35_獲取資料庫連線共耗時【336】ms.
查詢資料完成,歸還連線!
當前有26個執行緒等待資料庫連線!!可用連線數:0
Thread_37_獲取資料庫連線共耗時【347】ms.
查詢資料完成,歸還連線!
當前有25個執行緒等待資料庫連線!!可用連線數:0
Thread_38_獲取資料庫連線共耗時【359】ms.
查詢資料完成,歸還連線!
當前有24個執行緒等待資料庫連線!!可用連線數:0
Thread_39_獲取資料庫連線共耗時【369】ms.
查詢資料完成,歸還連線!
當前有23個執行緒等待資料庫連線!!可用連線數:0
Thread_40_獲取資料庫連線共耗時【381】ms.
查詢資料完成,歸還連線!
當前有22個執行緒等待資料庫連線!!可用連線數:0
Thread_41_獲取資料庫連線共耗時【401】ms.
查詢資料完成,歸還連線!
當前有21個執行緒等待資料庫連線!!可用連線數:0
Thread_42_獲取資料庫連線共耗時【404】ms.
查詢資料完成,歸還連線!
當前有20個執行緒等待資料庫連線!!可用連線數:0
Thread_43_獲取資料庫連線共耗時【446】ms.
查詢資料完成,歸還連線!
當前有19個執行緒等待資料庫連線!!可用連線數:0
Thread_44_獲取資料庫連線共耗時【459】ms.
查詢資料完成,歸還連線!
當前有18個執行緒等待資料庫連線!!可用連線數:0
Thread_45_獲取資料庫連線共耗時【479】ms.
查詢資料完成,歸還連線!
當前有17個執行緒等待資料庫連線!!可用連線數:0
Thread_46_獲取資料庫連線共耗時【509】ms.
查詢資料完成,歸還連線!
當前有16個執行緒等待資料庫連線!!可用連線數:0
查詢資料完成,歸還連線!
Thread_47_獲取資料庫連線共耗時【520】ms.
當前有15個執行緒等待資料庫連線!!可用連線數:0
Thread_48_獲取資料庫連線共耗時【521】ms.
查詢資料完成,歸還連線!
當前有14個執行緒等待資料庫連線!!可用連線數:0
Thread_49_獲取資料庫連線共耗時【533】ms.
查詢資料完成,歸還連線!
當前有13個執行緒等待資料庫連線!!可用連線數:0
Thread_50_獲取資料庫連線共耗時【542】ms.
查詢資料完成,歸還連線!
查詢資料完成,歸還連線!
當前有12個執行緒等待資料庫連線!!可用連線數:0
當前有12個執行緒等待資料庫連線!!可用連線數:0
Thread_51_獲取資料庫連線共耗時【557】ms.
Thread_52_獲取資料庫連線共耗時【557】ms.
查詢資料完成,歸還連線!
當前有10個執行緒等待資料庫連線!!可用連線數:0
Thread_53_獲取資料庫連線共耗時【564】ms.
查詢資料完成,歸還連線!
當前有9個執行緒等待資料庫連線!!可用連線數:0
Thread_54_獲取資料庫連線共耗時【599】ms.
查詢資料完成,歸還連線!
當前有8個執行緒等待資料庫連線!!可用連線數:0
Thread_55_獲取資料庫連線共耗時【613】ms.
查詢資料完成,歸還連線!
當前有7個執行緒等待資料庫連線!!可用連線數:0
Thread_56_獲取資料庫連線共耗時【629】ms.
查詢資料完成,歸還連線!
當前有6個執行緒等待資料庫連線!!可用連線數:0
Thread_57_獲取資料庫連線共耗時【635】ms.
查詢資料完成,歸還連線!
當前有5個執行緒等待資料庫連線!!可用連線數:0
Thread_58_獲取資料庫連線共耗時【654】ms.
查詢資料完成,歸還連線!
當前有4個執行緒等待資料庫連線!!可用連線數:0
Thread_59_獲取資料庫連線共耗時【672】ms.
查詢資料完成,歸還連線!
當前有3個執行緒等待資料庫連線!!可用連線數:0
Thread_60_獲取資料庫連線共耗時【697】ms.
查詢資料完成,歸還連線!
當前有2個執行緒等待資料庫連線!!可用連線數:0
Thread_61_獲取資料庫連線共耗時【709】ms.
查詢資料完成,歸還連線!
當前有1個執行緒等待資料庫連線!!可用連線數:0
Thread_29_獲取資料庫連線共耗時【717】ms.
查詢資料完成,歸還連線!
當前有0個執行緒等待資料庫連線!!可用連線數:0
查詢資料完成,歸還連線!
當前有0個執行緒等待資料庫連線!!可用連線數:1
查詢資料完成,歸還連線!
當前有0個執行緒等待資料庫連線!!可用連線數:2
查詢資料完成,歸還連線!
當前有0個執行緒等待資料庫連線!!可用連線數:3
查詢資料完成,歸還連線!
當前有0個執行緒等待資料庫連線!!可用連線數:4
查詢資料完成,歸還連線!
當前有0個執行緒等待資料庫連線!!可用連線數:5
查詢資料完成,歸還連線!
當前有0個執行緒等待資料庫連線!!可用連線數:6
查詢資料完成,歸還連線!
當前有0個執行緒等待資料庫連線!!可用連線數:7
查詢資料完成,歸還連線!
當前有0個執行緒等待資料庫連線!!可用連線數:8
查詢資料完成,歸還連線!
當前有0個執行緒等待資料庫連線!!可用連線數:9

最開始的執行緒獲取資料連的接耗時為0,隨著執行緒的遞減,耗時呈遞增狀態,最後都獲取到了資料連線 ,最後獲取到資料連線的10個執行緒歸還執行緒。

相關推薦

JAVA併發程式設計——Semaphore訊號

控制併發執行緒數的Semaphore Semaphore(訊號量)是用來控制同時訪問特定資源的執行緒數量,它通過協調各個執行緒,以保證合理的使用公共資源。應用場景Semaphore可以用於做流量控制,特別是公用資源有限的應用場景,比如資料庫連線。假如有一個需求,

java併發程式設計Semaphore訊號的用法

Semaphore類其實就是synchronized關鍵字的升級版,這個類主要作用就是控制執行緒併發的數量,而在這方面synchronized就有點力不足了,接下來我們就開始先了解一下Semaphore的一些常用方法就注意細節。 在new 這個類的時候需要給這個類傳遞一個引

Java併發程式設計實戰--計數訊號Semaphore

計數訊號量(Counting Semaphore)用來控制同時訪問某個特定資源的運算元量,或者同時執行某個指定操作的數量。計數訊號量還可以用來實現某種資源池,或者對容器施加邊界。 Semaphore中管理著一組虛擬的許可(permit),許可的初始數量可通過建

併發:控制併發執行緒數的Semaphore訊號

Semaphore是用來控制同時訪問特定資源的執行緒數量,他通過協調各個執行緒,以保證合理的使用公平資源。 多年以來,我都覺得從字面上很難理解Semaphore所表達的含義,只能把他比作是控制流量的紅綠燈。比如xx馬路要限制流量,只允許同時有一百兩車在這條路上行使,其他的都必須在路口等待,所以前

android 驅動中的併發和競爭——semaphore訊號

在現代的Linux系統中,有非常多的併發源,因此而帶來競爭情況,競爭來源於不同的程序對同一共享資源的同時存取。而Linux系統提供了一系列的處理併發和資源競爭的方法,下面介紹幾個: 1、semaphore(訊號量) 訊號量的使用類似於一對P、V函式,通常一個想進入臨界區的程

程序:程序同步——LockSemaphore訊號、Event事件

目錄 鎖 —— multiprocess.Lock 訊號量 —— multiprocess.Semaphore(瞭解) 事件 —— multiprocess.Event(瞭解) 鎖 —— multiprocess.Lock   當多個程序使用同一份資料資源的時候,就會引發資料

執行緒:Lock互斥鎖、RLock 遞迴鎖Semaphore訊號、Event事件、Condition條件、Timer定時器、queue佇列

目錄 一、鎖 1)同步鎖 2)死鎖與遞迴鎖 二、訊號量 三、事件 四、條件 五、定時器 六、執行緒佇列 一、鎖 1)同步鎖 #同步鎖的引用 from threading import Thread,Lock import os,time def wor

c# Semaphore訊號

http://www.cnblogs.com/tianzhiliang/archive/2010/08/31/1813635.html http://hi.baidu.com/bloodcrystal/blog/item/00ebd7f9da5aadd2b58f317c.h

JUC 中提供的限流利器-Semaphore訊號

在 JUC 包下,有一個 Semaphore 類,翻譯成訊號量,Semaphore(訊號量)是用來控制同時訪問特定資源的執行緒數量,它通過協調各個執行緒,以保證合理的使用公共資源。Semaphore 跟鎖(synchronized、Lock)有點相似,不同的地方是,鎖同一時刻只允許一個執行緒訪問某一資源,而

java併發Semaphore計數訊號

Semaphore一個計數訊號量。從概念上講,訊號量維護了一個許可集。如有必要,在許可可用前會阻塞每一個 acquire(),然後再獲取該許可。每個 release() 新增一個許可,從而可能釋放一個正在阻塞的獲取者。但是,不使用實際的許可物件,Semaphore 只對可用

Linux高階程式設計9————程序間通訊6訊號

訊號量是是什麼? 訊號量是程序間通訊方式之一,用來實現程序間的同步與互斥。訊號量的原理是一種資料操作鎖的概念,它本身不具備資料交換的功能,而是通過控制其他通訊資源(如文字、外部裝置等)來實現程序間通訊。訊號量本身不具備資料傳輸的功能,他只是一種外部資源的標識。 訊號量的本質是:具有等待佇

併發程式設計-concurrent指南-訊號Semaphore

Semaphore翻譯成字面意思為 訊號量,Semaphore可以控同時訪問的執行緒個數,通過 acquire() 獲取一個許可,如果沒有就等待,而 release() 釋放一個許可。 acquire()用來獲取一個許可,若無許可能夠獲得,則會一直等待,直到獲得許可。 release()用來釋放許可。注意

Java併發程式設計系列避免死鎖

避免死鎖 (1)避免一個執行緒同時獲取多個鎖 (2)避免一個執行緒在鎖內佔用多個資源,儘量保證每個鎖只佔用一個資源 (3)使用定時鎖,使用lock.trylock(timeout)替代內部鎖機制 (4)

淺談Java併發程式設計系列—— 如何保證執行緒安全

執行緒安全類 保證類執行緒安全的措施: 不共享執行緒間的變數; 設定屬性變數為不可變變數; 每個共享的可變變數都使用一個確定的鎖保護; 保證執行緒安全的思路: 通過架構設計 通過

Java併發程式設計學習——標準Thread

1、雖然System.out.println內部是加了鎖的,但是如果System.out.println(i- -),依然是執行緒不安全的,因為有的JVM,i- -需要三步才能完成。 2、通過interrupt方法停止執行緒 public class Int

江疏影讀書系列之Java併發程式設計實戰第一章 簡介

欲速則不達,欲達則欲速! 12年畢業,化工、零售。16年轉行,Java培訓五個月,17年1月,我人生中最悲慘的一個月,找工作處處

Java併發程式設計實戰3- 互斥鎖

我們在這篇文章中主要討論如何使用互斥鎖來解決併發程式設計中的原子性問題。 [toc] # 概述 併發程式設計中的原子性問題的源頭是執行緒切換,那麼禁止執行緒切換可以解決原子性問題嗎? 這需要分情況討論,在單核CPU的情況下,同一時刻只有一個執行緒執行,禁止CPU中斷,就意味著作業系統不會重新排程執行緒,

Java併發程式設計實戰4- 死鎖

在這篇文章中,我們主要討論一下死鎖及其解決辦法。 [toc] # 概述 在上一篇文章中,我們討論瞭如何使用一個互斥鎖去保護多個資源,以銀行賬戶轉賬為例,當時給出的解決方法是基於Class物件建立互斥鎖。 這樣雖然解決了同步的問題,但是能在現實中使用嗎?答案是不可以,尤其是在高併發的情況下,原因是我們使用

Java併發程式設計實戰5- 執行緒生命週期

在這篇文章中,我們來聊一下執行緒的生命週期。 [toc] # 概述 執行緒是作業系統中的一個概念,在Java中,它是實現併發程式的主要手段。 Java中的執行緒,本質上就是作業系統中的執行緒。 作業系統中的執行緒有“生老病死”,專業說法就是生命週期,雖然不同的開發語言對於作業系統的執行緒做了不同的封裝

iOS GCD中級篇 - dispatch_semaphore訊號的理解及使用

理解這個概念之前,先丟擲一個問題 問題描述: 假設現在系統有兩個空閒資源可以被利用,但同一時間卻有三個執行緒要進行訪問,這種情況下,該如何處理呢? 或者 我們要下載很多圖片,併發非同步進行,每個下載都會開闢一個新執行緒,可是我們又擔心太多執行緒肯定cpu吃不消,那麼我們這裡也可以用訊號量控制