理解synchronized的实现
想理解这个东西,我们要做一些知识预备,先明确下面这些问题
CAS问题
全称compare and swap
解决的问题:在无锁的状态下,保证线程一致性(线程安全)的去改共享数据。
1、获取当前共享数据的值,放到E
2、对共享数据进行计算结果,放到V
3、重新获取当前共享数据的值N,与刚才的E比较;相等则将共享数据更新为V,否则继续步骤1
ABA问题:过程中存在ABA问题,什么是ABA问题呢?就是其他线程修改数次最后值和原值E相同,也就是原来是A,中间被别人修改了数次,可能是B,但是当你重新读取当前最新值N的时候,发现N=A,没变,这就是ABA问题。
如何解决ABA问题?加版本号,或者加布尔值解决。
cas在java最底层、最终的实现:
lock cmpxchg 指令
对象在内存中的存储布局
要想更直观的了解对象在内存中的存储布局,我们引入一个工具类,我们可以利用这个工具,验证我们对象在内存中存储布局的结论。
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
我们要看下new Object()对象在内存中的存储布局
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
运行结果:
markword:8字节。关于锁定信息记录在markword里面
classpointer:开启压缩的情况下是4个字节;不开启压缩8个字节。java默认压缩
padding(对齐):当整体的字节数不能被8整除的话,要凑够被8整除的数,提高总线读取速度。
我们在命令行敲这个命令,结果如图
我们可以看到ClassPinter使用压缩,就是4个字节;oops即普通对象指针,也是使用压缩,就是4个字节。
我们回过头来看刚开始的object对象
面试题: Object o = new Object()在内存中占了多少个字节?(顺丰)
答:16个字节。
我们再来分析一下下面这个User user = new User();User对象的内存布局:
class User{
private int id;
private String name;
}
分析:
markword:8字节
classpointer:默认开启压缩的情况下4字节
int:4字节
string:默认开启普通对象指针压缩的情况下4字节
对齐:4字节
一共24字节
我们借用工具看一下这个对象的内存存储布局:
public class T01 {
public static void main(String[] args) {
// Object o = new Object();
// System.out.println(ClassLayout.parseInstance(o).toPrintable());
User user = new User();
System.out.println(ClassLayout.parseInstance(user).toPrintable());
}
}
class User{
private int id;
private String name;
}
运行结果:与我们的分析一致
下面聊一聊markword:markword的64位详细记录了锁的信息,
锁升级过程
new —> 偏向锁 —> 轻量级锁(无锁、自旋锁、自适应锁)—> 重量级锁