写点什么

Java 内存模型

作者:卢卡多多
  • 2022 年 6 月 25 日
  • 本文字数:3309 字

    阅读完需:约 11 分钟

Java内存模型

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


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


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 事务中的类似,不可分割,完整性,


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


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


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


丢失数据的原因:


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


分为三步;


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


②加一:


③写回主内存同步数据


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


随时可能会被打断;


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


  1. 怎么解决 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);}/


}`


`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()
复制代码


}


}`


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


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


单例模式:


我们所写的简单经典的:


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


//2.构造器私有化private  SingletonTest(){
System.out.println("构造器创建一个对象");
}

//静态的公共方法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 线程中只有简答的线程调用的;


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


解决


public static SingletonTest02 getSingletonTest(){


     //2.利用的是dcl的使用同步代码块;
if (Instance==null){
synchronized (SingletonTest02.class){ if (Instance==null){ Instance=new SingletonTest02(); } }} //在加锁前后都要检查return Instance;}
复制代码


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


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


但是 DCL:不能保证是绝对安全的所以;


在单例的这个对象前面加入 volatil


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

发布于: 刚刚阅读数: 3
用户头像

卢卡多多

关注

努力寻找生活答案的旅途者 2020.04.12 加入

公众号:卢卡多多,欢迎一起交流学习 2021年度优质创作者

评论

发布
暂无评论
Java内存模型_volatile_卢卡多多_InfoQ写作社区