JMM概念

Posted 偶像java练习生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JMM概念相关的知识,希望对你有一定的参考价值。

JMM

java 内存模型

  1. 什么是JMM ?
    JMM :java 内存模型,保证线程的安全,不存在的东西,概念。

关于JMM 的一些同步约定:

1.线程解锁前,必须把共享变量立刻 刷回主存。
2.线程加锁前,必须读取主存中的最新的值到工作内存中!
3.加锁和解锁是同意把锁

线程: 工作内存,主内存

8种操作:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
问题:程序不知道主内存的值已经被修改过了?
则我们就要用Volatile

Volatile

保证可见性

package tvolatile;

import java.util.concurrent.TimeUnit;

public class JMMDemo {

    // 不加volatile 程序就会死循环!
    //变量加关键字volatile 关键字,主线程中的num 修改对线程 while 可见,后while 停止
    private  volatile  static  int num =0;


    public static void main(String[] args) throws InterruptedException { //main 线程

        new Thread(()->{//线程1,对主内存的变化是不知道的
            while(num==0){

            }
        }).start();
        TimeUnit.SECONDS.sleep(1);

        num =1;
        System.out.println(num);
    }
}

输出结果:1

不保证原子性

原子性:不可分割
线程 A 在执行任务的时候,不能被打扰的,也不能被分割。要么同时成功,要么同时失败

package tvolatile;

//volatile 不保证原子性
public class VDemo2 {
    
    //volatile 不保证原子性
    private volatile static int num =0;

    public  static void add(){
        num++;
    }


    public static void main(String[] args) {
        //理论上num 结果应该为2万
        for (int i = 1; i <=20; i++) {
            new Thread(()->{
                for (int i1 = 0; i1 < 1000; i1++) {
                    add();
                }
            }).start();
        }
        while(Thread.activeCount()>2){// main 线程 gc
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+" "+num);
    }
}

如果不加lock 和synchronized,怎样保证原子性?
在这里插入图片描述
使用原子类,解决原子性问题
在这里插入图片描述

原子类怎么这么高级

package tvolatile;

import java.util.concurrent.atomic.AtomicInteger;

//volatile 不保证原子性
public class VDemo2 {

    //volatile 不保证原子性
    //原子类的Integer
    private volatile static AtomicInteger num =new AtomicInteger();

    //javap -c 反编译查看字节码文件
    public  static void add(){
        //num++;//不是原子性操作
        num.getAndIncrement();//加 1操作 方法,用的底层CAS 效率极高
    }


    public static void main(String[] args) {
        //理论上num 结果应该为2万
        for (int i = 1; i <=20; i++) {
            new Thread(()->{
                for (int i1 = 0; i1 < 1000; i1++) {
                    add();
                }
            }).start();
        }
        while(Thread.activeCount()>2){// main 线程 gc
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+" "+num);
    }
}

输出结果:20000

这些类的底层都直接和操作系统挂钩!在内存中修改至!== Unsafe 类是一个很特殊的存在!==

指令重排
什么是指令重排:你写的程序,计算机并不是按照你写的那样去执行的。
源代码–>编译器优化重排—>指令并行也可能会重排---->内存系统也会重排—>执行
处理器在进行指令重排的时候,考虑:数据之间的依赖性

int x =1;//1
int y =2;//2
x =x +5;//3
y= x*x; //4 
//我们所希望的是:按 1234 顺序执行, 但执行的时候可能按照2134 ,1324 也行
可不可能是 4123

可能造成影响的结果:a,b,x,y 这四个值都默认为0

线程A线程B
x=ay=b
b=1a=2

正常的结果: x=0;y=0,可能由于指令重排后,执行顺序变成这样了:

线程A线程B
b=1a=2
x=ay=b

指令重排导致的异常结果: x=2;y=1;

非计算机专业
效果如下:

只要你加了volatile 可以避免指令重排
内存屏障,CPU 指令

  1. 保证特定的操作顺序!
  2. 可以保证某些变量的内存可见性!(利用这些特性volatile 实现了可见性)
    在这里插入图片描述
    总结: Volatile 是可以保证可见性,不能保证原子性,由于内存屏障,可以保证比片指令重拍哦的现象产生!

读取=拷贝=操作=回写
3. JMM (Java Memory Model 的缩写)

2.他干嘛了?官方,博客,视频
作用:缓存一致性协议,用于定义数据读写的规则(遵守,找到这个规则)。
JMM 定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存
(Local Memory).
在这里插入图片描述

解决共享对象可见性这个问题:volilate 关键字(保证一致性)
在这里插入图片描述
volilate 可以将写完的代码立即刷新到主内存。

3.该如何学习
JMM :抽象的概念,理论
在这里插入图片描述
主线程,多个工作线程,看下JMM ?(百度)

1. JMM 面试题整理

  1. 什么是JMM
    答:JMM 是java 内存模型,它有几个重要的概念,CPU 和缓存一致性!

    场景:我们都知道,计算机在执行程序的时候每条执行都实在CPU 中执行的,而执行的时候,有免不了和数据打交道,而计算机上面的数据,是存放在计算机的物理内存上的。
    

当内存的读取速度和CPU 的执行速度相比差别不大的时候,这样的机制是没有任何问题,可是随着CPU 的技术发展,CPU 的执行速度和内存的读取速度差距越来越大,导致CPU 每次操作内存都要耗费很多等待时间。
为了解决这个问题,初代程序员大佬们想打了一个办法,就是在CPU 的物理内存上新增高速缓存,这样程序的执行过程也发生了改变,变成了程序在运行过程中,会将运算所需要的数据从主内存复制一份到CPU 的高速缓存中,当CPU 进行计算时就可以直接从高速高速缓存中读取数据和写数据了,当运算结束再将数据刷新到主内存就可以了。

多核处理问题
随着时代的变迁,程序员的越发能干,CPU 开始出现了多概念,每个核都有一套自己的缓存,并且随着计算机能力不断提升,还开始支持多线程,最终演变成,多个线程访问进程中的某个共享内存,切这多个线程分别在不通的核心上执行,则每个核心都会在各自的Cache 中保留一份共享内存的缓冲,我们知道多核是可以并行的额,这样就会出现多个线程同时写各自缓存的情况,导致各自的Cache之间的数据可能不同。
总结下来就是:在多核CPU 中,每个核的自己的缓存,关于同一个数据的缓存内容可能不一致

内存交互操作
在这里插入图片描述

请你谈谈你对Volatile 的理解
Volatile 是java内置关键字,是java 虚拟机提供轻量级的同步机制

  1. 保证可见性
  2. 不保证原子性
  3. 禁止指令重排

1.如何保证可见性?
1.线程解锁前,必须把共享变量立刻 刷回主存。
2.线程加锁前,必须读取主存中的最新的值到工作内存中!
3.加锁和解锁是同意把锁

内存屏障! 在单例模式下使用的最多

以上是关于JMM概念的主要内容,如果未能解决你的问题,请参考以下文章

JMM与happens-before

Java内存模型(JMM)中的happens-before

happens-before

Java并发编程之happens-before

JMM底层之happnes-before原则

JUC高级多线程_11:JMM与Volatile的具体介绍与使用