虽然cmpxchg指令不是原子的,但是加了lock指令后,则cmpxhg被上锁,不允许被打断。 在单核CPU中,无须加lock,在多核CPU中,必须加lock,可以参考stackoverflow上的这个回答:
使用CAS好处
jdk早期是重量级别锁 ,通过0x80中断 进行用户态和内核态转换,所以效率比较低,有了CAS操作,大大提升了效率。
对象的内存布局(Hotspot实现) 使用jol查看一个对象的内存布局我们可以通过jol包来查看一下某个对象的内存布局
引入jol依赖
<dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.15</version> </dependency>示例代码(ObjectModel.java)
public class ObjectModel { public static void main(String[] args) { T o = new T(); String s = ClassLayout.parseInstance(o).toPrintable(); System.out.println(s); } } class T{ }配置VM参数,开启指针压缩
-XX:+UseCompressedClassPointers运行结果如下:
OFF SZ TYPE DESCRIPTION VALUE 0 8 (object header: mark) 0x0000000000000005 (biasable; age: 0) 8 4 (object header: class) 0x00067248 12 4 (object alignment gap) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total其中8个字节的markword
4个字节的类型指针,可以找到T.class
这里一共是12个字节, 由于字节数务必是8的整数倍,所以补上4个字节,共16个字节
我们修改一下T这个类
class T{ public int a = 3; public long b = 3l; }再次执行,可以看到结果是
OFF SZ TYPE DESCRIPTION VALUE 0 8 (object header: mark) 0x0000000000000005 (biasable; age: 0) 8 4 (object header: class) 0x00067248 12 4 int T.a 3 16 8 long T.b 3 Instance size: 24 bytes Space losses: 0 bytes internal + 0 bytes external = 0 bytes total其中多了4位表示int这个成员变量,多了8位表示long这个成员变量, 相加等于24,正好是8的整数倍,不需要补齐。
内存布局详细说明使用synchronized就是修改了对象的markword信息,markword中还记录了GC信息,Hashcode信息
锁升级过程 偏向锁synchronized代码段多数时间是一个线程在运行,谁先来,这个就偏向谁,用当前线程标记一下。
轻量级锁(自旋锁,无锁)偏向锁撤销,然后竞争,每个线程在自己线程栈中存一个LR(lock record)锁记录
偏向锁和轻量级锁都是用户空间完成的,重量级锁需要向操作系统申请。
两个线程争抢的方式将lock record的指针,指针指向哪个线程的LR,哪个线程就拿到锁,另外的线程用CAS的方式继续竞争
JVM的ObjectMonitor去操作系统申请。
如果发生异常,synchronized会自动释放锁
interpreteRuntime.cpp --> monitorenter
锁重入synchronized是可重入锁
可重入次数必须记录,因为解锁需要对应可重入次数的记录
偏向锁:记录在线程栈中,每重入一次,LR+1,备份原来的markword
轻量级锁:类似偏向锁
重量级锁:记录在ObjectMonitor的一个字段中
自旋锁什么时候升级为重量级锁?
有线程超过十次自旋
-XX:PreBlockSpin(jdk1.6之前)
自旋的线程超过CPU核数一半
jdk1.6 以后,JVM自己控制
为什么有偏向锁启动和偏向锁未启动?未启动:普通对象001
已启动:匿名偏向101
因为自旋会占用CPU时间,消耗CPU资源,如果自旋的线程多,CPU资源会被消耗,所以会升级成重量级锁(队列)例如:ObjectMonitor里面的WaitSet,重量级锁会把线程都丢到WaitSet中冻结, 不需要消耗CPU资源
偏向锁是否一定比自旋锁效率高?