首页 > 其他分享 >JMM与Volatile

JMM与Volatile

时间:2023-03-16 20:23:57浏览次数:27  
标签:volatile 线程 内存 Volatile JMM 操作 public

一、JMM

我们知道Java是一个跨平台的语言,那么Java是如何屏蔽掉各种操作系统、各种机器对内存访问的差异呢?

在JVM规范中,Java定义了一种内存模型,用来屏蔽掉各种硬件和操作系统的内存访问差异

1.JMM中的三大特性

  • 可见性
    • 当一个线程修改了某一个共享变量的值,其他线程能够立即知道该值变更。
    • 类似于git,在仓库中拷贝下去,自己独立的备份,仓库原来的不变,更新完成后可以提交到仓库(主内存)
    •  

       

  • 有序性
    • 对于一个线程的执行代码,Java规范规定JVM线程内部维持顺序,只要运行结果和顺序化执行的结果相同,那么指令执行的顺序可以和代码顺序不一样
  • 原子性

2.happens-before(先行发生原则)

在JMM中,如果一个操作执行的结果需要对另一个操作可见性或者代码重排序,那么这两个操作之间必须存在happens-before(先行发生)原则。

比如

int x = 5; //线程A执行
int y = x; //线程B执行

y是否一定等于5呢?

如果线程A的操作复核happens-before操作,则y为5,反之则不一定。

hanppens-before原则有8条

①. 次序规则
一个线程内,按照代码顺序,写在前面的操作先行发生于写在后面的操作(强调的是一个线程)
前一个操作的结果可以被后续的操作获取。将白点就是前面一个操作把变量X赋值为1,那后面一个操作肯定能知道X已经变成了1

②. 锁定规则
(一个unlock操作先行发生于后面((这里的"后面"是指时间上的先后))对同一个锁的lock操作(上一个线程unlock了,下一个线程才能获取到锁,进行lock))

③. volatile变量规则
(对一个volatile变量的写操作先行发生于后面对这个变量的读操作,前面的写对后面的读是可见的,这里的"后面"同样是指时间是的先后)

④. 传递规则
(如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出A先行发生于操作C)

⑤. 线程启动规则(Thread Start Rule)
(Thread对象的start( )方法先行发生于线程的每一个动作)

⑥. 线程中断规则(Thread Interruption Rule)

对线程interrupt( )方法的调用先发生于被中断线程的代码检测到中断事件的发生
可以通过Thread.interrupted( )检测到是否发生中断
⑦. 线程终止规则(Thread Termination Rule)
(线程中的所有操作都先行发生于对此线程的终止检测)

⑧. 对象终结规则(Finalizer Rule)
(对象没有完成初始化之前,是不能调用finalized( )方法的 )

 

A Happens-before B,则意味着A发生过的事情对B来说是可见的

 

    private int value=0;
    public void setValue(){
        this.value=value;
    }
    public int getValue(){
        return value;
    }

如果get、set方法由两个进程进行,为了满足A先于B,要么加锁,要么使用volatile关键字

 

二、volatile

为了满足JMM内存模型的要求,我们使用了volatile关键字,关键字由两个特点:可见性、有序性。(并没有完全实现JMM的三个要求)

使用volatile修饰变量的时候,

如果写一个volatile变量,JMM会把该线程对应的本地内存中的共享变量立即刷新回到主内存中。

如果读一个volatile变量时,JMM会把该线程对应的工作内存设置为无效,直接从主内存中读取共享变量。

 

 

总结为一句话:一句话,volatile修饰的变量在某个工作内存修改后立刻会刷新会主内存,并把其他工作内存的该变量设置为无效。

那么volatile怎么保证呢?====》内存屏障

 

内存屏障

内存屏障粗分两种:

写屏障

在写指令之后插入写屏障,强制把写缓冲区的数据刷回到主内存中

读屏障

在读指令之前插入读屏障,让工作内存或CPU高速缓存当中的缓存数据失效,重新回到主内存中获取最新数据。

细分四种

 

 

因此volatile实现了有序性和和可见性

 

但是,volatile并不适合运算,如i++,因为volatile并没有实现原子性

 那么volatile平常适用于哪些场合呢

状态标志,判断业务是否结束

//这个前面讲过
public class UseVolatileDemo
{
    private volatile static boolean flag = true;

