锁状态
概述
- 无锁
没有synchronized关键字修饰 - 偏向锁
JVM开启偏向锁时只有单个线程调用同步代码(不存在竞争条件)且无法重新偏向(单个锁情况,不考虑批量重偏向^[t1线程实例化同一个类多个对象并加锁,t2线程也对其加锁 由于锁升级会进行多次锁撤销操作,JVM会认为接下来的对象需要批量重偏向而不是进行锁升级为轻量级] - 轻量级锁
多个线程调用同步代码,但是线程之间交替执行例如调用join().-自旋锁也是产生轻量级锁的一个方式 - 重量级锁
多个线程调用同步代码,且存在锁竞争
验证
对象头介绍
锁状态的标记就在对象头当中。网上32位操作系统对象头的文档很多,下面使用的是64位操作系统介绍
对象头的组成-mark Word
这部分主要用来存储对象自身的运行时数据,如hashcode、gc分代年龄等,32位操作系统和64位操作系统的位长度是不一样的,具体看图:
32位操作系统
|------------------------------------------------|-------------------|
| Mark Word (32 bits) | State |
|------------------------------------------------|-------------------|
| identity_hashcode:25|age:4|biased_lock:1|lock:2| Normal |
|------------------------------------------------|-------------------|
| thread:23 | epoch:2|age:4|biased_lock:1 |lock:2| Biased |
|------------------------------------------------|-------------------|
| ptr_to_lock_record:30 |lock:2|Lightweight Locked |
|------------------------------------------------|-------------------|
| ptr_to_heavyweight_monitor:30 |lock:2| Heavyweight Locked|
|------------------------------------------------|-------------------|
| |lock:2| Marked for GC |
|------------------------------------------------|-------------------|
64位操作系统
|-------------------------------------------------------------------|-------------------|
| Mark Word (64 bits) | State |
|-------------------------------------------------------------------|-------------------|
| unused:25|identity_hashcode:31|unused:1|age:4|biased_lock:1|lock:2| Normal |
|------------------------------------------------------------|------|-------------------|
| thread:54| epoch:2 |unused:1|age:4|biased_lock:1|lock:2| Biased |
|------------------------------------------------------------|------|-------------------|
| ptr_to_lock_record:62 |lock:2|Lightweight Locked |
|----------------------------------------------------------- |------|-------------------|
| ptr_to_heavyweight_monitor:62 |lock:2| Heavyweight Locked|
|------------------------------------------------------------|------|-------------------|
| |lock:2| Marked for GC |
|---------------------------------------------------------------------------------------|
JOL工具
JOL全称为Java Object Layout,是分析JVM中对象布局的工具,该工具大量使用了Unsafe、JVMTI来解码布局情况,分析结果是比较精准。使用时直接引入maven依赖即可
<!--java状态分析工具-->
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
偏向锁代码
- JVM偏向锁延迟: JVM启动有大量的同步操作,偏向锁的锁消除性能很差所以JVM启动时会延迟偏向锁的启用。
- 对象进行hashcode以后 就不存在偏向锁 这和对象头的设计有关
- 偏向锁开启后新建对象 会有一个可偏向状态 但是指向的线程号为空 体现为二机制 0
/**
* 由于在jvm启动时会延迟开启偏向锁 所以使用Thread.sleep(5000)
* 设置JVM启动参数 -XX:BiasedLockingStartupDelay=0 延迟偏向锁时间0
*/
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
//睡眠5秒 使得偏向锁开启 也可以设置启动参数
//-XX:BiasedLockingStartupDelay=0 延迟偏向锁时间0
Thread.sleep(5000);
A a = new A();
System.out.println("锁之前");
System.out.println(ClassLayout.parseInstance(a).toPrintable());
Thread thread = new Thread(){
@Override
public void run() {
synchronized (a){
System.out.println("锁状态");
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
};
thread.start();
System.out.println("锁之后");
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
结果
锁之前
com.example.concurrent.sync.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) a0 c1 00 f8 (10100000 11000001 00000000 11111000) (-134168160)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
锁之后
com.example.concurrent.sync.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 d0 11 1b (00000101 11010000 00010001 00011011) (454152197)
4 4 (object header) e6 7f 00 00 (11100110 01111111 00000000 00000000) (32742)
8 4 (object header) a0 c1 00 f8 (10100000 11000001 00000000 11111000) (-134168160)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
com.example.concurrent.sync.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 d0 11 1b (00000101 11010000 00010001 00011011) (454152197)
4 4 (object header) e6 7f 00 00 (11100110 01111111 00000000 00000000) (32742)
8 4 (object header) a0 c1 00 f8 (10100000 11000001 00000000 11111000) (-134168160)
结果分别为 101+0 偏向锁状态 101+pid 偏向锁 101+pid偏向锁,后面两次打印结果中可以看到锁指向线程id是相同。
轻量级锁
public class Demo2 {
//JVM启动设置关闭偏向锁延迟
public static void main(String[] args) throws InterruptedException {
A a = new A();
System.out.println("锁之前");
System.out.println(ClassLayout.parseInstance(a).toPrintable());
Thread thread = new Thread(){
@Override
public void run() {
synchronized (a){
System.out.println("线程锁状态");
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
};
/**
* 主线程先拿锁 执行完成启动第二个线程 否则会产出重量级锁
*/
synchronized (a){
System.out.println("主线程锁状态");
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
thread.start();
System.out.println("锁之后");
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
结果
锁之前
com.example.concurrent.sync.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 05 c4 00 f8 (00000101 11000100 00000000 11111000) (-134167547)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
主线程锁状态
com.example.concurrent.sync.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 18 00 7a (00000101 00011000 00000000 01111010) (2046826501)
4 4 (object header) 8d 7f 00 00 (10001101 01111111 00000000 00000000) (32653)
8 4 (object header) 05 c4 00 f8 (00000101 11000100 00000000 11111000) (-134167547)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
锁之后
线程锁状态
com.example.concurrent.sync.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) f8 c8 12 0f (11111000 11001000 00010010 00001111) (252889336)
4 4 (object header) 00 70 00 00 (00000000 01110000 00000000 00000000) (28672)
8 4 (object header) 05 c4 00 f8 (00000101 11000100 00000000 11111000) (-134167547)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
com.example.concurrent.sync.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) f8 c8 12 0f (11111000 11001000 00010010 00001111) (252889336)
4 4 (object header) 00 70 00 00 (00000000 01110000 00000000 00000000) (28672)
8 4 (object header) 05 c4 00 f8 (00000101 11000100 00000000 11111000) (-134167547)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
结果分别为 101+0 偏向锁状态 101+pid 偏向锁 000 轻量级锁 000 轻量级锁
重量级锁
/**
* 代码与轻量级锁的代码区别为thread.start()方法调用放到主线程加锁之前 使多个线程之间存在锁竞争
*(自选状态之外)升级重量级锁
*/
public class Demo3 {
//JVM启动设置关闭偏向锁延迟
public static void main(String[] args) throws InterruptedException {
A a = new A();
System.out.println("锁之前");
System.out.println(ClassLayout.parseInstance(a).toPrintable());
Thread thread = new Thread(){
@Override
public void run() {
synchronized (a){
System.out.println("线程锁状态");
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
};
thread.start();
/**
* 主线程先拿锁 执行完成启动第二个线程 否则会产出重量级锁
*/
synchronized (a){
System.out.println("主线程锁状态");
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
System.out.println("锁之后");
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
结果:
锁之前
com.example.concurrent.sync.A 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) 05 c4 00 f8 (00000101 11000100 00000000 11111000) (-134167547)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
主线程锁状态
com.example.concurrent.sync.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 5a 57 00 50 (01011010 01010111 00000000 01010000) (1342199642)
4 4 (object header) a8 7f 00 00 (10101000 01111111 00000000 00000000) (32680)
8 4 (object header) 05 c4 00 f8 (00000101 11000100 00000000 11111000) (-134167547)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
锁之后
线程锁状态
com.example.concurrent.sync.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 5a 57 00 50 (01011010 01010111 00000000 01010000) (1342199642)
4 4 (object header) a8 7f 00 00 (10101000 01111111 00000000 00000000) (32680)
8 4 (object header) 05 c4 00 f8 (00000101 11000100 00000000 11111000) (-134167547)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
com.example.concurrent.sync.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 5a 57 00 50 (01011010 01010111 00000000 01010000) (1342199642)
4 4 (object header) a8 7f 00 00 (10101000 01111111 00000000 00000000) (32680)
8 4 (object header) 05 c4 00 f8 (00000101 11000100 00000000 11111000) (-134167547)
12 4 (loss due to the next object alignment)
结果分别为001 无锁 010 重量级锁 010重量级锁 010 重量级锁 (锁只会进行升级和锁释放,不会降级)
性能对比
偏向锁
//JVM启动设置关闭偏向锁延迟 -XX:BiasedLockingStartupDelay=0
public class Demo4 {
public Long max = 1000000000L;
public static void main(String[] args) throws InterruptedException {
Demo4 demo4 = new Demo4();
long start = System.currentTimeMillis();
while (demo4.max>0){
demo4.run1();
}
long end = System.currentTimeMillis();
System.out.println(String.format("%sms", end -start));
}
public synchronized void run1(){
max--;
}
}
执行结果 4294ms
轻量级锁
代码同上 将偏向锁延迟开启即可
结果 19682ms
重量级锁
public class Demo5 {
public CountDownLatch countDownLatch = new CountDownLatch(1000000000);
public static void main(String[] args) throws InterruptedException {
Demo5 demo4 = new Demo5();
long start = System.currentTimeMillis();
for (int i=0;i<2;i++){
Thread thread = new Thread(){
@Override
public void run() {
while (demo4.countDownLatch.getCount()>0){
demo4.run1();
}
};
};
thread.start();
}
demo4.countDownLatch.await();
long end = System.currentTimeMillis();
System.out.println(String.format("%sms", end -start));
}
public synchronized void run1(){
countDownLatch.countDown();
}
}
结果:44597ms
对象头的介绍在下次具体介绍推荐直接到oracle官网查看介绍
赏
使用支付宝打赏
使用微信打赏
若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏
扫描二维码,分享此文章