您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關(guān)volatile和synchronized的區(qū)別是什么,小編覺得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
Java 內(nèi)存模型(JMM)
CPU 增加了緩存均衡了與內(nèi)存的速度差異,這一增加還是好幾層。
此時(shí)內(nèi)存的短板不再那么明顯,CPU甚喜。但隨之卻帶來很多問題
看上圖,每個(gè)核都有自己的一級(jí)緩存(L1 Cache),有的架構(gòu)里面還有所有核共用的二級(jí)緩存(L2 Cache)。使用緩存之后,當(dāng)線程要訪問共享變量時(shí),如果 L1 中存在該共享變量,就不會(huì)再逐級(jí)訪問直至主內(nèi)存了。所以,通過這種方式,就補(bǔ)上了訪問內(nèi)存慢的短板
具體來說,線程讀/寫共享變量的步驟是這樣:
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
從主內(nèi)存復(fù)制共享變量到自己的工作內(nèi)存
在工作內(nèi)存中對(duì)變量進(jìn)行處理
處理完后,將變量值更新回主內(nèi)存
假設(shè)現(xiàn)在主內(nèi)存中有共享變量 X, 其初始值為 0
線程1先訪問變量 X, 套用上面的步驟就是這樣:
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
L1 和 L2 中都沒有發(fā)現(xiàn)變量 X,直到在主內(nèi)存中找到
拷貝變量 X 到 L1 和 L2 中
在 L1 中將 X 的值修改為1,并逐層寫回到主內(nèi)存中
此時(shí),在線程 1 眼中,X 的值是這樣的:
接下來,線程 2 同樣按照上面的步驟訪問變量 X
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
L1 中沒有發(fā)現(xiàn)變量 X
L2 中發(fā)現(xiàn)了變量X
從L2中拷貝變量到L1中
在L1中將X 的值修改為2,并逐層寫回到主內(nèi)存中
此時(shí),線程 2 眼中,X 的值是這樣的:
結(jié)合剛剛的兩次操作,當(dāng)線程1再訪問變量x,我們看看有什么問題:
此刻,如果線程 1 再次將 x=1回寫,就會(huì)覆蓋線程2 x=2 的結(jié)果,同樣的共享變量,線程拿到的結(jié)果卻不一樣(線程1眼中x=1;線程2眼中x=2),這就是共享變量?jī)?nèi)存不可見的問題。
怎么補(bǔ)坑呢?今天的兩位主角閃亮登場(chǎng),不過在說明 volatile關(guān)鍵字之前,我們先來說說你最熟悉的 synchronized 關(guān)鍵字
synchronized
遇到線程不安全的問題,習(xí)慣性的會(huì)想到用 synchronized 關(guān)鍵字來解決問題,暫且先不論該辦法是否合理,我們來看 synchronized 關(guān)鍵字是怎么解決上面提到的共享變量?jī)?nèi)存可見性問題的
【進(jìn)入】synchronized 塊的內(nèi)存語(yǔ)義是把在 synchronized 塊內(nèi)使用的變量從線程的工作內(nèi)存中清除,從主內(nèi)存中讀取
【退出】synchronized 塊的內(nèi)存語(yǔ)義事把在 synchronized 塊內(nèi)對(duì)共享變量的修改刷新到主內(nèi)存中
二話不說,無情向下看 volatile
volatile
當(dāng)一個(gè)變量被聲明為 volatile 時(shí):
線程在【讀取】共享變量時(shí),會(huì)先清空本地內(nèi)存變量值,再?gòu)闹鲀?nèi)存獲取最新值
線程在【寫入】共享變量時(shí),不會(huì)把值緩存在寄存器或其他地方(就是剛剛說的所謂的「工作內(nèi)存」),而是會(huì)把值刷新回主內(nèi)存
有種換湯不換藥的感覺,你看的一點(diǎn)都沒錯(cuò)
所以,當(dāng)使用 synchronized 或 volatile 后,多線程操作共享變量的步驟就變成了這樣:
簡(jiǎn)單點(diǎn)來說就是不再參考 L1 和 L2 中共享變量的值,而是直接訪問主內(nèi)存
來點(diǎn)踏實(shí)的,上例子
public class ThreadNotSafeInteger { /** * 共享變量 value */ private int value; public int getValue() { return value; } public void setValue(int value) { this.value = value; } }
經(jīng)過前序分析鋪墊,很明顯,上面代碼中,共享變量 value 存在大大的隱患,嘗試對(duì)其作出一些改變
先使用 volatile 關(guān)鍵字改造:
public class ThreadSafeInteger { /** * 共享變量 value */ private volatile int value; public int getValue() { return value; } public void setValue(int value) { this.value = value; } }
再使用 synchronized 關(guān)鍵字改造
public class ThreadSafeInteger { /** * 共享變量 value */ private int value; public synchronized int getValue() { return value; } public synchronized void setValue(int value) { this.value = value; } }
這兩個(gè)結(jié)果是完全相同,在解決【當(dāng)前】共享變量數(shù)據(jù)可見性的問題上,二者算是等同的
如果說 synchronized 和 volatile 是完全等同的,那就沒必要設(shè)計(jì)兩個(gè)關(guān)鍵字了,繼續(xù)看個(gè)例子
@Slf4j public class VisibilityIssue { private static final int TOTAL = 10000; // 即便像下面這樣加了 volatile 關(guān)鍵字修飾不會(huì)解決問題,因?yàn)椴]有解決原子性問題 private volatile int count; public static void main(String[] args) { VisibilityIssue visibilityIssue = new VisibilityIssue(); Thread thread1 = new Thread(() -> visibilityIssue.add10KCount()); Thread thread2 = new Thread(() -> visibilityIssue.add10KCount()); thread1.start(); thread2.start(); try { thread1.join(); thread2.join(); } catch (InterruptedException e) { log.error(e.getMessage()); } log.info("count 值為:{}", visibilityIssue.count); } private void add10KCount(){ int start = 0; while (start ++ < TOTAL){ this.count ++; } } }
其實(shí)就是將上面setValue 簡(jiǎn)單賦值操作 (this.value = value;)變成了 (this.count ++;)形式,如果你運(yùn)行代碼,你會(huì)發(fā)現(xiàn),count的值始終是處于1w和2w之間的
將上面方法再以 synchronized 的形式做改動(dòng)
@Slf4j public class VisibilityIssue { private static final int TOTAL = 10000; private int count; //... 同上 private synchronized void add10KCount(){ int start = 0; while (start ++ < TOTAL){ this.count ++; } } }
再次運(yùn)行代碼,count 結(jié)果就是 2w
以上就是volatile和synchronized的區(qū)別是什么,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見到或用到的。希望你能通過這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。