偏向锁
轻量锁在没有竞争时(就自己的这个线程),每次重入仍然需要执行CAS操作。Java 6中引入了偏向锁来做进一步优化:只有第一次使用CAS将线程ID设置到对象的Mark Word头,之后发现这个线程 ID 是自己的就表示没有竞争,不用重新CAS操作。以后只要不发生竞争,这个对象就归该线程所有。通俗的讲,偏向锁就是在运行过程中,对象的锁偏向某个线程。即在开启偏向锁机制的情况下,某个线程获得锁,当该线程下次再想要获得锁时,不需要再获得锁(即忽略synchronized关键词),直接就可以执行同步代码,比较适合竞争较少的情况。比如下图:
偏向锁是默认是延迟的,不会在程序启动时立即生效,如果想避免延迟,可以加JVM参数 - XX:BiasedLockingStartupDelay=0来禁用延迟。
添加JVM参数-XX:-UseBiasedLocking关闭偏向锁,就可以直接进入轻量级锁。
static final Object obj = new Object();
public static void m1() {
synchronized( obj ) {
// 同步块 A
m2();
}
}
public static void m2() {
synchronized( obj ) {
// 同步块 B
m3();
}
}
public static void m3() {
synchronized( obj ) {
// 同步块 C
}
}
分析如图所示:
偏向状态
对象头格式:
一个对象创建时:
- 如果开启了偏向锁(默认开启),那么对象创建后,
markword值为0x05即最后3位为101,这时它的thread、epoch、age都为0。 - 偏向锁是默认是延迟的,不会在程序启动时立即生效,如果想避免延迟,可以加
VM参数- XX:BiasedLockingStartupDelay=0来禁用延迟。 - 如果没有开启偏向锁,那么对象创建后,
markword值为0x01即最后3位为001,这时它的hashcode、age都为 0,第一次用到hashcode时才会赋值。 - 在添加 VM 参数 -XX:-UseBiasedLocking 可以禁用偏向锁。
public class Test1 {
//未设置`VM`参数 `-XX:BiasedLockingStartupDelay=0`来禁用偏向锁的延迟。
public static void main(String[] args) throws InterruptedException {
System.out.println(ClassLayout.parseInstance(new Teacher()).toPrintable());
//因为偏向锁有延迟,因此睡眠一会。
Thread.sleep(4000);
System.out.println(ClassLayout.parseInstance(new Teacher()).toPrintable());
}
}
class Teacher{
int i = 0;
}
//执行结果:
com.wuke.test.Teacher object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
8 4 (object header: class) 0xf800c143
12 4 int Teacher.i 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
com.wuke.test.Teacher object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000005 (biasable; age: 0)
8 4 (object header: class) 0xf800c143
12 4 int Teacher.i 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
由以上结果可以看到,偏向锁是有延迟的,未设置VM参数 -XX:BiasedLockingStartupDelay=0来禁用偏向锁的延迟时MarkWord的VALUE为0x0000000000000001 (non-biasable; age: 0),表示无锁状态 01 。睡眠 2s 后的VALUE为0x0000000000000005 (biasable; age: 0)。5(十进制)= 101(二进制),表示偏向锁状态 101 。
撤销偏向锁
撤销情况一:
当调用了锁对象的hashCode时,但偏向锁的对象中存储的是线程ID,如果调用hashCode会导致偏向锁被撤销。
- 轻量级锁会在线程栈帧的所记录中记录
hashCode。 - 重量级锁则会在
Monitor中记录hashCode。
@Slf4j
public class Test {
//记得取消虚拟机参数 -XX:BiasedLockingStartupDelay=0(取消偏向锁延迟)
public static void main(String[] args) throws IOException {
Student student = new Student();
//调用该对象的hashCode的时候会禁用掉这个对象的偏向锁,直接升级为轻量级锁。因为偏向锁的对象中存储的是线程ID,不是hashCode。
student.hashCode();
log.info(ClassLayout.parseInstance(student).toPrintable());
synchronized (student) {
log.info(ClassLayout.parseInstance(student).toPrintable());
}
log.info(ClassLayout.parseInstance(student).toPrintable());
}
}
class Student {
private String name;
private Integer age;
private String address;
}
//运行结果:
15:55:51.499 [main] INFO com.wuke.test.Test - com.wuke.test.Student object internals:
OFF SZ TYPE DESCRIPTION VALUE
//转为二进制后缀为001,表示无锁状态。
0 8 (object header: mark) 0x0000001963006a01 (hash: 0x1963006a; age: 0)
8 4 (object header: class) 0xf800ef95
12 4 java.lang.String Student.name null
16 4 java.lang.Integer Student.age null
20 4 java.lang.String Student.address null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
15:55:51.501 [main] INFO com.wuke.test.Test - com.wuke.test.Student object internals:
OFF SZ TYPE DESCRIPTION VALUE
//转为二进制后缀为000,表示轻量级锁状态。
//thin lock:轻量级锁
0 8 (object header: mark) 0x000000f8921ff328 (thin lock: 0x000000f8921ff328)
8 4 (object header: class) 0xf800ef95
12 4 java.lang.String Student.name null
16 4 java.lang.Integer Student.age null
20 4 java.lang.String Student.address null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
15:55:51.501 [main] INFO com.wuke.test.Test - com.wuke.test.Student object internals:
OFF SZ TYPE DESCRIPTION VALUE
////转为二进制后缀为001,表示无锁状态。
0 8 (object header: mark) 0x0000001963006a01 (hash: 0x1963006a; age: 0)
8 4 (object header: class) 0xf800ef95
12 4 java.lang.String Student.name null
16 4 java.lang.Integer Student.age null
20 4 java.lang.String Student.address null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
撤销情况二:
当其它线程使用偏向锁对象时,会将偏向锁升级为轻量级锁。
@Slf4j
public class Test {
//需要添加JVM参数:-XX:BiasedLockingStartupDelay=0 来禁用掉偏向锁延迟
public static void main(String[] args) {
Student student = new Student();
new Thread(() -> {
log.debug(ClassLayout.parseInstance(student).toPrintable());
synchronized (student) {
log.debug(ClassLayout.parseInstance(student).toPrintable());
}
log.debug(ClassLayout.parseInstance(student).toPrintable());
synchronized (Test.class) {
//唤醒t2线程
Test.class.notify();
}
}, "t1").start();
new Thread(() -> {
synchronized (Test.class) {
try {
//等待t1线程唤醒
Test.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug(ClassLayout.parseInstance(student).toPrintable());
synchronized (student) {
log.debug(ClassLayout.parseInstance(student).toPrintable());
}
log.debug(ClassLayout.parseInstance(student).toPrintable());
}, "t2").start();
}
}
class Student {
private String name;
private Integer age;
private String address;
}
//执行结果:
16:12:07.380 [t1] DEBUG com.wuke.test.Test - com.wuke.test.Student object internals:
OFF SZ TYPE DESCRIPTION VALUE
//偏向锁
0 8 (object header: mark) 0x0000000000000005 (biasable; age: 0)
8 4 (object header: class) 0xf800ef95
12 4 java.lang.String Student.name null
16 4 java.lang.Integer Student.age null
20 4 java.lang.String Student.address null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
16:12:07.383 [t1] DEBUG com.wuke.test.Test - com.wuke.test.Student object internals:
OFF SZ TYPE DESCRIPTION VALUE
//偏向锁
0 8 (object header: mark) 0x00000142f8b9e805 (biased: 0x0000000050be2e7a; epoch: 0; age: 0)
8 4 (object header: class) 0xf800ef95
12 4 java.lang.String Student.name null
16 4 java.lang.Integer Student.age null
20 4 java.lang.String Student.address null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
16:12:07.383 [t1] DEBUG com.wuke.test.Test - com.wuke.test.Student object internals:
OFF SZ TYPE DESCRIPTION VALUE
//偏向锁
0 8 (object header: mark) 0x00000142f8b9e805 (biased: 0x0000000050be2e7a; epoch: 0; age: 0)
8 4 (object header: class) 0xf800ef95
12 4 java.lang.String Student.name null
16 4 java.lang.Integer Student.age null
20 4 java.lang.String Student.address null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
16:12:07.384 [t2] DEBUG com.wuke.test.Test - com.wuke.test.Student object internals:
OFF SZ TYPE DESCRIPTION VALUE
//偏向锁
0 8 (object header: mark) 0x00000142f8b9e805 (biased: 0x0000000050be2e7a; epoch: 0; age: 0)
8 4 (object header: class) 0xf800ef95
12 4 java.lang.String Student.name null
16 4 java.lang.Integer Student.age null
20 4 java.lang.String Student.address null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
16:12:07.385 [t2] DEBUG com.wuke.test.Test - com.wuke.test.Student object internals:
OFF SZ TYPE DESCRIPTION VALUE
//轻量级锁
0 8 (object header: mark) 0x000000022efff4c0 (thin lock: 0x000000022efff4c0)
8 4 (object header: class) 0xf800ef95
12 4 java.lang.String Student.name null
16 4 java.lang.Integer Student.age null
20 4 java.lang.String Student.address null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
16:12:07.385 [t2] DEBUG com.wuke.test.Test - com.wuke.test.Student object internals:
OFF SZ TYPE DESCRIPTION VALUE
//无锁
0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
8 4 (object header: class) 0xf800ef95
12 4 java.lang.String Student.name null
16 4 java.lang.Integer Student.age null
20 4 java.lang.String Student.address null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
撤销情况三 调用wait/notify
@Slf4j
public class Test {
//需要添加JVM参数:-XX:BiasedLockingStartupDelay=0来禁用掉偏向锁延迟
public static void main(String[] args) throws InterruptedException {
Student student = new Student();
new Thread(() -> {
log.debug(ClassLayout.parseInstance(student).toPrintable());
synchronized (student) {
log.debug(ClassLayout.parseInstance(student).toPrintable());
try {
student.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug(ClassLayout.parseInstance(student).toPrintable());
}
}, "t1").start();
new Thread(() -> {
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (student) {
System.err.println("notify");
student.notify();
}
}, "t2").start();
}
}
class Student {
private String name;
private Integer age;
private String address;
}
//执行结果:
19:27:59.991 [t1] DEBUG com.wuke.test.Test - com.wuke.test.Student object internals:
OFF SZ TYPE DESCRIPTION VALUE
//可偏向锁状态。
//biasable:表示可以偏向。
0 8 (object header: mark) 0x0000000000000005 (biasable; age: 0)
8 4 (object header: class) 0xf800ef95
12 4 java.lang.String Student.name null
16 4 java.lang.Integer Student.age null
20 4 java.lang.String Student.address null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
19:27:59.993 [t1] DEBUG com.wuke.test.Test - com.wuke.test.Student object internals:
OFF SZ TYPE DESCRIPTION VALUE
//偏向锁。
//biased:表示已经偏向。
0 8 (object header: mark) 0x0000022b4cb8d805 (biased: 0x000000008ad32e36; epoch: 0; age: 0)
8 4 (object header: class) 0xf800ef95
12 4 java.lang.String Student.name null
16 4 java.lang.Integer Student.age null
20 4 java.lang.String Student.address null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
notify
19:28:04.630 [t1] DEBUG com.wuke.test.Test - com.wuke.test.Student object internals:
OFF SZ TYPE DESCRIPTION VALUE
//重量级锁
0 8 (object header: mark) 0x0000022b2de70e4a (fat lock: 0x0000022b2de70e4a)
8 4 (object header: class) 0xf800ef95
12 4 java.lang.String Student.name null
16 4 java.lang.Integer Student.age null
20 4 java.lang.String Student.address null
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
批量重偏向
如果对象虽然被多个线程访问,但没有竞争,这时偏向了线程 T1 的对象仍有机会重新偏向 T2,重偏向会重置对象的 Thread ID
当撤销偏向锁阈值超过 20 次后,jvm 会这样觉得自己偏向错了,于是会在给这些对象加锁时重新偏向至加锁线程。
@Slf4j
public class Test1 {
//需要添加JVM参数:-XX:BiasedLockingStartupDelay=0来禁用掉偏向锁延迟
private static void test3() throws InterruptedException {
Vector<Dog> list = new Vector<>();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 30; i++) {
Dog d = new Dog();
list.add(d);
synchronized (d) {
log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
}
}
synchronized (list) {
list.notify();
}
}, "t1");
t1.start();
Thread t2 = new Thread(() -> {
synchronized (list) {
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("===============> ");
for (int i = 0; i < 30; i++) {
Dog d = list.get(i);
log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
synchronized (d) {
log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
}
log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
}
}, "t2");
t2.start();
}
}
//执行结果
[t1] - 0 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 1 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 2 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 3 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 4 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 5 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 6 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 7 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 8 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 9 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 10 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 11 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 12 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 13 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 14 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 15 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 16 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 17 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 18 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 19 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 20 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 21 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 22 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 23 00000000 00000000 00000000 00000000 00011111 11110011 11100000 0000010
[t1] - 24 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 25 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 26 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 27 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 28 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 29 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - ===============>
[t2] - 0 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 0 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 1 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 1 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 1 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 2 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 2 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 2 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 3 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 3 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 3 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 4 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 4 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 4 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 5 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 5 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 5 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 6 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 6 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 6 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 7 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 7 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 7 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 8 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 8 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 8 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 9 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 9 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 9 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 10 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 10 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 10 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 11 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 11 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 11 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 12 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 12 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 12 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 13 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 13 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 13 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 14 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 14 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 14 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 15 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 15 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 15 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 16 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 16 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 16 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 17 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 17 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 17 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 18 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 18 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 18 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 19 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 19 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 19 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 20 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 20 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 20 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 21 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 21 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 21 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 22 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 22 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 22 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 23 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 23 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 23 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 24 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 24 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 24 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 25 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 25 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 25 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 26 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 26 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 26 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 27 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 27 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 27 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 28 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 28 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 28 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 29 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 29 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 29 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
批量撤销
当撤销偏向锁阈值超过 40 次后,jvm 会这样觉得,自己确实偏向错了,根本就不该偏向。于是整个类的所有对象都会变为不可偏向的,新建的对象也是不可偏向的
@Slf4j
public class Test1 {
static Thread t1, t2, t3;
//需要添加JVM参数:-XX:BiasedLockingStartupDelay=0来禁用掉偏向锁延迟
private static void test4() throws InterruptedException {
Vector<Dog> list = new Vector<>();
int loopNumber = 39;
t1 = new Thread(() -> {
for (int i = 0; i < loopNumber; i++) {
Dog d = new Dog();
list.add(d);
synchronized (d) {
log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
}
}
LockSupport.unpark(t2);
}, "t1").start();
t2 = new Thread(() -> {
LockSupport.park();
log.debug("===============> ");
for (int i = 0; i < loopNumber; i++) {
Dog d = list.get(i);
log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
synchronized (d) {
log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
}
log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
}
LockSupport.unpark(t3);
}, "t2").start();
t3 = new Thread(() -> {
LockSupport.park();
log.debug("===============> ");
for (int i = 0; i < loopNumber; i++) {
Dog d = list.get(i);
log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
synchronized (d) {
log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
}
log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
}
}, "t3");
t3.start();
t3.join();
log.debug(ClassLayout.parseInstance(new Dog()).toPrintableSimple(true));
}
}
锁消除
锁消除是在编译器级别的事情。在即时编译器时,如果发现不可能被共享的对象,则可以消除这 些对象的锁操作,多数是因为程序员编码不规范引起。 Just In Time Compiler,一般翻译为即时编译器。
/**
* 锁消除
* 从JIT角度看相当于无视它,synchronized (o)不存在了,这个锁对象并没有被共用扩散到其它线程使
* 用,极端的说就是根本没有加这个锁对象的底层机器码,消除了锁的使用。
*/
public class LockClearUPDemo {
static Object objectLock = new Object();//正常的
public void m1() {
//锁消除,JIT会无视它,synchronized(对象锁)不存在了。不正常的
Object o = new Object();
synchronized (o) {
System.out.println("-----hello LockClearUPDemo" + "\t" + o.hashCode() + "\t" + objectLock.hashCode());
}
}
public static void main(String[] args) {
LockClearUPDemo demo = new LockClearUPDemo();
for (int i = 1; i <= 10; i++) {
new Thread(() -> {
demo.m1();
}, String.valueOf(i)).start();
}
}
}
锁粗化
通常情况下,为了保证多线程间的有效并发,会要求每个线程持有锁的时间尽量短,即在使用完 公共资源后,应该立即释放锁。但是,凡事都有一个度,如果对同一个锁不停的进行请求、同步 和释放,其本身也会消耗系统宝贵的资源,反而不利于性能的优化。
/**
* 锁粗化
* 假如方法中首尾相接,前后相邻的都是同一个锁对象,那JIT编译器就会把这几个synchronized块合并成一个大块,
* 加粗加大范围,一次申请锁使用即可,避免次次的申请和释放锁,提升了性能
*/
public class LockBigDemo {
static Object objectLock = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (objectLock) {
System.out.println("11111");
}
synchronized (objectLock) {
System.out.println("22222");
}
synchronized (objectLock) {
System.out.println("33333");
}
}, "a").start();
new Thread(() -> {
synchronized (objectLock) {
System.out.println("44444");
}
synchronized (objectLock) {
System.out.println("55555");
}
synchronized (objectLock) {
System.out.println("66666");
}
}, "b").start();
}
}