Semaphore也是一個線程同步的輔助類,能夠維護當前訪問自身的線程個數,並提供了同步機制。
使用Semaphore能夠控制同時訪問資源的線程個數,例如,實現一個文件容許的併發訪問數。安全
一個計數信號量。從概念上講,信號量維護了一個許可集。
若有必要,在許可可用前會阻塞每個 acquire(),而後再獲取該許可。每一個 release() 添加一個許可,從而可能釋放一個正在阻塞的獲取者。
可是,不使用實際的許可對象,Semaphore 只對可用許可的號碼進行計數,並採起相應的行動。拿到信號量的線程能夠進入代碼,不然就等待。併發
主要方法摘要:
void acquire():今後信號量獲取一個許可,在提供一個許可前一直將線程阻塞,不然線程被中斷
void release():釋放一個許可,將其返回給信號量
int availablePermits():返回此信號量中當前可用的許可數
boolean hasQueuedThreads():查詢是否有線程正在等待獲取
1.簡單例子dom
維護當前訪問自身的線程個數
@Test public void semaphore1Test() throws InterruptedException { // 線程池 ExecutorService exec = Executors.newCachedThreadPool(); // 只能5個線程同時訪問 final Semaphore semp = new Semaphore(5); // 模擬20個客戶端訪問 for (int index = 0; index < 20; index++) { final int NO = index; Runnable run = new Runnable() { public void run() { try { // 請求得到許可,若是有可得到的許可則繼續往下執行,許可數減1。不然進入阻塞狀態 semp.acquire(); System.out.println("Accessing: " + NO); Thread.sleep((long) (Math.random() * 10000)); // 訪問完後,釋放許可,許可數加1,若是屏蔽下面的語句,則在控制檯只能打印5條記錄,以後線程一直阻塞 semp.release(); System.out.println("線程" + Thread.currentThread().getName() + "已離開,當前已有" + (3-semp.availablePermits()) + "個併發"); } catch (InterruptedException e) { } } }; exec.execute(run); } // 退出線程池 exec.shutdown(); }
2.當鎖工具
當信號量的數量上限是1時,Semaphore能夠被當作鎖來使用。經過acquire和release方法來保護關鍵區域。測試
@Test public void semaphore2Test() throws InterruptedException { final Business business = new Business(); ExecutorService executor = Executors.newFixedThreadPool(3); for (int i = 0; i < 3; i++) { executor.execute(new Runnable() { public void run() { business.service(); } } ); } executor.shutdown(); } class Business { private int count; Lock lock = new ReentrantLock(); Semaphore sp = new Semaphore(1); public void service() { // lock.lock(); try { sp.acquire(); // 當前線程使用count變量的時候將其鎖住,不容許其餘線程訪問 } catch (InterruptedException e1) { e1.printStackTrace(); } try { count++; try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(count); } catch (RuntimeException e) { e.printStackTrace(); } finally { // lock.unlock(); sp.release(); // 釋放鎖 } } }
3.生產者消費者模型,生成阻塞隊列ui
a.消息通道this
class SemaphoreQueue { private List<Integer> valueList; private Semaphore putActionNum;// 能夠進行put操做的許可數量 private Semaphore getActionNum;// 能夠進行take操做的許可數量 private Semaphore mutex; //至關於鎖 控制非線程安全的valueList的操做 public SemaphoreQueue(int capacity) { putActionNum = new Semaphore(capacity);// 維護隊列大小 getActionNum = new Semaphore(0); // 初始化時,隊列爲空,put操做許可數量爲0 mutex = new Semaphore(1); // 用於保護非線程安全的valueList操做,用於併發生產時控制 valueList = new ArrayList<Integer>(capacity); } public void put(Integer message) { try { putActionNum.acquire();// put操做許可減1 mutex.acquire(); valueList.add(message); mutex.release(); getActionNum.release();// get操做許可加1 } catch (InterruptedException e) { e.printStackTrace(); } } public Integer take() { Integer message = null; try { getActionNum.acquire();// get操做許可減1 mutex.acquire(); if (valueList.size() > 0) { message = valueList.get(0); valueList.remove(0); } else { return null; } mutex.release(); putActionNum.release();// put操做許可加1 } catch (InterruptedException e) { e.printStackTrace(); } return message; } }
b. 生產者和消費者spa
class Productor extends Thread { SemaphoreQueue queue; public Productor(SemaphoreQueue queue) { this.queue = queue; } public void run() { int i = 0; try { while (true) { i++; Integer message = new Integer(i); queue.put(message); if (i % 20 == 0) { System.out.println("======== " + this.getName() + " 累計生產了 " + i + " 條消息 ======="); Thread.currentThread().sleep(1000); } } } catch (Exception e) { e.printStackTrace(); } } } class Cousumertor extends Thread { SemaphoreQueue queue; public Cousumertor(SemaphoreQueue queue) { this.queue = queue; } public void run() { try { while (true) { Integer message = queue.take(); if (message != null) { System.out.println("======== " + this.getName() + " 消費消息:" + message + " ======="); } Thread.currentThread().sleep(100); } } catch (Exception e) { e.printStackTrace(); } } }
c.測試線程
public static void main(String[] args) { SemaphoreQueue queue = new SemaphoreQueue(20); // 開始生產 Productor productor = new Productor(queue); productor.setName("生產者"); productor.start(); // 開始消費 Cousumertor c1 = new Cousumertor(queue); c1.setName("消費者-c1"); Cousumertor c2 = new Cousumertor(queue); c2.setName("消費者-c2"); c1.start(); c2.start(); }
4. Semaphore vs. CountDownLatch
相同點 :
二者都是用於線程同步的工具類,都經過定義了一個繼承AbstractQueuedSynchronizer的內部類Sync來實現具體的功能.
不一樣點 :
a. Semaphore提供了公平和非公平兩種策略, 而CountDownLatch則不具有.
b. CountDownLatch: 一個或者是一部分線程,等待另一部線程都完成操做。
Semaphorr: 維護一個許可集.一般用於限制能夠訪問某些資源(物理或邏輯的)的線程數目. code
c. CountDownLatch中計數是不能被重置的。CountDownLatch適用於一次同步。當使用CountDownLatch時,任何線程容許屢次調用countDown(). 那些調用了await()方法的線程將被阻塞,直到那些沒有被阻塞線程調用countDown()使計數到達0爲止 。
Semaphore容許線程獲取許可, 未得到許可的線程須要等待.這樣防止了在同一時間有太多的線程執行.Semaphore的值被獲取到後是能夠釋放的,並不像CountDownLatch那樣一直減到0。
d. 使用CountDownLatch時,它關注的一個線程或者多個線程須要在其它在一組線程完成操做以後,在去作一些事情。好比:服務的啓動等。使用Semaphore時,它關注的是某一個資源最多同時能被幾個線程訪問.