最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java synchronized 關鍵字原理、用法、優(yōu)化與實戰(zhàn)指南

 更新時間:2026年04月18日 11:15:48   作者:江湖中的阿龍  
文章主要討論了schronized關鍵字在Java中的核心定義和使用場景,synchronized用于實現線程互斥和同步,保證了原子操作、可見性和有序性,它通過互斥鎖實現線程安全問題的解決方案,感興趣的朋友一起看看吧

一、synchronized 核心定義

synchronized(同步的)是 Java 的關鍵字,用于實現線程互斥同步,核心作用:

  1. 原子性:保證臨界區(qū)代碼(被 synchronized 修飾的代碼)同一時間只有一個線程執(zhí)行,解決復合操作的線程安全問題;
  2. 可見性:線程釋放鎖時,會將工作內存中的修改刷新到主內存;獲取鎖時,會清空工作內存,從主內存重新加載變量,保證變量值的實時同步;
  3. 有序性:通過 “鎖的獲取 - 釋放” 規(guī)則,阻止指令重排序(臨界區(qū)代碼按順序執(zhí)行)。

設計初衷:以最簡單的方式解決多線程并發(fā)訪問共享資源的安全問題,無需手動管理鎖的生命周期(JVM 自動加鎖 / 釋放鎖)。

二、synchronized 解決的核心問題

多線程下對共享資源的 “復合操作”(如 i++、修改集合、更新對象屬性)會導致線程安全問題(如數據臟讀、重復寫入、值覆蓋),synchronized 通過 “互斥鎖” 解決該問題:

  • 互斥性:同一時刻只有一個線程能進入同步代碼塊 / 方法;
  • 排他性:持有鎖的線程可重復進入(可重入鎖),其他線程需等待鎖釋放。

線程安全問題演示(無 synchronized)

public class SyncProblem {
    // 共享資源
    private int count = 0;
    // 非同步方法:多線程下count++會出現值覆蓋
    public void increment() {
        count++; // 復合操作:讀→加1→寫,非原子性
    }
    public static void main(String[] args) throws InterruptedException {
        SyncProblem demo = new SyncProblem();
        // 10個線程,每個執(zhí)行1000次increment
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    demo.increment();
                }
            }).start();
        }
        Thread.sleep(2000);
        // 預期10000,實際遠小于(如9876)
        System.out.println("count最終值:" + demo.count);
    }
}

三、synchronized 核心用法

synchronized 可修飾實例方法、靜態(tài)方法、代碼塊,不同修飾方式對應不同的 “鎖對象”:

3.1 修飾實例方法(對象鎖)

  • 鎖對象:當前實例(this);
  • 特點:不同實例的同步方法互不影響,同一實例的同步方法互斥。

修復上面的線程安全問題

public class SyncInstanceMethod {
    private int count = 0;
    // synchronized修飾實例方法:鎖對象是this
    public synchronized void increment() {
        count++;
    }
    public static void main(String[] args) throws InterruptedException {
        SyncInstanceMethod demo = new SyncInstanceMethod();
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    demo.increment();
                }
            }).start();
        }
        Thread.sleep(2000);
        // 預期10000,實際準確
        System.out.println("count最終值:" + demo.count);
    }
}

3.2 修飾靜態(tài)方法(類鎖)

  • 鎖對象:當前類的 Class 對象(如 SyncStaticMethod.class);
  • 特點:所有實例的該靜態(tài)方法互斥(因為類對象唯一)。

示例

public class SyncStaticMethod {
    private static int staticCount = 0;
    // synchronized修飾靜態(tài)方法:鎖對象是SyncStaticMethod.class
    public static synchronized void staticIncrement() {
        staticCount++;
    }
    public static void main(String[] args) throws InterruptedException {
        // 兩個不同實例,調用靜態(tài)同步方法仍互斥
        SyncStaticMethod demo1 = new SyncStaticMethod();
        SyncStaticMethod demo2 = new SyncStaticMethod();
        new Thread(() -> {
            for (int j = 0; j < 1000; j++) {
                demo1.staticIncrement();
            }
        }).start();
        new Thread(() -> {
            for (int j = 0; j < 1000; j++) {
                demo2.staticIncrement();
            }
        }).start();
        Thread.sleep(2000);
        // 預期2000,實際準確
        System.out.println("staticCount最終值:" + SyncStaticMethod.staticCount);
    }
}

