Java多线程(八)生产者消费者——Condition和精准唤醒

本文详细介绍了Java中的Condition接口如何用于解决生产者消费者问题,通过实例展示了如何利用Condition实现精准唤醒,以及其内部等待队列的工作原理。Condition提供了比wait/notify更精确的线程同步机制,允许在多个条件队列间进行切换,提高了并发控制的灵活性。
摘要由CSDN通过智能技术生成

Java多线程(八)生产者消费者——Condition和精准唤醒

什么是Condition

对于任意一个java对象,它都拥有一组定义在java.lang.Object上监视器方法,包括wait(),wait(long timeout),notify(),notifyAll(),这些方法配合synchronized关键字一起使用可以实现等待/通知模式。同样,Condition接口也提供了类似Object监视器的方法,通过与Lock配合来实现等待/通知模式。可以看一下Object类的监视器方法和Condition接口的对比:

在这里插入图片描述

Condition解决生产者消费者问题

假设生产者可以生产票,但是现存的票只能有一张,只有顾客买走了才能再生产一张票,因此可以用Condition来保证同步。havenum表示有票,需要生产者等待;nonum表示没票,需要消费者等待。代码如下:

class tickets{
    private int num = 0;
    ReentrantLock lock = new ReentrantLock();

    Condition nonum = lock.newCondition();
    Condition havenum = lock.newCondition();


    public void put() throws InterruptedException {
        lock.lock();
        try{
            while(num==1)
            {
                nonum.await();
            }
            num++;
            System.out.println(Thread.currentThread().getName()+" 生产了一份,现存数量是 "+num);
            havenum.signalAll();
        }
        finally {
            lock.unlock();
        }
    }

    public void take() throws InterruptedException {
        lock.lock();
        try {
            while(num==0)
            {
                havenum.await();
            }
            num--;
            System.out.println(Thread.currentThread().getName()+" 消耗了一份,现存数量是 "+num);
            nonum.signalAll();
        }
        finally {
            lock.unlock();
        }
    }
}

在这里插入图片描述

Condition 精准唤醒

上一篇文章Java多线程(七)生产者消费者——wait && notify && 虚假唤醒中说到可以用Condition机制进行按顺序唤醒,通过上面的例子我们也可以发现使用不同的Condition对象可以唤醒不同的线程,使用这一机制就可以做到精准唤醒。

class Aweaken
{
    ReentrantLock lock = new ReentrantLock();
    int num = 1;
    Condition conditionA = lock.newCondition();
    Condition conditionB = lock.newCondition();
    Condition conditionC = lock.newCondition();

    public void weakA()
    {
        lock.lock();
        try {
            while(num != 1)
            {
                conditionA.await();
            }
            num = 2;
            System.out.println("现在是线程 "+Thread.currentThread().getName()+", 下一个应该是线程B");
            conditionB.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void weakB()
    {
        lock.lock();
        try {
            while(num != 2)
            {
                conditionB.await();
            }
            num = 3;
            System.out.println("现在是线程 "+Thread.currentThread().getName()+", 下一个应该是线程C");
            conditionC.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void weakC()
    {
        lock.lock();
        try {
            while(num != 3)
            {
                conditionC.await();
            }
            num = 1;
            System.out.println("现在是线程 "+Thread.currentThread().getName()+", 下一个应该是线程A");
            conditionA.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

public class PCold {

    public static void main(String[] args) {
        Aweaken b = new Aweaken();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                b.weakA();
            }
        },"A").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                b.weakB();
            }
        },"B").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                b.weakC();
            }
        },"C").start();

    }
}

Condition 实现分析

等待队列

ConditionObject的等待队列是一个FIFO队列,队列的每个节点都是等待在Condition对象上的线程的引用,该线程就是在Condition对象上等待的线程,如果一个线程调用了Condition.await(),那么该线程就会释放锁,构成节点加入等待队列并进入等待状态。

从下图可以看出来Condition拥有首尾节点的引用,而新增节点只需要将原有的尾节点nextWaiter指向它,并更新尾节点即可。上述节点引用更新过程没有使用CAS机制,因为在调用await()的线程必定是获取了锁的线程,该过程由锁保证线程的安全。

一个Lock(同步器)拥有一个同步队列和多喝等待队列(如下图所示)
在这里插入图片描述

等待

调用Condition的await()方法,会使得当前线程进入等待队列并释放锁,同时线程状态变为等待状态。当从await()返回时,当前线程一定是获取了Condition相关联的锁。

线程触发await()这个过程可以看作是同步队列的首节点(当前线程肯定是成功获得了锁,因此一定是在同步队列的首节点)移动到了Condition的等待队列的尾节点,并释放同步状态进入等待状态,同时会唤醒同步队列的后继节点。

在这里插入图片描述

唤醒

调用Condition的signal()方法将会唤醒再等待队列中的首节点,该节点也是到目前为止等待时间最长的节点。调用Condition的signalAll()方法,将等待队列中的所有节点全部唤醒,相当于将等待队列中的每一个节点都执行一次signal()。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值