volatile 关键字
volatile关键字前置知识
前言:因为voaltile涉及的因素,太多,笔者先从学习volatile需要的前置知识说起。
1、高并发编程的三个要素
1.原子性
保证一个操作或者多个操作要么都成功要么都失败中间不能由于任何的因素中断
2.可见性
多么线程共享一个数据时,其中一个线程修改了变量需要保证其他线程能够看见
3.有序性
即程序执行的顺序按照代码的先后顺序执行,有序性涉及到一个happens before规则
3.1happens before relationship
1.代码的执行顺序,编写在前面发生在编写后面的前面
2.unlock必须发生在lock之后
3.volatile修饰的变量,对变量的写操作必须先于对变量的读操作
4.传递规则:操作A先于B, 操作B先于A,那么A肯定先于C
5.线程启动规则:线程的start方法先于run方法
6.线程的中断规则:interrupt方法必须在执行捕获之前
7.对象销毁规则:初始化先于finalize方法
8.线程终结规则:所有的操作都发生在线程死亡之前
2、JMM (Java Memory Model)Java内存模型
大家都知道CPU的速度是非常快的,但是我们的RAM相对来说会满一点,JMM中,有了一个实现,每一个CPU或者说每一个线程都会有自己的一块高速缓冲区,如下图所示
解决高速缓存数据一致性问题
1.给数据总线加锁
总线(数据总线,地址总线,控制总线)
2.CPU高速缓存一致性协议
Intel MESI
核心思想
1.当CPU写入数据的时候,如果发现该变量被其他线程共有(其他线程存有变量的副本),就会发出一个信号,并且通知其他CPU该变量无效
2.当其他CPU发现该变量已经失效了就会去缓存中更新数据
voaltile
一旦一个共享变量被volatile就具备两层语义
1.保证了多个线程之间的可见性
2.禁止了重排序,也就是保证了有序性
3.不保证原子性
Code Demo
/**
* @Author: 陈一臣 <a href='mailto:chenyichenmail@163.com'></a>
* @Description:
* @Date:
*/
public class VolatileSimpleDemo {
static boolean flag = true;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " start---");
while (flag) { // 因为这个线程没有进行“写”操作,所以它不会去主存中更新数据,导致线程间不可见
}
System.out.println(Thread.currentThread().getName() + " end---");
}).start();
// 保证分线程先执行
TimeUnit.SECONDS.sleep(1);
flag = false;
}
}
解决方案
给共享的变量加上volatile,这样就保证了可见性
/**
* @Author: 陈一臣 <a href='mailto:chenyichenmail@163.com'></a>
* @Description:
* @Date:
*/
public class VolatileSimpleDemo {
volatile static boolean flag = true;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " start---");
while (flag) {
}
System.out.println(Thread.currentThread().getName() + " end---");
}).start();
// 保证分线程先执行
TimeUnit.SECONDS.sleep(1);
flag = false; // 加上voaltile之后,main线程更新了数据就会通知其他线程变量失效了
}
}
voaltile总结
1.保证了线程间的可见性
2.禁止重排序,保证了有序性
LOOK
volatile不保证原子性