锁相关的原理要掌握

简介: 《基础》

sychronize的实现原理是怎么样的?

public class SyncTest {
    public void syncBlock(){
        synchronized (this){
            System.out.println("hello block");
        }
    }
    public synchronized void syncMethod(){
        System.out.println("hello method");
    }
}点击复制代码复制出错复制成功

当SyncTest.java被编译成class文件的时候,synchronized关键字和synchronized方法的字节码略有不同,我们可以用javap -v 命令查看class文件对应的JVM字节码信息,部分信息如下:

{
  public void syncBlock();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: aload_0
         1: dup
         2: astore_1
         3: monitorenter                       // monitorenter指令进入同步块
         4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         7: ldc           #3                  // String hello block
         9: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        12: aload_1
        13: monitorexit                          // monitorexit指令退出同步块
        14: goto          22
        17: astore_2
        18: aload_1
        19: monitorexit                          // monitorexit指令退出同步块
        20: aload_2
        21: athrow
        22: return
      Exception table:
         from    to  target type
             4    14    17   any
            17    20    17   any
  public synchronized void syncMethod();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED      //添加了ACC_SYNCHRONIZED标记
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #5                  // String hello method
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
}点击复制代码复制出错复制成功

对于synchronized关键字而言,javac在编译时,会生成对应的monitorentermonitorexit指令分别对应synchronized同步块的进入和退出,有两个monitorexit指令的原因是为了保证抛异常的情况下也能释放锁,所以javac为同步代码块添加了一个隐式的try-finally,在finally中会调用monitorexit命令释放锁。

而对于synchronized方法而言,javac为其生成了一个ACC_SYNCHRONIZED关键字,在JVM进行方法调用时,发现调用的方法被ACC_SYNCHRONIZED修饰,则会先尝试获得锁。

synchronized锁执行流程图

这是网上看到的一个流程图:

就是Java对象的内存布局其实由对象头+实例数据+对齐填充三部分组成,而对象头主要包含Mark Word+指向对象所属的类的指针组成。Mark Word主要用于存储对象自身的运行时数据,哈希码,GC分代年龄,锁标志等。

下面就是Mark Word的数据映射表

偏向锁

根据上面的表来看,Mark Word后三位为101时,加锁对象的状态为偏向锁,偏向锁的意义在于同一个线程访问sychronize代码块时不需要进行加锁,解锁操作,性能开销更低(HotSpot[1]的作者经过研究发现,大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。)

因为正常情况下,当一个线程访问同步块并获取轻量级锁时,需要进行CAS操作将对象头的锁记录里指向当前线程的栈中的锁记录,执行完毕后需要释放轻量级锁。如果是同一个线程多次访问sychronize代码块,多次获取和释放轻量级,开销会偏大,所以会一开始判断对象是无锁状态,会将对象头设置为偏向锁,并且这个的线程ID添加到锁对象的Mark Word中,后续同一线程判断加锁标志是偏向锁,并且线程ID一致就可以直接执行。偏向锁的加锁过程:

场景一:当锁对象第一次被线程获得锁的时候

线程发现是匿名偏向状态(也就是锁对象的Mark Word没有存储线程ID),则会用CAS指令,将 mark word中的thread id由0改成当前线程Id。如果成功,则代表获得了偏向锁,继续执行同步块中的代码。否则,将偏向锁撤销,升级为轻量级锁。

场景二:当获取偏向锁的线程再次进入同步块时

发现锁对象存储的线程ID就是当前线程的ID,会往当前线程的栈中添加一条 DisplacedMarkWord为空的 LockRecord中,然后继续执行同步块的代码,因为操纵的是线程私有的栈,因此不需要用到CAS指令;由此可见偏向锁模式下,当被偏向的线程再次尝试获得锁时,仅仅进行几个简单的操作就可以了,在这种情况下, synchronized关键字带来的性能开销基本可以忽略。

场景二:当没有获得锁的线程进入同步块时

当没有获得锁的线程进入同步块时,发现当前是偏向锁状态,并且存储的是其他线程ID(也就是其他线程正在持有偏向锁),则会进入到撤销偏向锁的逻辑里,一般来说,会在 safepoint中去查看偏向的线程是否还存活

  • 如果线程存活且还在同步块中执行, 则将锁升级为轻量级锁,原偏向的线程继续拥有锁,只不过持有的是轻量级锁,继续执行代码块,执行完之后按照轻量级锁的解锁方式进行解锁,而其他线程则进行自旋,尝试获得轻量级锁。
  • 如果偏向的线程已经不存活或者不在同步块中, 则将对象头的 mark word改为无锁状态(unlocked)

由此可见,偏向锁升级的时机为:当一个线程获得了偏向锁,在执行时,只要有另一个线程尝试获得偏向锁,并且当前持有偏向锁的线程还在同步块中执行,则该偏向锁就会升级成轻量级锁。

相关文章
|
Java
java8中List对象转另一个List对象
java8中List对象转另一个List对象
846 0
|
数据采集 JavaScript 定位技术
代理IP以及动态拨号VPS的关系是什么?
虽然这两种技术在表面上看似相似,实际上它们在功能、应用场景以及用户需求满足方面有着本质的区别。
|
IDE 开发工具 Android开发
Kotlin 的静态代码分析工具
Kotlin 的静态代码分析工具
511 0
|
缓存 算法 Java
GC垃圾收集算法
这篇文章详细讨论了垃圾收集(GC)的几种算法,包括引用计数、可达性分析、标记-清除、标记-复制和标记-整理算法,并介绍了这些算法的优缺点和适用场景。
237 0
GC垃圾收集算法
|
人工智能
妙笔生词是AI音乐创作业内自动写原创歌词的软件
妙笔生词智能写歌词软件,能软件助你轻松谱写多样化风格的歌词,流行、民谣、摇滚、中国风、儿歌,应有尽有。按需押韵,接龙续写,甚至能仿写喜爱歌曲。支持中文、英文、粤语、日语四种语言
|
存储 API
Elasticsearch快照备份与恢复 - 蓝易云
以上步骤可以帮助你在Elasticsearch中实现快照备份和恢复。注意,这些操作可能需要特定的权限和配置,所以在进行操作前,确保你具备足够的权限并已正确配置Elasticsearch。
382 0
|
JavaScript 安全 前端开发
【超详细】Zod 入门教程
Zod 是一个以 TypeScript 为首的模式声明和验证库 ,弥补了 TypeScript 无法在运行时进行校验的问题 ,既可以用在服务端也可以运行在客户端,以保障 Web Apps 的类型安全
1450 0
【超详细】Zod 入门教程
element 下拉框滚动加载
element 下拉框滚动加载
458 0
IDEA、关闭当前文件、快捷键改为Ctrl + W
IDEA、关闭当前文件、快捷键改为Ctrl + W
963 0
IDEA、关闭当前文件、快捷键改为Ctrl + W
|
机器学习/深度学习 自然语言处理 算法
Transformer模型详解
考虑到RNN(或者LSTM、GRU等)的计算限制为是顺序的,也就是说RNN相关算法只能从左向右依次计算或者从右向左依次计算,这种机制带来了两个问题: 时间片t的计算依赖t-1时刻的计算结果,这样限制了模型的并行能力; 顺序计算的过程中信息会丢失,尽管LSTM等门机制的结构一定程度上缓解了长期依赖的问题,但是对于特别长期的依赖现象,LSTM依旧无能为力。
3363 0
Transformer模型详解

热门文章

最新文章