目录
volatile :英文的字面意思是易变的、不稳定的。
特性(一):实时可见性
(什么意思?一个线程对属性进行写操作,通知其他线程。告诉他们,这个属性已经发生了改变。)
通过一个demo讲解下。如下:
package com.test.testDemo3;
import com.test.testThread.TestThread;
import java.util.concurrent.TimeUnit;
public class TestDemo1 extends Thread {
private static volatile boolean flag = false;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
while (true) {
if (flag)
System.out.println("test success");
}
}).start();
Thread thread = new TestDemo1();
thread.start();
TimeUnit.SECONDS.sleep(2);
TestDemo1.flag = true;
}
}
启动了两个线程。之后sleep两秒。再修改TestDemo1线程的flag值为true。
运行看看:
test success
test success
test success
test success
test success
test success
test success
会一直输出 test success。说明什么? 说明TestDemo1通过对flag 设置volatile。再进行写操作,做出了改变。然后会通知另一个线程,另一个线程会根据这个变化后的flag做出处理,执行if语句中的代码。
注:作为比较,去掉 volatile 试试,看看结果。
再看一个例子:
public class TestDemo1 extends Thread {
private static volatile boolean flag = false;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
while (true) {
if (flag)
System.out.println("test success");
}
}).start();
Thread.sleep(2000);
Thread thread = new TestDemo1();
TestDemo1.flag = true;
thread.start();
}
}
这个例子与上面的类似,区别是TestDemo1是在启动之前修改flag的值。
也会一直输出:
test success
test success
test success
test success
去掉volatile,就会什么都不输出。
为什么要让sleep两秒呢? 因为会发生指令重排序。线程的运行先后顺序并不能确定,这就导致可能TestDemo1先运行。那这个测试就没有意义了。
从上面的例子以及叙述能得出:
一个线程对volatile修饰的属性进行写操作,会通知其他线程,(包括正在运行中的线程)。告诉他们,这个属性已经发生了改变。体现一种实时性。
volatile底层基于内存屏障。
关于内存屏障:
编译器和CPU可以在保证输出结果一样的情况下对指令重排序,使性能得到优化。插入一个内存屏障, 相当于告诉CPU和编译器先于这个命令的必须先执行,后于这个命令的必须后执行。内存屏障另一个作用是强制更新一次不同主内存的工作内存。例如,一个写屏障会 把这个屏障前写入的数据刷新到工作内存,这样任何试图读取该数据的线程将得到最新值
(节选于:https://www.cnblogs.com/churao/p/8494160.html)
特性(二):禁止指令重排序
先来谈一下 volatile 并不能保证原子性。
通过两个线程,对一个变量i,进行++操作。即i++
public class TestDemo1 extends Thread {
private static volatile int i = 0;
@Override
public void run() {
for (int j = 0; j < 10000; j++) {
i++;
}
}
public static void main(String[] args) {
for (int j = 0; j < 5; j++) {
new TestDemo1().start();
}
System.out.println(i);
}
}
即使有volatile,结果也不一定是50000。先来看如下图所示:
可能会发生这样一种情况:线程A将i = 1,传到主内存的同时,B正在读取i的值。i = 0,再进行+ 1操作,i = 1。这时,主内存向各个工作内存通知i已有线程A 变更为 1 。但是线程B进行+ 1操作后已经变更为1了。再次将i = 1,传给主内存。造成结果不等于50000。
最后再谈谈禁止指令重排序。
详见: