欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳細(xì)解讀java同步之synchronized解析

 更新時(shí)間:2019年05月24日 10:27:11   作者:彤哥讀源碼  
synchronized關(guān)鍵字是Java里面最基本的同步手段,下面我們來一起學(xué)習(xí)一下

問題

(1)synchronized的特性?

(2)synchronized的實(shí)現(xiàn)原理?

(3)synchronized是否可重入?

(4)synchronized是否是公平鎖?

(5)synchronized的優(yōu)化?

(6)synchronized的五種使用方式?

簡(jiǎn)介

synchronized關(guān)鍵字是Java里面最基本的同步手段,它經(jīng)過編譯之后,會(huì)在同步塊的前后分別生成 monitorenter 和 monitorexit 字節(jié)碼指令,這兩個(gè)字節(jié)碼指令都需要一個(gè)引用類型的參數(shù)來指明要鎖定和解鎖的對(duì)象。

實(shí)現(xiàn)原理

在學(xué)習(xí)Java內(nèi)存模型的時(shí)候,我們介紹過兩個(gè)指令:lock 和 unlock。

lock,鎖定,作用于主內(nèi)存的變量,它把主內(nèi)存中的變量標(biāo)識(shí)為一條線程獨(dú)占狀態(tài)。

unlock,解鎖,作用于主內(nèi)存的變量,它把鎖定的變量釋放出來,釋放出來的變量才可以被其它線程鎖定。

但是這兩個(gè)指令并沒有直接提供給用戶使用,而是提供了兩個(gè)更高層次的指令 monitorenter 和 monitorexit 來隱式地使用 lock 和 unlock 指令。

而 synchronized 就是使用 monitorenter 和 monitorexit 這兩個(gè)指令來實(shí)現(xiàn)的。

根據(jù)JVM規(guī)范的要求,在執(zhí)行monitorenter指令的時(shí)候,首先要去嘗試獲取對(duì)象的鎖,如果這個(gè)對(duì)象沒有被鎖定,或者當(dāng)前線程已經(jīng)擁有了這個(gè)對(duì)象的鎖,就把鎖的計(jì)數(shù)器加1,相應(yīng)地,在執(zhí)行monitorexit的時(shí)候會(huì)把計(jì)數(shù)器減1,當(dāng)計(jì)數(shù)器減小為0時(shí),鎖就釋放了。

我們還是來上一段代碼,看看編譯后的字節(jié)碼長(zhǎng)啥樣來學(xué)習(xí):

public class SynchronizedTest {

 public static void sync() {
 synchronized (SynchronizedTest.class) {
 synchronized (SynchronizedTest.class) {
 }
 }
 }
 public static void main(String[] args) {
 }
}

我們這段代碼很簡(jiǎn)單,只是簡(jiǎn)單地對(duì)SynchronizedTest.class對(duì)象加了兩次synchronized,除此之外,啥也沒干。

編譯后的sync()方法的字節(jié)碼指令如下,為了便于閱讀,彤哥特意加上了注釋:

// 加載常量池中的SynchronizedTest類對(duì)象到操作數(shù)棧中
0 ldc #2 <com/coolcoding/code/synchronize/SynchronizedTest>
// 復(fù)制棧頂元素
2 dup
// 存儲(chǔ)一個(gè)引用到本地變量0中,后面的0表示第幾個(gè)變量
3 astore_0
// 調(diào)用monitorenter,它的參數(shù)變量0,也就是上面的SynchronizedTest類對(duì)象
4 monitorenter
// 再次加載常量池中的SynchronizedTest類對(duì)象到操作數(shù)棧中
5 ldc #2 <com/coolcoding/code/synchronize/SynchronizedTest>
// 復(fù)制棧頂元素
7 dup
// 存儲(chǔ)一個(gè)引用到本地變量1中
8 astore_1
// 再次調(diào)用monitorenter,它的參數(shù)是變量1,也還是SynchronizedTest類對(duì)象
9 monitorenter
// 從本地變量表中加載第1個(gè)變量
10 aload_1
// 調(diào)用monitorexit解鎖,它的參數(shù)是上面加載的變量1
11 monitorexit
// 跳到第20行
12 goto 20 (+8)
15 astore_2
16 aload_1
17 monitorexit
18 aload_2
19 athrow
// 從本地變量表中加載第0個(gè)變量
20 aload_0
// 調(diào)用monitorexit解鎖,它的參數(shù)是上面加載的變量0
21 monitorexit
// 跳到第30行
22 goto 30 (+8)
25 astore_3
26 aload_0
27 monitorexit
28 aload_3
29 athrow
// 方法返回,結(jié)束
30 return

