這一篇博文是【大數據技術●降龍十八掌】系列文章的其中一篇,點擊查看目錄:大數據技術●降龍十八掌
信號量(Semaphore)是可以定義共享資源的個數,允許多個線程同時使用共享資源,在信號量內部有一個計數器,當有線程訪問資源時候,計數器將自動遞減,當它爲0時,不再允許其他線程對共享資源訪問,只到有一個線程釋放共享資源,這樣就完成就共享資源的保護。定義N個長度信號量(Semaphore)可以理解爲定義了N個令牌,每個線程只有拿到令牌後才能使用資源,當使用資源完畢後,歸還令牌。
看一個實例:
import java.util.Date;
import java.util.concurrent.Semaphore;
/**
* Created by 鳴宇淳 on 2017/12/26.
*/
public class Demo1 {
//定義一個20長度的Semaphore信號量
private static Semaphore semaphore = new Semaphore(20);
public static void main(String[] args) {
ThreadDemo threadDemo1 = new ThreadDemo(semaphore, 10);
ThreadDemo threadDemo2 = new ThreadDemo(semaphore, 16);
ThreadDemo threadDemo3 = new ThreadDemo(semaphore, 5);
Thread t1 = new Thread(threadDemo1, "線程1");
Thread t2 = new Thread(threadDemo2, "線程2");
Thread t3 = new Thread(threadDemo3, "線程3");
t1.start();
t2.start();
t3.start();
}
static class ThreadDemo implements Runnable {
private int num;
private Semaphore semaphore;
public ThreadDemo(Semaphore semaphore, int num) {
this.num = num;
this.semaphore = semaphore;
}
public void run() {
try {
System.out.println(new Date() + Thread.currentThread().getName() + "申請獲得" + this.num + "個令牌");
//佔用num個令牌
semaphore.acquire(this.num);
System.out.println(new Date() + Thread.currentThread().getName() + "[已經]得到" + this.num + "個令牌.......");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//釋放num個令牌
semaphore.release(this.num);
System.out.println(new Date() + Thread.currentThread().getName() + "釋放" + this.num + "個令牌++++++++");
}
}
}
}
輸出:
Tue Dec 26 19:03:26 CST 2017線程2申請獲得16個令牌
Tue Dec 26 19:03:26 CST 2017線程1申請獲得10個令牌
Tue Dec 26 19:03:26 CST 2017線程3申請獲得5個令牌
Tue Dec 26 19:03:26 CST 2017線程2[已經]得到16個令牌.......
Tue Dec 26 19:03:28 CST 2017線程2釋放16個令牌++++++++
Tue Dec 26 19:03:28 CST 2017線程3[已經]得到5個令牌.......
Tue Dec 26 19:03:28 CST 2017線程1[已經]得到10個令牌.......
Tue Dec 26 19:03:30 CST 2017線程1釋放10個令牌++++++++
Tue Dec 26 19:03:30 CST 2017線程3釋放5個令牌++++++++
這個例子中,一共有20個令牌,線程1、線程2、線程3都去申請令牌,線程2首先領到16個令牌,還剩下4個,不滿足線程2和線程1的需求,他們兩個只能等待,當線程2釋放了16個令牌後,首先分配給線程3令牌,還剩下17個,也滿足線程1的10個需求,所以也分配給了線程1,當他們使用結束後,就都歸還了令牌。
領取令牌使用acquire()方法,歸還令牌使用release()方法,一定要記得將歸還令牌的代碼放入finally中,以保證肯定能歸還。