欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中 synchronized 和 volatile的核心區(qū)別解析

 更新時(shí)間:2025年09月16日 10:39:18   作者:木易 士心  
本文給大家介紹Java中synchronized和volatile的核心區(qū)別,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧

概述

Java 并發(fā)編程中的兩個(gè)核心關(guān)鍵字:synchronized 和 volatile。它們都是為了解決多線程環(huán)境下的數(shù)據(jù)一致性問題,但在作用機(jī)制、保證的特性以及適用場(chǎng)景上有著本質(zhì)的區(qū)別。

簡(jiǎn)單來(lái)說(shuō):
synchronized 是一把“重量級(jí)的鎖”,它通過(guò)互斥訪問來(lái)保證原子性、可見性和有序性。
volatile 是一個(gè)“輕量級(jí)的同步機(jī)制”,它主要保證可見性和有序性,但不保證原子性。

1. synchronized 關(guān)鍵字詳解

synchronized 是 Java 中最基礎(chǔ)、最常用的同步機(jī)制,它通過(guò)獲取和釋放對(duì)象的“監(jiān)視器鎖”(Monitor Lock)來(lái)實(shí)現(xiàn)線程間的互斥訪問。

1.1 作用與核心特性

  • 互斥性 (Mutual Exclusion)
    這是 synchronized 最核心的作用。它確保在同一時(shí)刻,只有一個(gè)線程能夠執(zhí)行被 synchronized 保護(hù)的代碼塊或方法。其他試圖進(jìn)入的線程會(huì)被阻塞,直到當(dāng)前線程釋放鎖。
  • 原子性 (Atomicity)
    由于互斥性,被 synchronized 保護(hù)的代碼塊被視為一個(gè)不可分割的整體。線程要么執(zhí)行完整個(gè)代碼塊,要么完全不執(zhí)行,不會(huì)被其他線程打斷。這保證了復(fù)合操作(如 i++)的原子性。
  • 可見性 (Visibility)
    synchronized 不僅提供互斥,還保證了內(nèi)存可見性。根據(jù) Java 內(nèi)存模型 (JMM) 的規(guī)定:
    進(jìn)入 synchronized 塊時(shí):線程會(huì)清空其工作內(nèi)存中共享變量的副本,強(qiáng)制從主內(nèi)存重新加載最新的值。退出 synchronized 塊時(shí):線程會(huì)將其工作內(nèi)存中對(duì)共享變量的修改強(qiáng)制刷新回主內(nèi)存。
    這樣,一個(gè)線程在臨界區(qū)內(nèi)對(duì)變量的修改,對(duì)下一個(gè)進(jìn)入該臨界區(qū)的線程是立即可見的。
  • 有序性 (Ordering)
    synchronized 通過(guò)“一個(gè)變量在同一時(shí)刻只允許一個(gè)線程對(duì)其進(jìn)行 lock 操作”的規(guī)則,天然地禁止了指令重排序。在 synchronized 塊內(nèi)部,代碼的執(zhí)行順序與程序的書寫順序一致。

1.2. 使用方式

synchronized 可以修飾方法或代碼塊,鎖定的對(duì)象不同,其作用范圍也不同。

1.2.1 修飾實(shí)例方法 (非靜態(tài)方法)

public class Counter {
    private int count = 0;
    // 鎖定的是當(dāng)前對(duì)象實(shí)例 (this)
    public synchronized void increment() {
        count++; // 這個(gè)操作是原子的
    }
    public synchronized int getCount() {
        return count;
    }
}

鎖對(duì)象 當(dāng)前對(duì)象實(shí)例 (this)。
作用范圍 同一個(gè)對(duì)象實(shí)例的多個(gè) synchronized 實(shí)例方法之間是互斥的。不同對(duì)象實(shí)例的 synchronized 方法可以并發(fā)執(zhí)行。

1.2.2 修飾靜態(tài)方法

public class GlobalCounter {
    private static int globalCount = 0;
    // 鎖定的是當(dāng)前類的 Class 對(duì)象 (GlobalCounter.class)
    public static synchronized void incrementGlobal() {
        globalCount++;
    }
    public static synchronized int getGlobalCount() {
        return globalCount;
    }
}

鎖對(duì)象 該類的 Class 對(duì)象。
作用范圍 無(wú)論創(chuàng)建多少個(gè)類的實(shí)例,所有線程在調(diào)用該類的 synchronized 靜態(tài)方法時(shí),都會(huì)競(jìng)爭(zhēng)同一把鎖,實(shí)現(xiàn)全局互斥。

