在前面初识synchronized关键字中介绍了synchronized的用法和特性,接着来看一下synchronized的原理。
synchronized实现的锁策略
1.既是乐观锁也是悲观锁;
2.既是轻量级锁也是重量级锁;
3.是普通互斥锁;
4.既是自旋锁也是挂起等待锁;
5.是可重入锁;
6.是非公平锁。
轻量级锁是基于自旋锁实现的,重量级锁是基于挂起等待锁实现的。
synchronized的原理
锁升级
通过前面对锁策略的学习可以知道,synchronized在不同的时期可能会用到不同的锁策略。
无锁状态——>偏向锁——>轻量级锁——>重量级锁
无锁:没有线程来争抢锁资源;
偏向锁:不是真的加锁, 而只是在锁的对象头中记录一个标记(记录该锁所属的线程). 如果没有其他线程参与竞争锁, 那么就不会真正执行加锁操作, 从而降低程序开销. 一旦真的涉及到其他的线程竞争, 再取消偏向锁状态, 进入轻量级锁状态.;
轻量级锁:用户态的CAS;
重量级锁:内核态的锁,通过指令实现。
随着线程间对锁竞争的激烈程度,锁的状态会不断升级。
public class Demo02_Layout_Synchronized {
// 定义一些变量
private int count;
private long count1 = 200;
private String hello = "";
// 定义一个对象变量
private TestLayout test001 = new TestLayout();
public static void main(String[] args) throws InterruptedException {
// 创建一个对象的实例
Object obj = new Object();
// 打印实例布局
System.out.println("=== 任意Object对象布局,起初为无锁状态");
System.out.println(ClassLayout.parseInstance(obj).toPrintable());
System.out.println("=== 延时4S开启偏向锁");
// 延时4S开启偏向锁
Thread.sleep(5000);
// 创建本类的实例
Demo02_Layout_Synchronized monitor = new Demo02_Layout_Synchronized();
// 打印实例布局,注意查看锁状态为偏向锁
System.out.println("=== 打印实例布局,注意查看锁状态为偏向锁");
System.out.println(ClassLayout.parseInstance(monitor).toPrintable());
System.out.println("==== synchronized加锁");
// 加锁后观察加锁信息
synchronized (monitor) {
System.out.println("==== 第一层synchronized加锁后");
System.out.println(ClassLayout.parseInstance(monitor).toPrintable());
// 锁重入,查看锁信息
synchronized (monitor) {
System.out.println("==== 第二层synchronized加锁后,锁重入");
System.out.println(ClassLayout.parseInstance(monitor).toPrintable());
}
// 释放里层的锁
System.out.println("==== 释放内层锁后");
System.out.println(ClassLayout.parseInstance(monitor).toPrintable());
}
// 释放所有锁之后
System.out.println("==== 释放 所有锁");
System.out.println(ClassLayout.parseInstance(monitor).toPrintable());
System.out.println("==== 多个线程参与锁竞争,观察锁状态");
Thread thread1 = new Thread(() -> {
synchronized (monitor) {
System.out.println("=== 在线程A 中获取锁,参与锁竞争,当前只有线程A 竞争锁,轻度锁竞争");
System.out.println(ClassLayout.parseInstance(monitor).toPrintable());
}
});
thread1.start();
// 休眠一会,不与线程A 激烈竞争
Thread.sleep(100);
Thread thread2 = new Thread(() -> {
synchronized (monitor) {
System.out.println("=== 在线程B 中获取锁,与其他线程进行锁竞争");
System.out.println(ClassLayout.parseInstance(monitor).toPrintable());
}
});
thread2.start();
// 不休眠直接竞争锁,产生激烈竞争
System.out.println("==== 不休眠直接竞争锁,产生激烈竞争");
synchronized (monitor) {
// 加锁后的类对象
System.out.println("==== 与线程B 产生激烈的锁竞争,观察锁状态为fat lock");
System.out.println(ClassLayout.parseInstance(monitor).toPrintable());
}
// 休眠一会释放锁后
Thread.sleep(100);
System.out.println("==== 释放锁后");
System.out.println(ClassLayout.parseInstance(monitor).toPrintable());
System.out.println("===========================================================================================");
System.out.println("===========================================================================================");
System.out.println("===========================================================================================");
System.out.println("===========================================================================================");
System.out.println("===========================================================================================");
System.out.println("===========================================================================================");
// 调用hashCode后才保存hashCode的值
monitor.hashCode();
// 调用hashCode后观察现象
System.out.println("==== 调用hashCode后查看hashCode的值");
System.out.println(ClassLayout.parseInstance(monitor).toPrintable());
// 强制执行垃圾回收
System.gc();
// 观察GC计数
System.out.println("==== 调用GC后查看age的值");
System.out.println(ClassLayout.parseInstance(monitor).toPrintable());
// 打印类布局,注意调用的方法不同
System.out.println("==== 查看类布局");
System.out.println(ClassLayout.parseClass(Demo02_Layout_Synchronized.class).toPrintable());
// 打印类对象布局
System.out.println("==== 查看类对象布局");
System.out.println(ClassLayout.parseInstance(Demo02_Layout_Synchronized.class).toPrintable());
}
}
class TestLayout {
}
1.在程序刚启动时,创建的对象不能用作偏向锁。
2.在程序运行过程中创建的对象是可偏向状态。
3.加入偏向锁。此时记录的是当前获取到锁的线程信息。
4.锁虽然被释放,只要没有争抢,锁一直是偏向状态,保存在对象头中。
5.当有一个线程和主线程竞争时,就会升级为轻量级锁。
6.继续创建线程参与锁竞争就会升级为重量级锁。
锁消除
在写代码时,程序员自己加synchronized来保证线程安全。如果加了synchronized的代码块中只有读操作没有写操作,JVM就认为这个代码块没有必要加锁,从JVM运行时就会被优化掉,这个现象就称为锁消除,过滤掉无效的synchronized,从而提高了效率。JVM在有100%的把握时才会进行优化。
锁粗化
执行一个业务逻辑发生了四次锁竞争,在保证程序执行正确的情况下,JVM会做出优化,只加一次锁,整个逻辑执行完之后再释放,从而提高效率。
继续加油~