volatile的作用
volatile是Java虚拟机提供的轻量级的同步机制。
- 可见行:被volatile修饰的属性在工作内存被修改后,会将值刷新至主内存,因此任何时刻,不同线程总能看到该变量的最新值,保证了可见性。
- volatile修饰的属性不能保证原子性操作,即不保证原子性
- 防止指令重排
验证可见行
public class Demo {
//如果将volatile去除那么程序将一直运行
volatile int i = 0;
public static void main(String[] args) {
checkVolatile();
}
public static void checkVolatile() {
Demo demo = new Demo();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
demo.i = 60;}).start();
System.out.println("update i = 60");
//主线程
while (demo.i == 0) {
}
}
}
验证不保证原子性
public class Demo {
volatile int i = 0;
//synchronized
public void addI() {
i++;
}
//开启20个线程,每个线程相加1000次,理论上应为20000,但是实际执行却不是小于理论值
public static void main(String[] args) {
Demo demo = new Demo();
for (int i =0; i < 20; i++){
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
demo.addI();
}
}).start();
}
//判断是否还有其他线程在运行
//后台默认两个线程主线程和GC线程
while (Thread.activeCount() > 2) {
Thread.yield();
}
System.out.println(demo.i);
}
}
验证防止指令重排
参考:https://www.infoq.cn/article/java-memory-model-2/
应用场景
单例模式(多线程下的单例)
public class SingletonDemo {
//防止指令重排,对象初始化和分配对象内存存在指令重排的情况,导致会出现获取的instance等于null,导致出错
private static volatile SingletonDemo instance;
private SingletonDemo() {
System.out.println("SingletonDemo instance!");
}
//加锁前后进行判断是否已经实例化(双端检索)
public static SingletonDemo getInstance() {
if (instance == null) {
synchronized (SingletonDemo.class) {
if (instance == null) {
instance = new SingletonDemo();
}
}
}
return instance;
}
}