Lock中AbstractQueuedSynchronizer分析

Lock的实现基于队列同步器AbstractQueuedSynchronizer的子类实现的AbstractQueuedSynchronizer用一个int表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。同步器的设计基于模板方法,即开发者需要继承同步器并重写指定的方法,随后同步器组合将在自定义同步组件的过程中,调用同步器的模板方法,这些模板方法又去调用开发者编写的方法。

Lock接口的方法主要为lock(),unlock(),tryLock()(非阻塞式获取锁,没有获取时返回false)等。对于一个需要实现特定需求的Lock(比如独占锁,两个线程共享锁等),都需要通过实现AbstractQueuedSynchronizer的子类来完成。
AbstractQueuedSynchronizer类中需要重写的方法有:tryAcquire(int ),tryRelease(int),tryAcquireShared(int ),tryReleaseShared(int ),isHeldExclusively().其中前面两个是独占式获取锁和释放锁,后面两个是共享式获取和释放锁,最后一个是当前锁是否被同步器独占。
参考《并发编程的艺术》的例子,

```
public class Mutex implements Lock{

// 静态内部类,自定义同步器
private static class Sync extends AbstractQueuedSynchronizer {

    // 当状态为0的是获取锁
    @Override
    protected boolean tryAcquire(int arg) {
    // 如果经过CAS设置状态成功(同步状态设置为1),则代表获取了同步状态
    if (compareAndSetState(0, 1)) {
        setExclusiveOwnerThread(Thread.currentThread());
        return true;
    }
    return false;
    }

    // 释放锁,将状态设置为0
    @Override
    protected boolean tryRelease(int arg) {
    if (getState() == 0)
        throw new IllegalMonitorStateException();

    // 将同步状态重置为0.
    setExclusiveOwnerThread(null);
    setState(0);
    return true;
    }

    // 是否处于独占状态
    @Override
    protected boolean isHeldExclusively() {
    return getState() == 1;
    }

    // 返回一个Condition,每个condition都包含了一个condition队列(暂时不管,后面讲这个Condition是干嘛的.)
    Condition newConditio() {
    return new ConditionObject();
    }

    }

    // 静态内部类继承并重写了AbstractQueuedSynchronizer之后,仅需要将操作代理到Sync上即可
    private final Sync sync = new Sync();

    @Override
    public void lock() {
    sync.acquire(1);
    }

    @Override
    public boolean tryLock() {
    return sync.tryAcquire(1);
    }

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

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

    // 不是继承来的,原书中这么写的,官方样例也是这么写的,感觉是对外提供的方法
    public boolean isLocked() {
    return sync.isHeldExclusively();
    }

    // 不是继承来的,原书中这么写的,官方样例也是这么写的,感觉是对外提供的方法
    public boolean hasQueueThreads() {
    return sync.hasQueuedThreads();
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(time));
    }

/**
 * @param args
 */
public static void main(String[] args) {
    // TODO Auto-generated method stub

}

}

这是一个自定义的独占式的锁。其实现Lock接口。内部建立一个内部类Sync,Sync继承同步队列,并重写tryAcquire等方法。

@Override
protected boolean tryAcquire(int arg) {
// 如果经过CAS设置状态成功(同步状态设置为1),则代表获取了同步状态
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread()); //如果获取锁,就将当前线程设为独占线程
return true;
}
return false;
}

在程序中,使用此独占锁的方法是:mutex.lock();
然后,在mutex的lock方法中,调用了同步队列的sync.acquire(1)方法。

public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}

这个方法的逻辑是:
a.首先调用自定义Sync实现的tryAcquire(arg) 方法,尝试获取独占锁。
b. 如果获取不到,(addWaiter(Node.EXCLUSIVE), arg)就将此线程生成同步队列的node元素节点,并尝试加到队列的结尾。
c. acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法是不断的循环,判断其pre节点是否为头节点,如果是的话,就通过tryAcquire判断是否能独占式获取锁,如果成功的话,将当前节点设为头节点,返回false
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&  //如果没获取到,就将调用LockSupport的park阻塞此线程
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
acquireQueued方法只有获取锁情况下才会返回,返回后,acquire()方法返回,说明此线程获取锁

在调用Mutex的unlock()方法释放锁时,mutex内部调用Sync的Sync.release(1)方法,同步器中的release方法如下:

public final boolean release(int arg) {
if (tryRelease(arg)) { //调用Sync的tryRelease()方法对锁的释放
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}


unparkSuccessor(h)通过LockSupport.unpark(s.thread);唤醒后续节点的线程,其中s=h.next;

2、对于共享式获取锁,其区别和独占式获取锁在于:Lock和Unlock方法中,调用的是tryAcquireShared和tryReleaseShared方法。这两个方法是以原子操作设置状态量,比如能被两个线程占用的锁,状态量就在0,1,2之间不断变动,被一个线程占用,状态量就减1,被线程unlock,状态量就加1。

猜你喜欢

转载自blog.csdn.net/andyzhu_2005/article/details/79878430
今日推荐