標籤:semaphore 計數資訊量 java訊號量
訊號量(Semaphore)又稱為訊號量、旗語,它以一個整數變數,提供訊號,以確保在並行計算環境中,不同進程在訪問共用資源時,不會發生衝突。是一種不需要使用忙碌等待(busy waiting)的一種方法。
訊號量的概念是由荷蘭電腦科學家艾茲格·迪傑斯特拉(Edsger W. Dijkstra)發明的,廣泛的應用於不同的作業系統中。在系統中,給予每一個進程一個訊號量,代表每個進程目前的狀態,未得到控制權的進程會在特定地方被強迫停下來,等待可以繼續進行的訊號到來。如果訊號量是一個任意的整數,通常被稱為計數訊號量(Counting semaphore),或一般訊號量(general semaphore);如果訊號量只有二進位的0或1,稱為二進位訊號量(binary semaphore)。在linux系中,二進位訊號量(binary semaphore)又稱Mutex。
java中實現的是計數訊號量,用來控制同時訪問某個特定資源的運算元量,或者同時執行某個指定操作的數量。計數訊號量還可以用來實現某種資源集區,或者對容器施加邊界。
java中實現計數訊號量的類為java.util.concurrent.Semaphore,是在1.5中引入的。Semaphore中管理著一組許可,許可的初始數量可以通過構造方法來指定。在執行操作時需要先獲得許可(acquire),並在使用完後釋放許可(release)。如果當前沒有許可,那麼acquire將阻塞下到有許可可用,或者直到被中斷,或者操作逾時。
使用方式
使用方式可以像下面這樣:
package com.mikan.thread;import java.util.concurrent.Semaphore;/** * @author Mikan * @date 2015-08-29 18:40 */public class SemaphoreTest { private Semaphore semaphore; public SemaphoreTest(int permits) { if (permits <= 0) { throw new IllegalArgumentException("permits must be greater than 0"); } semaphore = new Semaphore(permits); } public void operation() throws InterruptedException { semaphore.acquire(); try { // do something...... } finally { semaphore.release(); } }}Semaphore的實現是基於java.util.concurrent.locks.AbstractQueuedSynchronizer(AQS)基類,JDK中許多同步類都是基於它來實現的,像ReentrantLock、CountDownLatch、ReentrantReadWriteLock等。Semaphore的實現使用AQS的狀態來儲存許可數量,它實現了公平和非公平兩種策略,預設下是非公平策略,可以在建立Semaphore時指定公平策略。當許可為1時,可以當作互斥鎖來使用。
使用Semaphore來實現有界容器
比如這裡使用Semaphore來實現一個有指定容量的List:
package com.mikan.thread;import java.util.Collections;import java.util.LinkedList;import java.util.List;import java.util.concurrent.Semaphore;/** * @author Mikan * @date 2015-08-29 19:11 */public class BoundedList<T> { private final List<T> list; private final Semaphore semaphore; public BoundedList(int bound) { list = Collections.synchronizedList(new LinkedList<T>()); semaphore = new Semaphore(bound); } public boolean add(T obj) throws InterruptedException { semaphore.acquire(); boolean addedFlag = false; try { addedFlag = list.add(obj); } finally { if (!addedFlag) { semaphore.release(); } } return addedFlag; } public boolean remove(Object obj) { boolean removedFlag = list.remove(obj); if (removedFlag) { semaphore.release(); } return removedFlag; } // 其他動作委託給底層的List,這裡只列舉出一個方法 public T get(int index) { return list.get(index); } // 其他方法……}
使用訊號量來控制同時執行的線程數量
package com.mikan.thread;import java.util.concurrent.CountDownLatch;import java.util.concurrent.Semaphore;/** * @author Mikan * @date 2015-08-29 19:47 */public class BoundedThreadExecution { public static void main(String[] args) { final Semaphore semaphore = new Semaphore(5); final CountDownLatch gate = new CountDownLatch(1); int maxThreads = 100; for (int i = 0; i < maxThreads; i++) { final int index = i; new Thread(new Runnable() { @Override public void run() { try { gate.await(); semaphore.acquire(); System.out.println(Thread.currentThread().getName() + "-->" + index + "--available permits=" + semaphore.availablePermits()); } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } finally { // 不管當前線程是否正常結束,都釋放許可 semaphore.release(); } } }).start(); } // 使用閉鎖來實現所有線程同時開始執行 gate.countDown(); }}
自己實現一個計數訊號量
package com.mikan.thread;/** * @author Mikan * @date 2015-08-29 19:19 */public class CountingSemaphore { private final int bound; private int permits = 0; public CountingSemaphore(int permits) { if (permits <= 0) { throw new IllegalArgumentException("permits must be greater than 0"); } this.bound = permits; } public synchronized void acquired() throws InterruptedException { // 當許可達到上限時,則阻塞 while (permits == bound) { wait(); } permits++; } public synchronized void release() { permits--; // 釋放了許可,通知等待的線程 notifyAll(); }}
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
java中的計數訊號量(Counting Semaphore)