并发编程(6):Condition的实现者ConditionObject的实现原理

1、什么是Condition?

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

 

2、Object的监视器方法Condition接口方法以及功能对比。 

 

3、Condition接口的主要方法:

     

 

4、Condition接口的主要实现

      主要是实现有:

      1、AbstractQueuedLongSynchronizer.ConditionObject.class

      2、AbstractQueuedSynchronizer.ConditionObject.class

      

5、Condition的使用方式

      1、获取一个Condition实例,必须是使用Lock的newCondition()方法,同一个lock可以创建多个Condition实例。

      2、使用案例-->实现自己的阻塞队列:

           我们使用ReentrantLock + Condition 来实现一个阻塞队列,实现说明,我们实现的这个队列是有界队列,当队列满了以后,在往里面添加元素的话将会阻塞当前线程,同样的当队列空了,获取队列元素的线程也将阻塞,直到获取到队列的元素为止。

/**
 * 使用ReentrantLock + Condition 来实现阻塞队列。
 *
 * @param <T>
 */
public class MyBlockingQueue<T> {

    private Object[] elements;
    private Lock lock = new ReentrantLock();

    /*
    添加元素的(生产者)Condition,主要做两件事情:
               1、在队列满了的时候,将后续的生产者线程添加到其条件队列中并阻塞。
               2、在消费者消费的时候(表示队列有空位了),唤醒阻塞的生产者线程继续生产。
     */
    private Condition producerCondition = lock.newCondition();

    /*
    获取元素的(消费者)Condition,主要做两件事情:
           1、在队列被消费空了的时候,将后续的消费者线程添加到其条件队列中并阻塞。
           2、在消生产者生产的时候(表示队列里有新加的元素了,消费者可以消费了),唤醒阻塞的消费者线程继续消费。
   */
    private Condition consumerCondition = lock.newCondition();

    private int addIndex = 0;
    private int removeIndex = 0;
    private int count = 0;

    public MyBlockingQueue(int queueSize) {
        this.elements = new Object[queueSize];
    }

    public void add(T t) throws InterruptedException {
        lock.lock();
        try {
            while (count == elements.length) {
                /*
                如果生产满了后,后续来添加元素的线程添加到producerCondition的条件队列中等待。
                 */
                producerCondition.await();
            }
            elements[addIndex] = t;
            if (++addIndex == elements.length) {
                addIndex = 0;
            }
            ++count;

            //每生产一个元素,就唤醒在等待的消费者去消费。
            consumerCondition.signal();
        } finally {
            lock.unlock();
        }
    }

    public T get() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0) {
                /*
                当消费完了后,还有消费者来消费的话,那就将其放在consumerCondition的条件队列中等待。
                 */
                consumerCondition.await();
            }
            Object x = elements[removeIndex];
            if (++removeIndex == elements.length) {
                removeIndex = 0;
            }
            --count;

            //每当消费者消费一个,就通知在等待的生产者添加元素。
            producerCondition.signal();
            return (T) x;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyBlockingQueue<String> myBlockingQueue = new MyBlockingQueue<>(5);

        for (int i = 0; i <= 5; i++) {
            int j = i;
            new Thread(() -> {
                try {
                    myBlockingQueue.add("message" + j);
                    System.out.println("add success message" + j);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "T1").start();
        }

        Thread.sleep(2000L);
        new Thread(() -> {
            String s = null;
            try {
                s = myBlockingQueue.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("获取message" + s);
        }).start();

        Thread.sleep(10000L);

    }
}

         

 6、Condition实现原理分析:

       1、ConditionObject是同步器AbstractQueuedSynchronizer的内部类,因为Condition的操作需要 获取相关联的锁,所以作为同步器的内部类也较为合理。每个Condition对象都包含着一个队列(以下称为等待队列),我们的案例中创建了两个Condition实例,因此会有两个等待队列。

       2、等待队列的基本结构:

             

         3、我们使用4条线程来模拟一个过程来说明Condition的实现原理

                threadA、threadB、threadC、threadD

               步骤1:threadA获取到锁lock开始执行,threadB、threadC未获取到锁,进入AQS同步队列,那么lock的情况如下:

                  

               步骤2:threadA 调用condition.await()方法,因为condition是使用lock来创建的,因此在threadA调用await()方法时。

                    第一件事就是将threadA构建好节点,放入到condition实例的等待队列里面。

                    第二件事情就是释放做锁lock。

                    第三件事就是阻塞threadA线程。

                    我们假设在threadA释放锁后,threadD插队获取到了lock,然后threadD执行了condition.await()。我们假设threadD调用condition.await()后,threadB获取到了lock。 那么此时lock锁的情况就是如下:

                    

                步骤三:此时threadB时锁lock的持有者,吃喝threadB调用了condition.signal()方法,调用signal方法会condition队列中的firstWiter节点移动到AQS的sync(同步队列中),让其去竞争锁,那么就是如下情况:

                  

 

以上就是condition的整体实现原理,其实实现很简单,condition.await()方法主要工作就是1、将调用condition.await()方法的线程构建节点添加到同步队列中。2、完完全全释放锁。3、阻塞调用condition.await()方法的线程。 而调用condition.await()方法的线程只有在其他线程调用condition.signal()方法才会被放入到AQS的同步队列中等待获取锁,一旦获取到锁,那就是被唤醒了。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值