JUC并发编程之Synchronized锁优化

编程入门 行业动态 更新时间:2024-10-28 00:26:58

<a href=https://www.elefans.com/category/jswz/34/1748327.html style=JUC并发编程之Synchronized锁优化"/>

JUC并发编程之Synchronized锁优化

目录

1. Java对象头

 2. Synchronized锁优化

2.1 偏向锁

2.2 轻量级锁

 2.3 重量级锁

2.4 各种锁对比

1. Java对象头

HotSpot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。

对象头是对象在内存中的元数据信息,它包含了对象的一些重要信息,如对象的锁状态、类元数据指针、数组长度等。对象头是Java虚拟机(JVM)管理和操作对象的关键部分。

Java对象头通常包括以下信息:

  1. Mark Word(标记字):Mark Word是对象头中最重要的部分,占据了对象头的大部分空间。它包含了对象的哈希码、锁信息、垃圾回收信息等。Mark Word的具体结构因JVM实现而异,通常包括以下内容:

    • 对象标志位:用于标识对象的状态,例如是否被锁定、是否已经被回收等。
    • 哈希码:用于支持hashCode方法,以及在哈希表中查找对象的位置。
    • 分代年龄:用于垃圾回收的分代算法,标记对象所属的年代。
    • 锁信息:用于支持同步操作,包括偏向锁、轻量级锁和重量级锁的状态信息。
  2. 类型指针:类型指针指向对象的类元数据,它用于确定对象所属的类,以支持对象的方法调用和字段访问。

  3. 数组长度(仅对数组对象有效):如果对象是数组,对象头中会包括数组长度信息。

对象头的结构和大小可能会因不同的JVM实现和对象类型而异。对象头通常占用一定的内存空间,因此它会增加每个对象的内存开销。对象头的信息对于垃圾回收、同步和虚拟机运行时的对象操作非常重要。其中Mark Word结构如下图所示。

 2. Synchronized锁优化

jdk1.6之前,synchronized属于重量级锁(悲观锁) ,jdk1.6之后被进行了大幅度优化,支持锁升级制度缓解加锁和解锁造成的性能费,锁的级别采用: 偏向锁 -> 轻量级锁 -> 重量级锁。

  • 原子性: synchronized依靠两个字节码指令monitorentermonitorexit,可以保证被synchronized修饰的代码在同一时间只能被一个线程访问

  • 可见性: JMM(Java内存模型)规定,内存主要分为主内存和工作内存两种,每个线程拥有不同的工作内存,线程工作时会从主内存中拷贝一份变量到工作内存中。代码执行后,有时工作内存中的变量无法及时刷新到主内存中,或者工作内存无法及时获取主内存的最新值,导致共享变量在不同线程间处于不可见性,由此JMM对synchronized做了2条规定:

    a.线程解锁前,必须把变量的最新值刷新到主内存中。

    b.线程加锁时,先清空工作内存中的变量值,从主内存中重新获取最新值到工作内存中

  • .有序性: 有时候编译器和处理器为了提升代码效率,会进行指令重排序,但是as-if-serial规定无论怎么重排序,单线程程序的执行结果都不能被改变,而synchronized保证了被修饰的程序在同一时间内只能被同一线程访问, 所以其也算是保证了有序性,但synchronized实际上并不是禁止了被修饰的代码指令重排序。

2.1 偏向锁

偏向锁的思想是,如果一个线程获得了锁,那么锁就进入偏向模式,此时,Mark Word的结构也变为偏向锁结构,当这个线程再次请求锁时,无需再做任何同步操作即可再次获取锁,这样就省去了大量有关锁申请的操作,从而也就提供程序的性能。所以对于没有锁竞争的场合,偏向锁有很好的优化效果,但是在有多线程竞争锁的场合,偏向锁就失效了,这种场合下不应该使用偏向锁,否则会得不偿失,偏向锁失败后,将会优先升级为轻量级锁 基本工作过程:
当线程A访问代码块并获取锁对象时,会通过CAS在Mark Word中记录偏向的锁的threadID,因为偏向锁不会主动释放锁,因此以后再次获取锁的时候,需要比较当前线程的 threadID 和 Mark Word 中的threadID是否一致,如果一致,则无需使用CAS来加锁、解锁;如果不一致,则是因为有其他线程如线程b 来竞争该锁,而偏向锁时不会主动释放锁,因此 Mark Word 存储的还是 线程a 的threadID,那么需要查看 Mark Word 中记录的 线程a 是否存活,如果没有存活,那么锁对象被重置为无锁状态,线程b 可以竞争将其设置为偏向锁;如果存活,那么立刻查找线程a的栈帧信息,如果还是需要继续持有这个锁, 那么暂停当前线程a,撤销偏向锁,升级为轻量级锁,如果 线程不再使用该锁,那么将锁状态设为无锁状态,重新偏向新的线程。

在java中偏向锁是默认开启的,绝大多数 情况下,对于加锁的程序大多都会有两个以上的线程去竞争,如果开启偏向锁,反而会加剧锁的资源消耗,可以通过jvm参数启动或关闭偏向锁:

-XX:-UseBiasedLocking = false//偏向锁的启动延迟默认为5秒,可以取消这个延迟:
XX:BiasedLockingStartUpDelay=0

2.2 轻量级锁

当多个线程竞争同一个锁时,偏向锁将转化为轻量级锁。轻量级锁通过CAS(Compare and Swap)操作来尝试获取锁,而不会让线程阻塞。如果CAS操作成功,线程获得锁;否则,线程将升级为重量级锁。轻量级锁是由偏向锁升级而来,它考虑的情况是竞争锁的线程不多,而且线程持有锁的时间也不长的情景。轻量级锁能够提升程序性能的依据是“对绝大部分的锁,在整个同步周期内都不存在竞争”。轻量级锁加锁流程如下图所示。

 

 2.3 重量级锁

当轻量级锁无法满足需求,多个线程仍然竞争同一个锁时,锁会升级为重量级锁。在这种情况下,线程将阻塞,直到获取锁。

2.4 各种锁对比

参考:
链接:

更多推荐

JUC并发编程之Synchronized锁优化

本文发布于:2023-12-03 07:02:54,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1652441.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:JUC   Synchronized

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!