全面詳解Synchronized關鍵字

目錄

示意圖

一、Synchronized的作用

  • 保證同一時刻最多隻有1個線程執行 被Synchronized修飾的方法 / 代碼,
    其他線程 必須等待當前線程執行完該方法 / 代碼塊後才能執行該方法 / 代碼塊。

二、 應用場景

保證線程安全,解決多線程中的併發同步問題(實現的是阻塞型併發),具體場景如下:

  • 修飾 實例方法 / 代碼塊時,(同步)保護的是同一個對象方法的調用 & 當前實例對象
  • 修飾 靜態方法 / 代碼塊時,(同步)保護的是 靜態方法的調用 & class 類對象

三、 原理

  • 依賴 JVM 實現同步
  • 底層通過一個監視器對象(monitor)完成, wait()、notify() 等方法也依賴於 monitor 對象

監視器鎖(monitor)的本質 依賴於 底層操作系統的互斥鎖(Mutex Lock)實現

四、 具體使用

Synchronized 用於 修飾 代碼塊、類的實例方法 & 靜態方法

1、使用規則

示意圖

2、鎖的類型 & 等級
  • 由於Synchronized 會修飾 代碼塊、類的實例方法 & 靜態方法,故分爲不同鎖的類型
    具體如下:
    在這裏插入圖片描述

  • 之間的區別
    示意圖

3、使用方式
/**
 * 對象鎖
 */
    public class Test{ 
    // 對象鎖:形式1(方法鎖) 
    public synchronized void Method1(){ 
        System.out.println("我是對象鎖也是方法鎖"); 
        try{ 
            Thread.sleep(500); 
        } catch (InterruptedException e){ 
            e.printStackTrace(); 
        } 
 
    } 
 
    // 對象鎖:形式2(代碼塊形式) 
    public void Method2(){ 
        synchronized (this){ 
            System.out.println("我是對象鎖"); 
            try{ 
                Thread.sleep(500); 
            } catch (InterruptedException e){ 
                e.printStackTrace(); 
            } 
        } 
 
    }/**
 * 方法鎖(即對象鎖中的形式1)
 */
    public synchronized void Method1(){ 
        System.out.println("我是對象鎖也是方法鎖"); 
        try{ 
            Thread.sleep(500); 
        } catch (InterruptedException e){ 
            e.printStackTrace(); 
        } 
 
    } 

/**
 * 類鎖
 */
public class Test{ 
   // 類鎖:形式1 :鎖靜態方法
    public static synchronized void Method1(){ 
        System.out.println("我是類鎖一號"); 
        try{ 
            Thread.sleep(500); 
        } catch (InterruptedException e){ 
            e.printStackTrace(); 
        } 
 
    } 
 
    // 類鎖:形式2 :鎖靜態代碼塊
    public void Method2(){ 
        synchronized (Test.class){ 
            System.out.println("我是類鎖二號"); 
            try{ 
                Thread.sleep(500); 
            } catch (InterruptedException e){ 
                e.printStackTrace(); 
            } 
 
        } 
 
    }
4、特別注意

Synchronized修飾方法時存在缺陷:若修飾1個大的方法,將會大大影響效率

  • 示例
    若使用Synchronized關鍵字修飾 線程類的run(),由於run()在線程的整個生命期內一直在運行,因此將導致它對本類任何Synchronized方法的調用都永遠不會成功

  • 解決方案
    使用 Synchronized關鍵字聲明代碼塊

該解決方案靈活性高:可針對任意代碼塊 & 任意指定上鎖的對象

代碼如下:

 synchronized(syncObject) { 
    // 訪問或修改被鎖保護的共享狀態 
    // 上述方法 必須 獲得對象 syncObject(類實例或類)的鎖
}

五、特點

示意圖
注:原子性、可見性、有序性的定義
在這裏插入圖片描述

六、其他控制併發 / 線程同步方式

1、Lock、ReentrantLock(重入鎖)
  • 簡介
    示意圖
  • 區別
    在這裏插入圖片描述
2、CAS
2.1 定義

Compare And Swap,即 比較 並 交換,是一種解決併發操作的樂觀鎖

synchronized鎖住的代碼塊:同一時刻只能由一個線程訪問,屬於悲觀鎖

2.2 原理
// CAS的操作參數
內存位置(A)
預期原值(B)
預期新值(C)

// 使用CAS解決併發的原理:
// 1. 首先比較A、B,若相等,則更新A中的值爲C、返回True;若不相等,則返回false;
// 2. 通過死循環,以不斷嘗試嘗試更新的方式實現併發

// 僞代碼如下
public boolean compareAndSwap(long memoryA, int oldB, int newC){
    if(memoryA.get() == oldB){
        memoryA.set(newC);
        return true;
    }
    return false;
}
2.3 優點
  • 資源耗費少:相對於synchronized,省去了掛起線程、恢復線程的開銷,但,若遲遲得不到更新,死循環對CPU資源也是一種浪費
2.4 具體實現方式
  • 使用CAS有個“先檢查後執行”的操作
  • 而這種操作在Java中是典型的不安全的操作,所以 CAS在實際中是由C++通過調用CPU指令實現的
  • 具體過程
// 1. CAS在Java中的體現爲Unsafe類
// 2. Unsafe類會通過C++直接獲取到屬性的內存地址
// 3. 接下來CAS由C++的Atomic::cmpxchg系列方法實現
2.5 典型應用:AtomicInteger

對 i++ 與 i–,通過compareAndSet & 一個死循環實現

而compareAndSet函數內部 = 通過jni操作CAS指令。直到CAS操作成功跳出循環

   private volatile int value; 
    /** 
     * Gets the current value. 
     * 
     * @return the current value 
     */ 
    public final int get() { 
        return value; 
    } 
    /** 
     * Atomically increments by one the current value. 
     * 
     * @return the previous value 
     */ 
    public final int getAndIncrement() { 
        for (;;) { 
            int current = get(); 
            int next = current + 1; 
            if (compareAndSet(current, next)) 
                return current; 
        } 
    } 
 
    /** 
     * Atomically decrements by one the current value. 
     * 
     * @return the previous value 
     */ 
    public final int getAndDecrement() { 
        for (;;) { 
            int current = get(); 
            int next = current - 1; 
            if (compareAndSet(current, next)) 
                return current; 
        } 
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章