首页 > 编程语言 >java并发 第四章共享模型之管程 3 synchronized 原理

java并发 第四章共享模型之管程 3 synchronized 原理

时间:2024-09-02 20:50:16浏览次数:7  
标签:java synchronized 对象 管程 线程 轻量级 自旋 偏向

1. 轻量级锁

轻量级锁的使用场景:如果一个对象虽然有多线程要加锁,但加锁的时间是错开的(也就是没有竞争),那么可以
使用轻量级锁来优化。
轻量级锁对使用者是透明的,即语法仍然是 synchronized
假设有两个方法同步块,利用同一个对象加锁
 

static final Object obj = new Object();

    public static void method1() {
        synchronized (obj) {
        // 同步块 A
            method2();
        }
    }

    public static void method2() {
        synchronized (obj) {
        // 同步块 B
        }
    }

1. 创建锁记录(Lock Record)对象,每个线程都的栈帧都会包含一个锁记录的结构,内部可以存储锁定对象的Mark Word 

2.让锁记录中 Object reference 指向锁对象,并尝试用 cas 替换 Object 的 Mark Word,将 Mark Word 的值存入锁记录 

3. 如果 cas 替换成功,对象头中存储了锁记录地址和状态 00 ,表示由该线程给对象加锁,这时图示如下 

4. 如果 cas 失败,有两种情况
如果是其它线程已经持有了该 Object 的轻量级锁,这时表明有竞争,进入锁膨胀过程
如果是自己执行了 synchronized 锁重入,那么再添加一条 Lock Record 作为重入的计数

5. 当退出 synchronized 代码块(解锁时)如果有取值为 null 的锁记录,表示有重入,这时重置锁记录,表示重入计数减一 

6.当退出 synchronized 代码块(解锁时)锁记录的值不为 null,这时使用 cas 将 Mark Word 的值恢复给对象头
        成功,则解锁成功。
        失败,说明轻量级锁进行了锁膨胀或已经升级为重量级锁,进入重量级锁解锁流程 。

2. 锁膨胀

如果在尝试加轻量级锁的过程中,CAS 操作无法成功,这时一种情况就是有其它线程为此对象加上了轻量级锁(有竞争),这时需要进行锁膨胀,将轻量级锁变为重量级锁。

这时 Thread-1 加轻量级锁失败,进入锁膨胀流程
        即为 Object 对象申请 Monitor 锁,让 Object 指向重量级锁地址
        然后自己进入 Monitor 的 EntryList BLOCKED 

        当 Thread-0 退出同步块解锁时,使用 cas 将 Mark Word 的值恢复给对象头,失败。这时会进入重量级解锁流程,即按照 Monitor 地址找到 Monitor 对象,设置 Owner 为 null,唤醒 EntryList 中 BLOCKED 线程。

3. 自旋优化

        重量级锁竞争的时候,还可以使用自旋来进行优化,如果当前线程自旋成功(即这时候持锁线程已经退出了同步块,释放了锁),这时当前线程就可以避免阻塞。

        自旋重试成功的情况。

         自旋重试失败的情况。 

         自旋会占用 CPU 时间,单核 CPU 自旋就是浪费,多核 CPU 自旋才能发挥优势。
        在 Java 6 之后自旋锁是自适应的,比如对象刚刚的一次自旋操作成功过,那么认为这次自旋成功的可能性会高,就多自旋几次;反之,就少自旋甚至不自旋,总之,比较智能。
        Java 7 之后不能控制是否开启自旋功能。

4. 偏向锁

        轻量级锁在没有竞争时(就自己这个线程),每次重入仍然需要执行 CAS 操作。
        Java 6 中引入了偏向锁来做进一步优化:只有第一次使用 CAS 将线程 ID 设置到对象的 Mark Word 头,之后发现这个线程 ID 是自己的就表示没有竞争,不用重新 CAS。以后只要不发生竞争,这个对象就归该线程所有。

一个对象创建时:
        如果开启了偏向锁(默认开启),那么对象创建后,markword 值为 0x05 即最后 3 位为 101,这时它的thread、epoch、age 都为 0,        
        偏向锁是默认是延迟的,不会在程序启动时立即生效,如果想避免延迟,可以加 VM 参数 -
XX:BiasedLockingStartupDelay=0 来禁用延迟
        如果没有开启偏向锁,那么对象创建后,markword 值为 0x01 即最后 3 位为 001,这时它的 hashcode、age 都为 0,第一次用到 hashcode 时才会赋值 

public class TestBiased {


    public static void main(String[] args) throws InterruptedException {
        // 添加虚拟机参数 -XX:BiasedLockingStartupDelay=0
        public static void main (String[]args) throws IOException {
            Dog d = new Dog();
            ClassLayout classLayout = ClassLayout.parseInstance(d);
            new Thread(() -> {
                log.debug("synchronized 前");
                System.out.println(classLayout.toPrintableSimple(true));
                synchronized (d) {
                    log.debug("synchronized 中");
                    System.out.println(classLayout.toPrintableSimple(true));
                }
                log.debug("synchronized 后");
                System.out.println(classLayout.toPrintableSimple(true));
            }, "t1").start();
        }

    }
}

class Dog {

}

注意  处于偏向锁的对象解锁后,线程 id 仍存储于对象头中 

这个一长串里的id是操作系统分配的id和java的id不对应。

撤销偏向锁

1.调用hashCode

     正常状态对象一开始是没有 hashCode 的,第一次调用才生成,调用hashcode后会禁用偏向锁,从对象头格式可知存了hashcode后就不够放线程id了,只能禁了偏向锁。

2.其它线程使用对象

        当有其它线程使用偏向锁对象时,会将偏向锁升级为轻量级锁

