成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

面試官:談?wù)勀銓?Volatile 的理解吧

開發(fā) 后端
volatile 在指令之間插入內(nèi)存屏障 + 緩存一致性協(xié)議,保證按照特定順序執(zhí)行和某些變量的可見性。volatile 通過 內(nèi)存屏障通知 CPU 和編譯器阻止指令重排優(yōu)化來維持有序性。

前言

在之前的文章 深入分析 Synchronized 原理 介紹了 Synchronized是一種鎖的機(jī)制,存在阻塞和性能的問題,而 volatile 是 java 虛擬機(jī)提供的最輕量級的同步機(jī)制,volatile 主要提供修飾共享變量賦予 “可見性” 和 “有序性”。從簡單的 Demo 引出我們今天的主題 -- volatile。

Demo -- 多線程共享對象 控制執(zhí)行開關(guān)。

public class Demo {
private static boolean switchStatus = false;
public static void main(String[] args) {
new Thread(() -> {
System.out.println("開始工作");
while (!switchStatus) ;
System.out.println("結(jié)束工作");
}).start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
switchStatus = true;
System.out.println("命令停止工作");
}
}

本意是想通過 switchStatus 作為控制工作線程的開關(guān),但是實(shí)際執(zhí)行后,會(huì)發(fā)現(xiàn)結(jié)果并沒有按照預(yù)期 輸出"結(jié)束工作",而是失聯(lián)了一樣停不下來了,在死循環(huán)中出不來了。

但是如果在上面的 Demo 進(jìn)行稍微的修改即可滿足預(yù)期: private static volatile boolean switchStatus = false; 此時(shí)符合預(yù)期關(guān)閉開關(guān)時(shí),工作線程也隨之關(guān)閉了。接下我會(huì)針對這2個(gè)現(xiàn)象原理進(jìn)行解答,為了讀者更好的理解,得先引入幾個(gè)知識點(diǎn)(計(jì)算機(jī)內(nèi)存模型、JMM-Java 內(nèi)存模型)。

計(jì)算機(jī)內(nèi)存模型

為了更好地理解后續(xù) JMM 和 volatile,我們先了解下計(jì)算機(jī)內(nèi)存模型,簡單地介紹下:

程序執(zhí)行時(shí),CPU接收到指令 需要進(jìn)行計(jì)算時(shí),讀取所需要的數(shù)據(jù),會(huì)先嘗試從 CPU Cache 中獲取,若沒有再從主內(nèi)存中獲取,計(jì)算完成后,將結(jié)果寫入 CPU Cache ,若沒有特殊指令的情況下,會(huì)根據(jù)操作系統(tǒng)自身定義的時(shí)間 一段時(shí)間會(huì)將 CPU Cache 刷新到主內(nèi)存中(未被volatile 修飾的普通變量);當(dāng)然遇到特殊的指令會(huì)將 CPU Cache 刷新到主內(nèi)存中(被volatile 修飾的變量 就是依賴這個(gè)特性實(shí)現(xiàn)可見性)。

  • CPU:處理程序中各種指令,需要和CPU Cache 和 內(nèi)存打交道。
  • CPU Cache:由于 CPU 和內(nèi)存的速度差 幾個(gè)數(shù)量級,CPU 直接和內(nèi)存打交道很浪費(fèi) CPU 性能,因此引入了 CPU Cache 降低 CPU 的性能損耗。
  • 緩存一致性協(xié)議/總線鎖機(jī)制:引入 CPU Cache降低了 CPU性能損耗的問題,同時(shí)引入了緩存不一致的問題,為了解決這個(gè)這個(gè)問題通過緩存一致性協(xié)議/總線鎖機(jī)制 進(jìn)行解決。

總線鎖機(jī)制

CPU和其他功能部件是通過總線通信的,如果在總線加LOCK#鎖,那么在鎖住總線期間,其他CPU是無法訪問內(nèi)存,這樣一來,效率就比較低了。因此需要進(jìn)行優(yōu)化,細(xì)化控制鎖的粒度,我們只需要保證,對于被多個(gè)CPU緩存的同一份數(shù)據(jù)是一致的就行,所以引入了緩存鎖,他的核心機(jī)制就是緩存一致性協(xié)議。

緩存一致性協(xié)議

為了達(dá)成數(shù)據(jù)訪問的一致性,需要各個(gè)處理器在訪問內(nèi)存時(shí),遵循一些協(xié)議,在讀寫時(shí)根據(jù)協(xié)議來操作,常見的協(xié)議有,MSI,MESI,MOSI等等,最常見的就是MESI協(xié)議;MESI表示緩存行的四種狀態(tài)(modify、 Exclusive、Shared、 Invalid)。

嗅探技術(shù)

如何保證當(dāng)前處理器的內(nèi)部緩存、主內(nèi)存和其他處理器的緩存數(shù)據(jù)在總線上保持一致的?多處理器總線嗅探。

