最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

死磕 java同步系列之synchronized解析

 更新時間:2021年06月28日 15:59:22   作者:彤哥讀源碼  
這篇文章主要介紹了Java中syncronized正確使用方法解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下

問題

(1)synchronized的特性?

(2)synchronized的實現原理?

(3)synchronized是否可重入?

(4)synchronized是否是公平鎖?

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

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

簡介

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

實現原理

在學習Java內存模型的時候,我們介紹過兩個指令:lock 和 unlock。

lock,鎖定,作用于主內存的變量,它把主內存中的變量標識為一條線程獨占狀態(tài)。

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

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

而 synchronized 就是使用 monitorenter 和 monitorexit 這兩個指令來實現的。

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

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

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

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

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

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

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

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

原子性、可見性、有序性

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

那么,synchronized關鍵字能否保證這三個特性呢?

還是回到Java內存模型上來,synchronized關鍵字底層是通過monitorenter和monitorexit實現的,而這兩個指令又是通過lock和unlock來實現的。

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

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

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

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

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

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

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

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

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

公平鎖 VS 非公平鎖

通過上面的學習,我們知道了synchronized的實現原理,并且它是可重入的,那么,它是否是公平鎖呢?

直接上菜:

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();
     }
}

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

但是,實際運行的結果幾乎不會出現上面的樣子,所以,synchronized是一個非公平鎖。

鎖優(yōu)化

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

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

我們這里稍做一些簡單地介紹:

(1)偏向鎖,是指一段同步代碼一直被一個線程訪問,那么這個線程會自動獲取鎖,降低獲取鎖的代價。

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

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

總結

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

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

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

(4)synchronized是可重入鎖;

(5)synchronized是非公平鎖;

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

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

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

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

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

總結

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

另外,多個synchronized只有鎖的是同一個對象,它們之間的代碼才是同步的,這一點在使用synchronized的時候一定要注意。

這篇文章就到這里,希望可以給你帶來幫助,也希望您可以多多關注腳本之家的更多內容!

相關文章

  • 淺談Spring中@Import注解的作用和使用

    淺談Spring中@Import注解的作用和使用

    這篇文章主要介紹了淺談Spring中@Import注解的作用和使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-05-05
  • idea關聯maven的使用詳解

    idea關聯maven的使用詳解

    這篇文章主要介紹了idea關聯maven的使用詳解,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • 詳解Java利用ExecutorService實現同步執(zhí)行大量線程

    詳解Java利用ExecutorService實現同步執(zhí)行大量線程

    這篇文章主要介紹了Java利用ExecutorService實現同步執(zhí)行大量線程,ExecutorService可以維護我們的大量線程在操作臨界資源時的穩(wěn)定性。
    2017-03-03
  • Java多線程死鎖與資源限制操作

    Java多線程死鎖與資源限制操作

    這篇文章主要介紹了Java多線程死鎖與資源限制操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • Spring中的@ControllerAdvice和ResponseBodyAdvice詳解

    Spring中的@ControllerAdvice和ResponseBodyAdvice詳解

    這篇文章主要介紹了Spring中的@ControllerAdvice和ResponseBodyAdvice詳解,@ControllerAdvice作用于@Controller修飾的類里面的所有方法,ResponseBodyAdvice作用于@ResponseBody注解修飾的方法,它可以對這些方法的返回值進行修改,需要的朋友可以參考下
    2024-01-01
  • jfinal添加jcaptcha驗證碼實現方法

    jfinal添加jcaptcha驗證碼實現方法

    這篇文章主要介紹了jfinal的jcaptcha驗證碼實現方法,大家參考使用吧
    2014-01-01
  • Java結合JS實現URL編碼與解碼

    Java結合JS實現URL編碼與解碼

    這篇文章介紹了Java結合JS實現URL編碼與解碼的方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-03-03
  • Java?9?中的模塊Module系統(tǒng)

    Java?9?中的模塊Module系統(tǒng)

    Java?9?引入的模塊是在Java包(package)的基礎上又引入的一個新的抽象層,基于package這一點很重要,這里需要強調一下,接下來通過本文給大家介紹Java?9?中的模塊Module系統(tǒng),感興趣的朋友一起看看吧
    2022-03-03
  • Elasticsearch 基礎介紹及索引原理分析

    Elasticsearch 基礎介紹及索引原理分析

    這篇文章主要介紹了Elasticsearch 基礎介紹及索引原理分析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-07-07
  • java簡單解析xls文件的方法示例【讀取和寫入】

    java簡單解析xls文件的方法示例【讀取和寫入】

    這篇文章主要介紹了java簡單解析xls文件的方法,結合實例形式分析了java針對xls文件的讀取和寫入相關操作技巧與注意事項,需要的朋友可以參考下
    2017-06-06

最新評論

若尔盖县| 上思县| 新田县| 南皮县| 阳西县| 武隆县| 云林县| 昌吉市| 金平| 彝良县| 科技| 唐山市| 甘肃省| 彩票| 通山县| 耒阳市| 班戈县| 自治县| 富源县| 吉首市| 静乐县| 八宿县| 德格县| 盐边县| 三穗县| 延川县| 土默特左旗| 云林县| 抚顺县| 高青县| 肥乡县| 北海市| 翼城县| 垣曲县| 弥勒县| 西华县| 甘谷县| 吴忠市| 中宁县| 嘉祥县| 永福县|