1.2.3 修飾代碼塊 (Synchronized Block)

public class FineGrainedCounter {
    private int countA = 0;
    private int countB = 0;
    private final Object lockA = new Object();
    private final Object lockB = new Object();
    // 只鎖定操作 countA 的部分,提高并發(fā)度
    public void incrementA() {
        synchronized (lockA) { // 鎖定指定的對(duì)象 lockA
            countA++;
        }
    }
    // 只鎖定操作 countB 的部分
    public void incrementB() {
        synchronized (lockB) { // 鎖定指定的對(duì)象 lockB
            countB++;
        }
    }
    // 鎖定當(dāng)前對(duì)象實(shí)例
    public void doSomething() {
        synchronized (this) {
            // ... 臨界區(qū)代碼
        }
    }
}

鎖對(duì)象 synchronized 括號(hào)內(nèi)指定的任意對(duì)象。
作用范圍 靈活性最高??梢跃_控制需要同步的代碼范圍,避免將整個(gè)方法都鎖定,從而減少鎖的競(jìng)爭(zhēng),提高并發(fā)性能。

1.3. 實(shí)現(xiàn)原理

JVM 通過(guò)對(duì)象內(nèi)部的“監(jiān)視器鎖”(Monitor)來(lái)實(shí)現(xiàn) synchronized。在字節(jié)碼層面:

  • 進(jìn)入 synchronized 代碼塊時(shí),會(huì)執(zhí)行 monitorenter 指令。
  • 退出 synchronized 代碼塊(正常退出或發(fā)生異常)時(shí),會(huì)執(zhí)行 monitorexit 指令。

為了優(yōu)化性能,JDK 1.6 引入了鎖升級(jí)機(jī)制:

  • 無(wú)鎖狀態(tài)
  • 偏向鎖 (Biased Locking)
    針對(duì)只有一個(gè)線程訪問同步塊的場(chǎng)景,將鎖偏向于該線程,減少不必要的 CAS 操作。
  • 輕量級(jí)鎖 (Lightweight Locking)
    當(dāng)有第二個(gè)線程競(jìng)爭(zhēng)時(shí),升級(jí)為輕量級(jí)鎖,通過(guò)自旋 CAS 嘗試獲取鎖,避免線程阻塞。
  • 重量級(jí)鎖 (Heavyweight Locking)
    當(dāng)自旋一定次數(shù)后仍未獲取到鎖,或有多個(gè)線程競(jìng)爭(zhēng)時(shí),升級(jí)為重量級(jí)鎖,線程會(huì)被掛起,進(jìn)入阻塞狀態(tài)。

1.4. 優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn)
    功能強(qiáng)大,能同時(shí)保證原子性、可見性和有序性。
    使用簡(jiǎn)單,是解決并發(fā)問題的首選方案。
    支持重入,同一個(gè)線程可以多次獲取同一把鎖。
  • 缺點(diǎn)
    性能開銷: 獲取和釋放鎖需要操作系統(tǒng)介入,可能導(dǎo)致線程上下文切換,帶來(lái)性能損耗。
    可能導(dǎo)致死鎖: 如果多個(gè)線程以不同的順序獲取多個(gè)鎖,可能會(huì)發(fā)生死鎖。
    阻塞: 未獲取到鎖的線程會(huì)被阻塞,無(wú)法做其他事情。

1.5. 適用場(chǎng)景

適用于需要對(duì)共享資源進(jìn)行復(fù)雜操作、保證操作原子性的場(chǎng)景,例如:

  • 銀行轉(zhuǎn)賬(需要保證扣款和加款兩個(gè)操作的原子性)。
  • 計(jì)數(shù)器的遞增 (i++)。
  • 對(duì)集合進(jìn)行增刪改查操作。

2. volatile 關(guān)鍵字詳解

volatile 是一個(gè)變量修飾符,它不提供任何互斥機(jī)制,而是通過(guò)內(nèi)存屏障(Memory Barrier)來(lái)保證變量的可見性和禁止指令重排序。

