美文网首页
并发编程--Condition

并发编程--Condition

作者: 守住阳光 | 来源:发表于2018-09-15 16:17 被阅读0次

        任意一个java对象,都拥有一组监视器方法(定义在java.lang.Object上),主要包括wait(),wait(long timeout)、notify()以及notifyAll()方法,他们与synchronized关键字配合,可以实现等待/通知模式。Condition接口也提供了类似Object的监视器方法,与Lock配合可以实现等待/通知知模式,但是这两者在使用方式以及功能特性上还是有差别的。

一、Condition接口

        Condition定义了等待/通知两种类型的方法,当前线程调用这些方法时,需要提前获取到Condition对象关联的锁。Condition对象是由Lock对象(调用Lock对象的newCondition()方法)创建出来的,换句话说,Condition是依赖Lock对象的。

        主要方法包括:

        1、void await() throws InterruptedException

        当前线程进入等待状态直到被通知(signal)或中断。

        2、void awaitUninterruptibly()

        当前线程进入等待状态直到被通知,对中断不敏感。

        3、long awaitNanos(long nanosTimeout) throws InterruptedException

        当前线程进入等待状态直到被通知、中断或超时。

        4、boolean awaitUntil(Date deadline) throws InterruptedException

        当前线程进入等待状态直到被通知、中断或到某个时间。

        5、signal()

        唤醒一个等待在Condition上的线程,该线程从等待方法返回前必须获得与condition对象对应的锁。

        6、signalAll()

        唤醒所有等待在condition上的线程,能够从等待方法返回的线程必须获得与condition对象相关联的锁。

二、使用condition实现一个有界队列

        有界队列是一种特殊的队列,当队列为空时,队列的获取操作将会阻塞获取线程,直到队列中有新增元素,当队列已满时,队列的插入操作将会阻塞插入线程,直到队列出现“空位”。

public class BoundQueue<T> {

        private Lock lock = new ReentrantLock();

        //数组添加的下标、删除的下标和当前的数量

        private int addIndex , removeIndex , count;

        private Object[] items;

        private Condition notFull = lock.newCondition();

        private Condition notEmpty = lock.newCondition();

        public BoundQueue(int size){

                    items = new Object[size];

        }

        //添加元素

        public final 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();

                   }

        }

        //获取元素

        public final T remove() throws InterruptedException{

                lock.lock();

                try{

                        while(count==0){

                                notEmpty.await();

                        }

                        Object object = items[removeIndex];

                        if(++removeIndex == items.length){

                                removeIndex = 0;

                         }

                        count --;

                        return (T)object;

                }finally{

                        lock.unlock();

                }

         }

}

        首先需要获得锁,目的是确保数组修改的可见性和排他性。当数组数量等于数组长度时,表示数组已满,则调用notFull.await(),当前线程随之释放锁并进入等待状态。如果数组数量不等于数组长度,表示数组未满,则添加元素到数组中,同时通知等待在notEmpty上的线程,数组中已经有新元素可以获取。在添加和删除方法中使用while循环而非if判断,目的是防止过早或意外的通知,只有条件符合才能够退出循环。回想之前提到的等待/通知的经典范式,二者是非常类似的。

三、 Condition的实现

        ConditionObject是同步器AbstractQueuedSynchronizer的内部类,因为Condition的操作需要获取相关联的锁,所以作为同步器的内部类也较为合理。每个Condition对象都包含着一个队列(以下称为等待队列),该队列是Condition对象实现等待/通知功能的关键。

        当线程调用等待方法,该线程会被构造成等待节点并加入到等待队列中。

        当线程调用通知方法会将等待队列中的首节点或者全部节点(notifyAll)移到同步队列中,然后唤醒相应的节点加入到同步状态的获取中(自旋获取)。当节点中的线程获取到了同步状态即锁时,会从等待方法返回。

相关文章

网友评论

      本文标题:并发编程--Condition

      本文链接:https://www.haomeiwen.com/subject/wkftnftx.html