Java多线程学习笔记 (4)

虽然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的整数倍,不需要补齐。

内存布局详细说明

object_model_of_hotspot

使用synchronized就是修改了对象的markword信息,markword中还记录了GC信息,Hashcode信息

锁升级过程

image.png

偏向锁

synchronized代码段多数时间是一个线程在运行,谁先来,这个就偏向谁,用当前线程标记一下。

轻量级锁(自旋锁,无锁)

偏向锁撤销,然后竞争,每个线程在自己线程栈中存一个LR(lock record)锁记录

偏向锁和轻量级锁都是用户空间完成的,重量级锁需要向操作系统申请。
两个线程争抢的方式将lock record的指针,指针指向哪个线程的LR,哪个线程就拿到锁,另外的线程用CAS的方式继续竞争

重量级锁

JVM的ObjectMonitor去操作系统申请。

如果发生异常,synchronized会自动释放锁

interpreteRuntime.cpp --> monitorenter

image.png

锁重入

synchronized是可重入锁
可重入次数必须记录,因为解锁需要对应可重入次数的记录
偏向锁:记录在线程栈中,每重入一次,LR+1,备份原来的markword
轻量级锁:类似偏向锁
重量级锁:记录在ObjectMonitor的一个字段中

自旋锁什么时候升级为重量级锁?

有线程超过十次自旋

-XX:PreBlockSpin(jdk1.6之前)

自旋的线程超过CPU核数一半

jdk1.6 以后,JVM自己控制

为什么有偏向锁启动和偏向锁未启动?

未启动:普通对象001
已启动:匿名偏向101

为什么有自旋锁还需要重量级锁?

因为自旋会占用CPU时间,消耗CPU资源,如果自旋的线程多,CPU资源会被消耗,所以会升级成重量级锁(队列)例如:ObjectMonitor里面的WaitSet,重量级锁会把线程都丢到WaitSet中冻结, 不需要消耗CPU资源

偏向锁是否一定比自旋锁效率高?

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppdzy.html