上一篇文章我们学习了AQS的核心规范,通过官方提供的使用示例,实现一把锁似乎变得更简单了,下面我们就来手搓一把锁,基于AQS用代码来实现自己的一把自定义锁。通过这个过程,加深对AQS的理解,以及对锁的进一步理解。
自定义锁需求分析
首先,我们先来分析一把锁有哪些特性?实现一把锁需要做哪些工作?理清这些再结合AQS提供基础框架提供的能力去实现。以ReentrantLock为例,一把锁通常具有以下核心特性:
(1)可重入性。同一线程可多次获取同一把锁而不会死锁,每次获取锁时持有计数递增,释放时递减直至完全解锁。
(2)公平性控制。支持公平锁与非公平锁两种模式。非公平锁(默认)即允许线程插队获取锁,吞吐量较高但可能导致线程饥饿;公平锁即按请求顺序分配锁,保证先到先得但性能较低。
(3)可中断与超时机制。支持在等待锁时响应中断请求。
(4)条件变量支持。可创建多个Condition对象,实现精细化的线程等待/唤醒控制。
特性搞清楚了,那么锁要对外提供哪些能力?根据前面我们学过的锁相关知识,我们已经知道java.util.concurrent.locks.Lock接口已经帮我们抽象好了一把锁的基础能力,也就是lock、tryLock、unlock、newCondition等能力,所以我们的自定义锁需要实现Lock接口。
还有一些关键的问题,用什么表示锁状态?用什么实现线程排队?用什么实现线程阻塞唤醒?刚好AQS给我们提供了实现这些功能的基础框架:
(1)AQS内部通过 volatile int state 字段表示同步状态;0表示空闲,1表示被占用。通过CAS操作保证同步状态修改的原子性,这是锁实现的基础。对于可重入锁,state需要记录重入次数,每次重入时state递增。
(2)AQS内部维护CLH变体的FIFO双向队列,之所以称为CLH变体,是因为源自Craig、Landin和Hagersten提出的自旋锁队列,原始CLH为单向链表,而AQS将其改造为双向链表结构。每个节点(Node)包含等待线程引用(waiter)、状态标志(status)及前驱节点(prev)和后继节点(next)引用,支持高效插入与删除操作,状态字段用于控制线程阻塞与唤醒逻辑。将未获取锁的线程封装为Node节点入队,实现线程唤醒机制公平锁需严格按队列顺序获取锁,非公平锁允许插队竞争。
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
...
/** CLH Nodes */
abstract static class Node {
volatile Node prev; // 初始时通过casTail操作附加到前驱节点
volatile Node next; // 当可被信号通知时,该值明显不为null
Thread waiter; // 当节点入队时,该值明显不为null
volatile int status; // 由持有者线程写入,其他线程通过原子位操作修改
...
}
/**
* 同步状态
*/
private volatile int state;
...
}
好了,下面开始进入代码实现环节。
自定义锁代码实现
实现Lock接口
第一步,创建自定义锁类,实现Lock接口。
public class MyReentrantLock implements Lock {
@Override
public void lock() {
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public boolean tryLock() {
return false;
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public void unlock() {
}
@Override
public Condition newCondition() {
return null;
}
}
Lock接口的这几个方法之前已经学过,在此不再详述。
创建AQS子类并实现相关方法
第二步,根据AQS规范,就是自定义锁类内部创建AbstractQueuedSynchronizer的子类并实现tryAcquire/tryRelease/tryAcquireShared/tryReleaseShared/isHeldExclusively等方法。我们将这个子类称做同步器。
public class MyReentrantLock implements Lock {
//同步器
private static class Sync extends AbstractQueuedSynchronizer {
protected Sync() {
super();
}
@Override
protected boolean tryAcquire(int arg) {
return super.tryAcquire(arg);
}
@Override
protected boolean tryRelease(int arg) {
return super.tryRelease(arg);
}
@Override
protected int tryAcquireShared(int arg) {
return super.tryAcquireShared(arg);
}
@Override
protected boolean tryReleaseShared(int arg) {
return super.tryReleaseShared(arg);
}
@Override
protected boolean isHeldExclusively() {
return super.isHeldExclusively();
}
}
@Override
public void lock()</
基于AQS手写可重入锁实战

最低0.47元/天 解锁文章
458

被折叠的 条评论
为什么被折叠?



