開啟正文之前,先說一下源碼剖析這一系列,就以“死磕到底”的精神貫徹始終,最少追蹤到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.修飾普通方法
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 }
結(jié)果:
1 進(jìn)入方法12 方法1執(zhí)行3 方法1 end4 進(jìn)入方法25 方法2執(zhí)行6 方法2 end
可見:修飾普通方法,線程2需要等待線程1的method1執(zhí)行完成才能開始執(zhí)行method2方法,方法級(jí)別串行執(zhí)行。
2.修飾靜態(tài)方法
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 }
運(yùn)行結(jié)果:
1 進(jìn)入方法12 方法1執(zhí)行3 進(jìn)入方法24 方法1 end5 方法2執(zhí)行6 方法2 end
可見:修飾靜態(tài)方法,本質(zhì)是對(duì)類的同步,任何實(shí)例調(diào)用方法,都類級(jí)別串行執(zhí)行。
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("進(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 }
運(yùn)行結(jié)果:
1 進(jìn)入方法12 方法1執(zhí)行3 進(jìn)入方法24 方法1 end5 方法2執(zhí)行6 方法2 end
可見,修飾代碼塊,只鎖住代碼塊的執(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
上圖中,紅框框中說明了,1.monitorenter+monitorexit(上圖中的onlyMe方法中同步代碼塊) 2.修飾方法
這一切都是規(guī)范說的,下面自測(cè)一下吧~
下面,我們通過JDK自帶工具反編譯,查看包含java字節(jié)碼的指令。
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個(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)入指令
主要看上圖中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)視器釋放指令
主要看上圖中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源碼如下:
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文件,發(fā)現(xiàn)method上加了同步標(biāo)簽,本質(zhì)上還是monitor
四、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ā)言討論。共同提高! ------------------