symaphore(信號量)用來控制同時訪問某個資源的線程數量,通常用在併發流量控制。我的對它的理解至關因而接待室每次只能接待固定數量的人,當達到最高接待數的時候,其餘人就會被攔截在外等待,當前面接待完走出接待室,纔會繼續接待下面的人。數據庫
symaphore有兩個構造方法:構造方法Semaphore(int permits)接受一個int參數,表示可用的許可證數量,內部默認建立一個非公平鎖;構造方法Semaphore(int permits, boolean fair)接受一個併發
int和一個boolean值,分別表示可用許可證數量和是否使用公平鎖。(公平鎖和非公平鎖後面文章會單獨提到)less
通常在作流量控制的時候,咱們就能夠經過控制許可證數量來控制併發數的大小,接下來具體聊聊怎麼實現對線程池的控制,代碼以下:dom
1 public class DBPoolSemaphore { 2 3 private final static int POOL_SIZE = 10; 4 private final Semaphore useful,useless;//useful表示可用的數據庫鏈接,useless表示已用的數據庫鏈接 5 6 public DBPoolSemaphore() { 7 this. useful = new Semaphore(POOL_SIZE); 8 this.useless = new Semaphore(0); 9 } 10 11 //存放數據庫鏈接的容器 12 private static LinkedList<Connection> pool = new LinkedList<Connection>(); 13 //初始化池 14 static { 15 for (int i = 0; i < POOL_SIZE; i++) { 16 pool.addLast(SqlConnectImpl.fetchConnection()); 17 } 18 } 19 20 /*歸還鏈接*/ 21 public void returnConnect(Connection connection) throws InterruptedException { 22 if(connection!=null) { 23 System.out.println("當前有"+useful.getQueueLength()+"個線程等待數據庫鏈接!!" 24 +"可用鏈接數:"+useful.availablePermits()); 25 useless.acquire(); 26 synchronized (pool) { 27 pool.addLast(connection); 28 } 29 useful.release(); 30 } 31 } 32 33 /*從池子拿鏈接*/ 34 public Connection takeConnect() throws InterruptedException { 35 useful.acquire(); 36 Connection conn; 37 synchronized (pool) { 38 conn = pool.removeFirst(); 39 } 40 useless.release(); 41 return conn; 42 } 43 }
首先建立了兩個symaphore對象,分別用來表示已用線程池和可用線程池,在設計拿鏈接和歸還鏈接時,分別前後調用acquire()和release(),acquire()是用來獲取許可,release()歸還許可,至關於在拿鏈接時先去可用鏈接池獲取許可,獲取到纔會繼續執行,不然阻塞等待,直到有鏈接池歸還了鏈接,可用線程許可中能夠獲取到,獲取數據庫鏈接,以後將不可用鏈接許可增長;歸還鏈接恰好相反。本質就是經過控制可用和不可用許可數目,達到控制併發流量的效果。ide
下面是我設計的一段執行上面代碼的示例:高併發
1 public class AppTest { 2 3 private static DBPoolSemaphore dbPool = new DBPoolSemaphore(); 4 5 //業務線程 6 private static class BusiThread extends Thread{ 7 @Override 8 public void run() { 9 Random r = new Random();//讓每一個線程持有鏈接的時間不同 10 long start = System.currentTimeMillis(); 11 try { 12 Connection connect = dbPool.takeConnect(); 13 System.out.println("Thread_"+Thread.currentThread().getId() 14 +"_獲取數據庫鏈接共耗時【"+(System.currentTimeMillis()-start)+"】ms."); 15 SleepTools.ms(100+r.nextInt(100));//模擬業務操做,線程持有鏈接查詢數據 16 System.out.println("查詢數據完成,歸還鏈接!"); 17 dbPool.returnConnect(connect); 18 } catch (InterruptedException e) { 19 } 20 } 21 } 22 23 public static void main(String[] args) { 24 for (int i = 0; i < 50; i++) { 25 Thread thread = new BusiThread(); 26 thread.start(); 27 } 28 } 29 30 }
有興趣的同窗可使用上述代碼跑一下,能夠看到前10個線程不耗時間,能夠直接獲取到,後面的會阻塞(花費時間愈來愈長),歸還一個纔會去獲取鏈接,由於個人鏈接許可設置爲10,因此每次最高併發數爲10。fetch
Semaphore還提供一些其餘方法:ui
ps:部分引用自http://ifeve.com/concurrency-semaphore/this