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

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

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

概述

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

簡單來說:
synchronized 是一把“重量級的鎖”,它通過互斥訪問來保證原子性、可見性和有序性。
volatile 是一個“輕量級的同步機制”,它主要保證可見性和有序性,但不保證原子性。

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

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

1.1 作用與核心特性

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

1.2. 使用方式

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

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

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

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

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

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

鎖對象 該類的 Class 對象。
作用范圍 無論創(chuàng)建多少個類的實例,所有線程在調(diào)用該類的 synchronized 靜態(tài)方法時,都會競爭同一把鎖,實現(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) { // 鎖定指定的對象 lockA
            countA++;
        }
    }
    // 只鎖定操作 countB 的部分
    public void incrementB() {
        synchronized (lockB) { // 鎖定指定的對象 lockB
            countB++;
        }
    }
    // 鎖定當(dāng)前對象實例
    public void doSomething() {
        synchronized (this) {
            // ... 臨界區(qū)代碼
        }
    }
}

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

1.3. 實現(xiàn)原理

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

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

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

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

1.4. 優(yōu)缺點

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

1.5. 適用場景

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

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

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

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

2.1 作用與核心特性

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

2.2. 使用方式

volatile 只能用來修飾變量。

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

2.3 實現(xiàn)原理

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

2.4. 優(yōu)缺點

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

2.5. 適用場景

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

  • 狀態(tài)標(biāo)志位 如上面例子中的 shutdownRequested,用于通知其他線程停止工作。
  • 一次性安全發(fā)布 (Safe Publication) 在對象構(gòu)造完成后,通過 volatile 引用發(fā)布,可以保證其他線程看到的是完全構(gòu)造好的對象。
  • 雙重檢查鎖定 (DCL) 的單例模式 在單例模式中,volatile 用于防止指令重排序?qū)е缕渌€程拿到一個未完全初始化的對象。
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
作用對象方法、代碼塊變量
核心機制互斥鎖 (Monitor)內(nèi)存屏障 (Memory Barrier)
原子性保證 (通過互斥實現(xiàn))不保證 (僅保證單次讀/寫原子)
可見性保證 (進出同步塊時刷新主內(nèi)存)保證 (強制讀寫主內(nèi)存)
有序性保證 (通過互斥和禁止重排序)保證 (通過內(nèi)存屏障禁止重排序)
線程阻塞會阻塞 (未獲取鎖的線程進入阻塞狀態(tài))不會阻塞 (線程可以繼續(xù)執(zhí)行)
性能開銷較大 (涉及操作系統(tǒng),可能上下文切換)較小 (主要是內(nèi)存屏障開銷)
適用場景復(fù)雜的原子操作、臨界區(qū)保護 簡單的狀態(tài)標(biāo)志、一次性安全發(fā)布、DCL單例模式

3.2 適用場景

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

僅需保證可見性進需要操作是原子的 (如 flag = true): 優(yōu)先使用 volatile,因為它更輕量。

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()的分解步驟重排序,避免返回未初始化的對象

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ū)保護 使用synchronized

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

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

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

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

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

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

