Java中volatile關鍵字的作用是什么舉例詳解
前言
volatile
是 Java 中的一個關鍵字,用于修飾變量。它提供了 可見性 和 禁止指令重排 的特性,但不保證 原子性。
1. 可見性 (Visibility)
- 問題背景: 在多線程環(huán)境下,每個線程都有自己的工作內存(例如 CPU 緩存),線程對共享變量的讀寫操作可能不是直接在主內存中進行的,而是在工作內存中進行的。這可能導致一個線程修改了共享變量的值,而其他線程卻看不到這個修改,從而讀取到過期的值。
- volatile 的作用: 當一個變量被
volatile
修飾時,它會保證:- 寫操作: 當一個線程修改了
volatile
變量的值,這個新值會立即被 刷新 到主內存中。 - 讀操作: 當一個線程讀取
volatile
變量時,它會從主內存中讀取最新的值,而不是從自己的工作內存中讀取。
- 寫操作: 當一個線程修改了
- 實現(xiàn)原理:
volatile
通過 內存屏障 (Memory Barrier) 來實現(xiàn)可見性。內存屏障是一種 CPU 指令,它可以阻止編譯器和 CPU 對指令進行重排序,并強制將工作內存中的數(shù)據(jù)刷新到主內存,或從主內存讀取數(shù)據(jù)。
代碼示例:
public class VolatileVisibilityExample { private static volatile boolean flag = false; // private static boolean flag = false; 如果不使用volatile,可能出現(xiàn)線程1無法停止的情況 public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(() -> { while (!flag) { // ... (循環(huán)執(zhí)行某些操作) ... } System.out.println("Thread 1 stopped."); }); Thread thread2 = new Thread(() -> { try { Thread.sleep(1000); // 讓 thread1 先執(zhí)行 } catch (InterruptedException e) { e.printStackTrace(); } flag = true; // 修改 flag 的值 System.out.println("Thread 2 set flag to true."); }); thread1.start(); thread2.start(); } }
2. 禁止指令重排 (Ordering)
- 問題背景: 為了優(yōu)化性能,編譯器和 CPU 可能會對指令的執(zhí)行順序進行重排序(reordering),只要不影響單線程程序的執(zhí)行結果。但在多線程環(huán)境下,指令重排可能導致程序出現(xiàn)意外的行為。
- volatile 的作用:
volatile
關鍵字可以禁止特定類型的指令重排:- 寫后讀: 不能將
volatile
變量的寫操作重排到讀操作之后。 - 寫后寫: 不能將
volatile
變量的寫操作重排到另一個volatile
變量的寫操作之后。 - 讀后讀/寫: 不能將
volatile
變量的讀操作重排到另一個volatile
變量的讀操作或寫操作之后。
- 寫后讀: 不能將
- 實現(xiàn)原理:
volatile
通過插入特定類型的內存屏障來禁止指令重排。例如,在volatile
變量的寫操作之后會插入一個 StoreStore 屏障,在讀操作之前會插入一個 LoadLoad 屏障,以及可能的StoreLoad屏障。 - 雙重檢查鎖定 (Double-Checked Locking) 的例子:
volatile
經常用于雙重檢查鎖定模式,以防止指令重排導致的問題。//單例模式 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(); } } } return instance; } }
3. 不保證原子性 (Atomicity)
- 重要說明:
volatile
不保證 操作的原子性。原子性指的是一個操作是不可分割的,要么全部執(zhí)行,要么不執(zhí)行,不會出現(xiàn)執(zhí)行一半的情況。 - 例子:
volatile
變量的自增操作 (i++
) 不是原子性的。它實際上包含了三個步驟:讀取i
的值、將i
的值加 1、將新值寫回i
。在多線程環(huán)境下,這三個步驟之間可能會被其他線程打斷,導致結果錯誤。 - **解決:**如果需要保證原子性,可以使用
synchronized
、ReentrantLock
或原子類(如AtomicInteger
)。
總結:
volatile
關鍵字用于修飾變量,提供可見性和禁止指令重排的特性。- 可見性保證線程對
volatile
變量的讀寫操作都是直接在主內存中進行的。 - 禁止指令重排防止編譯器和 CPU 對
volatile
變量相關的指令進行重排序。 volatile
不保證 原子性。volatile
通常用于狀態(tài)標志、雙重檢查鎖定等場景。
問題分析:
這個問題考察了對 volatile
關鍵字的理解,包括它的作用、特性(可見性、禁止指令重排)以及局限性(不保證原子性)?;卮饡r需要清晰地解釋這些概念,并提供一些代碼示例來說明問題。
與其他問題的知識點聯(lián)系:
- 什么是 Java 內存模型(JMM)?
volatile
是 Java 內存模型 (JMM) 的一部分,它定義了線程和主內存之間的交互規(guī)則。 - 什么是 Java 中的原子性、可見性和有序性?
volatile
提供了可見性和有序性(禁止指令重排),但不保證原子性。 - 什么是 Java 的 happens-before 規(guī)則?
volatile
變量的寫操作 happens-before 于后續(xù)對該變量的讀操作。 - 什么是 Java 中的指令重排?
volatile
可以禁止特定類型的指令重排。 - Java 中的 synchronized 是怎么實現(xiàn)的?
synchronized
提供了原子性、可見性和有序性。在某些情況下,volatile
可以作為synchronized
的一種輕量級替代方案(如果只需要保證可見性和有序性)。 - 你使用過 Java 中的哪些原子類? 原子類(如
AtomicInteger
)提供了原子操作,可以用來替代volatile
+ 額外同步的方案。 - Java 中的 final 關鍵字是否能保證變量的可見性? final關鍵字可以保證可見性。
理解這些聯(lián)系可以幫助你更全面地掌握 Java 并發(fā)編程的知識,并了解如何在實際應用中選擇合適的同步機制。
到此這篇關于Java中volatile關鍵字的作用是什么的文章就介紹到這了,更多相關Java volatile關鍵字的作用內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
eclipse輸出Hello World的實現(xiàn)方法
這篇文章主要介紹了eclipse輸出Hello World的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-11-11Spring Boot 配置和使用多線程池的實現(xiàn)
這篇文章主要介紹了Spring Boot 配置和使用多線程池的實現(xiàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-06-06SpringBoot使用Prometheus采集自定義指標數(shù)據(jù)的方法詳解
隨著微服務在生產環(huán)境大規(guī)模部署和應用,隨之而來也帶來了新的問題,其中比較關鍵的就是關于微服務的運維和監(jiān)控,本文將結合微服務運維監(jiān)控中的指標監(jiān)控進行詳細的說明,需要的朋友可以參考下2024-07-07Java?MyBatis實戰(zhàn)之QueryWrapper中and和or拼接技巧大全
在Java中QueryWrapper是MyBatis-Plus框架中的一個查詢構造器,它提供了豐富的查詢方法,其中包括and和or方法,可以用于構建復雜的查詢條件,這篇文章主要給大家介紹了關于Java?MyBatis實戰(zhàn)之QueryWrapper中and和or拼接技巧的相關資料,需要的朋友可以參考下2024-07-07