按照彤哥的注釋讀起來,字節(jié)碼比較簡(jiǎn)單,我們的synchronized鎖定的是SynchronizedTest類對(duì)象,可以看到它從常量池中加載了兩次SynchronizedTest類對(duì)象,分別存儲(chǔ)在本地變量0和本地變量1中,解鎖的時(shí)候正好是相反的順序,先解鎖變量1,再解鎖變量0,實(shí)際上變量0和變量1指向的是同一個(gè)對(duì)象,所以synchronized是可重入的。

至于,被加鎖的對(duì)象具體在對(duì)象頭中是怎么存儲(chǔ)的,彤哥這里就不細(xì)講了,有興趣的可以看看《Java并發(fā)編程的藝術(shù)》這本書。

原子性、可見性、有序性

前面講解Java內(nèi)存模型的時(shí)候我們說過內(nèi)存模型主要就是用來解決緩存一致性的問題的,而緩存一致性主要包括原子性、可見性、有序性。

那么,synchronized關(guān)鍵字能否保證這三個(gè)特性呢?

還是回到Java內(nèi)存模型上來,synchronized關(guān)鍵字底層是通過monitorenter和monitorexit實(shí)現(xiàn)的,而這兩個(gè)指令又是通過lock和unlock來實(shí)現(xiàn)的。

而lock和unlock在Java內(nèi)存模型中是必須滿足下面四條規(guī)則的:

(1)一個(gè)變量同一時(shí)刻只允許一條線程對(duì)其進(jìn)行l(wèi)ock操作,但lock操作可以被同一個(gè)線程執(zhí)行多次,多次執(zhí)行l(wèi)ock后,只有執(zhí)行相同次數(shù)的unlock操作,變量才能被解鎖。

(2)如果對(duì)一個(gè)變量執(zhí)行l(wèi)ock操作,將會(huì)清空工作內(nèi)存中此變量的值,在執(zhí)行引擎使用這個(gè)變量前,需要重新執(zhí)行l(wèi)oad或assign操作初始化變量的值;

(3)如果一個(gè)變量沒有被lock操作鎖定,則不允許對(duì)其執(zhí)行unlock操作,也不允許unlock一個(gè)其它線程鎖定的變量;

(4)對(duì)一個(gè)變量執(zhí)行unlock操作之前,必須先把此變量同步回主內(nèi)存中,即執(zhí)行store和write操作;

通過規(guī)則(1),我們知道對(duì)于lock和unlock之間的代碼,同一時(shí)刻只允許一個(gè)線程訪問,所以,synchronized是具有原子性的。

通過規(guī)則(1)(2)和(4),我們知道每次lock和unlock時(shí)都會(huì)從主內(nèi)存加載變量或把變量刷新回主內(nèi)存,而lock和unlock之間的變量(這里是指鎖定的變量)是不會(huì)被其它線程修改的,所以,synchronized是具有可見性的。

通過規(guī)則(1)和(3),我們知道所有對(duì)變量的加鎖都要排隊(duì)進(jìn)行,且其它線程不允許解鎖當(dāng)前線程鎖定的對(duì)象,所以,synchronized是具有有序性的。

綜上所述,synchronized是可以保證原子性、可見性和有序性的。

公平鎖 VS 非公平鎖

通過上面的學(xué)習(xí),我們知道了synchronized的實(shí)現(xiàn)原理,并且它是可重入的,那么,它是否是公平鎖呢?

