Volatile详解

volatile

简介

java虚拟机提供的轻量级的同步机制

1.保证可见性

添加volatile关键词以后,当线程A改变了a的值,那么其它调用a的值其它线

程,就会得到通知

 1 class Mydata{
 2     volatile int a=0;
 3     public void resizeA() {
 4         this.a=60;
 5     }
 6 }
 7 public class VolatileDemo {
 8     public static void main(String[] args) {
 9         Mydata mydata = new Mydata();
10         new Thread(()->{
11             System.out.println(Thread.currentThread().getName()+"\t com in");
12             try {
13                 TimeUnit.SECONDS.sleep(3);//延迟3秒
14             }catch (InterruptedException e){
15                 e.printStackTrace();
16             }
17             mydata.resizeA();
18             System.out.println(Thread.currentThread().getName()+"\t updata number value:"+mydata.a);
19         },"aaa").start();
20 21         while (mydata.a == 0) {
22             //等待
23         }
24         System.out.println(Thread.currentThread().getName()+"\t over"+mydata.a);
25     }
26 27 }

 

不加volatile 结果1

aaa com in

aaa updata number value:60

不加volatile 线程main 得到的值一直是a=0,所以一直套在死循环中出不来

 

加volatile 结果2

aaa com in

aaa updata number value:60

main over60

加了volatile 线程aaa改了a的值,立马刷新到主内存中去,并通知其它线程

2.不保证原子性

当多个线程同时拿到值后,同时返回到主内存时,一个线程占用了主内存,将值写入,其它线程写主内存,挂起,当通知其它线程时,其它线程就会写进来,从而导致数据损失;

 1 class Mydata{
 2     volatile int a=0;
 3     public void resizeA() {
 4         this.a++;
 5     }
 6 }
 7 public class VolatileDemo {
 8     public static void main(String[] args) {
 9         Mydata mydata = new Mydata();
10         for (int i = 0; i < 200; i++) {
11             for (int j = 0; j <100 ; j++) {
12                 new Thread(()->{
13                     mydata.resizeA();
14                 },"aaa").start();
15             }
16         }
17         while (Thread.activeCount() > 2) {//还剩2个,main和gc
18             Thread.yield();//让出cpu
19         }
20         System.out.println(Thread.currentThread().getName()+"\t  "+mydata.a);
21     }
22 23 }

 

计算的数值一定要大,很难发生数据损失

3.禁止指令重排

什么是指令重排?

计算机为了提高性能在执行程序是,编译器和处理器会对程序执行指令重排;

 

指令重排的要求:

  1. 单线程的情况下不会改变最终的执行结果

  2. 必须考虑数据的依赖性

案例

 1 public class demo{
 2     int a=0;
 3     boolean flag=false;
 4     public void test(){
 5         a=1;//语句1
 6         flag=true;//语句2
 7     }
 8     //因为没有依赖关系,编译器可能将1和2换位置
 9     //多线程时,由于速度太快,先执行2,1还没有执行,就执行了test2里面的方法,导致结果无法预测
10     public void test2(){
11         if(flag){
12             a+=5;
13            
14         }
15     }
16 }

 

volatile的应用案例

单例模式

 1 public class demo{
 2     int a=0;
 3     boolean flag=false;
 4     public void test(){
 5         a=1;//语句1
 6         flag=true;//语句2
 7     }
 8     //因为没有依赖关系,编译器可能将1和2换位置
 9     //多线程时,由于速度太快,先执行2,1还没有执行,就执行了test2里面的方法,导致结果无法预测
10     public void test2(){
11         if(flag){
12             a+=5;
13            
14         }
15     }
16 }

 

synchronized不能禁止指令重排,所以要加volatile来禁止指令重排

因为实例化会发生三个步骤

1.分配内存空间

2.初始化对象

3.指向instance 此时!=null

因为2 3没有数据依赖,所以可能发生指令重排,3先执行,2后,而此时2实际为空对象,所以多线程,在3执行前,使用对象会发生异常;

 

 

拓展:不使用线程锁来实现懒汉单例模式同时保证线程安全

 1 //静态内部类
 2 public class Singleton4 {
 3     private Singleton4(){}
 4     private static class Inner{
 5         private static final Singleton4 INSTANCE=new Singleton4();
 6     }
 7     public static Singleton4 getInstance(){
 8         return Inner.INSTANCE;
 9     }
10 }

 

 

posted @ 2019-07-18 23:39  胡萝卜88号  阅读(128)  评论(0编辑  收藏  举报