JMM
java内存模型,这不是jvm
有cas的思想,每个线程搞一手副本,自己在副本修改,联系就是主存。
并发编程三个特性:
-
可见性
-
原子性
-
顺序性
先简单了解:
可见性:
A改了共享变量, B知道A改了,并把自己工作内存更新了(Volatile),或是A整个流程锁起来,B才能开始执行(lock),这就叫可见。就是可见别人的修改。
原子性:
也就是一组操作要末全做,要末不做,就是事务的原子性,就是线程的业务代码的原子性。(num++是一组操作,临时变量,操作栈,下篇博客了解)。
顺序性:指令重排
什么是 指令重排:你写的程序,计算机并不是按照你写的那样去执行的。
源代码–>编译器优化的重排–> 指令并行也可能会重排–> 内存系统也会重排—> 执行
处理器在进行指令重排的时候,考虑:数据之间的依赖性!也就是单线程没问题,但是多线程之间可能会互相依赖,不可重排。
下面是内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类 型的变量来说,load、store、read和write操作在某些平台上允许例外)
概述:
-
1.就是主内存read到临时变量,临时变量load进线程的工作去内存
-
2。线程就会use工作内存的数据(线程里的操作保证原子性),处理后align回工作内存
-
3.工作内存store到临时变量,临时变量write回主内存
Volatile 保证了可见性和顺序性,不保证原子性
可见性
问题:
主存:num=0
A改了共享变量num++;1
B不知道,num++;1
B不知道A改了,因为主存改了,B不知道,还是操作自己的工作区内存,这就叫不可见。
解决:
-
1。在最开始时jmm为了实现并发安全,就lock整个流程,注意lock是共享标量被多线程操作时,很浪费,就像同步了。
-
2.1 Volatile就不一样,他先是通过cpu总线嗅探来监视有没有人修改共享变量,有,就把工作取数据失效重新获取,
-
2.2 这其实还不i行啊,在传到总线时,B就会嗅探到,但还没从总线写回主内存,B就失效获取的主内存还是原来的,所以Volatile也加了锁,就是锁总线到写入主内存,不许B访问
-
这区别就是Volatile的锁的粒度小多了
顺序性
就是java优化的啊
源代码–>编译器优化的重排–> 指令并行也可能会重排–> 内存系统也会重排—> 执行
在单线程没啥,但是多线程就不行了,会有出乎意料的,貌似违背语句从上向下执行的原则,其实是指令重排,
Volatile有内存屏障。作用:
1、保证特定的操作的执行顺序!就是有依赖的操作有序
2、可以保证某些变量的内存可见性 (利用这些特性volatile实现了可见性)
原子性
为啥内原子性:
-
A在修改的时候,就是正在修改,还没asgin回工作内存
-
B已经改了,并且回写到主内存,
-
A刚好运行完**,写回工作内存**,突然嗅探到改变,工作内存失效,重新加载主内存,A做的修改被覆盖了。
现在看来底层的问题解决了,线程的实际业务操作还没控制,我们可以操作一手原子类 AtomicInteger 啥的,就是保证原子类对象是原子操作.
那比如
private volatile static AtomicInteger num = new AtomicInteger();
num.getAndIncrement();
getAndIncrement();这是原子类的方法,不是sigalnized锁,底层用的unseafe类,操作的this,offet偏移量,内存地址式的修改,比较高效,还用到了自旋锁。还用到了CAS。