Synchronized是什么?
synchronized是用来实现同步代码块的关键字,通过C++代码实现,在JDK1.6以前synchronized通过mutex互斥量实现,涉及用户态和内核态的切换,效率低下,直接就是重量级锁。而JUC包下的Lock通过AQS实现对共享资源的同步控制,效率相对synchronized要高,因此1.6以后对synchronized进行了优化,避免直接使用系统函数加锁。
偏向锁、轻量锁、重量锁
偏向锁:同一个线程多次访问共享资源,在加锁之前检查对象头中的线程ID,如果是当前线程则不需要再加锁,避免了不必要的CAS操作,CAS对应汇编指令CMPXCHG,也是要消耗资源的。轻量锁:多个线程交替访问共享资源时,且不存在竞争,此时让锁偏向某一个线程也是不合适的,回发生频繁的CAS操作修改偏向锁指向的线程ID,所以将锁升级为轻量锁,得到轻量锁的线程执行完同步代码块之后会主动释放锁。尽管获取轻量锁也需要CAS操作将对象头的Mark Word中lockrecord指针指向当前线程的lockrecord,但是省去了对线程ID的判断以及对原持有锁的线程的撤销。重量锁:多个线程访问共享资源存在竞争,升级为monitor机制实现的重量锁。
批量重偏向
package com
.depthmind
.concurrent
.jol
;
import java
.util
.ArrayList
;
import java
.util
.List
;
import org
.openjdk
.jol
.info
.ClassLayout
;
import static java
.lang
.System
.out
;
public class Example3 {
public static void main(String
[] args
) throws InterruptedException
{
List
<A1> list
= new ArrayList<>(100);
for (int i
= 0; i
< 100; i
++) {
A1 a
= new A1();
list
.add(a
);
}
Thread t1
= new Thread(() -> {
for (int i
= 0; i
< 100; i
++) {
A1 a
= list
.get(i
);
synchronized (a
) {
if (i
== 10) {
out
.println("t1--------预期偏向t1");
out
.print(ClassLayout
.parseInstance(a
).toPrintable());
}
}
}
});
t1
.start();
t1
.join();
out
.println("main 预期是偏向锁"+10 + ClassLayout
.parseInstance(list
.get(10)).toPrintable());
Thread t2
= new Thread(() -> {
for (int i
= 0; i
< 100; i
++) {
A1 a
= list
.get(i
);
synchronized (a
) {
if (i
== 18) {
out
.println("预期轻量锁");
out
.print(ClassLayout
.parseInstance(a
).toPrintable());
}
if (i
== 19) {
out
.println("预期偏向t2");
out
.print(ClassLayout
.parseInstance(a
).toPrintable());
}
if (i
== 20) {
out
.println("预期偏向t2");
out
.print(ClassLayout
.parseInstance(a
).toPrintable());
}
if (i
== 41) {
out
.println("预期偏向t2");
out
.print(ClassLayout
.parseInstance(a
).toPrintable());
}
}
}
});
t2
.start();
}
}
class A1{}
参考示例代码,t1线程执行完之后,list中A类的实例的对象头都保存了t1的线程ID,即偏向t1,t2执行时会发现t1持有该对象的偏向锁,此时就撤销偏向锁,升级为轻量锁,经过多次之后JVM就认为这样效率低,因此设置了一个阈值,当撤销到第20次的时候会将list中第20个之后的对象全部偏向线程t2。
偏向锁延迟
关闭偏向锁延迟的JVM参数: -XX:BiasedLockingStartupDelay=0