【Java】再谈synchronized关键字(原理)

在前面初识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会做出优化,只加一次锁,整个逻辑执行完之后再释放,从而提高效率。
在这里插入图片描述


继续加油~
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_43243800/article/details/131215577