锁膨胀其实就是锁升级的过程,从偏离锁升级为轻量级锁,以及从轻量级锁升级成重量级锁的过程。这里以从轻量级锁升级成重量级锁的过程直接上代码吧。
轻量级锁升级成重量级锁的过程过程。
package com.bo.threadstudy.four;
import lombok.extern.slf4j.Slf4j;
import org.openjdk.jol.info.ClassLayout;
import java.util.concurrent.locks.LockSupport;
/**
* 锁膨胀模拟
*/
@Slf4j
public class LockSwellTest {
private static Object lock = new Object();
private static volatile boolean flag = true;
public static void main(String[] args) throws InterruptedException {
log.debug("初始状态锁状态:");
getStatus();
Thread t1;
Thread t2;
t2 = new Thread(() -> {
LockSupport.park();
synchronized (lock) {
//t2竞争锁的状态
log.debug("t2竞争锁的状态为");
getStatus();
}
}, "t2");
t2.start();
t1 = new Thread(() -> {
synchronized (lock) {
log.debug("刚进入synchronized方法锁状态:");
//刚进入,肯定是轻量级锁
getStatus();
//使用park方法来解决,保证之前的代码在t2的synchronized前解决
LockSupport.unpark(t2);
//开始竞争了
log.debug("t1竞争时锁状态:");
getStatus();
}
}, "t1");
t1.start();
//最终锁状态为
Thread.sleep(2000);
log.debug("最终锁状态为:");
getStatus();
}
private static void getStatus() {
String s = ClassLayout.parseInstance(lock).toPrintable();
log.debug(s);
}
}
结果如下:
18:08:44.755 [main] DEBUG com.bo.threadstudy.four.LockSwellTest - 初始状态锁状态:
18:08:49.985 [main] DEBUG com.bo.threadstudy.four.LockSwellTest - java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
18:08:49.985 [t1] DEBUG com.bo.threadstudy.four.LockSwellTest - 刚进入synchronized方法锁状态:
18:08:49.985 [t1] DEBUG com.bo.threadstudy.four.LockSwellTest - java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) e0 f1 2f bb (11100000 11110001 00101111 10111011) (-1154485792)
4 4 (object header) 16 00 00 00 (00010110 00000000 00000000 00000000) (22)
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
18:08:49.985 [t1] DEBUG com.bo.threadstudy.four.LockSwellTest - t1竞争时锁状态:
18:08:49.985 [t1] DEBUG com.bo.threadstudy.four.LockSwellTest - java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 1a 61 d1 fe (00011010 01100001 11010001 11111110) (-19832550)
4 4 (object header) c1 01 00 00 (11000001 00000001 00000000 00000000) (449)
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
18:08:49.985 [t2] DEBUG com.bo.threadstudy.four.LockSwellTest - t2竞争锁的状态为
18:08:49.985 [t2] DEBUG com.bo.threadstudy.four.LockSwellTest - java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 1a 61 d1 fe (00011010 01100001 11010001 11111110) (-19832550)
4 4 (object header) c1 01 00 00 (11000001 00000001 00000000 00000000) (449)
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
18:08:52.005 [main] DEBUG com.bo.threadstudy.four.LockSwellTest - 最终锁状态为:
18:08:52.005 [main] DEBUG com.bo.threadstudy.four.LockSwellTest - java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
Disconnected from the target VM, address: '127.0.0.1:62238', transport: 'socket'
Process finished with exit code 0
看OFFSET SIZE TYPE DESCRIPTION VALUE这行下面第一行的括号内部内容的前八位,可以看到,初始状态锁状态为001(无锁),刚进入synchronized时为000(轻量级锁),在使用Locksupport.unpark()方法来解除暂停时,发生竞争现象。此时锁状态是010(重量级锁),最终从synchronized中出来后是001(无锁)。
锁膨胀的流程
1.当t1使用object来施加轻量级锁时,t0已经对Object对象施加了轻量级锁了并正在执行Synchronized中的代码。
2.此时t1试图采用CAS以Lock Record锁记录来替换Object中mark word中的值,发现内部的值已经是t0的锁记录地址以及锁状态00(轻量级锁),不能替换。进入锁膨胀流程:
为Object对象申请monitor锁,然后将monitor对象地址存放至object中的mark word中。
然后自身进入monitor对象中的entryList中。
3.在t0执行完成后,使用CAS替换掉mark word中的锁记录信息以及锁状态,发生错误。此时会采用重量级锁的方式,从Object中的mark word查找到monitor对象的地址,设置owner为null,并唤醒entryList中的Blocked线程,来获取到当前锁。
这里说了Blocked线程,那么waited线程是否会进入锁膨胀呢?写一下吧。
//测试一下wait线程锁的状态
@Slf4j
class WaitLockTest{
private static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
//没有其它线程竞争的情况下,有其它线程竞争肯定是重量级,不用看
synchronized (lock){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
Thread.sleep(10000);
getStatus();
}
private static void getStatus() {
String s = ClassLayout.parseInstance(lock).toPrintable();
log.debug(s);
}
}
单独一个线程,没有发生竞争的情况下,是重量级锁。
不过也正常,阻塞状态,用到了monitor中的waitSet,肯定是重量级了。所以,三种阻塞状态的数据,必定是重量级锁。