1. 程式人生 > >Synchronized關鍵字解析

Synchronized關鍵字解析

一、Synchronized用法

synchronized是Java提供的一個併發控制的關鍵字。主要有兩種用法,分別是同步方法和同步程式碼塊。也就是說,synchronized既可以修飾方法也可以修飾程式碼塊。程式碼如下:

/**
 * @author Hollis 18/08/04.
 */
public class SynchronizedDemo {
     //同步方法
    public synchronized void doSth(){
        System.out.println("Hello World");
    }

    //同步程式碼塊
    public void doSth1(){
        synchronized (SynchronizedDemo.class){
            System.out.println("Hello World");
        }
    }
}

synchronized修飾的程式碼塊及方法,在同一時間,只能被單個執行緒訪問。

二、Synchronized同步原理

synchronized,是Java中用於解決併發情況下資料同步訪問的一個很重要的關鍵字。當我們想要保證一個共享資源在同一時間只會被一個執行緒訪問到時,我們可以在程式碼中使用synchronized關鍵字對類或者物件加鎖。下面簡單介紹其原理,我們對上面的程式碼進行反編譯,可以得到如下程式碼:

public synchronized void doSth();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String Hello World
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return

  public void doSth1();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: ldc           #5                  // class com/hollis/SynchronizedTest
         2: dup
         3: astore_1
         4: monitorenter
         5: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         8: ldc           #3                  // String Hello World
        10: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        13: aload_1
        14: monitorexit
        15: goto          23
        18: astore_2
        19: aload_1
        20: monitorexit
        21: aload_2
        22: athrow
        23: return

通過反編譯後代碼可以看出:

對於同步方法,JVM採用ACC_SYNCHRONIZED標記符來實現同步。 

對於同步程式碼塊。JVM採用monitorentermonitorexit兩個指令來實現同步。

無論是ACC_SYNCHRONIZED還是monitorentermonitorexit都是基於Monitor實現的,在Java虛擬機器(HotSpot)中,Monitor是基於C++實現的,由ObjectMonitor實現。

三、Sychronized特性

1)原子性

在Java中,為了保證原子性,提供了兩個高階的位元組碼指令monitorentermonitorexit

。前面介紹過,這兩個位元組碼指令,在Java中對應的關鍵字就是synchronized。通過monitorentermonitorexit指令,可以保證被synchronized修飾的程式碼在同一時間只能被一個執行緒訪問,在鎖未釋放之前,無法被其他執行緒訪問到

2)可見性

可見性是指當多個執行緒訪問同一個變數時,一個執行緒修改了這個變數的值,其他執行緒能夠立即看得到修改的值。Java記憶體模型規定了所有的變數都儲存在主記憶體中,每條執行緒還有自己的工作記憶體,執行緒的工作記憶體中儲存了該執行緒中是用到的變數的主記憶體副本拷貝,執行緒對變數的所有操作都必須在工作記憶體中進行,而不能直接讀寫主記憶體。不同的執行緒之間也無法直接訪問對方工作記憶體中的變數,執行緒間變數的傳遞均需要自己的工作記憶體和主存之間進行資料同步進行。所以,就可能出現執行緒1改了某個變數的值,但是執行緒2不可見的情況。前面我們介紹過,被synchronized修飾的程式碼,在開始執行時會加鎖,執行完成後會進行解鎖。而為了保證可見性,有一條規則是這樣的:對一個變數解鎖之前,必須先把此變數同步回主存中。這樣解鎖後,後續執行緒就可以訪問到被修改後的值。所以,synchronized關鍵字鎖住的物件,其值是具有可見性的。