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