一文读懂 volatile 关键字
??點(diǎn)擊上方?好好學(xué)java?,選擇?星標(biāo)?公眾號(hào)
重磅資訊、干貨,第一時(shí)間送達(dá) 今日推薦:有了這 4 款工具,老板再也不怕我寫(xiě)爛SQL了個(gè)人原創(chuàng)+1博客:點(diǎn)擊前往,查看更多 作者:對(duì)弈 來(lái)源:https://www.cnblogs.com/MessiXiaoMo3334/p/12615823.htmlvolatile是Java虛擬機(jī)提供的輕量級(jí)的同步機(jī)制(“乞丐版”的synchronized)
保證可見(jiàn)性
不保證原子性
禁止指令重排
可見(jiàn)性
指當(dāng)多個(gè)線程訪問(wèn)同一個(gè)變量時(shí),如果其中一個(gè)線程修改了這個(gè)變量的值,其他線程能夠立即看得到修改的值
驗(yàn)證可見(jiàn)性demo:
import java.util.concurrent.TimeUnit;class MyData {volatile int number = 0;public void addTo60() {number = 60;} } public class VolatileDemo {public static void main() {MyData myData = new MyData();new Thread(() -> {System.out.println(Thread.currentThread().getName() + "\t come in");try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}myData.addTo60();System.out.println(Thread.currentThread().getName() + "\t updated number: " + myData.number);}, "AAA").start();while (myData.number == 0) {}System.out.println(Thread.currentThread().getName() + "\t mission is over");} }結(jié)果:
AAA come in main mission is over AAA updated number: 60不保證原子性
原子性:程序中的所有操作是不可中斷的,要么全部執(zhí)行成功要么全部執(zhí)行失敗
不保證原子性正是volatile輕量級(jí)的體現(xiàn),多個(gè)線程對(duì)volatile修飾的變量進(jìn)行操作時(shí),會(huì)出現(xiàn)容易出現(xiàn)寫(xiě)覆蓋的情況(i++)
驗(yàn)證不保證原子性demo:
import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger;class MyData {volatile int number = 0;public void addPlusPlus() {number++;} } public class VolatileDemo {public static void main(String[] args) {MyData myData = new MyData();for (int i = 0; i < 20; i++) {new Thread(() -> {for (int j = 0; j < 1000; j++) {myData.addPlusPlus();}}, String.valueOf(i)).start();}while (Thread.activeCount() > 2) {Thread.yield();}System.out.println(Thread.currentThread().getName() + "\t finally number value: " + myData.number);} }結(jié)果:
main finally number value: 19109解決不保證原子性問(wèn)題:Atomic
import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger;class MyData {volatile int number = 0;public void addPlusPlus() {number++;}AtomicInteger atomicInteger = new AtomicInteger();public void addAtmic() {atomicInteger.getAndIncrement();} } public class VolatileDemo {public static void main(String[] args) {MyData myData = new MyData();for (int i = 0; i < 20; i++) {new Thread(() -> {for (int j = 0; j < 1000; j++) {myData.addAtmic();}}, String.valueOf(i)).start();}while (Thread.activeCount() > 2) {Thread.yield();}System.out.println(Thread.currentThread().getName() + "\t finally number value: " + myData.number);System.out.println(Thread.currentThread().getName() + "\t AtomicInteger type,finally number value: "+ myData.atomicInteger);} }結(jié)果:
main finally number value: 19746 main AtomicInteger type,finally number value: 20000禁止指令重排
指令重排:為了提高程序運(yùn)行效率,編譯器可能會(huì)對(duì)輸入指令進(jìn)行重新排序,即程序中各個(gè)語(yǔ)句的執(zhí)行先后順序同代碼中的順序不一定一致。(但是它會(huì)保證單線程程序最終執(zhí)行結(jié)果和代碼順序執(zhí)行的結(jié)果是一致的,它忽略了數(shù)據(jù)的依賴性)
源代碼 -> 編譯器優(yōu)化重排 -> 指令并行重排 -> 內(nèi)存系統(tǒng)重排 -> 最終執(zhí)行指令
volatile能夠?qū)崿F(xiàn)禁止指令重排的底層原理:
內(nèi)存屏障(Memory Barrier):它是一個(gè)CPU指令。由于編譯器和CPU都能夠執(zhí)行指令重排,如果在指令間插入一條Memory Barrier則會(huì)告訴編譯器和CPU,任何指令都不能和該條Memory Barrier指令進(jìn)行重排序,即通過(guò)插入內(nèi)存屏障指令能夠禁止在內(nèi)存屏障前后的指令執(zhí)行重排序 優(yōu)化
內(nèi)存屏障的另外一個(gè)作用是強(qiáng)制刷新各種CPU的緩存數(shù)據(jù),因此任何CPU上的線程都能夠讀取到這些數(shù)據(jù)的最新版本。以上兩點(diǎn)正好對(duì)應(yīng)了volatile關(guān)鍵字的禁止指令重排序和內(nèi)存可見(jiàn)性的特點(diǎn)
對(duì)volatile變量進(jìn)行寫(xiě)操作時(shí),會(huì)在寫(xiě)操作之后加入一條store屏障指令,將工作內(nèi)存中的共享變量copy刷新回主內(nèi)存中;對(duì)volatile變量進(jìn)行讀操作時(shí),會(huì)在讀操作之前加入一條load的屏障指令,從主內(nèi)存中讀取共享變量
應(yīng)用場(chǎng)景:
高并發(fā)環(huán)境下DCL單例模式使用volatile
JUC包下AtomicXxx類:原子類AtomicXxx中都有一個(gè)成員變量value,該value變量被聲明為volatile,保證 AtomicXxx類的內(nèi)存可見(jiàn)性,而原子性由CAS算法&Unsafe類保證,結(jié)合這兩點(diǎn)才能讓AtomicXxx類很好地替代synchronized關(guān)鍵字。
總結(jié)
以上是生活随笔為你收集整理的一文读懂 volatile 关键字的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 面试题:一条 sql 语句是如何经过 M
- 下一篇: Java 完美实现添加、读取和删除 Ex