并发机制底层实现原理
volatile
使用volatile
修饰的变量,在编译的时候会加上LOCK前缀加锁指令,LOCK前缀会使处理器执行当前指令时产生一个LOCK#信号,这个总是会引起显式总线的锁定。
1)LOCK前缀指令会引起处理器缓存回写到内存
2)一个处理器的缓存回写到内存会导致其他处理器的缓存无效volatile
使用优化:缓存行填充。缓存行不支持部分填充并且也不能部分提取,如果不同的数据读取到同一个缓存行,则会增加缓存行被锁定的概率。比如Disruptor框架中SingleProducerSequencer.Padding
,其源码如下:public long nextValue; public long cachedValue; public long p2; public long p3; public long p4; public long p5; public long p6; public long p7; private Padding() { this.nextValue = -1L; this.cachedValue = -1L; }
SingleProducerSequencer.Padding
使用了额外的6个long型变量,SingleProducerSequencer.Padding
共占64个字节,对于64字节的缓存行就刚好铺满一个缓存行。对于其他不是64字节的缓存行,SingleProducerSequencer.Padding
也不能处理伪共享的问题。synchronized
synchronized
通过加锁方式来实现同步,有三种形式:- 普通的同步方法,锁是当前对象
- 静态同步方法,锁是当前类的Class的对象
- 同步方法块,锁是
synchronized
括号里的对象
synchronized
用的锁在java对象的头里。有两种情况:数组对象,虚拟机使用3个字宽存储对象头。非数组对象,则使用2个字宽来存储对象头。32位虚拟机中,1个字宽等于4字节,即32字节。
长度 | 内容 | 说明 |
---|---|---|
32/64bit | mark word | 存储对象的hashCode或者锁信息 |
32/64bit | Class metadata address | 存储对象描述数据的指针 |
32/64bit | mark word | 存储对象的hashCode或者锁信息 |
其中mark word具体存储格式如下:
mark word | 状态 |
---|---|
对象的hashCode:25bit | 对象分代年龄:4bit | 偏向锁标识:1bit | 锁标志位:2bit(01) | 无锁状态 |
线程ID:23bit | 标识偏向锁是否有效:2bit | 对象分代年龄:4bit | 偏向锁标识:1bit | 锁标志位:2bit(01) | 偏向锁 |
指向栈中锁记录的指针:30bit | 锁标志位:2bit(00) | 轻量级锁 |
指向互斥量的指针:30 | 锁标志位:2bit(10) | 重量级锁 |
空 | 锁标志位:2bit(11) | GC标记 |
锁的级别从低到高依次为:无锁状态–>偏向锁–>轻量级锁–>重量级锁。锁可以随着竞争情况进行升级,但不能降级。
64位的对象头如下:
mark word | 状态 |
---|---|
空:25 | 对象的hashCode:31bit | 空:1 | 对象分代年龄:4bit | 偏向锁标识:1bit | 锁标志位:2bit(01) | 无锁状态 |
线程ID:54bit | 标识偏向锁是否有效:2bit | 对象分代年龄:4bit | 偏向锁标识:1bit | 锁标志位:2bit(01) | 偏向锁 |
指向栈中锁记录的指针:62bit | 锁标志位:2bit(00) | 轻量级锁 |
指向互斥量的指针:62 | 锁标志位:2bit(10) | 重量级锁 |
空 | 锁标志位:2bit(11) | GC标记 |
在64位中,OOP(Ordinary Object Pointers)为64位,在最大堆大小在4G到32G之间,可以采用指针压缩技术来提高性能。从JDK 1.6 update14开始,可以使用:-XX:+UseCompressedOops
开启指针压缩,java7 之后默认开启,如果最大堆大小符合条件。指针压缩会将OOP压缩为32位,32位的指针怎么映射到大于4G内存上呢?压缩指针会在右侧补上3位0,也即会乘以8,然后得到操作系统的指针地址.
那么只有4G的地址怎么使用到32G的堆内存呢?在JVM中,32位寻址地址代表的是4G个对象的指针,不是4G字节。而在32位JVM中,一个对象的对象头至少有8字节,在64位JVM中至少有16+8(压缩指针)=24字节。所以不论怎样,4G个指针所指向的对象都能达到4G*8=32G字节。