Java多线程(二十三)---Condition接口

2018-09-29  本文已影响22人  凯玲之恋

移步java多线程系列文章

1. 通过对比Object的监视器方法和Condition接口

qq_pic_merged_1535605094135.jpg

2 Condition接口与示例

Condition是依赖Lock对象的
使用示例

Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void conditionWait() throws InterruptedException {
    lock.lock();
    try {
            condition.await();
    } finally {
            lock.unlock();
    }
}
public void conditionSignal() throws InterruptedException {
    lock.lock();
    try {
            condition.signal();
    } finally {
            lock.unlock();
    }
}

v般都会将Condition对象作为成员变量。当调用await()方法后,当前线程会释放锁并在此等待,而其他线程调用Condition对象的signal()方法,通知当前线程后,当前线程才从await()方法返回,并且在返回前已经获取了锁。

Condition定义的方法

qq_pic_merged_1535605905139.jpg qq_pic_merged_1535605921452.jpg

获取一个Condition必须通过Lock的newCondition()方法。

Condition的使用方式
一个有界队列的示例来深入了解Condition的使用方式。有界队列是一种特殊的队列,当队列为空时,队列的获取操作将会阻塞获取线程,直到队列中有新增元素,当队列已满时,队列的插入操作将会阻塞插入线程,直到队列出现“空位”

public class BoundedQueue<T> {
    private Object[]    items;
    // 添加的下标,删除的下标和数组当前数量
    private int addIndex, removeIndex, count;
    private Lock lock     = new ReentrantLock();
    private Condition    notEmpty = lock.newCondition();
    private Condition    notFull = lock.newCondition();
    public BoundedQueue(int size) {
            items = new Object[size];
    }
    // 添加一个元素,如果数组满,则添加线程进入等待状态,直到有"空位"
    public void add(T t) throws InterruptedException {
            lock.lock();
            try {
                    while (count == items.length)
                            notFull.await();
                    items[addIndex] = t;
                    if (++addIndex == items.length)
                            addIndex = 0;

                    ++count;
                    notEmpty.signal();
            } finally {
                    lock.unlock();
            }
    }
    // 由头部删除一个元素,如果数组空,则删除线程进入等待状态,直到有新添加元素
    @SuppressWarnings("unchecked")
    public T remove() throws InterruptedException {
            lock.lock();
            try {
                    while (count == 0)
                            notEmpty.await();
                    Object x = items[removeIndex];
                    if (++removeIndex == items.length)
                            removeIndex = 0;
                    --count;
                    notFull.signal();
                    return (T) x;
            } finally {
                    lock.unlock();
            }
    }
}

上述示例中,BoundedQueue通过add(T t)方法添加一个元素,通过remove()方法移出一个元素。以添加方法为例。

3 Condition的实现分析

3.1 等待队列

等待队列的基本结构图


qq_pic_merged_1535807449758.jpg

Condition拥有首尾节点的引用,而新增节点只需要将原有的尾节点nextWaiter指向它,并且更新尾节点即可。

没有使用CAS保证,原因在于调用await()方法的线程必定是获取了锁的线程,也就是说该过程是由锁来保证线程安全的。

在Object的监视器模型上,一个对象拥有一个同步队列和等待队列,
并发包中的Lock(更确切地说是同步器)拥有一个同步队列和多个等待队列

qq_pic_merged_1538233209041.jpg
Condition的实现是同步器的内部类,因此每个Condition实例都能够访问同步器提供的方法,相当于每个Condition都拥有所属同步器的引用。

3.2 等待

Condition的await()方法

 public final void await() throws Interrupt
edException {
    if (Thread.interrupted())
            throw new InterruptedException();
    // 当前线程加入等待队列
    Node node = addConditionWaiter();
    // 释放同步状态,也就是释放锁
    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)
            unlinkCancelledWaiters();
    if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
}
qq_pic_merged_1538234152233.jpg

3.3 通知

调用Condition的signal()方法,将会唤醒在等待队列中等待时间最长的节点(首节点),在唤醒节点之前,会将节点移到同步队列中。
ConditionObject的signal方法

  public final void signal() {
    if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    if (first != null)
            doSignal(first);
}

调用该方法的前置条件是当前线程必须获取了锁,可以看到signal()方法进行了isHeldExclusively()检查,也就是当前线程必须是获取了锁的线程。接着获取等待队列的首节点,将其移动到同步队列并使用LockSupport唤醒节点中的线程。
节点从等待队列移动到同步队列的过程

qq_pic_merged_1538234438845.jpg

参考

《java并发编程的艺术》

上一篇 下一篇

猜你喜欢

热点阅读