Java 并發專題 : Semaphore 實現 互斥 與 連接池
繼續并發方面的知識。今天介紹Semaphore,同樣在java.util.concurrent包下。
本來準備通過例子,從自己實現到最后使用并發工具實現,但是貌似效果并不是很好,有點太啰嗦的感覺,所有準備直入主題。
介紹:Semaphore中管理著一組虛擬的許可,許可的初始數量可通過構造函數來指定【new Semaphore(1);】,執行操作時可以首先獲得許可【semaphore.acquire();】,并在使用后釋放許可【semaphore.release();】。如果沒有許可,那么acquire方法將會一直阻塞直到有許可(或者直到被終端或者操作超時)。
作用:可以用來控制同時訪問某個特定資源的操作數量,或者某個操作的數量。
下面使用Semaphore實現兩個例子:
1、互斥
大家都學過操作系統,都知道互斥的概念,比較簡單的互斥實現,比如PV操作,判斷資源,然后忙等實現互斥;上一篇博客也說過,忙等對CPU的消耗巨大,下面我們通過Semaphore來實現一個比較好的互斥操作:
假設我們公司只有一臺打印機,我們需要對這臺打印機的打印操作進行互斥控制:
package com.zhy.concurrency.semaphore; import java.util.concurrent.Semaphore; /** * 使用信號量機制,實現互斥訪問打印機 * * @author zhy * */ public class MutexPrint { /** * 定義初始值為1的信號量 */ private final Semaphore semaphore = new Semaphore(1); /** * 模擬打印操作 * @param str * @throws InterruptedException */ public void print(String str) throws InterruptedException { //請求許可 semaphore.acquire(); System.out.println(Thread.currentThread().getName()+" enter ..."); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "正在打印 ..." + str); System.out.println(Thread.currentThread().getName()+" out ..."); //釋放許可 semaphore.release(); } public static void main(String[] args) { final MutexPrint print = new MutexPrint(); /** * 開啟10個線程,搶占打印機 */ for (int i = 0; i < 10; i++) { new Thread() { public void run() { try { print.print("helloworld"); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); } } }
輸出結果:
Thread-1 enter ... Thread-1正在打印 ...helloworld Thread-1 out ... Thread-2 enter ... Thread-2正在打印 ...helloworld Thread-2 out ... Thread-0 enter ... Thread-0正在打印 ...helloworld Thread-0 out ... Thread-3 enter ... Thread-3正在打印 ...helloworld Thread-3 out ...
通過初始值為1的Semaphore,很好的實現了資源的互斥訪問。
2、連接池的模擬實現
在項目中處理高并發時,一般數據庫都會使用數據庫連接池,假設現在數據庫連接池最大連接數為10,當10個連接都分配出去以后,現在有用戶繼續請求連接,可能的處理:
a、手動拋出異常,用戶界面顯示,服務器忙,稍后再試
b、阻塞,等待其他連接的釋放
從用戶體驗上來說,更好的選擇當然是阻塞,等待其他連接的釋放,用戶只會覺得稍微慢了一點,并不影響他的操作。下面使用Semaphore模擬實現一個數據庫連接池:
package com.zhy.concurrency.semaphore; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Semaphore; /** * 使用Semaphore模擬數據庫鏈接池的使用 * @author zhy * */ public class ConnectPool { private final List<Conn> pool = new ArrayList<Conn>(3); private final Semaphore semaphore = new Semaphore(3); /** * 初始化分配3個連接 */ public ConnectPool() { pool.add(new Conn()); pool.add(new Conn()); pool.add(new Conn()); } /** * 請求分配連接 * @return * @throws InterruptedException */ public Conn getConn() throws InterruptedException { semaphore.acquire(); Conn c = null ; synchronized (pool) { c = pool.remove(0); } System.out.println(Thread.currentThread().getName()+" get a conn " + c); return c ; } /** * 釋放連接 * @param c */ public void release(Conn c) { pool.add(c); System.out.println(Thread.currentThread().getName()+" release a conn " + c); semaphore.release(); } public static void main(String[] args) { final ConnectPool pool = new ConnectPool(); /** * 第一個線程占用1個連接3秒 */ new Thread() { public void run() { try { Conn c = pool.getConn(); Thread.sleep(3000); pool.release(c); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); /** * 開啟3個線程請求分配連接 */ for (int i = 0; i < 3; i++) { new Thread() { public void run() { try { Conn c = pool.getConn(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); } } private class Conn { } }
Thread-0 get a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@12b6651 Thread-2 get a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@e53108 Thread-1 get a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@1888759 Thread-0 release a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@12b6651 Thread-3 get a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@12b6651我們測試時,讓Thread-0持有一個連接3秒,然后瞬間讓3個線程再去請求分配連接,造成Thread-3一直等到Thread-0對連接的釋放,然后獲得連接。
通過兩個例子,基本已經了解了Semaphore的用法,這里的線程池例子只是為了說明Semaphore的用法,真實的實現代碼比這復雜的多,而且可能也不會直接用Semaphore。
好了,之后會繼續Java并發的博客。
來自: http://blog.csdn.net//lmj623565791/article/details/26810813