【試驗局】ReentrantLock中非公平鎖與公平鎖的性能測試


硬件環境:

  CPU:AMD Phenom(tm) II X4 955 Processor

  Memory:8G

  SSD(128G):/

  HDD(1T):/home/

軟件環境:

  OS:Ubuntu14.04.3 LTS

  Java:JDK1.7

  關於ReentrantLock中非公平鎖和公平鎖詳細區別以及實現方式在這里不再敘述,有關ReentrantLock的源碼解析參照。

  首先我們用實例驗證,非公平鎖以及公平鎖是否是其介紹的那樣,非公平鎖在獲取鎖的時候會首先進行搶鎖,在獲取鎖失敗后才會將當前線程加入同步隊列隊尾中,而公平鎖則是符合請求的絕對順序,也就是會按照先來后到FIFO。

 1 package com.lock;
 2 
 3 import org.junit.Test;
 4 
 5 import java.util.ArrayList;
 6 import java.util.Collection;
 7 import java.util.Collections;
 8 import java.util.List;
 9 import java.util.concurrent.locks.Lock;
10 import java.util.concurrent.locks.ReentrantLock;
11 
12 /**
13  * Created by yulinfeng on 5/24/17.
14  */
15 public class FairAndUnfairTest {
16     private static Lock fairLock = new ReentrantLockMine(true);
17     private static Lock unfairLock = new ReentrantLockMine(false);
18 
19     @Test
20     public void unfair() throws InterruptedException {
21         testLock("非公平鎖", unfairLock);
22     }
23 
24     @Test
25     public void fair() throws InterruptedException {
26         testLock("公平鎖", fairLock);
27     }
28 
29     private void testLock(String type, Lock lock) throws InterruptedException {
30         System.out.println(type);
31         for (int i = 0; i < 5; i++) {
32             Thread thread = new Thread(new Job(lock)){
33                 public String toString() {
34                     return getName();
35                 }
36             };
37             thread.setName("" + i);
38             thread.start();
39         }
40         Thread.sleep(11000);
41     }
42 
43     private static class Job implements Runnable{
44         private Lock lock;
45         public Job(Lock lock) {
46             this.lock = lock;
47         }
48 
49         public void run() {
50             for (int i = 0; i < 2; i++) {
51                 lock.lock();
52                 try {
53                     Thread.sleep(1000);
54                     System.out.println("獲取鎖的當前線程[" + Thread.currentThread().getName() + "], 同步隊列中的線程" + ((ReentrantLockMine)lock).getQueuedThreads() + "");
55                 } catch (InterruptedException e) {
56                     e.printStackTrace();
57                 } finally {
58                     lock.unlock();
59                 }
60             }
61         }
62     }
63 
64     private static class ReentrantLockMine extends ReentrantLock {  //重新實現ReentrantLock類是為了重寫getQueuedThreads方法,便於我們試驗的觀察
65         public ReentrantLockMine(boolean fair) {
66             super(fair);
67         }
68 
69         @Override
70         protected Collection<Thread> getQueuedThreads() {   //獲取同步隊列中的線程
71             List<Thread> arrayList = new ArrayList<Thread>(super.getQueuedThreads());
72             Collections.reverse(arrayList);
73             return arrayList;
74         }
75     }
76 }

  上面這段代碼:創建5個線程,每個線程中有兩次獲取鎖與釋放鎖的行為。運行代碼觀察結果:

  

  顯然,試驗結果與我們的預期相符。在以非公平鎖的方式獲取鎖,當一個線程在獲取鎖又釋放鎖,但又立即獲取鎖的時候,這個時候這個線程有很大的概率會成功(只是很大概率,試驗結果也有可能不連續兩次獲取鎖)。而公平鎖則不一樣,哪怕是同一個線程連續兩次獲取鎖和釋放鎖,在第一次獲取鎖釋放鎖過后接着准備第二次獲取鎖時,這個時候當前線程會被加入到同步隊列的隊尾。

  那么有了上面的結果除了說明非公平鎖和公平鎖之間的區別還能說明什么問題呢?其實,這就是本篇的主題——性能測試。非公平鎖的一個線程連續兩次獲取鎖和釋放鎖的工程中,是沒有做上下文切換的,也就是一共只做了5次上下文切換。而公平鎖實際上做了10次上下文切換。而這個上下文切換的開銷實際是很大的,我們通過測試在10個線程,每個線程獲取100000次鎖的情況下兩者的執行速度,以及使用vmstat命令來統計系統上下文切換的次數(cs欄表示系統每秒切換的上下文次數)。

 1 package com.lock;
 2 
 3 import java.util.ArrayList;
 4 import java.util.Collection;
 5 import java.util.Collections;
 6 import java.util.List;
 7 import java.util.concurrent.BrokenBarrierException;
 8 import java.util.concurrent.CyclicBarrier;
 9 import java.util.concurrent.locks.Lock;
