AQS 理解 及不可重入锁实现
由于目前水平有限,只是写出作者目前对 aqs的简单理解,有错误还请评论指证
AQS是什么
Aqs是java.util.concurrent 包下的一个抽象队列 同步器类,被简写为英文AQS,
我认为 可以把他理解 为一个 实现 自定义 锁的 一个具体的框架 ,我们可以根据这个 框架来实现出 自己的锁, 比如我们经常用到的 ReentrantLock,线程池 等 其他需要锁的类 内部类都实现了
AQS也就是 AbstractQueuedSynchronizer
AQS解释
AQS类的内部 是怎么来实现 原子性呢? 就是用Unsafe 来进行的,
它的内部 维护了一个共享的变量 state , 当satae 为0的时候,代表 没有被 其他线程 抢占到,如果state 为1
就代表 被其他线程抢占到了 这时候 其他线程会进入fifo 队列中等待
这么说太抽象了,我们直接上代码 来简单理解作者目前认为的 AQS
代码实现
首先 我们有一个 类 来继承了 AQS
final class Sync extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
//在 自定义锁中的lock方法去尝试给线程上锁的时候
//根据传入的 arg 表示 来修改 AbstractQueuedSynchronizer类内部 的state 表示 进行上锁
if (arg == 1) {//代表 要执行的是上锁操作
if (compareAndSetState(0, 1)) {//原来0 是 锁未被其他线程使用
// 修改为1 代表 上锁成功
setExclusiveOwnerThread(Thread.currentThread());//设置 拥有锁的线程为 当前线程
System.out.println(6666666);
return true;
}
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
if (arg == 1) { //代表执行的是解锁操作
if (getState() == 0) {//当前线程 进行解锁操作 发现 锁 并未被占用 所以抛出异常
throw new IllegalMonitorStateException();
}
setExclusiveOwnerThread(null);
setState(0);
return true;
}
return false;
}
@Override
protected boolean isHeldExclusively() {//看当前线程是否 被占用
return getState() == 1;
}
protected Condition newCondition() {
return new ConditionObject();
}
}
然后 我们 有一个自定义的锁 类, 内部 有一个该 Sync类的实现,通过操作 我们重写的AQS类的部分逻辑 来进行 加锁 操作
//不可重入锁
class MyLock implements Lock {
static Sync sync = new Sync();
@Override
public void lock() {//不成功 进入等待队列
sync.acquire(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {//可打断 不成功进入等待队列
sync.acquireInterruptibly(1);
}
@Override
public boolean tryLock() {//尝试上锁 立即返回
return sync.tryAcquire(1);
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, TimeUnit.SECONDS.toNanos(1));
}
@Override
public void unlock() {
sync.release(1);
}
@Override
public Condition newCondition() {
return sync.newCondition();
}
}
看完这些代码 可能 有人会有疑惑,为什么 这个自定义的锁类,中调用的sync的方法 有的在 上面自定义的Sync类中没有,因为 我们调用的是其父类AQS的方法,我们目前自定义的sync类中的方法 就是 简单来模拟 一些AQS内部主要的加锁解锁的方法
我们再看 测试类
public class AqsTest {
public static void main(String[] args) {
MyLock myLock = new MyLock();
new Thread(new Runnable() {
@Override
public void run() {
try {
myLock.lock();
System.out.println("上锁了t1");
sleep(1000);
// myLock.lock();
// System.out.println("上锁了t1重入");
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
System.out.println("解锁了t1");
myLock.unlock();
}
}
}, "t1").start();
new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("线程t2尝试获得锁");
myLock.lock();
System.out.println("上锁了t2");
} finally {
System.out.println("解锁了t2");
myLock.unlock();
}
}
}, "t2").start();
}
}
代码分析
我们运行出来结果为
我们有的可能看到上面的代码实现就懵逼了,现在我们现在来看代码,稍微解读一下
首先 我们的 t1线程调用了 自定义锁的lock方法,开始去上锁
然后 到 自定义的 lock方法内部 去调用 sync 的acquire方法,我们点进去看acquire方法的实现
在aqs类内部,首先判断 这个线程是否获得锁,而这个 tryAcquire方法 走的逻辑 就是我们前面 重写的方法逻辑,先判断是否为上锁操作,然后用cas修改sate的状态值,然后设置 锁的拥有线程为当前线程,然后返回true,这时候 t2线程进来的话,t1线程由于没有释放锁tryacquire必然是false,这时候 就会走 aqs内部的acquire的方法,来进入等待队列
这时候 我们可能就对 aqs 有了一点点的理解,现在 我们再来看 ReentrantLock的源码实现,发现 好像用的是类似的逻辑
总之 我目前认为aqs 就是 一个功能强大 锁实现框架