暮年|20年架构师深入讲解java多线程与高并发:volatile与CAS,涨薪5K

volatile我们先来看这个volatile的概念 , volatile它是什么意思 , 现在像大的互联网企业的面试 , 基本上volatile是必会的 , 有时候他也不会太问 , 认为你应该会 , 但是中小企业也就开始问这方面的问题 。
我们来看一下这个小程序 , 写了一个方法啊 , 首先定义了一个变量布尔类型等于true , 这里模拟的是一个服务器的操作 , 我的值为true你就给我不间断的运行 , 什么时候为false你再停止 。测试new Thread启动一个线程 , 调用m方法 , 睡了一秒 , 最后running等于false , 运行方法他是不会停止的 。如果你要把volatile打开 , 那么结果就是启动程序一秒之后他就会m end停止 。 (volatile就是不停的追踪这个值 , 时刻看什么时候发生了变化)
/*** volatile 关键字 , 使一个变量在多个线程间可见* A B线程都用到一个变量 , java默认是A线程中保留一份copy,这样如果B线程修改了该变量 , 则A线程未必知道* 使用volatile关键字 , 会让所有线程都会读到变量的修改值** 在下面的代码中 , running是存在于堆内存的t对象中* 当线程t1开始运行的时候 , 会把running值从内存中读到t1线程的工作区 , 在运行过程中直接使用这个copy,并不会每次都去* 读取堆内存 , 这样 , 当线程修改running的值之后 , t1线程感知不到 , 所以不会停止运行** 使用volatile , 将会强制所有线程都会去堆内存中读取running的值** 可以阅读这个文章加深讲解* ** volatile并不能保证多个线程共同修改running变量时所带来的不一致问题 , 也就是说volatile不能替代synchronized* @author mashibing*/package com.mashibing.juc.c_012_Volatile;import java.util.concurrent.TimeUnit;public class T01_HelloVolatile {/*volatile*/ boolean running = true; //对比一下有无volatile的情况下 , 整个程序运行结果的区别void m() {System.out.println("m start");while(running) {}System.out.println("m end!");}public static void main(String[] args) {T01_HelloVolatile t = new T01_HelloVolatile();new Thread(t::m, "t1").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}t.running = false;}}volatile作用(由于面试常考到 , 必须记着)
暮年|20年架构师深入讲解java多线程与高并发:volatile与CAS,涨薪5K1:保证线程的可见性
大家知道java里面是有堆内存的 , 堆内存是所有线程共享里面的内存 , 除了共享的内存之外呢 , 每个线程都有自己的专属的区域 , 都有自己的工作内存 , 如果说在共享内存里有一个值的话 , 当我们线程 , 某一个线程都要去访问这个值的时候 , 会将这个值copy一份 , copy到自己的这个工作空间里头 , 然后对这个值的任何改变 , 首先是在自己的空间里进行改变 , 什么时候写回去 , 就是改完之后会马上写回去 。
什么时候去检查有没有新的值 , 也不好控制 。
在这个线程里面发生的改变 , 并没有及时的反应到另外一个线程里面 , 这就是线程之间的不可见, 对这个变量值加了volatile之后就能够保证 一个线程的改变 , 另外一个线程马上就能看到 。
大家可以去查这个词:MESI, 他的本质上是使用了cpu的一个叫做 高速缓存一致性协议
注:在和面《JVM》的课程中 , 老师深入讲解了MESI协议和volatile的底层实现 。
2:禁止指令重新排序
指令重排序也是和cpu有关系 , 每次写都会被线程读到 , 加了volatile之后 。 cpu原来执行一条指令的时候它是一步一步的顺序的执行 , 但是现在的cpu为了提高效率 , 它会把指令并发的来执行 , 第一个指令执行到一半的时候第二个指令可能就已经开始执行了 , 这叫做流水线式的执行 。 在这种新的架构的设计基础之上呢想充分的利用这一点 , 那么就要求你的编译器把你的源码编译完的指令之后呢可能进行一个指令的重新排序 。