Synchronized的小總結

一、Synchronized的作用

主要是保證多線程環境下的線程安全。

二、Synchronized種類

1. 對象鎖

  1. 包含方法鎖(默認鎖對象爲this當前實力對象),方法鎖形式:synchronized修飾普通方法,鎖默認對象爲this
public class MySynchronized {
  public synchronized void m1(){

  }
}
上面這種方法可以被:下面這種方式替代:
 public  void m3(){
	synchronized(this){ //synchronized 需要包住m3 所有的內部代碼片段
	
	  }
}
  1. 同步代碼塊鎖(自己制定鎖對象)代碼塊形式:手動指定鎖對象
public class MySynchronized {
  public void m1(){
	 synchronized (this){
	 
	  }
}
或者如下
public class MySynchronized {
Object obj= new Object();
  public void m1(){
	 synchronized(obj){
	 
	  }
}

2. 類鎖

指sychronized修飾靜態的方法或指鎖爲Class對象,
概念: java類可能有有很多個對象,但是隻有一個class對象
本質: 所以所謂的類鎖,不過是Class對象的鎖而已

用法和效果:類鎖只能在同一時刻被一個對象擁有
  形式1:synchronized加載static方法上

public class MySynchronized {
    public synchronized  static  void m1() {
        
    }
}

形式2:synchronized(*.class)代碼塊

public class MySynchronized {
    public void m2() {
        synchronized (MySynchronized.class) {

        }
    }
}

三、常見多線程情況下Synchronized的舉例

  1. 兩個線程同時訪問一個對象的相同的synchronized方法
    串行

  2. 兩個線程同時訪問兩個對象的相同的synchronized方法
    鎖對象不同,互不干擾,並行

  3. 兩個線程同時訪問兩個對象的相同的static的synchronized方法
    串行

  4. 兩個線程同時訪問同一對象的synchronized方法與非synchronized方法
    並行

  5. 兩個線程訪問同一對象的不同的synchronized方法
    同一對象鎖,串行

  6. 兩個線程同時訪問同一對象的static的synchronized方法與非static的synchronized方法
    鎖不同,並行

  7. 方法拋異常後,會釋放鎖嗎
    如果一個線程在進入同步方法後拋出了異常,則另一個線程會立刻進入該同步方法

  8. 目前進入到被synchronized修飾的方法,這個方法裏邊調用了非synchronized方法,是線程安全的嗎?
    線程安全

四、synchronized的性質

1. 可重入性

指的是同一線程的外層函數獲取鎖之後,內層函數可以直接再次獲取該鎖

Java典型的可重入鎖:synchronized、ReentrantLock
好處:避免死鎖,提升封裝性
粒度範圍證明:
情況一:同一方法是可重入的—遞歸調用本方法

/**
 * 可重入測試----使用遞歸方式   
 * 情況1:同一個類中,同一方法可重入
 */
public class SynchronizedTest {
    int i = 0;
    //主線程可以重入以this爲鎖對象的method方法
    public static void main(String[] args) {
        SynchronizedTest s1 = new  SynchronizedTest();
        s1.method();
    }
    // 同步方法
    private synchronized void method() {
        if (i <=3) {
            i++;
            System.out.println(i);
            method();
        }
    }
}
輸出:
1
2
3
4

情況二:可重入不要求是同一個方法

/**
 * 可重入測試----   
 * 情況2:同一個類中,不同方法可重入
 */
public class SynchronizedTest2 {
    //主線程可以重入以this爲鎖對象的method方法
    public static void main(String[] args) {
        SynchronizedTest2 s1 = new  SynchronizedTest2();
        s1.method1();
    }
    // 同步方法
    private synchronized void method1() {
        System.out.println("method1");
            method2();
        
    }
    private synchronized void method2() {
        System.out.println("method2");
    }
}
輸出:
method1
method2

情況三:可重入不要求是同一個類中的

public class Demo1 {
    public synchronized void method(){
        System.out.println("我是Demo1");
    }

}
class Demo1Zi extends  Demo1{
    public synchronized void method(){
        System.out.println("我是Demo1兒子");
        super.method();
    }

    public static void main(String[] args) {
        Demo1Zi zi = new Demo1Zi();
        zi.method();
    }
}
輸出:
我是Demo1兒子
我是Demo1

2. 不可中斷性

一旦這個鎖被別的線程獲取了,如果我現在想獲得,我只能選擇等待或者阻塞,直到別的線程釋放這個鎖,如果別的線程永遠不釋放鎖,那麼我只能永遠的等待下去。

相比之下,Lock類可以擁有中斷的能力,第一點:如果我覺得我等待的時間太長了,有權中斷現在已經獲取到鎖的線程執行;第二點:如果我覺得我等待的時間太長了不想再等了,也可以退出。

五、synchronized的缺點

1、效率低(jdk1.6對其進行了優化)

1)、鎖的釋放情況少(線程執行完成或者異常情況釋放)

2)、試圖獲得鎖時不能設定超時(只能等待)

3)、不能中斷一個正在試圖獲得鎖的線程(不能中斷)

jdk1.6對synchronized進行了優化,有一個鎖升級的過程,使得效率有一個提升,但是鎖升級不可逆。
在這裏插入圖片描述

2、不夠靈活

加鎖和釋放的時機比較單一,每個鎖僅有單一的條件(某個對象),可能是不夠的

比如:讀寫鎖更靈活

六、如何選擇Lock和synchronized關鍵字

本着優先避免出錯的原則,得出以下結論:

  1. 如果可以的話,儘量優先使用java.util.concurrent各種類(不需要考慮同步工作,不容易出錯)
  2. 優先使用synchronized,這樣可以減少編寫代碼的量,從而可以減少出錯率
  3. 若用到Lock或Condition獨有的特性,才使用Lock或Condition

七. 總結

JVM會自動通過使用monitor來加鎖和解鎖,保證了同一時刻只有一個線程可以執行指定的代碼,從而保證線程安全,同時具有可重入和不可中斷的特性。

八. 參考鏈接

阿里面試:跟我死磕Synchronized底層實現,我滿分回答拿了Offer

Java之戳中痛點 - (8)synchronized深度解析

Synchronized的作用

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