zoukankan      html  css  js  c++  java
  • JAVA并发编程--Condition

    Condition主要是为了在J.U.C框架中提供和Java传统的监视器风格的wait,notify和notifyAll方法类似的功能。

    AQS等待队列与Condition队列是两个相互独立的队列

    await()就是在当前线程持有锁的基础上释放锁资源,并新建Condition节点加入到Condition的队列尾部,阻塞当前线程
    signal()就是将Condition的头节点移动到AQS等待节点尾部,让其等待再次获取锁
     

    线程何时阻塞和释放

    阻塞:await()方法中,在线程释放锁资源之后,如果节点不在AQS等待队列,则阻塞当前线程,如果在等待队列,则自旋等待尝试获取锁
    释放:signal()后,节点会从condition队列移动到AQS等待队列,则进入正常锁的获取流程
     

    await方法

    public final void await() throws InterruptedException {
        // 如果当前线程被中断,则抛出中断异常
        if (Thread.interrupted())
            throw new InterruptedException();
        // 将节点加入到Condition队列中去,这里如果lastWaiter是cancel状态,那么会把它踢出Condition队列。
        Node node = addConditionWaiter();
        // 调用tryRelease,释放当前线程的锁
        long savedState = fullyRelease(node);
        int interruptMode = 0;
        // 为什么会有在AQS的等待队列的判断?
        // 解答:signal操作会将Node从Condition队列中拿出并且放入到等待队列中去,在不在AQS等待队列就看signal是否执行了
        // 如果不在AQS等待队列中,就park当前线程,如果在,就退出循环,这个时候如果被中断,那么就退出循环
        while (!isOnSyncQueue(node)) {
            LockSupport.park(this);
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;
        }
        // 这个时候线程已经被signal()或者signalAll()操作给唤醒了,退出了4中的while循环
        // 自旋等待尝试再次获取锁,调用acquireQueued方法
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            interruptMode = REINTERRUPT;
        if (node.nextWaiter != null)
            unlinkCancelledWaiters();
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
    }
    整个await的过程如下:
      1.将当前线程加入Condition锁队列。
      2.释放锁(fullyRelease)。这里可以看到将锁释放了,否则别的线程就无法拿到锁而发生死锁。进行3。
      3.自旋(while), 如果不在AQS等待队列,则挂起自己,直到被唤醒或者超时或者CACELLED等。进行4。
      4.获取锁(acquireQueued)。并将自己从Condition的FIFO队列中释放,表明自己不再需要锁(我已经拿到锁了)。
    可以看到,这个await的操作过程和Object.wait()方法是一样,只不过await()采用了Condition队列的方式实现了Object.wait()的功能。

    signal方法

    signal就是唤醒Condition队列中的第一个非CANCELLED节点线程,而signalAll就是唤醒所有非CANCELLED节点线程。当然了遇到CANCELLED线程就需要将其从FIFO队列中剔除。

    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) {
        /*
         * 设置node的waitStatus:Condition->0
         */
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;
    
        /*
         * 加入到AQS的等待队列,让节点继续获取锁
         * 设置前置节点状态为SIGNAL
         */
        Node p = enq(node);
        int c = p.waitStatus;
        if (c > 0 || !compareAndSetWaitStatus(p, c, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }
  • 相关阅读:
    java.math.BigDecimal
    抽象类和接口的区别:
    idea 快捷键
    java 抽象类
    mongodb3.2系统性学习——5、游标 模糊查询 findAndModify函数
    ZeroClipboard2.0 复制功能
    MySQL在远程访问时非常慢的解决skip-name-resolve 并且出现 Reading from net
    mongodb3.2系统性学习——2、write concern mongodb 写安全机制
    mysql 存储引擎 myisam innodb 区别
    MYSQL 错误 :Out of resources when opening file './datagather/mx_domain#P#p178.MYD' (Errcode: 24) 解决办法
  • 原文地址:https://www.cnblogs.com/snow-man/p/10900576.html
Copyright © 2011-2022 走看看