3.3 修飾代碼塊(自定義鎖對象)

  • 鎖對象:手動指定(如 this、類對象、任意非 null 對象);
  • 特點:粒度更細,僅鎖定關鍵代碼,性能更高(推薦優(yōu)先使用)。

示例 1:鎖當前實例(this)

public class SyncCodeBlockThis {
    private int count = 0;
    public void increment() {
        // 僅鎖定count++,而非整個方法
        synchronized (this) {
            count++;
        }
    }
    // 其他非同步代碼(不影響)
    public void printCount() {
        System.out.println("count:" + count);
    }
}

示例 2:鎖類對象(類鎖)

public class SyncCodeBlockClass {
    private static int staticCount = 0;
    public void staticIncrement() {
        // 鎖類對象,等價于靜態(tài)同步方法
        synchronized (SyncCodeBlockClass.class) {
            staticCount++;
        }
    }
}

示例 3:鎖自定義對象(私有鎖,推薦)

public class SyncCodeBlockCustom {
    private int count = 0;
    // 私有鎖對象:避免外部對象干擾(推薦寫法)
    private final Object lock = new Object();
    public void increment() {
        synchronized (lock) {
            count++;
        }
    }
}

3.4 關鍵對比:static synchronized 與 synchronized 方法的執(zhí)行邏輯

核心結論

static synchronized(類鎖,鎖對象為類的 Class 對象)和 synchronized(實例鎖,鎖對象為 this)是不同的鎖對象,二者執(zhí)行時互不阻塞。

完整示例代碼