在多處理器下,為了保證各個(gè)處理器的緩存是一致的,就會(huì)實(shí)現(xiàn)緩存緩存一致性協(xié)議,每個(gè)處理器通過嗅探在總線上傳播的數(shù)據(jù)來檢查自己的緩存值是不是過期了,如果處理器發(fā)現(xiàn)自己緩存行對應(yīng)的內(nèi)存地址被修改,就會(huì)將當(dāng)前處理器的緩存行設(shè)置無效狀態(tài),當(dāng)處理器對這個(gè)數(shù)據(jù)進(jìn)行修改操作的時(shí)候,會(huì)重新從系統(tǒng)內(nèi)存中把數(shù)據(jù)庫讀到處理器緩存中。

Java內(nèi)存模型

  • Java虛擬機(jī)規(guī)范試圖定義一種Java內(nèi)存模型,來屏蔽掉各種硬件和操作系統(tǒng)的內(nèi)存訪問差異,以實(shí)現(xiàn)讓Java程序在各種平臺上都能達(dá)到一致的內(nèi)存訪問效果。
  • 為了更好地執(zhí)行性能,java內(nèi)存模型并沒有限制執(zhí)行引擎使用處理器的特定寄存器或緩存來和主內(nèi)存打交道,也沒有限制編譯器進(jìn)行調(diào)整代碼順序優(yōu)化。所以Java內(nèi)存模型會(huì)存在緩存一致性問題和指令重排序問題的。
  • Java內(nèi)存模型規(guī)定所有的變量都是存在主內(nèi)存當(dāng)中(類似于計(jì)算機(jī)模型中的物理內(nèi)存),每個(gè)線程都有自己的工作內(nèi)存(類似于計(jì)算機(jī)模型的高速緩存)。這里的變量包括實(shí)例變量和靜態(tài)變量,但是不包括局部變量,因?yàn)榫植孔兞渴蔷€程私有的。
  • 線程的工作內(nèi)存保存了被該線程使用的變量的主內(nèi)存副本,線程對變量的所有操作都必須在工作內(nèi)存中進(jìn)行,而不能直接操作操作主內(nèi)存。并且每個(gè)線程不能訪問其他線程的工作內(nèi)存。

舉個(gè)例子:

# 初始值 
i = 0;
# 線程A 線程B同時(shí)進(jìn)行操作
i = i + 1;

首先,執(zhí)行線程A從主內(nèi)存中讀取到 i=0,到工作內(nèi)存。然后在工作內(nèi)存中,賦值 i+1,工作內(nèi)存就得到 i=1,最后把結(jié)果寫回主內(nèi)存。如果是單線程的話,該語句執(zhí)行是沒問題的。但是在多線程的情況下,線程B的本地工作內(nèi)存和線程A的工作內(nèi)存讀取的時(shí)間相同都是 i=0,但是線程A將 i=1寫入主內(nèi)存中,線程B不知情的情況下,也做了 i+1 的操作,此時(shí)就出現(xiàn)可見性帶來問題了:連續(xù)2次的 i=i+1 最終的結(jié)果是1。

volatiole 可見性、有序性

在之前的文章 深入分析 Synchronized 原理 已經(jīng)介紹過 原子性、可見性、有序性定義,這里也就不展開說了。

先說結(jié)論:依賴于 CPU 緩存一致性協(xié)議 和 內(nèi)存屏障 解決了可見性的問題。

正常來說,volatile 基于緩存一致性協(xié)議就應(yīng)該可以實(shí)現(xiàn)可見性(在上面已經(jīng)介紹過 緩存一致性協(xié)議和嗅探技術(shù)),但是由于 Java 為了提高性能允許重排序(編譯器重排序 和 處理器重排序),因此需要通過內(nèi)存屏障來防止重排序,來保證每個(gè)線程執(zhí)行的每個(gè)指令有一定的順序性。

java 內(nèi)存屏障

java的內(nèi)存屏障通常所謂的四種即 LoadLoad、StoreStore、 LoadStore、StoreLoad 實(shí)際上也是上述兩種的組合,完成一系列的屏障和數(shù)據(jù)同步功能。

  • LoadLoad屏障:對于這樣的語句Load1; LoadLoad; Load2,在Load2及后續(xù)讀取操作要讀取的數(shù)據(jù)被訪問前,保證Load1要讀取的數(shù)據(jù)被讀取完畢。
  • StoreStore屏障:對于這樣的語句Store1; StoreStore; Store2,在Store2及后續(xù)寫入操作執(zhí)行前,保證Store1的寫入操作對其它處理器可見。
  • LoadStore屏障:對于這樣的語句Load1; LoadStore; Store2,在Store2及后續(xù)寫入操作被刷出前,保證Load1要讀取的數(shù)據(jù)被讀取完畢。
  • StoreLoad屏障:對于這樣的語句Store1; StoreLoad; Load2,在Load2及后續(xù)所有讀取操作執(zhí)行前,保證Store1的寫入對所有處理器可見。它的開銷是四種屏障中最大的。在大多數(shù)處理器的實(shí)現(xiàn)中,這個(gè)屏障是個(gè)萬能屏障,兼具其它三種內(nèi)存屏障的功能。

