AQS学习笔记(三)开始使用AQS

写在前面

在上一篇AQS学习笔记(二)AbstractQueuedSynchronizer概览中,已经对AQS进行了一些通读,有了最基本的概念掌握。现在开始尝试着使用AQS

不过我们也已经知道了,Doug Lea告诉我们AQS应该被定义为非公开的内部类在类的内部封闭地的实现外部需要的某些属性。官方给出了一个示例实现了一个锁,即便AQS不仅仅可以被用来做成锁的组件,也可以用作其他的,但从学习角度上来讲,我们就先来研究一下官方用例。

不过在这之前,先大致了解一下Lock接口的使用

Lock接口

概览

Lock接口同样是在J.U.C下一个叫locks包中的一个接口,作者是Doug Lea。官方对Lock接口的说明是,其提供了一些比synchronized更易扩展更易维护锁操作的方法。也可以使用Condition来支持多关联。

方法很简单,如下

方法名及参数返回值说明
lock(): void获取锁。如果该锁不可用,当前线程会被阻塞休眠直到获取到锁
lockInterruptibly(): void与lock()不同的是,当前线程在阻塞时可接受其他线程的中断
tryLock(): boolean获取锁,在获取锁失败时会返回false而不是阻塞,成功时返回true
tryLock(long time, TimeUnit unit): boolearn获取锁,失败时线程阻塞,直到获取到锁;或者被中断;或者超时退出
unlock():void释放锁
newCondition(): Condition获取一个新绑定到该锁上的Condition实例

案例

互斥锁

官方Demo是一个独占的互斥锁,仅有一个线程能在同一时间内可获取锁,先把框架搭好

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

public class MyMutex implements Lock {


    private static class MySync extends AbstractQueuedSynchronizer {

    }

    @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;
    }
}

此时我们需要对AQS的行为进行定义
根据需求,要求同一时刻仅有单线程独占锁,所以将MySync的同步状态值总量定义1,同步值除了0和1都是非法状态。也因为是独占式的锁,所以需要重写AQS的tryAcquire()方法、tryRelease()方法和isHeldExclusively()方法;独占模式还需要使用Condition,所以对外提供获取其内部类ConditionObject的方法

对同步状态值的操作,使用getState()setState(int newState)compareAndSetState(int expect, int update)所以有

    private static class MySync extends AbstractQueuedSynchronizer {

        @Override
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }

        @Override
        protected boolean tryAcquire(int acquires) {
            assert acquires == 1; // 在这里使用断言,是因为其他值非法
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        @Override
        protected boolean tryRelease(int releases) {
            assert releases == 1; // 在这里使用断言,是因为其他值非法
            if(getState() == 0) throw new IllegalMonitorStateException();
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
        
        Condition newCondition() {
            return new ConditionObject();
        }
    }

其中代码使用了一个setExclusiveOwnerThread的独占式地方法,此方法来源于AQS的抽象父类AbstractOwnableSynchronizer,是Doug Lea在JDK1.6以后添加的
其内部维护了一个transient的线程成员变量exclusiveOwnerThread,除了set方法为其赋值以外,也提供了getExclusiveOwnerThread取出该变量供子类使用

官方示例中还有一个用于反序列化的readObject方法,这里就不再展示了

剩下的就是补充Lock的外层方法,经简单扩展后,代码为

import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

public class MyMutex implements Lock, Serializable {


    private static class MySync extends AbstractQueuedSynchronizer {

        @Override
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }

        @Override
        protected boolean tryAcquire(int acquires) {
            assert acquires == 1; // 在这里使用断言,是因为其他值非法
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        @Override
        protected boolean tryRelease(int releases) {
            assert releases == 1; // 在这里使用断言,是因为其他值非法
            if(getState() == 0) throw new IllegalMonitorStateException();
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        Condition newCondition() {
            return new ConditionObject();
        }
    }

    private final MySync sync = new MySync();

    // 重写Lock接口的方法
    @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 timeout, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

    @Override
    public void unlock() {
        sync.tryRelease(1);
    }

    @Override
    public Condition newCondition() {
        return sync.newCondition();
    }

    // 额外提供的方法
    public boolean isLocked() {
        return sync.isHeldExclusively();
    }

    public boolean hasQueuedThreads() {
        return sync.hasQueuedThreads(); // 是否有等待线程
    }
}

至此,官方用例就完成了。实现了一个简单的互斥锁

双线程共享锁

这个demo是《并发编程》里提供的一个共享锁例子。同一时刻最多允许有两个线程持有,所以同步状态最大值设置为2,每获取一次则状态值减一,0表示已经有两个线程持有;不支持重入。

该锁的取名为TwinsLock,实现过程与上个例子类似,先搭建骨架,再写AQS子类的实现,最后提供对外暴露的方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值