2.1 作用與核心特性

  • 可見性 (Visibility): 這是 volatile 最主要的作用。
    • 當(dāng)一個(gè)線程修改了 volatile 變量的值,這個(gè)新值會(huì)立即被寫入主內(nèi)存。
    • 當(dāng)其他線程讀取這個(gè) volatile 變量時(shí),會(huì)強(qiáng)制從主內(nèi)存中讀取最新的值,而不是使用自己工作內(nèi)存中的緩存副本。
    • 這樣就保證了所有線程看到的都是該變量的最新值。
  • 有序性 (Ordering) volatile 通過(guò)插入內(nèi)存屏障來(lái)禁止指令重排序。
    • 在寫一個(gè) volatile 變量之前,JVM 會(huì)插入一個(gè) StoreStore 屏障,確保之前的普通寫操作都已完成。
    • 在寫一個(gè) volatile 變量之后,JVM 會(huì)插入一個(gè) StoreLoad 屏障,確保寫操作對(duì)其他處理器可見。
    • 在讀一個(gè) volatile 變量之前,JVM 會(huì)插入一個(gè) LoadLoad 屏障,確保讀取到的是最新值。
    • 在讀一個(gè) volatile 變量之后,JVM 會(huì)插入一個(gè) LoadStore 屏障,確保后續(xù)的普通寫操作不會(huì)被重排序到讀操作之前。
    • 這保證了 volatile 變量的讀寫操作不會(huì)被重排序,并且建立了“happens-before”關(guān)系。
  • 不保證原子性 (No Atomicity)
    • volatile 無(wú)法保證復(fù)合操作的原子性。例如,volatile int count = 0; 語(yǔ)句 count++ 看起來(lái)是一條語(yǔ)句,但在底層是“讀取-修改-寫入”三個(gè)步驟。即使 count 是 volatile 的,多個(gè)線程同時(shí)執(zhí)行 count++ 時(shí),依然可能出現(xiàn)競(jìng)態(tài)條件,導(dǎo)致最終結(jié)果小于預(yù)期。

2.2. 使用方式

volatile 只能用來(lái)修飾變量。

public class VolatileExample {
    // 修飾一個(gè)布爾標(biāo)志位,用于線程間通信
    private volatile boolean shutdownRequested = false;
    // 修飾一個(gè)對(duì)象引用
    private volatile Config config;
    // 線程A:設(shè)置標(biāo)志位
    public void shutdown() {
        shutdownRequested = true; // 寫操作,會(huì)立即刷新到主內(nèi)存
    }
    // 線程B:檢查標(biāo)志位
    public void doWork() {
        while (!shutdownRequested) { // 讀操作,每次都從主內(nèi)存讀取最新值
            // ... 執(zhí)行任務(wù)
        }
        // 收到關(guān)閉請(qǐng)求,優(yōu)雅退出
    }
    // 注意:以下操作不是原子的!
    private volatile int counter = 0;
    public void unsafeIncrement() {
        counter++; // 讀-改-寫,非原子操作,多線程下結(jié)果可能錯(cuò)誤
    }
}

2.3 實(shí)現(xiàn)原理

volatile 的實(shí)現(xiàn)主要依賴于 CPU 的緩存一致性協(xié)議(如 MESI)和 JVM 插入的內(nèi)存屏障指令。它告訴 JVM 和 CPU,這個(gè)變量是“易變的”,不能對(duì)其進(jìn)行激進(jìn)的優(yōu)化(如緩存、重排序)。

2.4. 優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn)
    輕量級(jí): 相比 synchronized,開銷非常小,不會(huì)引起線程阻塞。
    保證可見性和有序性: 適用于簡(jiǎn)單的狀態(tài)標(biāo)志傳遞。
  • 缺點(diǎn)
    不保證原子性: 無(wú)法用于需要原子操作的場(chǎng)景。
    功能有限: 只能用于變量,不能用于方法或代碼塊。

2.5. 適用場(chǎng)景

適用于“一個(gè)線程寫,多個(gè)線程讀”,且寫操作是原子的(通常是直接賦值)的場(chǎng)景:

  • 狀態(tài)標(biāo)志位 如上面例子中的 shutdownRequested,用于通知其他線程停止工作。
  • 一次性安全發(fā)布 (Safe Publication) 在對(duì)象構(gòu)造完成后,通過(guò) volatile 引用發(fā)布,可以保證其他線程看到的是完全構(gòu)造好的對(duì)象。
  • 雙重檢查鎖定 (DCL) 的單例模式 在單例模式中,volatile 用于防止指令重排序?qū)е缕渌€程拿到一個(gè)未完全初始化的對(duì)象。
