概述
Java的锁可以分为乐观锁和悲观锁两类。而synchronized是悲观锁的代表。本文想要说明一下两个问题:
synchronized锁的是哪个对象
public void method(){
synchronized(this){
...
}
}
public synchronized void method(){
...
}
上述两种方式,synchronized锁的其实是this对象。
public static void method(){
synchronized(this){
...
}
}
public synchronized static void method(){
...
}
上述两种方式锁的其实是类对象。类对象是啥呢?通过类名.class获得到的对象或者通过Class.forName()得到的对象就是类对象。
怎么判断锁对象被某个线程占用呢
要了解锁对象被哪个线程所占用,就先要了解对象的内存结构,有三个部分,分别对象头,实例数据和对齐填充。
注:图片来源于: https://www.bilibili.com/video/BV1PJ411n7xZ?p=105&vd_source=3b276afc1d517ec28a00ae341f72050c
重量级锁
synchronized中存在锁升级优化,存在线程竞争锁对象时,synchronized会经过偏向锁-轻量级锁-重量级锁的优化过程。本文首先重量级锁为例,说明如何判断锁对象被哪个线程实例所占有。
正常对象的对象头中存在如下图所示,如果是普通对象,对象头中含有Mard Word和指向方法区中存储的对应的类信息。
如果是数组对象则多了一个数组长度信息。
注:图片来源于: https://www.bilibili.com/video/BV16J411h7Rd?p=75&vd_source=3b276afc1d517ec28a00ae341f72050c
正常情况下,锁对象中包含有对象的hash码,age表示在分代年龄,超过15,就会从新生代晋升到老年代。biased_lock就是判断synchronized是否为偏向锁,01表示锁的状态,即是偏向锁,轻量级锁还是重量级锁。
synchronized在重量级锁的情况下,会关联一个Monitor对象,这个对象中包含等待队列,阻塞队列和Owner三个信息。没有拿到锁的对象就在等待队列中。拿到锁,但是调用了wait方法后,对应线程就会进入阻塞队列,等待其他线程调用notify方法唤醒。
如果某个线程获取到该锁对象,会将锁对象中的Mark Word的信息替换为Monitor的地址,用30个bit表示,锁中表示锁的状态从01变为了10,10即表示该锁为重量级锁。
同样的道理,线程在获取轻量级锁和偏向锁时,都会对锁对象的对象头中的Mard Word进行修改,但是原理略有不同。
轻量级锁
轻量级锁不在关联Monitor的对象,而是在线程内部创建了一个锁记录,如下图Lock Record所示,对象头中Mark Word中的会与锁记录的地址信息进行替换,这个过程是一个原子操作。如果线程释放锁,则会将替换的信息重新交换回来。
轻量级锁中,锁对象中Mark Word中的信息经过交换后的结果如下图所示,其中30bit表示线程中的锁记录地址,而后两位00表示锁的状态,即轻量级锁,如果状态码为10则表示重量级锁。
偏向锁
偏向锁的实现更加暴力,直接就将Mard Word中的信息修改为Thread的id。如下图所示,Mard Word前23bit用来记录线程的id信息,可以看到锁的状态为01和锁对象初始状态相同,但是可以看到biased_lock从0变为了1,表示为偏向锁。
三种锁的存在时机
如果仅有一个线程,synchronized为偏向锁;如果有多个线程,但是在不同时段使用,synchronized为轻量级锁;如果在同一时刻,有多个线程都想获取锁,synchronized为重量级锁。
标签:Word,synchronized,对象,线程,Java,重量级,轻量级 From: https://blog.csdn.net/qq_43552690/article/details/136716216