一文讀懂 volatile 關(guān)鍵字
作者:對弈
來源:https://www.cnblogs.com/MessiXiaoMo3334/p/12615823.html
volatile是Java虛擬機(jī)提供的輕量級(jí)的同步機(jī)制(“乞丐版”的synchronized)
保證可見性 不保證原子性 禁止指令重排
可見性
指當(dāng)多個(gè)線程訪問同一個(gè)變量時(shí),如果其中一個(gè)線程修改了這個(gè)變量的值,其他線程能夠立即看得到修改的值
驗(yà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è)線程對volatile修飾的變量進(jìn)行操作時(shí),會(huì)出現(xiàn)容易出現(xiàn)寫覆蓋的情況(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
解決不保證原子性問題: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ì)對輸入指令進(jìn)行重新排序,即程序中各個(gè)語句的執(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)行重排序,即通過插入內(nèi)存屏障指令能夠禁止在內(nèi)存屏障前后的指令執(zhí)行重排序 優(yōu)化 內(nèi)存屏障的另外一個(gè)作用是強(qiáng)制刷新各種CPU的緩存數(shù)據(jù),因此任何CPU上的線程都能夠讀取到這些數(shù)據(jù)的最新版本。以上兩點(diǎn)正好對應(yīng)了volatile關(guān)鍵字的禁止指令重排序和內(nèi)存可見性的特點(diǎn) 對volatile變量進(jìn)行寫操作時(shí),會(huì)在寫操作之后加入一條store屏障指令,將工作內(nèi)存中的共享變量copy刷新回主內(nèi)存中;對volatile變量進(jìn)行讀操作時(shí),會(huì)在讀操作之前加入一條load的屏障指令,從主內(nèi)存中讀取共享變量
應(yīng)用場景:
高并發(fā)環(huán)境下DCL單例模式使用volatile
public class SingletonDemo {
private static volatile SingletonDemo instance = null;
private SingletonDemo() {
System.out.println(Thread.currentThread().getName() + "我是構(gòu)造方法SingletonDemo()");
}
public static SingletonDemo getInstance() {
if (instance == null) {
synchronized (SingletonDemo.class) {
if (instance == null) {
instance = new SingletonDemo();
}
}
}
return instance;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
SingletonDemo.getInstance();
}, String.valueOf(i)).start();
}
}
}
JUC包下AtomicXxx類:原子類AtomicXxx中都有一個(gè)成員變量value,該value變量被聲明為volatile,保證 AtomicXxx類的內(nèi)存可見性,而原子性由CAS算法&Unsafe類保證,結(jié)合這兩點(diǎn)才能讓AtomicXxx類很好地替代synchronized關(guān)鍵字。
public class AtomicInteger extends Number implements java.io.Serializable {
// ...
private volatile int value;
// ...
}

最后免費(fèi)給大家分享50個(gè)Java項(xiàng)目實(shí)戰(zhàn)資料,涵蓋入門、進(jìn)階各個(gè)階段學(xué)習(xí)內(nèi)容,可以說非常全面了。大部分視頻還附帶源碼,學(xué)起來還不費(fèi)勁!
附上截圖。(下面有下載方式)。





項(xiàng)目領(lǐng)取方式:
掃描下方公眾號(hào)回復(fù):50,
可獲取下載鏈接
???
?長按上方二維碼?2 秒 回復(fù)「50」即可獲取資料
點(diǎn)贊是最大的支持?