直接上菜:

public class SynchronizedTest {
 public static void sync(String tips) {
 synchronized (SynchronizedTest.class) {
 System.out.println(tips);
 try {
 Thread.sleep(1000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 }
 public static void main(String[] args) throws InterruptedException {
 new Thread(()->sync("線程1")).start();
 Thread.sleep(100);
 new Thread(()->sync("線程2")).start();
 Thread.sleep(100);
 new Thread(()->sync("線程3")).start();
 Thread.sleep(100);
 new Thread(()->sync("線程4")).start();
 }
}

這段程序中,我們起了四個(gè)線程,且分別間隔100ms啟動(dòng),每個(gè)線程里面打印一句話后等待1000ms,如果synchronized是公平鎖,那么打印的結(jié)果應(yīng)該依次是 線程1、2、3、4。

但是,實(shí)際運(yùn)行的結(jié)果幾乎不會(huì)出現(xiàn)上面的樣子,所以,synchronized是一個(gè)非公平鎖。

鎖優(yōu)化

Java在不斷進(jìn)化,同樣地,Java中像synchronized這種古老的東西也在不斷進(jìn)化,比如ConcurrentHashMap在jdk7的時(shí)候還是使用ReentrantLock加鎖的,在jdk8的時(shí)候已經(jīng)換成了原生的synchronized了,可見synchronized有原生的支持,它的進(jìn)化空間還是很大的。

那么,synchronized有哪些進(jìn)化中的狀態(tài)呢?

我們這里稍做一些簡(jiǎn)單地介紹:

(1)偏向鎖,是指一段同步代碼一直被一個(gè)線程訪問,那么這個(gè)線程會(huì)自動(dòng)獲取鎖,降低獲取鎖的代價(jià)。

(2)輕量級(jí)鎖,是指當(dāng)鎖是偏向鎖時(shí),被另一個(gè)線程所訪問,偏向鎖會(huì)升級(jí)為輕量級(jí)鎖,這個(gè)線程會(huì)通過自旋的方式嘗試獲取鎖,不會(huì)阻塞,提高性能。

(3)重量級(jí)鎖,是指當(dāng)鎖是輕量級(jí)鎖時(shí),當(dāng)自旋的線程自旋了一定的次數(shù)后,還沒有獲取到鎖,就會(huì)進(jìn)入阻塞狀態(tài),該鎖升級(jí)為重量級(jí)鎖,重量級(jí)鎖會(huì)使其他線程阻塞,性能降低。

總結(jié)

(1)synchronized在編譯時(shí)會(huì)在同步塊前后生成monitorenter和monitorexit字節(jié)碼指令;

(2)monitorenter和monitorexit字節(jié)碼指令需要一個(gè)引用類型的參數(shù),基本類型不可以哦;

(3)monitorenter和monitorexit字節(jié)碼指令更底層是使用Java內(nèi)存模型的lock和unlock指令;

(4)synchronized是可重入鎖;

(5)synchronized是非公平鎖;

(6)synchronized可以同時(shí)保證原子性、可見性、有序性;

(7)synchronized有三種狀態(tài):偏向鎖、輕量級(jí)鎖、重量級(jí)鎖;

彩蛋——synchronized的五種使用方式

通過上面的分析,我們知道synchronized是需要一個(gè)引用類型的參數(shù)的,而這個(gè)引用類型的參數(shù)在Java中其實(shí)可以分成三大類:類對(duì)象、實(shí)例對(duì)象、普通引用,使用方式分別如下:

public class SynchronizedTest2 {
 public static final Object lock = new Object();
 // 鎖的是SynchronizedTest.class對(duì)象
 public static synchronized void sync1() {
 }
 public static void sync2() {
 // 鎖的是SynchronizedTest.class對(duì)象
 synchronized (SynchronizedTest.class) {
 }
 }
 // 鎖的是當(dāng)前實(shí)例this
 public synchronized void sync3() {
 }
 public void sync4() {
 // 鎖的是當(dāng)前實(shí)例this
 synchronized (this) {
 }
 }
 public void sync5() {
 // 鎖的是指定對(duì)象lock
 synchronized (lock) {
 }
 }
}

在方法上使用synchronized的時(shí)候要注意,會(huì)隱式傳參,分為靜態(tài)方法和非靜態(tài)方法,靜態(tài)方法上的隱式參數(shù)為當(dāng)前類對(duì)象,非靜態(tài)方法上的隱式參數(shù)為當(dāng)前實(shí)例this。

另外,多個(gè)synchronized只有鎖的是同一個(gè)對(duì)象,它們之間的代碼才是同步的,這一點(diǎn)在使用synchronized的時(shí)候一定要注意。

推薦閱讀

細(xì)談java同步之JMM(Java Memory Model)

java同步之volatile解析

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • JavaSE的邏輯控制你了解嗎

    JavaSE的邏輯控制你了解嗎

    這篇文章主要為大家詳細(xì)介紹了JavaSE的邏輯控制,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • 數(shù)據(jù)結(jié)構(gòu)與算法之手撕排序算法

    數(shù)據(jù)結(jié)構(gòu)與算法之手撕排序算法

    排序算法看似簡(jiǎn)單,其實(shí)不同的算法中蘊(yùn)涵著經(jīng)典的算法策略。通過熟練掌握排序算法,就可以掌握基本的算法設(shè)計(jì)思想,本文主要介紹了Java中的排序算法,需要的朋友歡迎閱讀
    2023-04-04
  • Java設(shè)計(jì)模式之單例模式詳解

    Java設(shè)計(jì)模式之單例模式詳解

    這篇文章主要為大家詳細(xì)介紹了Java設(shè)計(jì)模式之單例模式的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • Java操作Excel的示例詳解

    Java操作Excel的示例詳解

    在平時(shí)可以使用IO流對(duì)Excle進(jìn)行操作,但是現(xiàn)在使用更加方便的第三方組件來實(shí)現(xiàn)。本文就來和大家聊聊Java如何通過第三方組件實(shí)現(xiàn)操作Excel,需要的可以參考一下
    2023-01-01
  • Spring關(guān)于@Scheduled限制的問題

    Spring關(guān)于@Scheduled限制的問題

    這篇文章主要介紹了Spring關(guān)于@Scheduled限制的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • SpringBoot中定時(shí)任務(wù)@Scheduled注解的使用解讀

    SpringBoot中定時(shí)任務(wù)@Scheduled注解的使用解讀

    這篇文章主要介紹了SpringBoot中定時(shí)任務(wù)@Scheduled注解的使用解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • SpringBoot靜態(tài)資源及原理解析

    SpringBoot靜態(tài)資源及原理解析

    這篇文章主要介紹了SpringBoot靜態(tài)資源及原理解析,當(dāng)創(chuàng)建一個(gè)jar工程時(shí),想引入css等靜態(tài)資源時(shí),需要遵守SpringBoot的靜態(tài)資源映射關(guān)系,通過WebMvcAutoConfiguration查看靜態(tài)配置資源的規(guī)則,需要的朋友可以參考下
    2023-12-12
  • Value注解支持對(duì)象類型ConfigurationProperties功能

    Value注解支持對(duì)象類型ConfigurationProperties功能

    這篇文章主要為大家介紹了Value注解支持對(duì)象類型ConfigurationProperties功能詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • Spring Security登錄添加驗(yàn)證碼的實(shí)現(xiàn)過程

    Spring Security登錄添加驗(yàn)證碼的實(shí)現(xiàn)過程

    這篇文章主要介紹了Spring Security登錄添加驗(yàn)證碼的實(shí)現(xiàn)過程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • NetBeans安裝提示neatbeans cannot find java 1.8 or higher

    NetBeans安裝提示neatbeans cannot find java 1.8 or higher

    今天小編就為大家分享一篇關(guān)于NetBeans安裝提示neatbeans cannot find java 1.8 or higher,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-04-04

最新評(píng)論