開啟正文之前,先說一下源碼剖析這一系列,就以“死磕到底”的精神貫徹始終,最少追蹤到JVM指令(再往下C語(yǔ)言實(shí)現(xiàn)了)。

=========正文分割線===========

目錄:

1. Synchronized作用

2.Synchronized常見用法分析

3.Synchronized 原理

4.Synchronized 優(yōu)缺點(diǎn)

5.總結(jié)

 

一、Synchronized作用:

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

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

(3)有效解決重排序問題。

 

二、Synchronized常見用法分析:

1.修飾普通方法

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

 1 package lock; 2  3 /** 4  * 
 5  * @ClassName:SynchronizedDemo 6  * @Description:測(cè)試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("進(jìn)入方法1");14         try {15             System.out.println("方法1執(zhí)行");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("進(jìn)入方法2");25         try {26             System.out.println("方法2執(zhí)行");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             @Override38             public void run() {39                 demo.method1();40             }41         }).start();42         43         new Thread(new Runnable() {44             @Override45             public void run() {46                 demo.method2();47             }48         }).start();49     }50 }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

結(jié)果:

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

1 進(jìn)入方法12 方法1執(zhí)行3 方法1 end4 進(jìn)入方法25 方法2執(zhí)行6 方法2 end

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

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

2.修飾靜態(tài)方法

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

 1 package lock; 2  3 /** 4  * 
 5  * @ClassName:SynchronizedDemo2 6  * @Description:修飾靜態(tài)方法 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("進(jìn)入方法1");14         try {15             System.out.println("方法1執(zhí)行");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("進(jìn)入方法2");25         try {26             System.out.println("方法2執(zhí)行");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             @Override37             public void run() {38                 SynchronizedDemo2.method1();39             }40         }).start();41         42         new Thread(new Runnable() {43             @Override44             public void run() {45                 SynchronizedDemo2.method2();46             }47         }).start();48     }49 }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

運(yùn)行結(jié)果:

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

1 進(jìn)入方法12 方法1執(zhí)行3 進(jìn)入方法24 方法1 end5 方法2執(zhí)行6 方法2 end

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

可見:修飾靜態(tài)方法,本質(zhì)是對(duì)類的同步,任何實(shí)例調(diào)用方法,都類級(jí)別串行執(zhí)行。

3.修飾代碼塊

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

 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("進(jìn)入方法1");14         try {15             synchronized (this) {16                 System.out.println("方法1執(zhí)行");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("進(jìn)入方法2");27         try {28             synchronized (this) {29                 System.out.println("方法2執(zhí)行");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             @Override42             public void run() {43                 demo.method1();44             }45         }).start();46         47         new Thread(new Runnable() {48             @Override49             public void run() {50                 demo.method2();51             }52         }).start();53     }54 }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

運(yùn)行結(jié)果:

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

1 進(jìn)入方法12 方法1執(zhí)行3 進(jìn)入方法24 方法1 end5 方法2執(zhí)行6 方法2 end

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

可見,修飾代碼塊,只鎖住代碼塊的執(zhí)行順序。代碼塊級(jí)別串行。

三、Synchronized 原理

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

 平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

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

 這一切都是規(guī)范說的,下面自測(cè)一下吧~

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

1.synchronized修飾代碼塊,java源碼如下:

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

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

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

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

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

 這里著重分析2個(gè)monitorenter、monitorexit這兩個(gè)指令。這里以JSE8位為準(zhǔn),查到屬于JVM指令集。官網(wǎng)各種API、JVM規(guī)范,指令等,傳送門:http://docs.oracle.com/javase/8/docs/。

1.monitorenter監(jiān)視器準(zhǔn)入指令

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)
 

主要看上圖中Description:

每個(gè)對(duì)象有一個(gè)監(jiān)視器鎖(monitor)。當(dāng)monitor被占用時(shí)就會(huì)處于鎖定狀態(tài),線程執(zhí)行monitorenter指令時(shí)嘗試獲取monitor的所有權(quán),過程如下:

1.如果monitor的進(jìn)入數(shù)為0,則該線程進(jìn)入monitor,然后將進(jìn)入數(shù)設(shè)置為1,該線程即為monitor的所有者。

2.如果線程已經(jīng)占有該monitor,只是重新進(jìn)入,則進(jìn)入monitor的進(jìn)入數(shù)加1.

3.’如果其他線程已經(jīng)占用了monitor,則該線程進(jìn)入阻塞狀態(tài),直到monitor的進(jìn)入數(shù)為0,再重新嘗試獲取monitor的所有權(quán)。

2.monitorexit監(jiān)視器釋放指令

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

 主要看上圖中Description:

1.執(zhí)行monitorexit的線程必須是objectref所對(duì)應(yīng)的monitor的所有者。

2.指令執(zhí)行時(shí),monitor的進(jìn)入數(shù)減1,如果減1后進(jìn)入數(shù)為0,那線程退出monitor,不再是這個(gè)monitor的所有者。其他被這個(gè)monitor阻塞的線程可以嘗試去獲取這個(gè) monitor 的所有權(quán)。

 

2.synchronized修飾方法,java源碼如下:

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

 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 }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

 

javap -v 查看class文件,發(fā)現(xiàn)method上加了同步標(biāo)簽,本質(zhì)上還是monitor

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

四、Synchronized 優(yōu)缺點(diǎn)

synchronized是通過軟件(JVM)實(shí)現(xiàn)的,簡(jiǎn)單易用,即使在JDK5之后有了Lock,仍然被廣泛地使用。

synchronized實(shí)際上是非公平的,新來(lái)的線程有可能立即獲得監(jiān)視器,而在等待區(qū)中等候已久的線程可能再次等待,不過這種搶占的方式可以預(yù)防饑餓。

synchronized只有鎖只與一個(gè)條件(是否獲取鎖)相關(guān)聯(lián),不靈活,后來(lái)Condition與Lock的結(jié)合解決了這個(gè)問題。

多線程競(jìng)爭(zhēng)一個(gè)鎖時(shí),其余未得到鎖的線程只能不停的嘗試獲得鎖,而不能中斷。高并發(fā)的情況下會(huì)導(dǎo)致性能下降。ReentrantLock的lockInterruptibly()方法可以優(yōu)先考慮響應(yīng)中斷。 一個(gè)線程等待時(shí)間過長(zhǎng),它可以中斷自己,然后ReentrantLock響應(yīng)這個(gè)中斷,不再讓這個(gè)線程繼續(xù)等待。有了這個(gè)機(jī)制,使用ReentrantLock時(shí)就不會(huì)像synchronized那樣產(chǎn)生死鎖了。

五、總結(jié)

相信讀過本文后,再也不用怕Synchronized了,一個(gè)很老JDK就自帶的鎖。

下一節(jié),源碼剖析ReentrantLock。

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

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

------------------ 本人實(shí)力有限,理解有誤之處大家一定要提出來(lái),多多發(fā)言討論。共同提高! ------------------