volatile关键字解构

86 阅读3分钟

这是我参与「掘金日新计划 · 6 月更文挑战」的第11天 ,点击查看活动详情

JMM: java memory model (java内存模型)

image.png

抽象的内存模型--不存在;

   volatile :

     保证可见性,不保证原子性(synchronized保证),

     禁止指令重排-(不让插队--保证有序)

   所以就是volatile的java虚拟机的轻量级的同步机制

一、可见性

 若是某一个线程改变了一个固定的变量,其他线程会立刻知晓;

   也就是及时通知

  //volatile可以保证可见性:

   及时的通知其他线程,主物理内存中的值已经被修改;



import java.util.concurrent.TimeUnit;

class volatileTest1{


     //加volatile可以看见结果
    volatile int  number=0;//成员变量默认为0

    public  void addTo60(){
        //将number变成60
      this.number=60;

    }



}

public class volatileDemo {



    //如何理解这个volatile的保证可见性:
      /*
      *   可见性: 保证在线程使用的过程中,将数据修改后,及时的通知其他线程更新数据;
      *
      *     demo的设计原理:  (需要添加的是一个睡眠时间3-不然结果会出错误)
      *      我们运行nto60的方法--看main线程是否能获得已经变化了的数值number
      *       否则将循环下去
      * */


    public static void main(String[] args) {

        //1.资源类的初始化
        volatileTest1 volatileTest1=new volatileTest1();

            new Thread(()->{

                System.out.println(Thread.currentThread().getName()+":come in"+volatileTest1.number);

                //这里必须要睡3秒--不然的结果就是main线程也会同步,因为线程的运行速度太快啦
                try {
                    //休眠3秒钟
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                }
                //2.操作高内聚的方法nTo60
                volatileTest1.addTo60();

                 System.out.println(Thread.currentThread().getName()+":ture后的"+volatileTest1.number);

                },"web").start();

       //上述的web线程已经将数据变成60;


        //测试main线程是否感知到;
        while (volatileTest1.number==0){

            //就一直循环。什么都不会打印出来
        }


        System.out.println(Thread.currentThread().getName() +":"+volatileTest1.number);


         //表示main线程也知晓了number的变化---满足了volatile的可见性的需求

        /*
        *
        * web:come in0
            web:ture后的60
            main:60

        *
        * */

    }

}

2.不保证原子性:

 volatile支持可见性--;

原子性: 与mysql事务中的类似,不可分割,完整性,

  也即是某个线程在工作时,中间不可以加塞和分割,要么整体同时成功,要么失败;

image.png

image.png

原子性:--丢数据的形式;基本是没可能;

 a++不能保证原子性; volatile不能保证原子性;

     

    丢失数据的原因:    

   

1. 细说a++的运行过程:

  分为三步;

   ①读取,从主物理内存中的a----》拷贝到本地的线程的工作内存;

   ②加一:

   ③写回主内存同步数据

不保证原子性就是--可能会在线程操作的过程中会有数据抢占;

随时可能会被打断;

image.png

不保证原子性--就会出现写覆盖的情况;--

image.png

2.  怎么解决volatile的不保证原子性情况:

     ①加synchronized的同步机制锁(太重啦)

     注意:atomicInteger的底层就是Cas的;

    ②juc中的atomic包中的一个atomicInteger的类

  a++;

 方法是一个

       atomicInteger.getAndIncrement(); //每次加1-保证原子性的加1

                     //底层就是CAS的; atomicInteger.getAndIncrement()

//解决的是volatile不保证原子性的AtomicInteger

//AtomicInteger是 java.util.concurrent.atomic原子包写的类
  AtomicInteger atomicInteger=new AtomicInteger();

  public void addMyatomic(){

   atomicInteger.getAndIncrement(); //每次加1-保证原子性的加1

   /**
    * Atomically increments by one the current value.
    *原子性增加1
    * @return the previous value
    */
   /*public final int getAndIncrement() {
       return unsafe.getAndAddInt(this, valueOffset, 1);
   }*/

}`

image.png

`package com.atguigu;

import com.sun.org.apache.xpath.internal.operations.Variable;

import javax.lang.model.element.VariableElement; import java.util.concurrent.atomic.AtomicInteger;

class VolatileTest2{ //资源类

volatile int a;  //全局变量的默认为0

 public  void addPlusPlus(){

     this.a++;

 }


 //解决的是volatile不保证原子性的AtomicInteger

//AtomicInteger是 java.util.concurrent.atomic原子包写的类
  AtomicInteger atomicInteger=new AtomicInteger();

  public void addMyatomic(){

   atomicInteger.getAndIncrement(); //每次加1-保证原子性的加1

   /**
    * Atomically increments by one the current value.
    *原子性增加1
    * @return the previous value
    */
   /*public final int getAndIncrement() {
       return unsafe.getAndAddInt(this, valueOffset, 1);
   }*/

}

}

public class VolatileNoAtomic {

//volatile 不保证原子性的小李子

public static void main(String[] args) {


    //1.创建资源类的对象

    VolatileTest2 volatileTest2=new VolatileTest2();

      //2.创建线程-开始循环-

  for(int i=1;i<=30;i++) {

      new Thread(()->{

          for (int j = 0; j <100 ; j++) {

              volatileTest2.addPlusPlus();
              volatileTest2.addMyatomic();
          }


      },String.valueOf(i)).start();
  }


  //3.main线程是在a++之中有感知的,

    System.out.println(Thread.currentThread().getName()+"addPluePlus"+":"+volatileTest2.a);
                     //得出加过后的最后的值atomiceInteger
     System.out.println(Thread.currentThread().getName()+"atomicInteger"+":"+volatileTest2.atomicInteger);


                 /*   mainaddPluePlus:2797
                    mainatomicInteger:3000
            */

                 //底层就是CAS的; atomicInteger.getAndIncrement()
}

} `

三、禁止指令重排序(保证有序的执行)

image.png

image.png