AbstractQueuedSynchronizer的介绍

简介:

在AQS中,实现了一个FIFO的队列,主要通过waitStatus来控制队列单元的状态(park和unpark)。AQS实现了共享锁和排它锁的算法。

 

Node {
    int waitStatus;
    Node prev;
    Node next;
    Node nextWaiter;
    Thread thread;
}

 waitStatus其中包含的状态有:

1. CANCELLED,值为1,表示当前的线程被取消;

2. SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;

3. CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中;

4. PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;

     值为0,表示当前节点在sync队列中,等待着获取锁

 

AQS中冗余了Head节点。也就是Head是第一个节点。但是Tail只是其中的一个指针引用。

 

AQS执行流程:

 

以下是排它锁获取的主要流程:

   tryAcquire:尝试是否需要使用排它锁(一般都是业务决定的,根据state这个成员变量的值,比如ReentrantLock中公平锁或者非公平锁的使用)

  acquireQueued:等待获取排它锁的使用权。在这一步中,addWaiter(Node.EXCLUSIVE)是将此线程操作放到排它锁的队列中等待,期望获取使用权。addWaiter是将此线程压入等待队列中

 

 /**
     * Acquires in exclusive mode, ignoring interrupts.  Implemented
     * by invoking at least once {@link #tryAcquire},
     * returning on success.  Otherwise the thread is queued, possibly
     * repeatedly blocking and unblocking, invoking {@link
     * #tryAcquire} until success.  This method can be used
     * to implement method {@link Lock#lock}.
     *
     * @param arg the acquire argument.  This value is conveyed to
     *        {@link #tryAcquire} but is otherwise uninterpreted and
     *        can represent anything you like.
     */
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

  acquireQueued中,因为Head是冗余节点,所以每个Node肯定都有前置节点(除了head),所以如果前置节点是head,那么就需要再次调用tryAcquire,是否需要使用排它锁,如果不需要的话,直接将此节点当做Head节点。否则就需要判断此节点是否挂起了。其中shouldParkAfterFailedAcquire方法主要修改节点状态的,标识为需要唤醒状态。parkAndCheckInterrupt就是调用park方法并且返回此线程是否中断。

 

 /**
     * Acquires in exclusive uninterruptible mode for thread already in
     * queue. Used by condition wait methods as well as acquire.
     *
     * @param node the node
     * @param arg the acquire argument
     * @return {@code true} if interrupted while waiting
     */
    final boolean acquireQueued(final Node node, int arg) {
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } catch (RuntimeException ex) {
            cancelAcquire(node);
            throw ex;
        }
    }
 
每一次的acquire对应的释放操作是release方法。tryRelease和tryAcquire一样,都是业务控制的。当允许释放时,会将队里里面的线程拿出来去unpark也就是唤醒其他阻塞掉的线程。
 /**
     * Releases in exclusive mode.  Implemented by unblocking one or
     * more threads if {@link #tryRelease} returns true.
     * This method can be used to implement method {@link Lock#unlock}.
     *
     * @param arg the release argument.  This value is conveyed to
     *        {@link #tryRelease} but is otherwise uninterpreted and
     *        can represent anything you like.
     * @return the value returned from {@link #tryRelease}
     */
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
 
共享锁的执行流程和排它锁差不多:
/**
     * Acquires in shared mode, ignoring interrupts.  Implemented by
     * first invoking at least once {@link #tryAcquireShared},
     * returning on success.  Otherwise the thread is queued, possibly
     * repeatedly blocking and unblocking, invoking {@link
     * #tryAcquireShared} until success.
     *
     * @param arg the acquire argument.  This value is conveyed to
     *        {@link #tryAcquireShared} but is otherwise uninterpreted
     *        and can represent anything you like.
     */
    public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }
 加粗部分为共享锁和排它锁的区别。共享锁允许获取多个线程获取,而排它锁只允许一个。
/**
     * Acquires in shared uninterruptible mode.
     * @param arg the acquire argument
     */
    private void doAcquireShared(int arg) {
        final Node node = addWaiter(Node.SHARED);
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } catch (RuntimeException ex) {
            cancelAcquire(node);
            throw ex;
        }
    }
 共享锁的释放过程:
/**
     * Releases in shared mode.  Implemented by unblocking one or more
     * threads if {@link #tryReleaseShared} returns true.
     *
     * @param arg the release argument.  This value is conveyed to
     *        {@link #tryReleaseShared} but is otherwise uninterpreted
     *        and can represent anything you like.
     * @return the value returned from {@link #tryReleaseShared}
     */
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
 
 AQS只是提供了共享锁和排它锁的执行流程,具体的业务逻辑控制需要自己实现。
在AQS的基础上,提供了很多有用的锁机制。
CountDownLatch:开闭锁,一般当做阀值开关。
ReentrantLock:可重入锁。和synchroized关键字差不多,在排它锁的基础上做业务,已经持有锁的线程,可以再次获取锁,锁的计数器加1,release的时候减一。
 
其他: Condition介绍

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值