Lock中Condition的实现

Condition

多个线程之间通信,JDK1.5以前使用wait和notify,到了JDK1.5我们有了更加高效的Condition。

通过源码分析Condition的实现

ReentrantLock的Condition基本使用

假如要做一个生产者消费者,当队列中没有元素时,生产者要生产,此时消费者线程不应该再去争夺CPU资源,同样如果队列已满,生产者也不该去争夺CPU资源,而是交由消费者取消费,如果通过Synchronized,则无法实现。
此时Codition可以解决上述的问题。我们使用Codndition来做个DEMO。

package cc.ai;

    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;

    /**
     * Created by yangkai.
     *
     * @author yangkai
     * @date 2016/12/9
     */
    public class ConditionTest {

        public static void main(String[] args) throws InterruptedException {
            ReentrantLock reentrantLock = new ReentrantLock();
            //定义一个只能存储1个元素的blockingQueue,此处只是示例,实际上
            //LinkedBlockingQueue 已经实现了,等待和通知的机制。
            LinkedBlockingQueue queue = new LinkedBlockingQueue(1);
            Condition consumerCon = reentrantLock.newCondition();
            Condition producerCon = reentrantLock.newCondition();

            for (int i = 0; i < 5; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //生产者
                        reentrantLock.lock();
                        try {
                            while(queue.size() == 1) {
                                System.out.println("生产者不能再生产,等着");
                                producerCon.await();
                            }
                            System.out.println("开始生产" +  queue.add(1));

                            consumerCon.signal();//唤醒消费者
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } finally {
                            reentrantLock.unlock();
                        }
                    }
                }).start();
            }

    //        TimeUnit.SECONDS.sleep(1);
            for (int i = 0; i < 5; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //消费者
                        reentrantLock.lock();
                        try {
                            while(queue.size() == 0) {
                                System.out.println("消费者不能再消费,等着");
                                consumerCon.await();
                            }
                            System.out.println("开始消费:"+ queue.remove());

                            producerCon.signal();//唤醒生产者
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } finally {
                            reentrantLock.unlock();
                        }
                    }
                }).start();
            }
        }
    }

运行结果:

从上面的运行结果可以看到,当队列只能容纳一个元素时,生产者和消费者是一对一单独出现的。

Conditon的await方法

用Synchronized中的wait和notify也能实现上面的生产者消费者啊,为什么我要使用Condition,它有什么优势?
下面我们通过源码来分一下Condition的await方法,但是在分析之前,我们要知道,调用lock.newCondition()的时候,java做了什么事。

ReentrantLock的newCondition实现.
    public Condition newCondition() {
        return sync.newCondition();
    }
    final ConditionObject newCondition() {
        return new ConditionObject();
    }

返回的ConditionObject在AQS中定义:
什么都没有,貌似看不出什么。再往下看await。

public final void await() throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        Node node = addConditionWaiter();//将node节点加入到codition队列
        int savedState = fullyRelease(node);//尝试获取锁
        int interruptMode = 0;
        while (!isOnSyncQueue(node)) {//当前节点是否在lock队列中(肯定不在)
            LockSupport.park(this);
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;
        }
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)//此时节点等待被激活。
            interruptMode = REINTERRUPT;
        if (node.nextWaiter != null) // clean up if cancelled
            unlinkCancelledWaiters();
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
    }

总结下来就是,codition自己也是一个队列,当调用await的时候,就向condition后面添加一个节点。

Conditon的signal方法

既然codition的await是向condition队列添加节点,但是节点并未添加到lock队列中啊,只有被添加到lock队列中的节点才有可能获得执行机会,所以我们大胆猜测,signal的功能就是从codition队列获取节点,然后放入lock队列中。现在看下代码实现:

public final void signal() {
        if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
        Node first = firstWaiter;
        if (first != null)
            doSignal(first);
    }
    private void doSignal(Node first) {
        do {//取出condition的节点,调用transferForSignal去添加到lock节点
            if ( (firstWaiter = first.nextWaiter) == null)
                lastWaiter = null;
            first.nextWaiter = null;
        } while (!transferForSignal(first) &&
                 (first = firstWaiter) != null);
    }
    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);//是不是很熟悉。将condition的节点添加到lock队列中
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值