掃描下方二維碼或者微信搜索公衆號
菜鳥飛呀飛
,便可關注微信公衆號,閱讀更多Spring源碼分析
和Java併發編程
文章。java
1.
非公平鎖必定不公平嗎?2.
公平鎖與非公平鎖的性能誰更好?Mr羽墨青衫
,文章連接:深刻剖析Java重入鎖ReentrantLock的實現原理)。從圖中能夠發現,當持有鎖的線程T1釋放鎖之後,會喚醒同步隊列中的T2線程,只要同步隊列中有線程在等待獲取鎖,那麼其餘剛進來想要獲取鎖的人,就不能插隊,包括T1本身還想要獲取鎖,也須要去排隊,這樣就保證了讓等待時間最長的線程獲取到鎖,即保證了公平性。編程
對於非公平鎖,線程獲取鎖的示意圖能夠用以下示意圖表示。(圖片來源於公衆號:Mr羽墨青衫
,文章連接:深刻剖析Java重入鎖ReentrantLock的實現原理)。設計模式
非公平鎖必定不公平嗎?
答案是不必定,對於剛剛上面圖中展現的那種狀況,此時非公平鎖是不公平的。可是存在一種特殊狀況,能夠參考以下示意圖,如當T1線程釋放鎖之後,AQS同步隊列外部沒有線程來爭搶鎖,T1線程在釋放鎖之後,本身也不須要獲取鎖了,此時T1由於喚醒的是T2,如今是有T2一個線程來搶鎖,因此此時能獲取到鎖的線程必定是T2,這種狀況下,非公平鎖又是公平的了,由於此時即便同步隊列中有T三、T四、...、Tn線程,可是T2等待的時間最長,因此是T2獲取到鎖(AQS的同步隊列遵循的原則是FIFO)。飢餓
的狀況。在最壞的狀況下,可能存在某個線程一直獲取不到鎖。不過相比性能而言,飢餓
問題能夠暫時忽略,這可能就是ReentrantLock默認建立非公平鎖的緣由之一了。public class Demo {
// 公平鎖
private static Lock fairLock = new ReentrantLock(true);
// 非公平鎖
private static Lock nonFairLock = new ReentrantLock(false);
// 計數器
private static int fairCount = 0;
// 計數器
private static int nonFairCount = 0;
public static void main(String[] args) throws InterruptedException {
System.out.println("公平鎖耗時: " + testFairLock(10));
System.out.println("非公平鎖耗時: " + testNonFairLock(10));
System.out.println("公平鎖累加結果: " + fairCount);
System.out.println("非公平鎖累加結果: " + nonFairCount);
}
public static long testFairLock(int threadNum) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(threadNum);
// 建立threadNum個線程,讓其以公平鎖的方式,對fairCount進行自增操做
List<Thread> fairList = new ArrayList<>();
for (int i = 0; i < threadNum; i++) {
fairList.add(new Thread(() -> {
for (int j = 0; j < 10000; j++) {
fairLock.lock();
fairCount++;
fairLock.unlock();
}
countDownLatch.countDown();
}));
}
long startTime = System.currentTimeMillis();
for (Thread thread : fairList) {
thread.start();
}
// 讓全部線程執行完
countDownLatch.await();
long endTime = System.currentTimeMillis();
return endTime - startTime;
}
public static long testNonFairLock(int threadNum) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(threadNum);
// 建立threadNum個線程,讓其以非公平鎖的方式,對nonFairCountCount進行自增操做
List<Thread> nonFairList = new ArrayList<>();
for (int i = 0; i < threadNum; i++) {
nonFairList.add(new Thread(() -> {
for (int j = 0; j < 10000; j++) {
nonFairLock.lock();
nonFairCount++;
nonFairLock.unlock();
}
countDownLatch.countDown();
}));
}
long startTime = System.currentTimeMillis();
for (Thread thread : nonFairList) {
thread.start();
}
// 讓全部線程執行完
countDownLatch.await();
long endTime = System.currentTimeMillis();
return endTime - startTime;
}
}
複製代碼
threadNum
個線程,而後讓這threadNum
個線程併發的對變量進行累加10000次的操做,分別用公平鎖和非公平鎖來保證線程安全,最後分別統計出公平鎖和非公平鎖的耗時結果。threadNum = 10
時,重複三次測試的結果以下。次數 | 公平鎖 | 非公平鎖 |
---|---|---|
1 | 618ms | 22ms |
2 | 544ms | 20ms |
3 | 569ms | 15ms |
threadNum = 20
時,重複三次測試的結果以下。次數 | 公平鎖 | 非公平鎖 |
---|---|---|
1 | 1208ms | 25ms |
2 | 1146ms | 26ms |
3 | 1215ms | 19ms |
threadNum = 30
時,重複三次測試的結果以下。次數 | 公平鎖 | 非公平鎖 |
---|---|---|
1 | 1595ms | 28ms |
2 | 1543ms | 31ms |
3 | 1601ms | 31ms |
非公平鎖的耗時遠遠小於公平鎖的耗時
,這說明非公平鎖在併發狀況下,性能更好,吞吐量更大
。當線程數越多時,差別越明顯。