jdk源碼剖析三:鎖Synchronized



一、Synchronized作用

(1)確保線程互斥的訪問同步代碼

(2)保證共享變量的修改能夠及時可見

(3)有效解決重排序問題。(Synchronized同步中的代碼JVM不會輕易優化重排序)

 

二、Synchronized常見用法分析

1.修飾普通方法

 1 package lock;
 2 
 3 /**
 4  * 
 5  * @ClassName:SynchronizedDemo
 6  * @Description:測試synchronized
 7  * @author diandian.zhang
 8  * @date 2017年4月1日下午7:02:34
 9  */
10 public class SynchronizedDemo {
11 
12     public synchronized void method1(){
13         System.out.println("進入方法1");
14         try {
15             System.out.println("方法1執行");
16             Thread.sleep(3000);
17         } catch (InterruptedException e) {
18             e.printStackTrace();
19         }
20         System.out.println("方法1 end");
21     }
22     
23     public synchronized void method2(){
24         System.out.println("進入方法2");
25         try {
26             System.out.println("方法2執行");
27             Thread.sleep(000);
28         } catch (InterruptedException e) {
29             e.printStackTrace();
30         }
31         System.out.println("方法2 end");
32     }
33         
34     public static void main(String[] args) {
35         SynchronizedDemo demo = new SynchronizedDemo();
36         new Thread(new Runnable() {
37             @Override
38             public void run() {
39                 demo.method1();
40             }
41         }).start();
42         
43         new Thread(new Runnable() {
44             @Override
45             public void run() {
46                 demo.method2();
47             }
48         }).start();
49     }
50 }

結果:

1 進入方法1
2 方法1執行
3 方法1 end
4 進入方法2
5 方法2執行
6 方法2 end

可見:修飾普通方法,線程2需要等待線程1的method1執行完成才能開始執行method2方法,方法級別串行執行。

2.修飾靜態方法

 1 package lock;
 2 
 3 /**
 4  * 
 5  * @ClassName:SynchronizedDemo2
 6  * @Description:修飾靜態方法
 7  * @author diandian.zhang
 8  * @date 2017年4月5日上午10:48:56
 9  */
10 public class SynchronizedDemo2 {
11 
12     public static synchronized void method1(){
13         System.out.println("進入方法1");
14         try {
15             System.out.println("方法1執行");
16             Thread.sleep(3000);
17         } catch (InterruptedException e) {
18             e.printStackTrace();
19         }
20         System.out.println("方法1 end");
21     }
22     
23     public static synchronized void method2(){
24         System.out.println("進入方法2");
25         try {
26             System.out.println("方法2執行");
27             Thread.sleep(1000);
28         } catch (InterruptedException e) {
29             e.printStackTrace();
30         }
31         System.out.println("方法2 end");
32     }
33         
34     public static void main(String[] args) {
35         new Thread(new Runnable() {
36             @Override
37             public void run() {
38                 SynchronizedDemo2.method1();
39             }
40         }).start();
41         
42         new Thread(new Runnable() {
43             @Override
44             public void run() {
45                 SynchronizedDemo2.method2();
46             }
47         }).start();
48     }
49 }

運行結果:

1 進入方法1
2 方法1執行
3 進入方法2
4 方法1 end
5 方法2執行
6 方法2 end

可見:修飾靜態方法,本質是對類的同步,任何實例調用方法,都類級別串行(每個實例不一定串行)執行。

3.修飾代碼塊

 1 package lock;
 2 
 3 /**
 4  * 
 5  * @ClassName:SynchronizedDemo3
 6  * @Description:Synchronized修飾代碼塊
 7  * @author diandian.zhang
 8  * @date 2017年4月5日上午10:49:50
 9  */