public class Singleton {
    // volatile 防止 instance = new Singleton() 指令重排序
    private static volatile Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) { // 第一次檢查
            synchronized (Singleton.class) {
                if (instance == null) { // 第二次檢查
                    instance = new Singleton(); // 可能發(fā)生重排序
                }
            }
        }
        return instance;
    }
}

3 總結(jié)

3.1 synchronized 與 volatile 的核心區(qū)別

特性synchronizedvolatile
作用對(duì)象方法、代碼塊變量
核心機(jī)制互斥鎖 (Monitor)內(nèi)存屏障 (Memory Barrier)
原子性保證 (通過(guò)互斥實(shí)現(xiàn))不保證 (僅保證單次讀/寫原子)
可見性保證 (進(jìn)出同步塊時(shí)刷新主內(nèi)存)保證 (強(qiáng)制讀寫主內(nèi)存)
有序性保證 (通過(guò)互斥和禁止重排序)保證 (通過(guò)內(nèi)存屏障禁止重排序)
線程阻塞會(huì)阻塞 (未獲取鎖的線程進(jìn)入阻塞狀態(tài))不會(huì)阻塞 (線程可以繼續(xù)執(zhí)行)
性能開銷較大 (涉及操作系統(tǒng),可能上下文切換)較小 (主要是內(nèi)存屏障開銷)
適用場(chǎng)景復(fù)雜的原子操作、臨界區(qū)保護(hù) 簡(jiǎn)單的狀態(tài)標(biāo)志、一次性安全發(fā)布、DCL單例模式

3.2 適用場(chǎng)景

3.2.1 狀態(tài)標(biāo)志控制 使用volatile

僅需保證可見性進(jìn)需要操作是原子的 (如 flag = true): 優(yōu)先使用 volatile,因?yàn)樗p量。

class TaskRunner {
    private volatile boolean stopped = false; // 線程安全的狀態(tài)標(biāo)志
    public void run() {
        while (!stopped) { /* 執(zhí)行任務(wù) */ }
    }
    public void stop() { stopped = true; } // 修改立即可見
}

3.2.2 單例模式(雙重檢查鎖定)synchronized+volatile

volatile防止new Singleton()的分解步驟重排序,避免返回未初始化的對(duì)象

class Singleton {
    private static volatile Singleton instance; // 禁止指令重排序
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton(); // 禁止重排序:分配內(nèi)存→初始化→賦值引用
                }
            }
        }
        return instance;
    }
}

3.2.3 臨界區(qū)保護(hù) 使用synchronized

強(qiáng)制原子性,適合需要互斥訪問的復(fù)合操作(如讀寫共享變量)。

class BankAccount {
    private double balance;
    public synchronized void deposit(double amount) { // 整個(gè)方法同步
        balance += amount;
    }
    public void withdraw(double amount) {
        synchronized (this) { // 代碼塊同步
            balance -= amount;
        }
    }
}

3.2.4 線程協(xié)作(等待/通知機(jī)制)

synchronized提供鎖的獲取/釋放機(jī)制,配合wait()/notifyAll()實(shí)現(xiàn)線程間協(xié)作。

class ProducerConsumer {
    private final Object lock = new Object();
    private boolean isProduced = false;
    public void produce() {
        synchronized (lock) {
            while (isProduced) { lock.wait(); } // 等待消費(fèi)
            // 生產(chǎn)數(shù)據(jù)...
            isProduced = true;
            lock.notifyAll(); // 通知消費(fèi)者
        }
    }
    public void consume() {
        synchronized (lock) {
            while (!isProduced) { lock.wait(); } // 等待生產(chǎn)
            // 消費(fèi)數(shù)據(jù)...
            isProduced = false;
            lock.notifyAll(); // 通知生產(chǎn)者
        }
    }
}

