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的变化,就会一直死循环,程序无法退出。
不保证原子性
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
}
}
网友评论