Java內存模型-鎖的內存語義

一 引言

  在說volatile的內存語義時,講過這樣一句話:想要理解透volatile特性有一個很好的方法,就是把對volatile變量的單個讀/寫,當作是使用同一個鎖對這些單個讀/寫操做作了同步。因此其實鎖的釋放和獲取與volatile的寫和讀具備相同的內存語義。html

二 鎖的釋放-獲取創建的happens-before關係

  不清楚happens-before規則的請前去看-看,這裏就不在細說了。因爲在以前沒有舉例說明監視器鎖規則,那麼在這裏就詳細說明下,下面是鎖釋放-獲取的示例代碼:併發

public class MonitorExample {
    int a = 0;

    public synchronized void writer() { // 1
        a += 1;                         // 2
    }                                   // 3

    public synchronized void reader() { // 4
        System.out.println(a);          // 5
    }                                   // 6

    public static void main(String[] args) {
        final MonitorExample me = new MonitorExample();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                me.writer();
            }
        });
        t1.start();

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                me.reader();
            }
        });
        t2.start();
    }
}

  這裏咱們假設線程1先執行writer()方法,隨後線程B執行reader()方法(知道爲何要假設?由於不是必定按這種順序發生,能夠測試下結果)。根據happens-before規則,這個過程包含的happens-before關係能夠分爲3類:app

  1) 根據程序次序規則:1 happens-before 2, 2 happens-before 3, 4 happens-before 5, 5 happens-before 6;ide

  2)根據監視器鎖規則:3 happens-before 4;測試

  3)根據傳遞性規則,2 happens-before 5。spa

  上述happens-before關係的圖形化表現形式以下:線程

 

三 鎖的釋放和獲取的內存語義

  當線程釋放鎖時,JMM會把該線程對應的本地內存中的共享變量刷新到主內存中。以上面的MonitorExample程序爲例,線程1釋放鎖後,共享數據的狀態示意圖以下:code

  當線程獲取鎖時,JMM會把該線程對應的本地內存置爲無效。從而使得被監視器保護的臨界區代碼必須從主內存中讀取共享變量。htm

   對比鎖釋放-讀取的內存語義與volatile寫-讀的內存語義能夠看出,鎖釋放與volatile寫具備相同的內存語義;鎖獲取與volatile讀具備相同的內存語義。下面對鎖釋放和鎖獲取的內存語義作個總結。blog

  • 線程1釋放一個鎖,實質上是線程1向接下來將要獲取這個鎖的某個線程發出了(線程1對共享變量所作修改的)消息。
  • 線程2獲取一個鎖,實質上是線程2接收了以前某個線程發出的(在釋放這個鎖以前對共享變量所作修改的)消息。
  • 線程1鎖釋放,隨後線程2獲取這個鎖,這個過程實質上是線程A經過主內存向線程B發送消息。

四 鎖內存語義的實現

  鎖有不少種,但其基本原理都是差很少的。書上是以ReentrantLock中的公平鎖與非公平鎖做爲案例分析,有興趣的同窗能夠去閱讀原籍和源碼。現總結以下:

  • 公平鎖和非公平鎖釋放時,最後都要寫一個volatile變量state。
  • 公平鎖獲取是,首先會去讀volatile變量。
  • 非公平鎖獲取時,首先會用CAS更新volatile變量,這個操做同時具備volatile讀和寫的內存語義。

  因此鎖釋放-獲取的內存語義的實現至少有下面兩種方式:

  1)利用volatile變量的寫-讀所具備的內存語義。

  2)利用CAS所附帶的volatile讀和volatile寫的內存語義。

  由此可知:併發包下的類的實現方式大部分都是基於這兩種方式實現的。

相關文章
相關標籤/搜索