到此這篇關(guān)于Java中 synchronized 和 volatile的核心區(qū)別解析的文章就介紹到這了,更多相關(guān)Java synchronized 和 volatile內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring Boot 中的 @PutMapping 注解原理及使用小結(jié)

    Spring Boot 中的 @PutMapping 注解原理及使用小結(jié)

    在本文中,我們介紹了 Spring Boot 中的 @PutMapping 注解,它可以將 HTTP PUT 請(qǐng)求映射到指定的處理方法上,我們還介紹了 @PutMapping 注解的原理以及如何在 Spring Boot 中使用它,感興趣的朋友跟隨小編一起看看吧
    2023-12-12
  • Spring源碼解密之自定義標(biāo)簽與解析

    Spring源碼解密之自定義標(biāo)簽與解析

    這篇文章主要給大家介紹了關(guān)于Spring源碼解密之自定義標(biāo)簽與解析的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-01-01
  • Java泛型與數(shù)據(jù)庫(kù)應(yīng)用實(shí)例詳解

    Java泛型與數(shù)據(jù)庫(kù)應(yīng)用實(shí)例詳解

    這篇文章主要介紹了Java泛型與數(shù)據(jù)庫(kù)應(yīng)用,結(jié)合實(shí)例形式詳細(xì)分析了java繼承泛型類實(shí)現(xiàn)增刪改查操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2019-08-08
  • Java ThreadLocal的使用場(chǎng)景總結(jié)

    Java ThreadLocal的使用場(chǎng)景總結(jié)

    ThreadLocal原本設(shè)計(jì)是為了解決并發(fā)時(shí),線程共享變量的問題,但由于過(guò)度設(shè)計(jì),從而導(dǎo)致它的理解難度大和使用成本高等問題。即便如此,ThreadLocal依舊有適合自己的使用場(chǎng)景,比如本文要介紹了這兩種使用場(chǎng)景,除了ThreadLocal之外,還真沒有合適的替代方案。
    2021-05-05
  • 如何基于FTP4J實(shí)現(xiàn)FTPS連接過(guò)程解析

    如何基于FTP4J實(shí)現(xiàn)FTPS連接過(guò)程解析

    這篇文章主要介紹了如何基于FTP4J實(shí)現(xiàn)FTPS連接過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • SpringBoot使用?Sleuth?進(jìn)行分布式跟蹤的過(guò)程分析

    SpringBoot使用?Sleuth?進(jìn)行分布式跟蹤的過(guò)程分析

    Spring Boot Sleuth是一個(gè)分布式跟蹤解決方案,它可以幫助您在分布式系統(tǒng)中跟蹤請(qǐng)求并分析性能問題,Spring Boot Sleuth是Spring Cloud的一部分,它提供了分布式跟蹤的功能,本文將介紹如何在Spring Boot應(yīng)用程序中使用Sleuth進(jìn)行分布式跟蹤,感興趣的朋友一起看看吧
    2023-10-10
  • Spring Boot 項(xiàng)目集成 Redisson 實(shí)現(xiàn)延遲隊(duì)列的詳細(xì)過(guò)程

    Spring Boot 項(xiàng)目集成 Redisson 實(shí)現(xiàn)延遲隊(duì)列的詳細(xì)過(guò)程

    本文介紹延遲隊(duì)列在訂單超時(shí)等場(chǎng)景的應(yīng)用及四種技術(shù)方案對(duì)比,推薦Redisson延遲隊(duì)列,提供項(xiàng)目結(jié)構(gòu)與測(cè)試源碼,對(duì)Spring Boot  Redisson延遲隊(duì)列相關(guān)知識(shí)感興趣的朋友一起看看吧
    2025-06-06
  • Spring Boot 集成 Quartz 使用Cron 表達(dá)式實(shí)現(xiàn)定時(shí)任務(wù)

    Spring Boot 集成 Quartz 使用Cron 表達(dá)式實(shí)現(xiàn)定

    本文介紹了如何在SpringBoot項(xiàng)目中集成Quartz并使用Cron表達(dá)式進(jìn)行任務(wù)調(diào)度,通過(guò)添加Quartz依賴、創(chuàng)建Quartz任務(wù)、配置任務(wù)調(diào)度以及啟動(dòng)項(xiàng)目,可以實(shí)現(xiàn)定時(shí)任務(wù)的執(zhí)行,Cron表達(dá)式提供了靈活的任務(wù)調(diào)度方式,適用于各種復(fù)雜的定時(shí)任務(wù)需求,感興趣的朋友一起看看吧
    2025-03-03
  • Java重寫與重載之間的區(qū)別

    Java重寫與重載之間的區(qū)別

    本文主要介紹了Java重寫與重載之間的區(qū)別。具有一定的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-01-01
  • java JOptionPane類的介紹

    java JOptionPane類的介紹

    java JOptionPane類的介紹,需要的朋友可以參考一下
    2013-04-04

最新評(píng)論