synchronized是java关键字,用于给对象枷锁,保证单机的并发情况下线程安全。
锁的状态
|--------------------------------------------------------------------------------------------------------------|
| Object Header (128 bits) |
|--------------------------------------------------------------------------------------------------------------|
| Mark Word (64 bits) | Klass Word (64 bits) |
|--------------------------------------------------------------------------------------------------------------|
| unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 | OOP to metadata object | 无锁
|----------------------------------------------------------------------|--------|------------------------------|
| thread:54 | epoch:2 | unused:1 | age:4 | biased_lock:1 | lock:2 | OOP to metadata object | 偏向锁
|----------------------------------------------------------------------|--------|------------------------------|
| ptr_to_lock_record:62 | lock:2 | OOP to metadata object | 轻量锁
|----------------------------------------------------------------------|--------|------------------------------|
| ptr_to_heavyweight_monitor:62 | lock:2 | OOP to metadata object | 重量锁
|----------------------------------------------------------------------|--------|------------------------------|
| | lock:2 | OOP to metadata object | GC
|--------------------------------------------------------------------------------------------------------------|
测试
引入
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
(JVM默认开启偏向锁,默认的偏向锁启动时间为4-5秒后,所以先让主线程睡5秒再加锁能保证对象处于偏向锁的状态,此处也可以在VM Options中添加参数
【-XX:BiasedLockingStartupDelay=0】来让JVM取消延迟启动偏向锁(本文的示例均未设置此参数),其效果跟不改变VM Options只在main方法中让主线程先睡眠5秒是一样的)
无锁
public class User {
private String name;
private Integer age;
}
测试锁
public class synTest {
public static void main(String[] args) {
User user = new User();
System.out.println(ClassLayout.parseInstance(user).toPrintable());
}
}
输出
com.util.lock.User 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) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 java.lang.String User.name null
16 4 java.lang.Integer User.age null
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
偏向锁
默认偏向锁是延迟5秒开启
public static void main(String[] args) {
try {
//延迟5秒保证偏向锁开启
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
User user = new User();
System.out.println(ClassLayout.parseInstance(user).toPrintable());
}
com.util.lock.User 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) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 java.lang.String User.name null
16 4 java.lang.Integer User.age null
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
对象头标示位 101 偏向锁,但这是未加锁情况下所以线程id是空的。其余数据跟上述无锁状态一样。
加锁
public static void main(String[] args) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
User user = new User();
synchronized (user){
System.out.println(ClassLayout.parseInstance(user).toPrintable());
}
}
com.util.lock.User object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 48 72 03 (00000101 01001000 01110010 00000011) (57821189)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 java.lang.String User.name null
16 4 java.lang.Integer User.age null
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
与上面对比多个线程id
轻量锁
不睡眠5秒,直接用synchronized给对象加锁
public static void main(String[] args) {
User user = new User();
synchronized (user){
System.out.println(ClassLayout.parseInstance(user).toPrintable());
}
}
打印
com.util.lock.User object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) e8 f1 a8 02 (11101000 11110001 10101000 00000010) (44626408)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 java.lang.String User.name null
16 4 java.lang.Integer User.age null
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
标示位为00.
重量
存在线程竞争的时候触发
public static void main(String[] args) {
User user = new User();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (user){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ClassLayout.parseInstance(user).toPrintable());
}
}
});
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (user){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.err.println(ClassLayout.parseInstance(user).toPrintable());
}
}
});
t2.start();
}
打印
com.util.lock.User object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 4a 06 a7 1c (01001010 00000110 10100111 00011100) (480708170)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 java.lang.String User.name null
16 4 java.lang.Integer User.age null
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
com.util.lock.User object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 4a 06 a7 1c (01001010 00000110 10100111 00011100) (480708170)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 java.lang.String User.name null
16 4 java.lang.Integer User.age null
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
标示位10
hashCode会取消偏向锁
public static void main(String[] args) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
User user = new User();
synchronized (user){
System.out.println(ClassLayout.parseInstance(user).toPrintable());
}
System.out.println(user.hashCode());
System.out.println(ClassLayout.parseInstance(user).toPrintable());
}
com.util.lock.User object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 48 13 03 (00000101 01001000 00010011 00000011) (51595269)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 java.lang.String User.name null
16 4 java.lang.Integer User.age null
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
1562557367
com.util.lock.User object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 b7 bb 22 (00000001 10110111 10111011 00100010) (582727425)
4 4 (object header) 5d 00 00 00 (01011101 00000000 00000000 00000000) (93)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 java.lang.String User.name null
16 4 java.lang.Integer User.age null
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
经过hashcode。对象会从偏向锁变为无锁状态,即使后续给对象加锁,该对象也只会进入轻量级或者重量级锁状态,不会再进入偏向状态了。因为该对象一旦进行Object的hashCode计算,那么对象头中会保存这个hashCode,此时再也无法存放偏向线程的id了因为对象头的长度无法同时存放hashCode和偏向线程id),所以此后该对象无法再进入偏向锁状态。
锁的膨胀
开启偏向锁,new对象->无线程id的偏向锁->有线程id的偏向锁->锁交替运行清亮锁->线程存在竞争重量锁
new对象->无线程id的偏向锁->有线程id的偏向锁->存在竞争重量锁
。。。待续
以上均为鲁班学院学习资料,欢迎大家报班学习,真心推荐!
转载:https://blog.csdn.net/qigemingzhendenan/article/details/104413949