当前位置:网站首页>volatile的解构| 社区征文

volatile的解构| 社区征文

2022-06-11 22:35:00 InfoQ



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


null


抽象的内存模型--不存在;
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事务中的类似,不可分割,完整性,

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




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

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

丢失数据的原因:
1. 细说a++的运行过程:
分为三步;

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

②加一:

③写回主内存同步数据

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

随时可能会被打断;

null

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

null
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);
 }*/

}`

null

`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()+&quot;addPluePlus&quot;+&quot;:&quot;+volatileTest2.a);
 //得出加过后的最后的值atomiceInteger
 System.out.println(Thread.currentThread().getName()+&quot;atomicInteger&quot;+&quot;:&quot;+volatileTest2.atomicInteger);


 /* mainaddPluePlus:2797
 mainatomicInteger:3000
 */

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

}`

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

null

null

四、面试:在哪些场景下遇见volatile?

null
单例模式:
我们所写的简单经典的:







 class SingletonTest {
 //静态的空对象
 private static SingletonTest Instance=null;

 //2.构造器私有化
 private SingletonTest(){

 System.out.println(&quot;构造器创建一个对象&quot;);

 }


 //静态的公共方法
 public static SingletonTest getSingletonTest(){

 while (Instance==null){

 Instance=new SingletonTest();
 }

 //返回一个对象
 return Instance;
 }

}


public class SingletonTest1 {

 public static void main(String[] args) {
 //单机版的;
 System.out.println(SingletonTest.getSingletonTest()==SingletonTest.getSingletonTest());
 System.out.println(SingletonTest.getSingletonTest()==SingletonTest.getSingletonTest());
 System.out.println(SingletonTest.getSingletonTest()==SingletonTest.getSingletonTest());
 System.out.println(SingletonTest.getSingletonTest()==SingletonTest.getSingletonTest());



 //多线程


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

 new Thread(()->{
 SingletonTest.getSingletonTest();
 System.out.println(Thread.currentThread().getName()+SingletonTest.getSingletonTest());
 },String.valueOf(i)).start();
 

 //构造器莫名其妙的输出了很多次--也即是说一个构造器被执行多次; 多线程环境下不行;
 }



 }



}

这个测试环境是在: mian线程中只有简答的线程调用的;

null
&nbsp;2.单例模式的DCL(double check Lock)双端检锁机制---》可重复锁
&nbsp; &nbsp; &nbsp;解决

 public static SingletonTest02 getSingletonTest(){

 //2.利用的是dcl的使用同步代码块;

 if (Instance==null){

 synchronized (SingletonTest02.class){
 if (Instance==null){
 Instance=new SingletonTest02();
 }
 }
 } 
 //在加锁前后都要检查
 return Instance;
 }


null
DCL---可能不安全.因为存在的是一个指令重排的过程

这里我们用的是volatile的解释;

null
但是DCL:不能保证是绝对安全的所以;
在单例的这个对象前面加入volatil &nbsp; &nbsp; &nbsp;&nbsp;

 //volatile的作用是禁止重排序
 private static volatile SingletonTest02 Instance=null;


原网站

版权声明
本文为[InfoQ]所创,转载请带上原文链接,感谢
https://xie.infoq.cn/article/43accad6885762b1d7b9d660f