AQS 队列同步器 AbstractQueuedSynchronizer 的简单使用

 队列同步器AbstractQueuedSynchronizer,是用来构建锁或者其他同步组 件的基础框架,它使用了一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获 取线程的排队工作。我们可以看一下前面介绍的重入锁,读写锁、或者信号量、CountDownLatch等源码,你会发现他们的源码中总会有一个静态内部类继承了AbstractQueuedSynchronizer,代码示例如下:

static class Sync extends AbstractQueuedSynchronizer {
    //具体逻辑
}

同步器的主要使用方式是继承,子类通过继承同步器并实现它的抽象方法来管理同步状态,在AbstractQueuedSynchronizer中有一个变量state标识同步状态,并且提供了getState()、setState(int newState)和compareAndSetState(int expect,int update))三个方法进行操作同步状态。它们能够保证状态的改变是安全的。

同步器的是基于模板方法模式设计,也就是说,使用者需要继承同步器并重写指定的方法,随后将同步器组合在自定义同步组件的实现中,并调用同步器提供的模板方法,而这些 模板方法将会调用使用者重写的方法。如下是同步器提供的模板方法:

方法名

说明

void acquire(int arg)

独占式获取同步状态,如果当前线程获取状态成功,则由该方法返回,否则将进入同步队列等待,该方法会调用重写的tryAcquire(iny arg)方法

void acquireInterruptibly(int arg)

与acquire(int arg)相同,但是该方法会响应中断,当前线程未获取到同步状态而进入同步队列中,如果当前线程被中断会抛出InterruptedException异常

 

boolean tryAcquireNanos(int arg,long nanos)

在acquireInterruptibly(int arg)基础上增加了超时限制,如果在指定时间内没获取到同步状态,会返回false,否则返回true。

 

void acquireShare(int arg)

共享式获取同步状态,与独占式的区别是同一时刻可以多个线程获取到同步状态。会调用重写方法tryAcquireShared

void acquireShareInterruptibly(int arg)

与 acquireShare(int arg)相同,但是该方法会响应中断

boolean tryAcquireSharedNanos(int arg,long nanos)

在acquireShareInterruptibly(int arg)基础上增加了超时限制,如果在指定时间内没获取到同步状态,会返回false,否则返回true。

boolean release(int arg)

独占式的释放同步状态,该方法会在释放同步状态之后,将同步队列中的第一个节点包含的线程唤醒。会调用重写方法tryRelease

boolean releaseShared(int arg)

共享式的释放同步状态,会调用重写方法tryReleaseShared

Collection<Thread>getQueueThreads()

获取等待队列上的线程集合

前面我们说过,模板方法会调用,我们覆盖重写的方法,下面我们看一下同步器提供了哪些方法需要我们覆盖重写,如下表所示,为同步器可重写的方法:

方法名

说明

protected boolean tryAcquire(int arg)

独占式获取同步状态,实现该方法需要查询当前状态并判断同步状态是否符合预期,然后再进行CAS设置同步状态

protected boolean tryRelease(int arg)

独占式释放同步状态,等待获取同步状态的线程将有机会获取同步状态

protected int tryAcquireShared(int arg)

共享式获取同步状态,返回大于0的值表示获取成功,否则获取失败。

protected boolean tryReleaseShared(int arg)

共享式释放同步状态。

protected boolean isHeldExclusively()

当前同步器是否在独占模式下被线程占用,一般该方法表示是否被当前线程独占

上面我们介绍了同步器的模板方法与可重写的方法,从方法可知同步器提供的模板方法基本上分为3类:独占式获取与释放同步状态、共享式获取与释放 同步状态和查询同步队列中的等待线程情况。自定义同步组件将使用同步器提供的模板方法 来实现自己的同步语义。下面将会通过一个独占锁的示例来深入了解一下同步器的使用。首先我们上一个模板,与JUC提供的工具类似,写一个内部类继承同步器。

public class MutexLock{
    private final Sync sync = new Sync();
    //内部类继承同步器
    private static class Sync extends AbstractQueuedSynchronizer {
	private static final long serialVersionUID = 1L;
    }
    //获取锁
    public void lock() {
    }
    //可中断获取锁
    public void lockInterruptibly() throws InterruptedException {
    }
    //尝试获取锁
    public boolean tryLock() {
	return false;
    }
    //可超时尝试获取锁
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
	return false;
    }
    //释放锁
    public void unlock() {
    }
}

如上代码所示,是写的一个模板,没有任何实现,而下面我们就需要使用同步器提供的模板方法以及可重写的方法实现上面的独占锁,这只是一个简单的例子作为学习,后面我们学习完同步器的实现原理之后,就会写出更加复杂的同步工具。我们通过上面的模板方法指导同步器提供了三种获取锁的方法,和一种释放锁的方法,我们可以实现下面的逻辑代码如下:

public class MutexLock{
    private final Sync sync = new Sync();
    private static class Sync extends AbstractQueuedSynchronizer {
	private static final long serialVersionUID = 1L;
    }
    public void lock() {
	//调用同步器的方法获取锁
	sync.acquire(1);
    }
    public void lockInterruptibly() throws InterruptedException {
	//调用同步器的方法可中断获取锁
	sync.acquireInterruptibly(1);
    }
    public boolean tryLock() {
	return false;
    }
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        //默认传入的是nanoseconds,不再做时间的转换
        return sync.tryAcquireNanos(1, time);
    }
    public void unlock() {
        //调用同步器的方法释放锁
        sync.release(1);
    }
}

前面我们说过void acquire(int arg)会调用重写的方法tryAcquire(iny arg),因此我们需要重写tryAcquire(iny arg)方法,该方法去改变同步器的状态,如果成功,则获取锁,如果失败添加到等待队列。如下代码的实现。

private static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = 1L;
    public boolean tryAcquire(int acquires) {
        //如果将状态改变为1,如果成功则获取到锁
	if (compareAndSetState(0, 1)) {
	    setExclusiveOwnerThread(Thread.currentThread());
	    return true;
	}
        return false;
    }
    //释放锁
    protected boolean tryRelease(int releases) {
        if (getState() == 0)
            throw new IllegalMonitorStateException();
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
    }
}

这篇博客主要是讲解同步器AbstractQueuedSynchronizer的基本使用方式,了解AbstractQueuedSynchronizer提供的模板方法以及可重写的方法。我们能用AbstractQueuedSynchronizer很快的构建出一个同步组件,后面我们会详细分析AbstractQueuedSynchronizer的实现原理。

猜你喜欢

转载自blog.csdn.net/wk19920726/article/details/108322547