• Condition接口


    Condition接口

    一、 Condition介绍及使用

    Condition接口是为了与Lock配合实现等待/通知模式, 可以将Condition等待通知和Lock的关系 与 Object的等待通知和Synchronized的关系类比;

    • Synchronized是通过锁对象即Object的wait() 和 notify() 实现等待通知;
    • Lock 则可以通过Condition的await() 和 signal() 实现等待通知;

    Object的监视器方法与Condition接口对比如下图:

    Condition对象定义了等待通知两种类型的方法, Condition对象有Lock对象(调用newCondition()方法)创建, Condition对象依赖于Lock对象;

    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class ConditionTest {
    
        private static Lock lock = new ReentrantLock();
        private static Condition condition = lock.newCondition();
    
        public static void main(String[] args) throws InterruptedException {
            new Thread(() -> {
                try {
                    conditionWait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
            //Thread.sleep(100);
            new Thread(() -> { conditionSignal(); }).start();
        }
    
        public static void conditionWait() throws InterruptedException {
            lock.lock();
            try{
                System.out.println("start wait");
                condition.await();
                System.out.println("wait end");
            }finally {
                lock.unlock();
            }
        }
        public static void conditionSignal() {
            lock.lock();
            try{
                System.out.println("signal");
                condition.signal();
            }finally {
                lock.unlock();
            }
        }
    }
    --- 输出:
    start wait
    signal
    wait end
    

    二、 Condition接口方法描述(基于jdk1.8)

    三、 Condition实现分析

    3.1 等待队列

    以ConditionObject为例, ConditionObject是同步器AbstractQueuedSynchronizer内部类; 每个Condition对象都包含一个等待队列, 该队列是实现通知等待的关键; 队列节点使用的是AbstractQueuedSynchronizer.Node

     public class ConditionObject implements Condition, java.io.Serializable {
        private static final long serialVersionUID = 1173984872572414699L;
        /** First node of condition queue. */
        private transient Node firstWaiter; // 队首
        /** Last node of condition queue. */
        private transient Node lastWaiter; // 队尾
    }
    

    等待队列结构:

    3.2 await()等待


    调用Condition的await()方法, 会使当前线程进入等待队列并释放锁, 同时线程变为等待状态;

    public final void await() throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        Node node = addConditionWaiter(); // 将当前线程添加到等待队列队尾
        // 调用AbstractQueuedSynchronizer的方法, 释放掉当前线程持有的资源state, 并唤醒同步队列中下一个后继节点
        long savedState = fullyRelease(node); 
        int interruptMode = 0;
        while (!isOnSyncQueue(node)) {
            LockSupport.park(this); // 最终还是依靠unsafe.park()实现的
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;
        }
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            interruptMode = REINTERRUPT;
        if (node.nextWaiter != null) // clean up if cancelled
            unlinkCancelledWaiters(); // 删除等待队列中等待状态不为CONDIITON的节点
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
    }
    // 将当前线程添加到等待队列队尾
    private Node addConditionWaiter() {
        Node t = lastWaiter;
        // If lastWaiter is cancelled, clean out.
        if (t != null && t.waitStatus != Node.CONDITION) {
            unlinkCancelledWaiters(); // 删除等待队列中等待状态不为CONDIITON的节点
            t = lastWaiter;
        }
        // 将当前线程封装成Node对象添加到等待队列队尾
        // 由于调用await()的前提是已经获取了锁, 所以下面操作不需要进行线程同步
        Node node = new Node(Thread.currentThread(), Node.CONDITION);
        if (t == null)
            firstWaiter = node;
        else
            t.nextWaiter = node;
        lastWaiter = node;
        return node;
    }
    
    // 遍历等待队列, 删除队列节点中等待状态不为CONDITION(表示在Condition队列中等待)的节点
    private void unlinkCancelledWaiters() {
        Node t = firstWaiter;
        Node trail = null;
        while (t != null) {
            Node next = t.nextWaiter;
            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;
        }
    }
    
    // AbstractQueuedSynchronizer中的方法, 释放掉当前线程持有的资源state, 并唤醒同步队列中下一个后继节点
    final long fullyRelease(Node node) {
        boolean failed = true;
        try {
            long savedState = getState(); // 获取当前线程所占有的资源
            if (release(savedState)) { // 将资源释放, 并唤醒同步队列中的后继节点
                failed = false;
                return savedState;
            } else { // 资源释放失败, 抛出异常
                throw new IllegalMonitorStateException();
            }
        } finally {
            if (failed) // 资源释放失败, 将该节点状态标记为CANCELLED(1)
                node.waitStatus = Node.CANCELLED;
        }
    }
    

    3.3 signal() 通知

    • Condition的signal()方法, 将唤醒在等待队列中等待时间最长的节点(首节点), 在唤醒节点前, 会将节点移动到同步队列中;
    public final void signal() {
        if (!isHeldExclusively()) // 判断当前线程是否获取了锁; 未获取锁则抛出异常
            throw new IllegalMonitorStateException();
        Node first = firstWaiter;
        if (first != null)
            doSignal(first); // 唤醒头结点
    }
    // 将first节点添加到同步队列尾部, 如果失败则尝试唤醒下一个节点;
    private void doSignal(Node first) {
        do {
            if ( (firstWaiter = first.nextWaiter) == null)
                lastWaiter = null;
            first.nextWaiter = null;
        } while (!transferForSignal(first) &&
                 (first = firstWaiter) != null);
    }
    // 将node添加到同步队列尾部, 并尝试将其唤醒
    final boolean transferForSignal(Node node) {
        /*
         * If cannot change waitStatus, the node has been cancelled.
         */
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;
    
        /*
         * Splice onto queue and try to set waitStatus of predecessor to
         * indicate that thread is (probably) waiting. If cancelled or
         * attempt to set waitStatus fails, wake up to resync (in which
         * case the waitStatus can be transiently and harmlessly wrong).
         */
        Node p = enq(node);
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread); // 最终还是依靠unsafe.unpark()实现的
        return true;
    }
    
    • Condition的signalAll()方法, 相当于对等待队列中每个节点执行一次signal()方法;
    /**
     * Moves all threads from the wait queue for this condition to
     * the wait queue for the owning lock.
     *
     * @throws IllegalMonitorStateException if {@link #isHeldExclusively}
     *         returns {@code false}
     */
    public final void signalAll() {
        if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
        Node first = firstWaiter;
        if (first != null)
            doSignalAll(first);
    }
    /**
    * Removes and transfers all nodes.
    * @param first (non-null) the first node on condition queue
    */
    private void doSignalAll(Node first) {
    	lastWaiter = firstWaiter = null;
    	do {
    	    Node next = first.nextWaiter;
    	    first.nextWaiter = null;
    	    transferForSignal(first);
    	    first = next;
    	} while (first != null);
    }
    
  • 相关阅读:
    18-10-11 关于触发器的学习
    18-10-29 关于设计器机器人等安装遇到的问题的解决方法
    18-10-25 全局函数测试总结 创建时间的目录 网页获取数据 写入数据
    18-09-08 关于Linux 的安装遇到的一些小坑
    18-08-27 机器人自动化之页面表格数据的定位拾取
    day 96 关于分页的使用
    day73 母版 中间件
    通过 U 盘启动重装 macOS 系统
    MAMP 环境下为 php 添加 pcntl 扩展
    使用 Composer 安装 Laravel 框架
  • 原文地址:https://www.cnblogs.com/jxkun/p/9381822.html
Copyright © 2020-2023  润新知