Java多线程原理
1、volatile关键字:
1. volatile关键字的实现原理:
1. Lock前缀指令会引起处理器缓存回写到内存,当对volatile变量进行写操作时,JVM会向处理器发送一条Lock前缀的指令,将这个缓存中的变量回写到系统主内存中.
2. 这个回写内存的操作会使其他CPU里缓存了该内存地址的数据无效.处理器使用嗅探技术保证内部缓存系统内存和其他处理器的缓存的数据在总线上保持一致.
2、synchronized关键字:
Java中的每一个对象都可以作为锁.
1. 对于普通同步方法,锁时当前实例对象.
2. 对于静态同步方法,锁时当前类的class对象.
3. 对于同步方法块,锁时synchronized括号里配置的对象.
synchronized在jvm里的实现原理,jvm基于进入和退出Monitor对象来实现方法同步和代码块同步.
代码块同步:基于使用monitor enter和monitor exit指令
Java对象头:
synchronized用的锁是存在Java对象头里的,对象头包含Mark word、class metadata address、array length
其中Mark word包含存储对象的hashcode、分代年龄、锁标识位.
32位虚拟机下,Mark word的存储结构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E1FK58ls-1684814831568)(media/16482228109235/16847424247558.jpg)]
32位虚拟机下,Mark word的状态变化:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kDAJ3DEo-1684814831614)(media/16482228109235/16847422003753.jpg)]
64位虚拟机下,Mark word的存储结构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u6Spuswf-1684814831617)(media/16482228109235/16847422470630.jpg)]
锁升级的过程:
1.6为了减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”.
锁的状态一共就有了4种,级别从低到高依次为:无锁、偏向锁、轻量级锁、重量级锁.
这几个状态会随着竞争情况逐渐升级,锁可以升级但不能降级.
偏向锁:
获取过程:尝试获取,测试对象头中的Mark word中是否存储当前的线程ID,如果测试失败,就查看偏向锁标识位是否为1,没有设置,就用CAS竞争,设置了,则使用CAS将对象头的偏向锁指向当前线程.
Java是如何实现原子操作的
什么是原子操作?-----原子指不可再分割的最小单位,原子操作指不可中断的一个或一系列操作
通过锁和循环CAS的方式来实现原子操作
1、使用CAS实现原子操作
- 使用循环CAS实现原子操作,原理是使用了处理器的CMPXCHG指令实现的.基本思路就是循环进行CAS操作,直到成功为止.
- CAS实现原子操作的三大问题
- ABA问题.因为CAS需要在操作值时,检查值有没有发生变化,如果没有发生变化,则更新,但A->B->A这种情况,对于CAS而言没有发生变化,实际上是发生变化的.解决思路如下:
- 使用版本号.在变量前追加版本号,每次变量更新的时候把版本加1,就变成了1A->2B>3A.
- 使用JDK里提供的Atomic包里提供的AtomicStampedReference来解决ABA问题.这个类的CAS方法的作用是首先检查当前引用是否等于预期引用,然后检查当前标志是否等于预期标志.全部相等,则更新引用和标志
- 循环时间长开销过大.自旋CAS如果长时间不成功,会给CPU带来巨大的执行开销.
- 只能保证一个共享变量的原子操作.对多个共享变量操作时,循环CAS无法保证原子操作,解决办法:
- 加锁
- 将多个变量合并成一个对象,并使用Atomic Reference类来保证引用对象之间的原子性,再进行CAS操作.
- ABA问题.因为CAS需要在操作值时,检查值有没有发生变化,如果没有发生变化,则更新,但A->B->A这种情况,对于CAS而言没有发生变化,实际上是发生变化的.解决思路如下:
2、使用锁来实现原子操作
锁机制,能保证只有获得锁的线程才能操作锁定的内存区域.