public class SyncStaticVsInstance {
    // 計數器:記錄方法執(zhí)行順序
    private static int step = 0;
    // 1. synchronized修飾實例方法(鎖對象:this)
    public synchronized void instanceSyncMethod() {
        System.out.println(Thread.currentThread().getName() + " 進入實例同步方法");
        try {
            // 模擬耗時操作(3秒),放大執(zhí)行差異
            Thread.sleep(3000);
            step++;
            System.out.println(Thread.currentThread().getName() + " 實例同步方法執(zhí)行完成,step=" + step);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    // 2. static synchronized修飾靜態(tài)方法(鎖對象:SyncStaticVsInstance.class)
    public static synchronized void staticSyncMethod() {
        System.out.println(Thread.currentThread().getName() + " 進入靜態(tài)同步方法");
        try {
            // 模擬耗時操作(3秒)
            Thread.sleep(3000);
            step++;
            System.out.println(Thread.currentThread().getName() + " 靜態(tài)同步方法執(zhí)行完成,step=" + step);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        // 創(chuàng)建類實例(用于調用實例方法)
        SyncStaticVsInstance demo = new SyncStaticVsInstance();
        // 線程1:調用實例同步方法(鎖this)
        Thread thread1 = new Thread(() -> demo.instanceSyncMethod(), "線程1");
        // 線程2:調用靜態(tài)同步方法(鎖類對象)
        Thread thread2 = new Thread(() -> SyncStaticVsInstance.staticSyncMethod(), "線程2");
        // 啟動兩個線程(幾乎同時執(zhí)行)
        thread1.start();
        thread2.start();
    }
}

執(zhí)行結果(無阻塞,并行執(zhí)行)

線程1 進入實例同步方法
線程2 進入靜態(tài)同步方法
// 等待3秒后,兩個方法同時完成
線程1 實例同步方法執(zhí)行完成,step=1
線程2 靜態(tài)同步方法執(zhí)行完成,step=2

結果分析

  1. 無阻塞:線程 1 和線程 2 幾乎同時進入各自的同步方法,沒有先后等待 —— 因為實例鎖和類鎖是兩個完全獨立的鎖對象;
  2. 執(zhí)行時序:兩個方法都耗時 3 秒,但總執(zhí)行時間僅約 3 秒(而非 6 秒),證明二者并行執(zhí)行;
  3. step 計數:step 最終為 2,說明兩個方法都完成了耗時操作,進一步驗證無阻塞。

擴展:如何讓兩個方法互斥?(統一鎖對象)

若希望實例方法和靜態(tài)方法互斥,需將二者的鎖對象統一為類對象,修改實例方法為 “鎖類對象的同步代碼塊”:

public class SyncUnifiedLock {
    private static int step = 0;
    // 改造實例方法:鎖類對象(而非this),與靜態(tài)方法互斥
    public void instanceSyncMethod() {
        synchronized (SyncUnifiedLock.class) { // 統一鎖對象:類對象
            System.out.println(Thread.currentThread().getName() + " 進入實例同步方法");
            try {
                Thread.sleep(3000);
                step++;
                System.out.println(Thread.currentThread().getName() + " 實例同步方法執(zhí)行完成,step=" + step);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    // 靜態(tài)同步方法(鎖對象:SyncUnifiedLock.class)
    public static synchronized void staticSyncMethod() {
        System.out.println(Thread.currentThread().getName() + " 進入靜態(tài)同步方法");
        try {
            Thread.sleep(3000);
            step++;
            System.out.println(Thread.currentThread().getName() + " 靜態(tài)同步方法執(zhí)行完成,step=" + step);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        SyncUnifiedLock demo = new SyncUnifiedLock();
        Thread thread1 = new Thread(() -> demo.instanceSyncMethod(), "線程1");
        Thread thread2 = new Thread(() -> SyncUnifiedLock.staticSyncMethod(), "線程2");
        thread1.start();
        thread2.start();
    }
}

改造后執(zhí)行結果(互斥執(zhí)行)

線程1 進入實例同步方法
// 等待3秒后線程1完成
線程1 實例同步方法執(zhí)行完成,step=1
// 線程2才開始執(zhí)行
線程2 進入靜態(tài)同步方法
// 再等待3秒后線程2完成
線程2 靜態(tài)同步方法執(zhí)行完成,step=2

四、synchronized 底層原理(JVM 層面)

4.1 字節(jié)碼層面

synchronized 修飾的代碼塊 / 方法,在字節(jié)碼中會生成 monitorenter(進入鎖)和 monitorexit(釋放鎖)指令:

  • monitorenter:嘗試獲取鎖(monitor 對象的所有權),成功則進入,失敗則阻塞;
  • monitorexit:釋放鎖,喚醒等待隊列中的線程。

4.2 JVM 鎖優(yōu)化(從重量級到輕量級)

Java 6 后,JVM 對 synchronized 做了大量優(yōu)化,使其性能大幅提升,核心優(yōu)化點:

  1. 偏向鎖:針對單線程重復獲取同一鎖的場景,減少鎖競爭開銷(默認開啟);
  2. 輕量級鎖:多線程交替獲取鎖(無競爭),通過 CAS 實現,無需操作系統內核態(tài)切換;
  3. 重量級鎖:多線程競爭激烈,需操作系統介入(阻塞線程),性能較低;
  4. 鎖消除:JVM 識別到無共享資源的同步代碼,自動移除鎖;
  5. 鎖粗化:將多次連續(xù)的鎖獲取 / 釋放合并為一次,減少開銷。

鎖升級流程:偏向鎖 → 輕量級鎖 → 重量級鎖(不可逆)。

五、synchronized 在 JDK 源碼中的應用

5.1 集合類(如 Vector、Hashtable)

Vector 的 add()get() 等方法被 synchronized 修飾,保證線程安全(但性能低,推薦用 CopyOnWriteArrayList):

public class Vector<E> extends AbstractList<E> {
    // synchronized修飾實例方法:保證線程安全
    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }
    public synchronized E get(int index) {
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);
        return elementData(index);
    }
}

5.2 Object 類的 wait ()/notify ()

synchronized 是使用 wait()、notify()、notifyAll() 的前提(必須在同步代碼塊 / 方法中調用):

public class WaitNotifyDemo {
    private final Object lock = new Object();
    public void waitDemo() throws InterruptedException {
        synchronized (lock) {
            lock.wait(); // 釋放鎖并等待
        }
    }
    public void notifyDemo() {
        synchronized (lock) {
            lock.notify(); // 喚醒一個等待線程
        }
    }
}

5.3 java.lang.Thread 的 join () 方法

join() 底層通過 synchronized + wait() 實現線程等待:

public class Thread implements Runnable {
    public final synchronized void join(long millis) throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }
        if (millis == 0) {
            while (isAlive()) {
                wait(0); // 等待當前線程結束
            }
        } else {
            // ... 超時等待邏輯
        }
    }
}

六、synchronized 核心特性

6.1 可重入性

synchronized可重入鎖:同一線程持有鎖后,可再次進入該鎖的同步代碼(避免死鎖)。

示例

public class ReentrantSync {
    public synchronized void method1() {
        System.out.println("進入method1");
        method2(); // 同一線程可再次進入同步方法
    }
    public synchronized void method2() {
        System.out.println("進入method2");
    }
    public static void main(String[] args) {
        ReentrantSync demo = new ReentrantSync();
        demo.method1();
        // 輸出:
        // 進入method1
        // 進入method2
    }
}

6.2 不可中斷性

線程獲取鎖失敗時,會進入阻塞狀態(tài),且無法被中斷(直到獲取到鎖);而 Lock 鎖支持可中斷獲取。

七、synchronized vs Lock(ReentrantLock)

特性synchronizedReentrantLock(Lock 接口實現)
鎖實現JVM 層面(內置鎖)JDK 層面(手動實現)
可重入性支持支持(可指定公平 / 非公平)
中斷性不支持(阻塞不可中斷)支持(lockInterruptibly ())
超時獲取不支持支持(tryLock (long time))
公平鎖僅非公平鎖可指定公平 / 非公平鎖
條件變量僅 wait ()/notify ()(單一條件)支持多個 Condition(精準喚醒)
性能Java 6 后優(yōu)化,與 Lock 接近高并發(fā)下略優(yōu)
使用復雜度簡單(自動加鎖 / 釋放)復雜(需手動 unlock (),建議 try-finally)

八、synchronized 常見面試題 & 易錯點

8.1 面試高頻問題

  1. synchronized 能保證可見性和有序性嗎?能。釋放鎖時刷新主內存(可見性),鎖的獲取 / 釋放阻止指令重排序(有序性),同時保證原子性。
  2. synchronized 是公平鎖嗎?不是。默認是非公平鎖(JVM 優(yōu)先喚醒先獲取鎖的線程,但不保證絕對公平),且無法指定為公平鎖。
  3. synchronized 修飾實例方法和靜態(tài)方法的鎖對象有什么區(qū)別?
    • 實例方法:鎖是 this(當前實例);
    • 靜態(tài)方法:鎖是 類名.class(類對象);兩者互不干擾(鎖對象不同)。
  4. 為什么 synchronized 是可重入的?JVM 會記錄鎖的持有線程和重入次數,線程再次獲取同一鎖時,僅增加重入次數,釋放時減少次數,次數為 0 時才真正釋放鎖。
  5. 一個類中同時有 static synchronized 和 synchronized 方法,多線程調用會互斥嗎?不會。因為鎖對象不同(類對象 vs this),只有當二者鎖對象統一時(如都鎖類對象),才會互斥。

8.2 開發(fā)易錯點

  1. 鎖對象為 null:synchronized 的鎖對象不能為 null(編譯通過,運行拋 NullPointerException);
  2. 鎖粒度太大:將整個方法同步,而非僅同步關鍵代碼,導致性能低下;
  3. 混淆對象鎖和類鎖:以為靜態(tài)同步方法和實例同步方法互斥(實際不互斥,鎖對象不同);
  4. 死鎖:多個線程互相持有對方需要的鎖(如線程 A 持有鎖 1,等待鎖 2;線程 B 持有鎖 2,等待鎖 1)。

死鎖示例

public class DeadLockDemo {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();
    public static void main(String[] args) {
        // 線程1:持有l(wèi)ock1,等待lock2
        new Thread(() -> {
            synchronized (lock1) {
                System.out.println("線程1持有l(wèi)ock1,等待lock2");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                synchronized (lock2) {
                    System.out.println("線程1獲取lock2");
                }
            }
        }).start();
        // 線程2:持有l(wèi)ock2,等待lock1
        new Thread(() -> {
            synchronized (lock2) {
                System.out.println("線程2持有l(wèi)ock2,等待lock1");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                synchronized (lock1) {
                    System.out.println("線程2獲取lock1");
                }
            }
        }).start();
        // 現象:死鎖,兩個線程永遠阻塞
    }
}

死鎖解決方案

  • 固定鎖的獲取順序(如所有線程先獲取 lock1,再獲取 lock2);
  • 使用 LocktryLock() 超時機制;
  • 避免嵌套鎖。

總結

  1. synchronized 核心作用是保證原子性、可見性、有序性,通過 “互斥鎖” 實現多線程安全;
  2. 三種用法:修飾實例方法(鎖 this)、靜態(tài)方法(鎖類對象)、代碼塊(自定義鎖對象,推薦);
  3. static synchronized(類鎖)和 synchronized(實例鎖)鎖對象不同,默認互不阻塞,需統一鎖對象才能實現互斥;
  4. Java 6 后 JVM 對其做了偏向鎖、輕量級鎖等優(yōu)化,性能大幅提升,無需過度擔心 “重量級” 問題;
  5. 核心邊界:可重入、非公平、不可中斷,鎖對象不能為 null,避免死鎖和過大的鎖粒度。

synchronized 是 Java 并發(fā)編程的基礎,掌握它的原理和用法,是寫出安全、高效并發(fā)代碼的關鍵。日常開發(fā)中,優(yōu)先使用 synchronized(簡單、不易出錯),復雜場景(如公平鎖、可中斷、多條件喚醒)再考慮 Lock。

到此這篇關于Java synchronized 關鍵字原理、用法、優(yōu)化與實戰(zhàn)指南的文章就介紹到這了,更多相關Java synchronized 關鍵字用法內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java并發(fā)之線程池Executor框架的深入理解

    Java并發(fā)之線程池Executor框架的深入理解

    這篇文章主要介紹了Java并發(fā)之線程池Executor框架的深入理解,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-11-11
  • MDC在多線程中的使用方式

    MDC在多線程中的使用方式

    這篇文章主要介紹了MDC在多線程中的使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • 基于SpringBoot啟動類靜態(tài)資源路徑問題

    基于SpringBoot啟動類靜態(tài)資源路徑問題

    這篇文章主要介紹了SpringBoot啟動類靜態(tài)資源路徑問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • MyBatis 多個條件使用Map傳遞參數進行批量刪除方式

    MyBatis 多個條件使用Map傳遞參數進行批量刪除方式

    這篇文章主要介紹了MyBatis 多個條件使用Map傳遞參數進行批量刪除方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Idea導入多個maven項目到同一目錄下的方法示例

    Idea導入多個maven項目到同一目錄下的方法示例

    這篇文章主要介紹了Idea導入多個maven項目到同一目錄下,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-07-07
  • 如何解決Mybatis-plus中@TableLogic注解失效問題

    如何解決Mybatis-plus中@TableLogic注解失效問題

    這篇文章主要介紹了如何解決Mybatis-plus中@TableLogic注解失效問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • Java?web實現頭像上傳以及讀取顯示

    Java?web實現頭像上傳以及讀取顯示

    這篇文章主要為大家詳細介紹了Java?web實現頭像上傳以及讀取顯示,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • Spring @Import注解的使用

    Spring @Import注解的使用

    @Import注解算是SpringBoot自動配置原理中一個很重要的注解,本文介紹了該注解的源碼分析及使用方法,感興趣的朋友可以了解下
    2021-05-05
  • 詳解Java中的checked異常和unchecked異常區(qū)別

    詳解Java中的checked異常和unchecked異常區(qū)別

    這篇文章主要介紹了詳解Java中的checked異常和unchecked異常區(qū)別,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-02-02
  • scala 隱式轉換與隱式參數的使用方法

    scala 隱式轉換與隱式參數的使用方法

    這篇文章主要介紹了scala 隱式轉換與隱式參數的使用方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-11-11

最新評論

抚州市| 丽江市| 长阳| 元谋县| 绥德县| 民勤县| 松潘县| 静海县| 惠州市| 普兰店市| 迭部县| 株洲县| 繁峙县| 义乌市| 瓦房店市| 巢湖市| 庄河市| 白朗县| 富顺县| 泉州市| 乌拉特前旗| 辰溪县| 蛟河市| 西华县| 申扎县| 柳林县| 武城县| 墨脱县| 秀山| 兴城市| 太和县| 华坪县| 融水| 宁晋县| 柳林县| 灯塔市| 寿宁县| 清丰县| 广元市| 泸定县| 晋宁县|