一探究竟:反编译
三种使用场景,示例代码;
public class SynchronizePrincipe {
public synchronized static void fun1(){}
public synchronized void fun2(){}
public void fun3() {
synchronized (this){}
}
}
通过javap -c -v
命令反编译后(部分代码);
public static synchronized void fun1();
descriptor: ()V
// 看这里
flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
public synchronized void fun2();
descriptor: ()V
// 看这里
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
public void fun3();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: aload_0
1: dup
2: astore_1
// 看这里
3: monitorenter
4: aload_1
// 看这里
5: monitorexit
6: goto 14
9: astore_2
10: aload_1
// 看这里
11: monitorexit
- 修饰方法:
ACC_SYNCHRONIZED
标识,会隐式调用到下面两个方法; - 修饰代码块:一个
monitorenter
,两个monitorexit
,避免异常未释放锁;
总结:sychronized
本质就是对monitor
的争夺,在持有重量级锁的对象头中,存储了monitor
的指针。
基础知识:对象结构
Java对象由三个部分组成;
- 对象头:对象年龄,锁标志,Class类型指针和数组长度(可选);
- 实例数据:真正存储的有效数据,例如当前的成员变量,包括父类;
- 对齐填充:HotSpot VM规定对象起始地址为8字节的整数倍,不够则需要填充;
Mark Word
重中之重:对象锁
JDK 1.6 增加的锁优化流程,之前只有重量级锁。
锁类型
- non-biasable 无锁且不可偏向
- biasable 无锁可偏向
- biased 偏向锁
- thin lock 轻量级锁
- fat lock 重量级锁
rebias & revoke
- bulk rebias(批量重偏向):如果已经偏向t1线程的对象,在t2线程申请锁时撤销偏向后升级为轻量级锁的对象数量达到一定值(20),后续的申请会批量重偏向到t2线程;
- bulk revoke(批量撤销):在单位时间(25s)内某种Class的对象撤销偏向的次数达到一定值(40),JVM认定该Class竞争激烈,撤销所有关联对象的偏向锁,且新实例也是不可偏向的;
对象初始化
申请加锁
证据第一:动手试试
JVM参数
-XX:+UseBiasedLocking 启用偏向锁,默认启用
-XX:+PrintFlagsFinal 打印JVM所有参数
-XX:BiasedLockingStartupDelay=4000 偏向锁启用延迟时间,默认4秒
-XX:BiasedLockingBulkRebiasThreshold=20 批量重偏向阈值,默认20
-XX:BiasedLockingBulkRevokeThreshold=40 批量撤销阈值,默认40
-XX:BiasedLockingDecayTime=25000
添加依赖
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.16</version>
</dependency>
统一打印对象头方法;
public static void print(Object flag, Object object) {
String str = ClassLayout.parseInstance(object).toPrintable();
System.out.println(flag + ".......................................");
System.out.println(str);
}
锁升级
public static void main(String[] args) {
Object init = new Object();
// non-biasable 不可偏向
print("init before", init);
synchronized (init) {
// thin lock 轻量级锁
print("init sync", init);
}
sleep(4000);
Object object = new Object();
// biasable 可偏向模式,默认4S后开启偏向模式
print("object before", object);
synchronized (object) {
// biased 持有偏向锁
print("object sync", object);
}
// biased 偏向锁不会被释放
print("object after", object);
Thread t1 = new Thread(() -> {
synchronized (object) {
// thin lock 持有轻量级锁,不同线程请求
print("[t1]object sync", object);
}
// non-biasable 不可偏向模式
print("[t1]object after", object);
});
t1.start();
sleep(100);
Thread t2 = new Thread(() -> {
synchronized (object) {
// thin lock 持有轻量级锁,不同线程请求
print("[t2]object sync", object);
sleep(1000);
// fat lock 重量级锁,下面加锁失败导致
print("[t2]object sync sleep", object);
}
});
t2.start();
sleep(500);
synchronized (object) {
// fat lock 重量级锁
print("[main]object sync", object);
}
// fat lock 重量级锁不会撤销,线程结束变为不可偏向模式
print("[main]object after", object);
}
批量重偏向&批量撤销
public static void main(String[] args) throws InterruptedException {
print("oldPrincipe", new SynchronizedPrincipe());
int loop = 39;
List<SynchronizedPrincipe> list = new ArrayList<>();
for (int i = 0; i < loop; i++) {
SynchronizedPrincipe principe = new SynchronizedPrincipe();
list.add(principe);
}
for (int thread = 1; thread <= 3; thread++) {
Thread t1 = new Thread(() -> {
for (int i = 0; i < list.size(); i++) {
print(i + 1, list.get(i));
synchronized (list.get(i)) {
// 19: thin lock
// 20: biased (Rebias)
print(i + 1, list.get(i));
}
print(i + 1, list.get(i));
}
});
t1.start();
sleep(1000);
}
SynchronizedPrincipe newPrincipe = new SynchronizedPrincipe();
// non-biasable (Revoke)
print("newPrincipe", newPrincipe);
}