10 import java.util.concurrent.locks.ReentrantLock;
11 
12 /**
13  * 改進后的代碼,利用CyclicBarrier當所有線程執行完畢時,統計執行時間。
14  * Created by yulinfeng on 5/24/17.
15  */
16 public class newFairAndUnfairLockTest {
17     private static Lock lock = new ReentrantLockMine(false);    //非公平鎖
18     //private static Lock lock = new ReentrantLockMine(true);   //公平鎖
19 
20     public static void main(String[] args) throws BrokenBarrierException, InterruptedException {
21         String lockType = "非公平鎖";  //String lockType = "公平鎖"
22         long start = System.currentTimeMillis();
23         CyclicBarrier cyclicBarrier = new CyclicBarrier(10, new Time(lockType, start));     //10個線程執行完畢時,執行Time線程統計執行時間
24 
25         for (int i = 0; i < 10; i++) {
26             Thread thread = new Thread(new Job(lock, cyclicBarrier)){
27                 public String toString() {
28                     return getName();
29                 }
30             };
31             thread.setName("" + i);
32             thread.start();
33         }
34 
35 
36     }
37 
38     private static class Job implements Runnable{
39         private Lock lock;
40         private CyclicBarrier cyclicBarrier;
41         public Job(Lock lock, CyclicBarrier cyclicBarrier) {
42             this.lock = lock;
43             this.cyclicBarrier = cyclicBarrier;
44         }
45 
46         public void run() {
47             for (int i = 0; i < 100000; i++) {
48                 lock.lock();
49                 try {
50                     System.out.println(i+"獲取鎖的當前線程[" + Thread.currentThread().getName() + "], 同步隊列中的線程" + ((ReentrantLockMine)lock).getQueuedThreads() + "");
51                 } finally {
52                     lock.unlock();
53                 }
54             }
55             try {
56                 cyclicBarrier.await();  //計數器+1,直到10個線程都到達
57             } catch (InterruptedException e) {
58                 e.printStackTrace();
59             } catch (BrokenBarrierException e) {
60                 e.printStackTrace();
61             }
62         }
63     }
64 
65     private static class ReentrantLockMine extends ReentrantLock {  //重新實現ReentrantLock類是為了重寫getQueuedThreads方法,便於我們試驗的觀察
66         public ReentrantLockMine(boolean fair) {
67             super(fair);
68         }
69 
70         @Override
71         protected Collection<Thread> getQueuedThreads() {   //獲取同步隊列中的線程
72             List<Thread> arrayList = new ArrayList<Thread>(super.getQueuedThreads());
73             Collections.reverse(arrayList);
74             return arrayList;
75         }
76     }
77 
78 
79     private static class Time implements Runnable {     //用於統計時間
80         private long start ;
81         private String lockType;
82 
83         public Time(String lockType, long start) {
84             this.start = start;
85             this.lockType = lockType;
86         }
87 
88         public void run() {
89             System.out.println(lockType + "耗時:" + String.valueOf(System.currentTimeMillis() - start));
90         }
91     }
92 }

  首先執行非公平鎖,並使用"vmstat 1(每秒實時查看系統資源占用情況)",結果如下:

  

  

  再執行公平鎖,並使用"vmstat 1(每秒實時查看系統資源占用情況)",結果如下:  

   

  

  通過上面的試驗結果可以得出結論,非公平鎖的性能因其系統上下文的切換較少,其性能一般要優於公平鎖。

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM