JMM概念
Posted 偶像java练习生
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JMM概念相关的知识,希望对你有一定的参考价值。
JMM
java 内存模型
- 什么是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=a | y=b |
b=1 | a=2 |
正常的结果: x=0;y=0,可能由于指令重排后,执行顺序变成这样了:
线程A | 线程B |
---|---|
b=1 | a=2 |
x=a | y=b |
指令重排导致的异常结果: x=2;y=1;
非计算机专业
效果如下:
只要你加了volatile 可以避免指令重排
内存屏障,CPU 指令
- 保证特定的操作顺序!
- 可以保证某些变量的内存可见性!(利用这些特性volatile 实现了可见性)
总结: Volatile 是可以保证可见性,不能保证原子性,由于内存屏障,可以保证比片指令重拍哦的现象产生!
读取=拷贝=操作=回写
3. JMM (Java Memory Model 的缩写)
2.他干嘛了?官方,博客,视频
作用:缓存一致性协议,用于定义数据读写的规则(遵守,找到这个规则)。
JMM 定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存
(Local Memory).
解决共享对象可见性这个问题:volilate 关键字(保证一致性)
volilate 可以将写完的代码立即刷新到主内存。
3.该如何学习
JMM :抽象的概念,理论
主线程,多个工作线程,看下JMM ?(百度)
1. JMM 面试题整理
-
什么是JMM
答:JMM 是java 内存模型,它有几个重要的概念,CPU 和缓存一致性!场景:我们都知道,计算机在执行程序的时候每条执行都实在CPU 中执行的,而执行的时候,有免不了和数据打交道,而计算机上面的数据,是存放在计算机的物理内存上的。
当内存的读取速度和CPU 的执行速度相比差别不大的时候,这样的机制是没有任何问题,可是随着CPU 的技术发展,CPU 的执行速度和内存的读取速度差距越来越大,导致CPU 每次操作内存都要耗费很多等待时间。
为了解决这个问题,初代程序员大佬们想打了一个办法,就是在CPU 的物理内存上新增高速缓存,这样程序的执行过程也发生了改变,变成了程序在运行过程中,会将运算所需要的数据从主内存复制一份到CPU 的高速缓存中,当CPU 进行计算时就可以直接从高速高速缓存中读取数据和写数据了,当运算结束再将数据刷新到主内存就可以了。
多核处理问题
随着时代的变迁,程序员的越发能干,CPU 开始出现了多概念,每个核都有一套自己的缓存,并且随着计算机能力不断提升,还开始支持多线程,最终演变成,多个线程访问进程中的某个共享内存,切这多个线程分别在不通的核心上执行,则每个核心都会在各自的Cache 中保留一份共享内存的缓冲,我们知道多核是可以并行的额,这样就会出现多个线程同时写各自缓存的情况,导致各自的Cache之间的数据可能不同。
总结下来就是:在多核CPU 中,每个核的自己的缓存,关于同一个数据的缓存内容可能不一致
内存交互操作
请你谈谈你对Volatile 的理解
Volatile 是java内置关键字,是java 虚拟机提供轻量级的同步机制
- 保证可见性
- 不保证原子性
- 禁止指令重排
1.如何保证可见性?
1.线程解锁前,必须把共享变量立刻 刷回主存。
2.线程加锁前,必须读取主存中的最新的值到工作内存中!
3.加锁和解锁是同意把锁
内存屏障! 在单例模式下使用的最多
以上是关于JMM概念的主要内容,如果未能解决你的问题,请参考以下文章