    public static void main(String[] args)
    {
        new Thread(() -> {
            while(flag) {
                //do something......循环
            }
        },"t1").start();

        //暂停几秒钟线程
        try { TimeUnit.SECONDS.sleep(2L); } catch (InterruptedException e) { e.printStackTrace(); }

        new Thread(() -> {
            flag = false;
        },"t2").start();
    }
}

开销较低的读,写锁策略

public class UseVolatileDemo
{
    //
   // 使用:当读远多于写,结合使用内部锁和 volatile 变量来减少同步的开销
   // 理由:利用volatile保证读取操作的可见性;利用synchronized保证复合操作的原子性
     
    public class Counter
    {
        private volatile int value;

        public int getValue()
        {
            return value;   //利用volatile保证读取操作的可见性
              }
        public synchronized int increment()
        {
            return value++; //利用synchronized保证复合操作的原子性
               }
    }
}

DCL双锁案例

public class SafeDoubleCheckSingleton
{
    private static SafeDoubleCheckSingleton singleton; //-----这里没加volatile
    //私有化构造方法
    private SafeDoubleCheckSingleton(){
    }
    //双重锁设计
    public static SafeDoubleCheckSingleton getInstance(){
        if (singleton == null){
            //1.多线程并发创建对象时,会通过加锁保证只有一个线程能创建对象
            synchronized (SafeDoubleCheckSingleton.class){
                if (singleton == null){
                    //隐患:多线程环境下,由于重排序,该对象可能还未完成初始化就被其他线程读取
                    singleton = new SafeDoubleCheckSingleton();
                    //实例化分为三步
                    //1.分配对象的内存空间
                    //2.初始化对象
                    //3.设置对象指向分配的内存地址
                }
            }
        }
        //2.对象创建完毕,执行getInstance()将不需要获取锁,直接返回创建对象
        return singleton;
    }
}

 

标签:volatile,线程,内存,Volatile,JMM,操作,public
From: https://www.cnblogs.com/wintermist/p/17220506.html

相关文章

  • 决战圣地玛丽乔亚Day33 ---Synchronized && volatile
    再Synchronized和ReetrantLock的区别:1.Synchronized可以对静态方法,普通方法,代码块加锁。自动加锁释放锁。ReetrantLock需要手动的加锁释放锁,加锁前需要创建一个ReetrantLo......
  • 理论:第一章:HashMap底层实现原理,红黑树,B+树,B树的结构原理,volatile关键字,CAS(比较与交换)
    首先HashMap是Map的一个实现类,而Map存储形式是键值对(key,value)的。可以看成是一个一个的Entry。Entry所存放的位置是由key来决定的。Map中的key是无序的且不可重复的,所......
  • java 线程安全(三) volatile
    一、volatile关键字说明volatile是Java提供的一种轻量级的同步机制。Java语言包含两种内在的同步机制:同步块(或方法)和volatile变量,相比于synchronized(synchronized通常......
  • 【多线程与高并发】- 浅谈volatile
    浅谈volatile目录浅谈volatile简介JMM概述volatile的特性1、可见性举个例子总结2、无法保证原子性举个例子分析使用volatile对原子性测试使用锁的机制总结3、禁止指令重......
  • volatile、mutable和explicit
    volatile用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化......
  • volatile
    缓存一致性问题每个线程都有自己的工作内存。线程在初始化时从主内存读取所需的变量值到工作内存。在线程运行时,如果是读取则从工作内存中读取,如果是写入则先写到工作内存......
  • 决战圣地玛丽乔亚Day14 ----Volatile关键字分析
    volatile:背景: CPU和主存读写速度不一致,出现了高速缓存。把一些经常读的数据存入高速缓存,交互计算和高速缓存进行,修改完毕后再从高速缓存刷回主存中。但是问题来了!CPU......
  • volatile的实现原理-内存屏障
     被volatile修饰的变量在编译成字节码文件时会多个lock指令,该指令在执行过程中会生成相应的内存屏障,以此来解决可见性跟重排序的问题。内存屏障的作用:1.在有内存屏障......
  • 05.深入理解JMM和Happens-Before
    大家好,我是王有志。关注​​王有志​​,一起聊技术,聊游戏,聊在外漂泊的生活。JMM都问啥?最近沉迷P5R,所以写作的进度很不理想,但不得不说​​高卷杏​​YYDS。话不多说,开始今天的......
  • JMM知识点总结
    JMM知识点总结一、什么是JMM?不知道大家在学习的过程有没有思考过这两个问题为什么说java是跨平台语言导致并发问题的原因是什么第一个问题,我是这么理解的,代码运行本......