3.调用 wait/notify

因为只有重量级锁才有wait/notify方法。

批量重偏向

        如果对象虽然被多个线程访问,但没有竞争,这时偏向了线程 T1 的对象仍有机会重新偏向 T2,重偏向会重置对象的 Thread ID
        当撤销偏向锁阈值超过 20 次后,jvm 会这样觉得,我是不是偏向错了呢,于是会在给这些对象加锁时重新偏向至加锁线程。

批量撤销

        当撤销偏向锁阈值超过 40 次后,jvm 会这样觉得,自己确实偏向错了,根本就不该偏向。于是整个类的所有对象,都会变为不可偏向的,新建的对象也是不可偏向的。

5. 锁消除

public static void main(String[] args) {
        @Fork(1)
        @BenchmarkMode(Mode.AverageTime)
        @Warmup(iterations = 3)
        @Measurement(iterations = 5)
        @OutputTimeUnit(TimeUnit.NANOSECONDS)
        public class MyBenchmark {
            static int x = 0;

            @Benchmark
            public void a() throws Exception {
                x++;
            }

            @Benchmark
            public void b() throws Exception {
                Object o = new Object();
                synchronized (o) {
                    x++;
                }
            }
        }
    }

由上图两个方法运行时间基本相同,因为jvm的锁消除,超过一定阈值自动去了锁,禁用锁消除后 

可见性能差异明显。 

本文基本都是参考的黑马满一行老师的讲义,在此致谢。批量重偏向和批量撤销的代码太长就不放了,有兴趣可参考链接04.036-synchronized优化原理-偏向锁-批量重偏向_哔哩哔哩_bilibili

04.037-synchronized优化原理-偏向锁-批量撤销_哔哩哔哩_bilibili  

标签:java,synchronized,对象,管程,线程,轻量级,自旋,偏向
From: https://blog.csdn.net/m0_56369671/article/details/141827941

相关文章

  • 浙江省住房和城乡建设行业继续教育刷课脚本-JavaScript编写
    脚本学习网站:https://zj.zjjsrc.cn/web/web_toUserSignUp.page脚本地址:浙江省住房和城乡建设行业专业技术人员继续教育-刷课脚本:https://greasyfork.org/zh-CN/scripts/506406-浙江省住房和城乡建设行业专业技术人员继续教育-刷课脚本教程1.插件安装(以MicrosoftEdge浏览器......
  • 浙江省建设人才远程继续教育平台刷课脚本-JavaScript编写
    脚本学习网站:https://zj.zjjsrc.cn/web/web_toUserSignUp.page脚本地址:浙江省建设人才远程继续教育平台刷课脚本:https://greasyfork.org/zh-CN/scripts/506406-浙江省住房和城乡建设行业专业技术人员继续教育-刷课脚本教程1.插件安装(以MicrosoftEdge浏览器为例)打开最中间......
  • 深入理解HTTP连接池及其在Java中的应用
    更多内容前往个人网站:孔乙己大叔        在现代的Web开发中,HTTP请求已经成为应用程序与外部服务交互的主要方式。随着微服务架构的流行,一个应用可能需要同时与多个外部服务进行通信,这导致HTTP请求的数量显著增加。为了提升性能和资源利用率,HTTP连接池成为了一个不可......
  • 【网络安全 | Java代码审计】Code-Breaking Puzzles-javacon
    未经许可,不得转载。源码:https://www.leavesongs.com/media/attachment/2018/11/23/challenge-0.0.1-SNAPSHOT.jar,下载至桌面。考察知识点:SpEL注入正文执行命令运行环境:java-jarC:\Users\86177\Desktop\challenge-0.0.1-SNAPSHOT.jar浏览器访问localhost:8080使用JD-......
  • Java生产环境下性能监控与调优指南
    Java生产环境下性能监控与调优指南目录引言Java性能监控2.1性能监控工具2.2关键性能指标Java应用性能调优3.1内存调优3.2垃圾回收调优3.3多线程优化3.4数据库连接优化3.5代码级优化结语1.引言在Java应用的生产环境下,性能监控及调优显得至关重要,它们......
  • Java线程生命周期:Java线程生命周期全景解读
    1.线程生命周期概述:不仅仅是状态转换在多线程编程中,理解线程的生命周期对于编写有效、高效的代码至关重要。线程生命周期通常描述了线程从创建到死亡的一系列状态变化过程,但其实不仅仅局限于这些状态的简单转换。线程生命周期的理解应该考虑系统资源的分配、线程调度、同步、通信......
  • JAVA基础:对象流
    目录前言序列化和反序列化对象流的创建前言之前的流我们都是使用他们读取一些字节或者字符的数据,但是在实际的应用中数据的类型除了基本数据类型之外,还有引用数据类型,对象流就是能读取引用数据类型的流序列化和反序列化对象流的本质是一个字节流,也是过程流对象流的......
  • Java小白一文讲清Java中集合相关的知识点(二)
    ListList接口和常用方法基本介绍List接口是Collection接口的子接口List集合类中的元素有序–即添加顺序和取出顺序一致、且可重复publicclassJourney{@SuppressWarnings({"all"})publicstaticvoidmain(String[]args){Listlist=newArray......
  • 【Java 基础】类和对象(构造&this&封装&static&代码块)
    ✨                           风起于青萍之末,浪成于微澜之间    ......
  • Java开发语言:ssm人力资源管理系统010(附免费源码)
    摘 要科技进步的飞速发展引起人们日常生活的巨大变化,电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流,人类发展的历史正进入一个新时代。在现实运用中,应用软件的工作规则和开发步骤,采用Java技术建设人......