Volatile 轻量级的同步机制:(三特点)
1.保证可见性
2.不保证原子性
3.禁止指令重排
回顾可见性
JVM 的运行实体是线程,1.每个线程的创建时都是jvm都会为其创建一个工作内存(有些地方称为栈空间)工作内存是每个线程的私有数据区域,而java内存模型中规定所有的变量都存储在主内存,主内存是内存共享区域,所有线程都可以访问,但是线程对变量的操作必须是在工作内存中进行的,2.首先要将变量从主内存中拷贝到自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写会主内存中,不能直接操作主内存中的变量,各线程中的工作内存存储着主内存中变量副本拷贝,因此不同的线程间是无法访问对方的工作内存的,3.线程间的通信必须痛过主内存来完成的,其简要访问过程如下图:
3的过程就是可见性过程:
volatile 的可见性具体代码
package com.ryx;
import java.util.concurrent.TimeUnit;
class MyData{
// 不加Volatile关键字的情况是 一 结果
// num变量之前根本没有添加volatile关键字修饰,没有可见性
// int num=0;
// 加Volatile关键字的情况是 二 结果
volatile int num=0;
public void addData(){
this.num=60;
}
}
public class VolatileDemos {
public static void main(String[] args) {
seeOkByVolatile();
}
// volatile 可以保证可见性,以及及时通知其他线程,主物理内存的值已经被修改过
private static void seeOkByVolatile() {
//操作资源类
MyData myData=new MyData();
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t come in ");
try{
TimeUnit.SECONDS.sleep(3);
}catch (Exception e){
e.printStackTrace();
}
myData.addData();
System.out.println(Thread.currentThread().getName()+" \t updated number value: "+myData.num);
},"AAA").start();
// 第二个就是我们的main线程了
while(myData.num == 0){
// main 函数一直在这里这里等待循环直到num值不在等于零
}
System.out.println(Thread.currentThread().getName()+" \t mission is over ,mian get number value: "+myData.num);
}
}
结果一:
AAA come in
AAA updated number value: 60
// main 就是打不出来,因为主内存的变量并没有被修改,不可见!
结果二:
AAA come in
AAA updated number value: 60
main mission is over ,mian get number value: 60
不保证原子性
具体代码:
package com.ryx;
import java.util.concurrent.TimeUnit;
class MyData{
//int num=0;
volatile int num=0;
public void addPlus(){
num++;
}
}
/**
*
* 验证volatile不保证原子性
* 原子性指的什么意思?
* 不可分割,完整性,也即某个线程整在做某个具体业务时,中间不被加塞,
* 或者被分割整体完整 要么同时成功,要么同时失败。
*
* 为什么不保证
* 原因是假如 num的值为0 有三个线程同时来进行数据资源的操作,把num=0
* 从主内存写会到线程自己的工作内存,开始进行在自己的工作内存中修改变量,
* 执行num++操作,将自己的num修改为1,接下来,都开始往主内存中写操作,线程一,二,三都同时写入,
* 线程一比较强势先修变量改为一,这时线程二,三被挂起了,当线程一还没有进行全部通知
* 其他线程的时候,线程二,三被又唤醒了,也对原num=1,进行原值覆盖,
* 这样线程二,三就被漏掉了!本来数值应该为3,但是这样的操作,导致数据为1!
*
*/
public class VolatileDemos {
public static void main(String[] args) {
MyData myData=new MyData();
for(int i=1;i<=20;i++){
new Thread(()->{
for (int j = 0; j <=1000; j++) {
myData.addPlus();
}
},String.valueOf(i)).start();
}
//需要等待上面的20个线程都全部完成,再用main线程去得最终的结果是多少。
while (Thread.activeCount() >2){
// 礼让线程
Thread.yield();
}
// 若是volatile能保证原子性,则myData.num打印出来的是2w。
System.out.println(Thread.currentThread().getName()+"\t final num value: "+myData.num);
}
}
结果是:
main final num value: 19987
Process finished with exit code 0