10 public class SynchronizedDemo3 {
11 
12     public void method1(){
13         System.out.println("進入方法1");
14         try {
15             synchronized (this) {
16                 System.out.println("方法1執行");
17                 Thread.sleep(3000);
18             }
19         } catch (InterruptedException e) {
20             e.printStackTrace();
21         }
22         System.out.println("方法1 end");
23     }
24     
25     public  void method2(){
26         System.out.println("進入方法2");
27         try {
28             synchronized (this) {
29                 System.out.println("方法2執行");
30                 Thread.sleep(1000);
31             }
32         } catch (InterruptedException e) {
33             e.printStackTrace();
34         }
35         System.out.println("方法2 end");
36     }
37         
38     public static void main(String[] args) {
39         SynchronizedDemo3 demo = new SynchronizedDemo3();
40         new Thread(new Runnable() {
41             @Override
42             public void run() {
43                 demo.method1();
44             }
45         }).start();
46         
47         new Thread(new Runnable() {
48             @Override
49             public void run() {
50                 demo.method2();
51             }
52         }).start();
53     }
54 }

運行結果:

1 進入方法1
2 方法1執行
3 進入方法2
4 方法1 end
5 方法2執行
6 方法2 end

可見,修飾代碼塊,只鎖住代碼塊的執行順序。代碼塊級別串行。(例如上面的方法1和方法2沒能串行,因為鎖住的是同一個對象,但是同步代碼塊只包住了方法中的一部分

三、Synchronized 原理

實際上,JVM只區分兩種不同用法 1.修飾代碼塊 2.修飾方法。什么,你不信?好吧,上SE8規范:http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-3.html#jvms-3.14

 

上圖中,紅框框中說明了,1.monitorenter+monitorexit(上圖中的onlyMe方法中同步代碼塊) 2.修飾方法

 這一切都是規范說的,下面自測一下吧~

 下面,我們通過JDK自帶工具反編譯,查看包含java字節碼的指令。

3.1 synchronized修飾代碼塊

java源碼如下:

1 public class SynchronizedDemo {
2     public void method (){
3         synchronized (this) {
4             System.out.println("method 1 start!!!!");
5         }
6     }
7 }

javac -encoding utf-8 SynchronizedDemo.java 編譯生成class 后,javap -c 反編譯一下,看指令:

 這里着重分析2個monitorenter、monitorexit這兩個指令。這里以JSE8位為准,查到屬於JVM指令集。官網各種API、JVM規范,指令等,傳送門:http://docs.oracle.com/javase/8/docs/。

1.monitorenter監視器准入指令


 

主要看上圖中Description:

每個對象有一個監視器鎖(monitor)。當monitor被占用時就會處於鎖定狀態,線程執行monitorenter指令時嘗試獲取monitor的所有權,過程如下:

1.如果monitor的進入數為0,則該線程進入monitor,然后將進入數設置為1,該線程即為monitor的所有者。

2.如果線程已經占有該monitor,只是重新進入,則進入monitor的進入數加1.

3.’如果其他線程已經占用了monitor,則該線程進入阻塞狀態,直到monitor的進入數為0,再重新嘗試獲取monitor的所有權。

2.monitorexit監視器釋放指令

 主要看上圖中Description:

1.執行monitorexit的線程必須是objectref所對應的monitor的所有者。

2.指令執行時,monitor的進入數減1,如果減1后進入數為0,那線程退出monitor,不再是這個monitor的所有者。其他被這個monitor阻塞的線程可以嘗試去獲取這個 monitor 的所有權。

3.2 synchronized修飾方法

java源碼如下:

 1 package lock;
 2 
 3 /**
 4  * 
 5  * @ClassName:SynchronizedDemo0
 6  * @Description: Synchronized修飾方法
 7  * @author diandian.zhang
 8  * @date 2017年4月5日下午6:18:12
 9  */
10 public class SynchronizedDemo0 {
11     public synchronized void method (){
12         System.out.println("method start!!!!");
13     }
14 }

 

javap -v 查看class文件,發現method上加了同步標簽,本質上還是monitor

3.3 synchronized終極原理C++實現
 
 
四、總結
  
JDK一直沒有放棄synchronized且一直在優化,到目前JDK8依然廣泛使用。本文從功能、常見用法、源碼實現三方面徹底剖析了synchronized鎖。相信讀者會有所收獲。

 

 ========附言分割線=====

附:全文參考http://www.cnblogs.com/paddix/p/5367116.html


免責聲明!

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



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