深入理解AQS独占锁之ReentrantLock源码分析

本文深入探讨Java并发编程,分析AQS(AbstractQueuedSynchronizer)原理,包括其同步等待队列、条件等待队列、独占与共享模式。同时,详细讲解了基于AQS的ReentrantLock实现,包括公平锁与非公平锁、可重入特性,以及线程的入队、出队逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在学习本文之前,我们来思考一个问题,思考: 如何设计一把独占锁?

1. 首先,我们可以定义一个变量state,标识加锁状态。0表示没有加锁,1表示已加锁。在并发场景下,会出现多个线程同时调用加锁方法,操作state变量(将state由0改为1),为了保证只有一个线程能够修改成功,可以使用CAS来实现。其它线程会修改失败,对于修改失败的线程,意味着竞争锁失败;

2. 对于竞争锁失败的线程怎么办呢?设置一个等待队列,用于存放竞争锁失败要等待的线程,这些线程会被阻塞。当持有锁的线程释放锁之后,可以唤醒等待队列中的线程,出队,去竞争锁;

3. 线程在拿到锁执行的过程中,发现所需的某些条件不满足,需要等待另外的线程把条件满足了才能继续往下执行。这个过程要阻塞、唤醒线程,又要用到等待通知机制。等待通知机制可以使用synchronized + obj.wait()/obj.notify()/obj.notifyAll() ,而这种方式显然会有问题:不能唤醒指定的线程,或者调用...All()方法会把所有的线程都唤醒。我们需要等待唤醒指定的某个线程,使用LockSupport.park/unpark;

4. 在实现独占锁、共享锁时,对于一些共性的东西,如:入口等待队列和条件等待队列,入队、出队、CAS操作这些属于共性操作,在java中针对这些公共逻辑,可以使用一种设计模式-模板方法模式实现。因此,可以定义个抽象类。于是,AQS就被设计出来了;

等待通知机制:

synchronized + obj.wait()/obj.notify()/obj.notifyAll()

ReentrantLock + condition.await()/condition.signal()/condition.signalAll()

这两种等待唤醒机制底层用到的阻塞、唤醒线程的API都是park/unpark。ReentrantLock + condition.await()/condition.signal()/condition.signalAll()里面用到的是LockSupport.park/unpark。synchronized + obj.wait()/obj.notify()/obj.notifyAll()底层也是用的park/unpark,只不过是在JVM层面对应的API park/unpark。

补充:线程的调度是由操作系统来实现的,Java中的线程和操作系内核线程是建立了1:1的对应关系, 也就是说Java 线程都是直接映射到一个操作系统原生线程来实现的。所以,要阻塞线程最终去调操作系统提供的接口去完成。例如,Linux操作系统中有一个线程库pthead。

1. 管程 — Java同步的设计

管程:指的是管理共享变量以及对共享变量的操作过程,让他们支持并发;

互斥:同一时刻只允许一个线程访问共享资源;

同步:线程之间如何通信、协作;

MESA模型

  在管程的发展史上,先后出现过三种不同的管程模型,分别是Hasen模型、Hoare模型和MESA模型。现在正在广泛使用的是MESA模型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值