Java AQS 公平与非公平锁的原理
引言
在多线程编程中,锁是一种重要的机制,用于控制多个线程对共享资源的访问。在 Java 中,AbstractQueuedSynchronizer(AQS)是一种用于构建锁和同步器的基础框架。它支持公平和非公平锁。本文将详细讲解 AQS 的工作原理,并展示如何实现公平和非公平锁。
流程概述
以下是实现公平/非公平锁的主要流程:
步骤 | 描述 |
---|---|
步骤1 | 创建一个锁的类,继承 AQS |
步骤2 | 重写 tryAcquire 方法以支持获取锁 |
步骤3 | 重写 tryRelease 方法以支持释放锁 |
步骤4 | 实现公平锁机制(可选) |
步骤5 | 实现非公平锁机制 |
步骤详解
步骤1:创建锁的类
首先,我们创建一个自定义锁类,并继承 AbstractQueuedSynchronizer
。
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
public class MyLock extends AbstractQueuedSynchronizer {
// 判断锁是否被占用
@Override
protected boolean isHeldExclusively() {
return getState() == 1; // 当状态为1时表示锁被占用
}
}
步骤2:重写 tryAcquire
tryAcquire
方法尝试获取锁,返回是否成功。
@Override
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0, 1)) { // 尝试将状态从0变为1
setExclusiveOwnerThread(Thread.currentThread()); // 设置当前线程为持有锁的线程
return true; // 获取锁成功
}
return false; // 获取锁失败
}
步骤3:重写 tryRelease
tryRelease
方法尝试释放锁。
@Override
protected boolean tryRelease(int arg) {
if (getExclusiveOwnerThread() != Thread.currentThread()) {
throw new IllegalMonitorStateException(); // 只有持有锁的线程才能释放锁
}
setExclusiveOwnerThread(null); // 清除持有锁的线程
setState(0); // 将状态重置为0
return true; // 释放锁成功
}
步骤4:实现公平锁机制
公平锁确保线程按照请求的顺序获取锁。
@Override
protected boolean tryAcquire(int arg) {
Thread currentThread = Thread.currentThread();
if (hasQueuedPredecessors()) { // 检查队列中是否有前驱线程
return false; // 如果有,则不获取锁
}
return super.tryAcquire(arg); // 否则,调用父类方法尝试获取锁
}
步骤5:实现非公平锁机制
非公平锁允许后续请求的线程可能直接获取锁。
@Override
protected boolean tryAcquire(int arg) {
// 直接尝试获取锁,不管前面是否有其他线程在排队
if (getState() == 0 && compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true; // 获取锁成功
}
return false; // 获取锁失败
}
代码结构图
以下是类结构示意图(简化):
classDiagram
class MyLock {
-boolean isHeldExclusively()
-boolean tryAcquire(int arg)
-boolean tryRelease(int arg)
}
交互序列图
在多线程获取锁的情景中,公平与非公平锁的区别如下:
sequenceDiagram
participant T1 as Thread 1
participant T2 as Thread 2
participant Lock as MyLock
T1->>Lock: 请求锁
Lock-->>T1: 获取锁成功
T2->>Lock: 请求锁
Lock-->>T2: 等待
T1->>Lock: 释放锁
Lock-->>T2: 获取锁成功 (公平锁)
时间线图
以下是公平和非公平锁的请求处理甘特图示例:
gantt
title 公平与非公平锁的请求处理
dateFormat HH:mm
section 公平锁
T1获取锁 :a1, 00:00, 1m
T2等待 :a2, 00:01, 1m
T1释放锁 :after a1 , 1m
T2获取锁 :after a2 , 1m
section 非公平锁
T1获取锁 :b1, 00:00, 1m
T2获取锁 :b2, 00:00, 1m
结论
通过以上步骤,我们实现了公平与非公平锁。公平锁确保线程按照顺序获取锁,而非公平锁则允许等待的线程在某些情况下先获取锁。AQS 提供了一个强大的框架,使得我们可以在此基础上构建更复杂的同步器。希望这篇文章能帮助你理解 AQS 的实现原理和使用方式。