Condition解析

先看完AQShttps://blog.csdn.net/zgsxhdzxl/article/details/95032337,再来看比较容易理解。

condition可以对线程进行阻塞和唤醒。类似wait/notify,不同的是condition可以方便地对持有锁的线程进行阻塞和唤醒。

我们知道AQS中有一个队列是同步队列,condition也有一个队列叫等待队列,不过同步队列是双向的,等待队列是单向的。

同步队列和等待队列(网上转载)
  • 使用示例

    static Lock lock = new ReentrantLock();
    static Lock lock2 = new ReentrantLock();
    static Condition one = lock.newCondition();
    static Condition two = lock2.newCondition();

    public static void main(String[] args) throws InterruptedException {
        Thread oneThread = new Thread(() -> {
            try {
                lock.lock();
                one.await();
                System.out.println("1");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        });
        Thread twoThread = new Thread(() -> {
            try {
                lock2.lock();
                two.await();
                System.out.println("2");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        });
        oneThread.start();
        twoThread.start();
        Thread.sleep(200);
        lock.lock();
        one.signal();
        lock.unlock();
    }
  • 主要方法

  • await

释放锁,线程被挂起,同Object.wait()类似。

方法总览,大致就是将获得锁的节点插入到等待队列,释放锁持有的锁,唤醒下一个节点,之后进入睡眠等待被唤醒。

图片来自网络
    public final void await() throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        // 将该节点插入到等待队列队尾
        Node node = addConditionWaiter();
        // 释放锁,返回值是释放锁之前的 state 值
        // await() 之前,当前线程是必须持有锁的,这里肯定要释放掉
        int savedState = fullyRelease(node);
        int interruptMode = 0;
        while (!isOnSyncQueue(node)) {
            // 如果节点不在同步队列中,那么就节点进入睡眠
            LockSupport.park(this);
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;
        }
        // 唤醒之后,进入同步队列,重新尝试获取锁
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            // 如果获取到锁并且没有被中断
            interruptMode = REINTERRUPT;
        if (node.nextWaiter != null)
            // 继续清理不是CONDITION的节点
            unlinkCancelledWaiters();
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
    }

将节点加入到等待队列

    // 将该节点插入到等待队列队尾
    private Node addConditionWaiter() {
        Node t = lastWaiter;
        // 如果节点状态不是CONDITION,则表示节点需要被清除
        if (t != null && t.waitStatus != Node.CONDITION) {
            unlinkCancelledWaiters();
            t = lastWaiter;
        }
        Node node = new Node(Thread.currentThread(), Node.CONDITION);
        // 如果等待队列为空,则设置该节点为头节点,否则放到队尾
        if (t == null)
            firstWaiter = node;
        else
            t.nextWaiter = node;
        lastWaiter = node;
        return node;
    }

    // 清除掉等待队列中状态不是CONDITION的节点
    private void unlinkCancelledWaiters() {
        Node t = firstWaiter;
        Node trail = null;
        while (t != null) {
            // 获取头节点的后驱节点
            Node next = t.nextWaiter;
            // 如果节点状态不是CONDITION,则表示节点需要被清除
            if (t.waitStatus != Node.CONDITION) {
                t.nextWaiter = null;
                if (trail == null)
                    firstWaiter = next;
                else
                    trail.nextWaiter = next;
                if (next == null)
                    lastWaiter = trail;
            }
            else
                trail = t; //
            t = next;
        }
    }

释放锁

    // 完全释放持有的锁
    final int fullyRelease(Node node) {
        boolean failed = true;
        try {
            int savedState = getState();
            if (release(savedState)) {
                failed = false;
                return savedState;
            } else {
                throw new IllegalMonitorStateException();
            }
        } finally {
            if (failed)
                node.waitStatus = Node.CANCELLED;
        }
    }

判断是否在同步队列中

    // 判断节点是否在同步队列中
    final boolean isOnSyncQueue(Node node) {
        if (node.waitStatus == Node.CONDITION || node.prev == null)
            return false;
        if (node.next != null) // If has successor, it must be on queue
            return true;
        return findNodeFromTail(node);
    }

    // 判断是否节点是否在同步队列中
    private boolean findNodeFromTail(Node node) {
        Node t = tail;
        for (;;) {
            if (t == node)
                return true;
            if (t == null)
                return false;
            t = t.prev;
        }
    }

线程被唤醒之后,判断线程是否被中断,并且将线程加入到同步队列中。

    // 判断线程是否被中断
    private int checkInterruptWhileWaiting(Node node) {
        return Thread.interrupted() ?
                (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
                0;
    }

    final boolean transferAfterCancelledWait(Node node) {
        if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
            // 将节点插入到同步队列尾部
            enq(node);
            return true;
        }
        while (!isOnSyncQueue(node))
            Thread.yield();
        return false;
    }

之后便是去获取锁。

 

  • signal

将Condition.await()挂起的第一个线程唤醒。

图片来自网络
    public final void signal() {
        // 判断当前线程是否持有锁,如果没有持有则跑出异常
        if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
        // 获取等待队列中的第一个节点
        Node first = firstWaiter;
        if (first != null)
            // 唤醒等待队列中的第一个线程
            doSignal(first);
    }

将节点插入到同步队列中,并且唤醒等待队列中的第一个节点。

    private void doSignal(Node first) {
        do {
            if ( (firstWaiter = first.nextWaiter) == null)
                lastWaiter = null;
            first.nextWaiter = null;
        } while (!transferForSignal(first) &&
                (first = firstWaiter) != null);
    }

    final boolean transferForSignal(Node node) {
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;
        // 将线程插入到同步队列中
        Node p = enq(node);
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            // 唤醒线程
            LockSupport.unpark(node.thread);
        return true;
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值