是存在在JAVA对象头中的一个区域大小为8字节
里面包含了
1.锁信息
2.GC信息
3.HashCode(如果有调用)
public class Demo1 {
public static void main(String[] args) throws IOException {
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
synchronized (o) {
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
}
}
输出
public class Demo {
public static void main(String[] args) throws IOException {
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
}
这段代码通过JOL工具打印出来是 最后2位是00,正好对应上面的无锁
public class Demo1 {
private static int i = 1;
public void inc(){
synchronized (Demo1.class) {
i++;
}
}
}
上面这段代码,假设你并发量不高,可能inc()并不会并发执行,但是以防万一,所以你为了保证i++没有并发安全问题于是你加了synchronized 关键字保证安全,如果没有竞争情况下使用可以就是一个偏向锁,因为没有竞争所以没有必要使用轻量锁、重量锁。
当没有并发竞争的情况下:
因为你的系统并发并不高,synchronized 的互斥机制一次都没有被触发过,也就是没有同一个时刻去执行i++,所以synchronized 认为你现在并没有出现竞争的情况,一直是单线程执行,它把当前执行这段代码的线程的指针放到对象头中,表示有一个线程在干活,并且设置为偏向锁,偏向这个线程。当这个线程执行完成后,另外一个线程来了,再把当前线程指针去覆盖上次的线程指针,以上是在没有并发竞争的情况下的逻辑。
当存在并发竞争的情况下:
此时synchronized 有多线程进行竞争了,这是多个线程都把想自己的线程指针放到对象头中让自己区执行,这个时候偏向锁会触发“ 撤销偏向锁”,还原对象MackWord区域为无锁状态,用到CAS去决定,谁可以把自己的线程指针到对象头中,谁就是抢到这把锁了,这个时候升级成一个轻量级锁 (又称:自旋锁/CAS/无锁) 。
在创建一个对象后,这个对象头的标志位为 001,是个无锁状态,但是等待4秒之后在创建一个对象,则是对象头是一个偏向锁状态,标志位为 101。
为什么怎么做?
JVM启动的时候一定是用到了多线程,所以明确的知道资源会出现竞争的情况,必然出现锁撤销,升级到轻量锁,所以前4秒直接禁用偏向锁。
那前4秒不禁用偏向锁呢?
使用偏向锁竞争激烈然后触发 “ 撤销偏向锁” ,还原对象MackWord区域为无锁状态,锁升级到轻量级锁。经历这一套流程最后还是会到轻量锁,“ 撤销偏向锁” 和还原对象MackWord区域也是一个消耗资源的地方,所以前4秒禁用偏向锁,使用轻量级锁。
可以通过JVM参数 -XX:BiasedLockingStartupDelau 设置延迟多少秒启动偏向锁,默认是4
public class Demo1 {
public static void main(String[] args) throws IOException, InterruptedException {
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
Thread.sleep(5000);
Object o1 = new Object();
System.out.println(ClassLayout.parseInstance(o1).toPrintable());
}
}
public class Demo {
public static void main(String[] args) throws IOException {
Object o = new Object();
synchronized (o) {
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
}
}
这段代码通过JOL工具打印出来是 最后2位是00,正好对应上面的轻量级锁
此时的轻量级锁由一个偏向锁升级而来,当前有一个线程持有了锁,其他线程就去进行自旋做CAS操作,其他线程就一直尝试使用CAS把自己的线程指针给放到对象头中,当持有锁的线程执行完后,其他在进行CAS操作的线程,某一个线程使用CAS成功把自己线程指针给放到对象头中。
但是,如果抢到锁的那个线程执行的时候很长,一直不释放锁,其他线程一直在自旋做CAS操作,一直拿不到锁,疯狂占用CPU资源。所以一直等下去不是办法,这时就升级到了重量级锁
升级条件
1.6自旋超过10次或超出CPU核数的2/1,并且有参数可以调整
1.7之后使用的是自适应自选,由JVM根据历史数据自己去计算应该自旋多少次升级
因为竞争太过于激烈,总不能一直让CAS做无用的自选,所以为了减少系统资源浪费,升级到了重量级锁,把等待的线程放到等待队列(把线程冻结了),不消耗CPU资源