Java 中的各种锁及其原理

本文详细探讨了Java中的锁机制,包括Synchronized锁的底层实现,如偏向锁、轻量级锁和重量级锁,以及字节码层面的使用方式。此外,还介绍了java.util.concurrent包中的AbstractQueuedSynchronizer(AQS)及其与自旋锁的关系,分析了AQS为何使用CLH锁而不是MCS锁的原因。最后,文章对比了synchronized与volatile的关键字区别,以及ReentrantLock与synchronized的异同,并讨论了死锁的概念和解决方案。
摘要由CSDN通过智能技术生成

概览

在并发编程中,锁是一种常用的保证线程安全的方法。Java 中常用的锁主要有两类,一种Synchronized 修饰的锁,被称为 Java 内置锁或监视器锁。另一种就是在 J2SE 1.5 版本之后的 java.util.concurrent 包(下称 j.u.c 包)中的各类同步器,包括 ReentrantLock(可重入锁),ReentrantReadWriteLock(可重入读写锁),Semaphore(信号量),CountDownLatch 等。这些同步器都是基于 AbstractQueuedSynchronizer(下称 AQS)这个简单的框架来构建的,而 AQS 类的核心数据结构是一种名为 Craig, Landin, and Hagersten locks(下称 CLH 锁)的变体。

Synchronized锁

Synchronized 锁的底层类别

不同锁下对象头中的内容

https://gitee.com/hzm_pwj/FigureBed/raw/master/giteeImg/20210321185335.png

这里主要是与锁相关的内容

偏向锁:线程 ID,锁标志为 01

轻量级锁:指向栈帧中锁记录的指针,锁标志为 00

重量级锁:指向互斥量(重量级锁)的指针,锁标志为 10

实现偏向锁两个额外的域,第一个域用于记录线程id,第二个域用于记录锁的状态。每次都是将自己的线程号放到标记字段中,这样每次如果每次线程号都是自己的,那么就不用进行锁处理了。

如果不是自己的,同时状态又是被锁,那么就会进行锁是升级,也就是升级为轻量级锁,以保证只有一个线程能获得锁。

偏向锁

背景:大多数时候都不会遇到锁,并且往往需要锁的都是同一个线程

加锁:当一个线程访问同步块并获得锁时,会在对象的头部和栈帧中记录存储该线程的ID

解锁:
如果存储的ID不是自己的ID,说明其它线程持有了锁,那么就会发起竞争,如果存储的线程没有使用,那么就会释放锁
如果是自己的ID,那么说明没有其他线程持有锁,就可以直接访问数据了

这样只有发生锁竞争的时候才会释放锁,从而提高锁的性能

轻量级锁

通过自旋的方式来对线程进行阻塞

加锁:在线程的栈帧中创建存储锁记录的空间,将对象头的mark word复制到锁记录中,尝试将对象头的mark word替换为指向锁记录的指针,如果成功,说明没有竞争直接获得锁,如果失败,说明锁已经被占用,通过自旋获得锁

解锁:用栈帧中的锁记录替换对象头的mark word,如果成功,说明没有竞争,如果失败,说明有其他线程在尝试获得锁,存在竞争,锁会膨胀为重量级锁,并不再恢复

重量级锁:获得锁失败时就会陷入阻塞,等待被唤醒。

轻量级锁加锁过程

线程在执行同步块之前,JVM 会先在当前线程的栈帧中创建用于存储锁记录的空间,然后将对象头中的 Mark Word 复制到锁记录中。然后线程尝试使用 CAS 将对象头中的 Mark Word 替换为指向锁记录的指针。

锁记录:用于存储锁对象目前的Mark Word的拷贝(也就是对象自身的 HashCode,分代年龄等信息)

偏向锁则只是将 Mark Word 中的线程 ID 修改为自己的 ID

重量级锁则是让线程进入等待状态,而不是自旋状态

字节码层面

synchronized关键字最主要的三种使⽤⽅式

修饰实例⽅法: 作⽤于当前对象实例加锁,进⼊同步代码前要获得当前对象实例的锁

修饰静态⽅法: 也就是给当前类加锁,会作⽤于类的所有对象实例,因为静态成员不属于任何⼀ 个实例对象,是类成员( static 表明这是该类的⼀个静态资源,不管new了多少个对象,只有 ⼀份)。所以如果⼀个线程A调⽤⼀个实例对象的⾮静态 synchronized ⽅法,⽽线程B需要调⽤ 这个实例对象所属类的静态 synchronized ⽅法,是允许的,不会发⽣互斥现象,因为访问静态 synchronized ⽅法占⽤的锁是当前类的锁,⽽访问⾮静态 synchronized ⽅法占⽤的锁是当前 实例对象锁。

修饰代码块: 指定加锁对象,对给定对象加锁,进⼊同步代码库前要获得给定对象的锁。

https://gitee.com/hzm_pwj/FigureBed/raw/master/giteeImg/20201226200709.png

synchronized 同步语句块的情况

public class SynchronizedDemo {
   
    public void method
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值