Volatile详解

volatile

简介

java虚拟机提供的轻量级的同步机制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 inthis

aaa updata number value:60spa

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

 

加volatile 结果2code

aaa com in对象

aaa updata number value:60blog

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 }
相关文章
相关标签/搜索