相關(guān)文章

  • IDEA如何切換數(shù)據(jù)庫版本mysql5或mysql8

    IDEA如何切換數(shù)據(jù)庫版本mysql5或mysql8

    本文介紹了如何將IntelliJ IDEA從MySQL5切換到MySQL8的詳細步驟,包括下載MySQL8、安裝、配置、停止舊服務(wù)、啟動新服務(wù)以及更改密碼等
    2025-01-01
  • java中以DES的方式實現(xiàn)對稱加密并提供密鑰的實例

    java中以DES的方式實現(xiàn)對稱加密并提供密鑰的實例

    這篇文章主要介紹了java中以DES的方式實現(xiàn)對稱加密并提供密鑰的實例的相關(guān)資料,這里提供實例幫助大家學(xué)習(xí)理解這部分知識,需要的朋友可以參考下
    2017-08-08
  • IDEA修改代碼后重啟不生效問題的解決方法

    IDEA修改代碼后重啟不生效問題的解決方法

    在使用 IntelliJ IDEA 進行 Java 項目開發(fā)時,有時會遇到一個令人頭疼的問題:修改了代碼后,重啟服務(wù)卻發(fā)現(xiàn)更改沒有生效,本文將簡單介紹幾種可能的解決方案,都是我在網(wǎng)上找的,算是一個匯總吧,大家可以一個一個實施,需要的朋友可以參考下
    2025-05-05
  • Java中計算時間差的方法

    Java中計算時間差的方法

    這篇文章主要介紹了Java中計算時間差的方法,實例分析了java常見的三種計算時間差的技巧,需要的朋友可以參考下
    2015-06-06
  • Java8中LocalDateTime與時間戳timestamp的互相轉(zhuǎn)換

    Java8中LocalDateTime與時間戳timestamp的互相轉(zhuǎn)換

    這篇文章主要給大家介紹了關(guān)于Java8中LocalDateTime與時間戳timestamp的互相轉(zhuǎn)換,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • MyBatis框架中Executor執(zhí)行器的使用及說明

    MyBatis框架中Executor執(zhí)行器的使用及說明

    文章詳細介紹了MyBatis的Executor執(zhí)行器,包括其定義、作用、生命周期、四種可選執(zhí)行器類型(SIMPLE、REUSE、BATCH、CachingExecutor)以及它們在執(zhí)行鏈路中的協(xié)作關(guān)系,文章還討論了插件機制、緩存機制、事務(wù)管理以及常見誤區(qū)澄清等內(nèi)容
    2025-11-11
  • Spring Boot整合全局異常處理器、junit、多環(huán)境、logback的過程

    Spring Boot整合全局異常處理器、junit、多環(huán)境、logback的過程

    本文總結(jié)了SpringBoot的異常處理、單元測試、多環(huán)境配置及日志配置的最佳實踐,重點介紹了前后端分離項目中的全局異常處理器,多環(huán)境配置文件的應(yīng)用及激活方式,以及如何使用Logback進行日志記錄,感興趣的朋友跟隨小編一起看看吧
    2026-04-04
  • Java利用redis實現(xiàn)防止接口重復(fù)提交

    Java利用redis實現(xiàn)防止接口重復(fù)提交

    本文主要為大家詳細介紹了Java如何利用redis實現(xiàn)防止接口重復(fù)提交,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-11-11
  • 深入剖析Java中的synchronized關(guān)鍵字

    深入剖析Java中的synchronized關(guān)鍵字

    在 Java 程序中,我們可以利用 synchronized 關(guān)鍵字來對程序進行加鎖,它既可以用來聲明一個 synchronized 代碼塊,也可以直接標(biāo)記靜態(tài)方法或者實例方法,本文就帶大家深入了解Java中的synchronized關(guān)鍵字,感興趣的同學(xué)可以參考閱讀
    2023-06-06
  • Springboot連接數(shù)據(jù)庫及查詢數(shù)據(jù)完整流程

    Springboot連接數(shù)據(jù)庫及查詢數(shù)據(jù)完整流程

    今天給大家?guī)淼氖顷P(guān)于Springboot的相關(guān)知識,文章圍繞著Springboot連接數(shù)據(jù)庫及查詢數(shù)據(jù)完整流程展開,文中有非常詳細的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06

最新評論

兴文县| 增城市| 鲁甸县| 桐乡市| 南召县| 商水县| 上饶市| 永泰县| 溧水县| 沈阳市| 阜阳市| 阿拉善左旗| 资中县| 丹阳市| 潍坊市| 安图县| 综艺| 安宁市| 肇庆市| 上栗县| 栾城县| 承德县| 榆林市| 桑植县| 当涂县| 乡宁县| 会泽县| 内江市| 盐山县| 长沙县| 墨玉县| 筠连县| 且末县| 邵武市| 乌鲁木齐市| 扎赉特旗| 兴安盟| 茌平县| 吴忠市| 封丘县| 卓尼县|