AQS java并发基石

AQS是java locks 包下的一个基础包,实现了serializable 接口 继承了 AbstractOwnableSynchronizer 。其 重要的类属性有state 代表其状态,Node head tail 代表队列,还有AbstractOwnableSynchronizer 的exclusiveOwnerThread。

AQS 字段:state 0代表无线程持有该锁 state >0 代表有线程持有该锁

Node类型的head tail 来代表AQS的队列

Node中有next pre waitStatus Thread waitStatus -1 代表需要叫醒后继结点 0是默认值 1代表是cancelled。

ReentrantLock的 lock的方法底层就是使用了aqs ,比如Thread 1 和Thread2 同时想要竞争一把锁,即同时想执行 lock方法,则结果是只能有一个方法进入代码块,那么底层AQS是怎么操作的呢?首先lock的时候会通过cas 把state 从0 设置成1 ,我们知道只有一个线程会设置成功,从而将exclusiveOwnerThread 设置为该线程。之后的线程就会设置失败,不过之后仍然会尝试再次设置,acquire(1).

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

tryAcquire 就是使用nonFairTryAcquire(int arg) 来尝试获取锁的。cas 设置state 从0到1,设置成功则将此线程设置为拥有锁的独占线程,失败则判断是否是已经拥有了该锁,若已经拥有了该锁,则需要将state设置为state+1,否则就是尝试获取锁失败了。

nonfairTryAcquire()方法如下

final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

之后会acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
addWaiter() 是将尝试失败的线程封装成为Node 结点 加入等待队列,即插入双向列表中.具体逻辑是首先将该线程封装成Node结点,然后cas设置tail 为 Node,成功之后则返回封装好的Node ,失败则循环进行尾结点的更新。

    private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

如果cas失败 或者tail 为 空 则进行循环 设置

    /**
     * Inserts node into queue, initializing if necessary. See picture above.
     * @param node the node to insert
     * @return node's predecessor
     */
    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

acquireQueue 是将加入队列的结点 进行判断 ,如果其前一个结点是头结点代表当前或者之前的线程获得过锁,那么此时可以尝试获取锁,如果不是则需要进行阻塞等待,阻塞等待也是有说法的比如你不能把新结点Node 放入任意结点的后一结点,比如前一结点的waitstatus是 0 或者大于0 都需要进行改变前一个结点的状态或者直接删除前一个结点 直到前一个结点waitstatus小于0.这样就可以进行有效的唤醒该线程。这就是shouldParkAfterFailedAcquire 的目的,之后便可以进行park。值得注意的是在线程park后 不能响应中断,苏醒之后会通过Thread.interrupted() 返回之前是否被其他线程中断并且会重置中断标志。之后在进行自我中断。注意Thread.isInterrupted() 函数与Thread.interrupted()函数的异同。isInterrupted 只是判断该线程是否被其他线程中断过,不会重新设置中断标志。

    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) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

自定义序列同步器应该主要实现一下方法:
isHeldExclusively() 是否持正在独占资源。只有在使用Condition 时候才需要去实现它
tryAcquire(int arg) 尝试获取锁 成功 true 失败返回false
tryRelease(int arg) 尝试释放锁 成功 true 失败返回false
tryAcquireShared(int) 尝试获取锁 负数 表示失败 0 成功 但是无剩余资源 整数代表成功。
tryRealeaseShared(int) 尝试释放锁 如果释放后允许唤醒后序等待结点 返回true 否则返回false。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值