volatile 的主要作用有兩點:
- 保證變量的內存可見性
- 內存可見性是指當一個線程修改了某個變量的值,其它線程總是能知道這個變量變化帝牡。也就是說麻献,如果線程 A 修改了共享變量 V 的值纲岭,那么線程 B 在使用 V 的值時晓淀,能立即讀到 V 的最新值
- 不保證原子性
- 禁止指令重排序
可見性驗證
package com.test.vtest;
import java.util.concurrent.TimeUnit;
public class VolatileTest {
public static volatile int num = 0; // 增加volatile修改變量num潭辈,使其它線程對該num可以使用最新的值
public static void main(String[] args) {
new Thread(()->{
while (num ==0) {
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
num = 1; // 主線程修改num的值鸯屿,子線程默認感受不到
}
}
上述demo澈吨,不用volatile 修飾的時候,子線程感受不到num的變化寄摆,就會一直死循環(huán)谅辣,程序無法退出。
不保證原子性
package com.test.vtest;
import java.util.concurrent.TimeUnit;
public class VolatileTest2 {
public static volatile int num = 0;
public static void add() {
num++; //num++不是原子性操作
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
for (int j = 0; j < 5; j++) {
add();
}
}).start();
}
while (Thread.activeCount() >2) {
Thread.yield();
}
System.out.println(num); // 正確的結果應該是50婶恼, 結果經常是錯誤的
}
}
使用原子類AtomicInteger 保證原子性
import java.util.concurrent.atomic.AtomicInteger;
public class VolatileTest2 {
public static volatile AtomicInteger num = new AtomicInteger();
public static void add() {
num.getAndIncrement();
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
for (int j = 0; j < 5; j++) {
add();
}
}).start();
}
while (Thread.activeCount() >2) {
Thread.yield();
}
System.out.println(num); // 正確的結果應該是50
}
}