多線程併發編程之Synchronized原理解析

Synchronized原理解析

synchronized(this) {
    i++;
}

思考這三個問題?

  1. this對象加鎖的狀態如何記錄?
  2. 狀態被記錄到this對象裏面了嗎?
  3. 若鎖佔用,線程掛起;釋放鎖時,喚醒掛起的線程,是如何做到的?

堆中對象的存儲

代碼示例:

public class Demo5_main {

    public static void main(String args[]){
        int a = 1;

        Teacher kody = new Teacher();
        kody.stu = new Student();
    }
}

class Teacher{
    String name = "Kody";
    int age = 40;
    boolean gender = true;

    Student stu;
}

class Student{
    String name = "Emily";
    int age = 18;
    boolean gender = false;
}

在JVM中的存儲結構

在這裏插入圖片描述

  1. 方法中定義的局部變量都存放在虛擬機棧中的局部變量表,堆中存放的是具體的值,局部變量表通過引用指向堆中的值。
  2. 對象中除了存放值外,還存放了對象頭,對象頭指向方法區中具體的類信息,與堆內存中的對象進行綁定
  3. 如果新創建了一個String對象(String str = new String(“abc”)),str在局部變量表中通過引用指向堆內存中的引用,堆中的引用再指向方法區中的字符串常量池。

對象頭的概念

對象除了在堆中存儲值外,還開闢了一個內存區域來存放對象頭。

Class Metadata Address:就是通過這個來指向Class對象的

Array Length:數組對象的長度就存放在這裏

Mark Word:用來存放對象鎖的信息,長度爲32位或64位

Mark Word

在這裏插入圖片描述

Unlocked : 未鎖定

Light-weight locked : 輕量級鎖

Heavy-weight locked : 重量級鎖

輕量級加鎖過程原理分析

在這裏插入圖片描述

  1. 線程進行鎖的爭搶,會在線程獨佔內存開闢一個Lock Record(鎖記錄空間),會將對象的Mark Word(最開始的未鎖定狀態 hashcode|age|0),拷貝到Lock Record中
  2. Lock Record 會用**CAS(Hashcode|age|0, Lock record address)**操作對Mark Word進行更改,只有一個線程會成功修改成功,另一個CAS失敗,進行自旋
  3. 棧幀中的Lock Record內存中還有個owner,指向堆中對象的Mark Word,Mark Word也會指向這個owner,表示現在哪個線程佔有了這把鎖
  4. 當自旋達到一定次數,或者這個時候第三個線程又來爭搶鎖,鎖就會升級爲重量級鎖。(自旋是一個很消耗性能的操作)

重量級鎖加鎖原理分析

在這裏插入圖片描述

  1. 重量級鎖是對jvm底層的對象監視器(ObjectMonitor)來實現的,ObjectMonitor用C++來實現的
  2. owner :用來存放搶鎖成功的線程
  3. entryList:阻塞池,搶鎖失敗的線程會進入到裏面,這裏面的線程狀態爲Blocking
  4. waitSet:等待池,調用wait()方法就會進入到裏面,線程狀態爲waitting進入等待池會釋放鎖
  5. count:synchronized是一個可重入鎖,用來記錄重入的次數
  6. 只有拿到對象的鎖,才能調用notify()喚醒等待池中的線程,喚醒的線程不一定會拿到鎖,所以synchronized是一個非公平鎖

通過這個原理我們可以更加深入的理解線程的6種狀態,以及調用wait()和notify()方法爲何必須要拿到對象的鎖

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章