volatile 語義的內(nèi)存屏障

  • 在每個(gè) volatile 寫操作前插入 StoreStore 屏障,在寫操作后插入 StoreLoad 屏障。
  • 在每個(gè) volatile 讀操作前插入 LoadLoad 屏障,在讀操作后插入 LoadStore 屏障。
  • 由于內(nèi)存屏障的作用,避免了 volatile 變量和其它指令重排序、線程之間實(shí)現(xiàn)了通信,使得 volatile 表現(xiàn)出了鎖的特性。

舉一個(gè) volatile 防止指令重排的場景

java 中 DLC單例模式 大家應(yīng)該很熟悉了,只不過大家是否有注意到 uniqueInstance 被 volatile 修飾的作用嗎? 就是為了防止指令重排。

public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getInstance() {
if (uniqueInstance != null) {
synchronized (Singleton.class) {
if (uniqueInstance != null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}

初始化一個(gè)類,會(huì)產(chǎn)生多條匯編指令,總結(jié)下來主要執(zhí)行下面三點(diǎn):

  1. 給 uniqueInstance 的實(shí)例分配內(nèi)存。
  2. 初始化 Singleton 的構(gòu)造器。
  3. 將 uniqueInstance 對象指向分配的內(nèi)存空間(按順序到這步 uniqueInstance 初始化完成)。

理想的狀態(tài)下:1 -> 2 -> 3,但是 Java 為了提高性能允許重排序,可能會(huì)將初始化一個(gè)類的順序進(jìn)行變化,比如:1 -> 3 -> 2,這種情況下就可能會(huì)出現(xiàn)NPE,修飾了volatile 防止重排序,避免獲取到 uniqueInstance 未初始化完成,導(dǎo)致NPE

最后簡單總結(jié)下:volatile 在指令之間插入內(nèi)存屏障 + 緩存一致性協(xié)議,保證按照特定順序執(zhí)行和某些變量的可見性。volatile 通過 內(nèi)存屏障通知 CPU 和編譯器阻止指令重排優(yōu)化來維持有序性。

責(zé)任編輯:姜華 來源: 今日頭條
相關(guān)推薦

2025-03-21 00:00:05

Reactor設(shè)計(jì)模式I/O 機(jī)制

2024-10-24 16:14:43

數(shù)據(jù)傳輸CPU零拷貝

2025-02-21 15:25:54

虛擬線程輕量級

2024-09-27 15:43:52

零拷貝DMAIO

2024-06-13 08:01:19

2024-08-27 12:36:33

2024-09-26 16:01:52

2024-08-26 14:52:58

JavaScript循環(huán)機(jī)制

2019-07-26 06:42:28

PG架構(gòu)數(shù)據(jù)庫

2024-10-12 16:25:12

2021-11-25 10:18:42

RESTfulJava互聯(lián)網(wǎng)

2021-08-09 07:47:40

Git面試版本

2025-01-13 09:24:32

2025-04-09 00:00:00

2024-08-23 09:02:56

2020-12-01 08:47:36

Java異常開發(fā)

2020-06-12 15:50:56

options前端服務(wù)器

2021-11-05 10:07:13

Redis哈希表存儲

2020-06-19 15:32:56

HashMap面試代碼

2021-09-16 07:52:18

算法應(yīng)用場景
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 九九热在线免费视频 | 国产九九九 | a级免费黄色片 | 精品欧美久久 | 美女三区| 亚洲综合字幕 | 国产精品久久久久久久粉嫩 | 久久一区| 丁香五月网久久综合 | 国产精品96久久久久久 | 日本三级电影免费观看 | 欧美激情在线观看一区二区三区 | 成人午夜免费视频 | 日韩在线视频免费观看 | 午夜视频导航 | 中文字幕国产精品 | 国产精品久久777777 | 亚洲欧洲精品成人久久奇米网 | 色久伊人 | 久久精品亚洲 | 日韩在线免费视频 | 青青草在线视频免费观看 | 欧美一级免费看 | 久久爱综合| 色综网 | 国产精品日韩欧美一区二区三区 | 精品国产久| 欧美激情精品久久久久久 | 欧美精品在线免费观看 | 韩国av一区二区 | 亚洲高清一区二区三区 | 色影视 | 国产精品成人av | 精品国产乱码久久久久久老虎 | 国产1区2区在线观看 | 国产一区二区在线播放视频 | av一二三区 | 在线国产视频 | 欧美国产日韩精品 | 玖草资源 | 亚洲一区日韩 |