细谈生产者消费者问题

其实 老版的 和 新版的 解决生产者消费者的思路几乎不差
主要区别就是下列:
老版 this.wait() this.notifyAll()
JUC版 condition.await() condition.signalAll()
在这里插入图片描述

传统版

我们在只有两个线程(一个生产线程,一个消费线程)的类中,用if来进行判断/循环 会发现 ,唉好像没什么问题,一切都是正常进行的样子,那我们在多来一个线程呢? 你还能保证你的线程安全吗?

首先我们来看官方文档:
在这里插入图片描述
从文档中我们可以很清晰的看到 使用if 进行判断会造成一个 ”虚假唤醒问题“ --> 线程可以唤醒,中断或超时

解决方法 :用while 判断

if判断–> 虚假唤醒

/**
 * @Author 码农天宇
 * @Version 1.0
 */

public class PC {
    /*
         线程通信问题: 生产者和消费者!  -->  等待唤醒,通知唤醒

     */

    public static void main(String[] args) {
       Data d = new Data();

        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++){
                    d.increment();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"c1").start();

        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++){
                    d.decrement();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"c2").start();

        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++){
                    d.increment();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"c3").start();

        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++){
                    d.decrement();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"c4").start();
    }
}

// 资源类
class Data{
    private int num = 0;

    // 生产者生产商品
    public synchronized void increment() throws InterruptedException {
        if ( num != 0) {
            // 等待
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName() + "正在生产: " + num);
        // 通知其他线程,我生产完毕了
        this.notifyAll();
    }

    // 消费者消费商品
    public synchronized void decrement() throws InterruptedException {
        if (num==0) {
            // 等待生产
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName() + "正在消费: " + num);
        // 通知其他线程,我消费完毕了
        this.notifyAll();
    }
}

if循环导致问题 --> 虚假唤醒
在这里插入图片描述

while解决唤醒

用whie 循环即可解决

/**
 * @Author 码农天宇
 * @Version 1.0
 */

public class PC {
    /*
         线程通信问题: 生产者和消费者!  -->  等待唤醒,通知唤醒
     */

    public static void main(String[] args) {
        Data d = new Data();

        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    d.increment();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"c1").start();

        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    d.decrement();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"c2").start();

        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    d.increment();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"c3").start();

        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    d.decrement();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"c4").start();
    }
}

// 资源类
class Data{
    private int num = 0;

    // 生产者生产商品
    public synchronized void increment() throws InterruptedException {
        while ( num != 0) {
            // 等待
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName() + "正在生产: " + num);
        // 通知其他线程,我生产完毕了
        this.notifyAll();
    }

    // 消费者消费商品
    public synchronized void decrement() throws InterruptedException {
        while (num==0) {
            // 等待生产
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName() + "正在消费: " + num);
        // 通知其他线程,我消费完毕了
        this.notifyAll();
    }
}

这样看来,我们的问题就会得到解决
在这里插入图片描述

JUC版

没有顺序的/不可控制的(多线程环境下)

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author 码农天宇
 * @Version 1.0
 */

public class PC {
    /*
         线程通信问题: 生产者和消费者!  -->  等待唤醒,通知唤醒
     */

    public static void main(String[] args) {
        Data d = new Data();

        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    d.increment();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"c1").start();

        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    d.decrement();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"c2").start();
    }
}

// 资源类
class Data{
    private int num = 0;

	// 可重入锁
    Lock lock = new ReentrantLock();

    // Condition 精准的唤醒和通知线程
    Condition cond = lock.newCondition();

    // 生产者生产商品
    public void increment() throws InterruptedException {
        // 加锁
        lock.lock();
        try {
            while ( num != 0) {
                // 等待
                cond.await();
            }
            num++;
            System.out.println(Thread.currentThread().getName() + "正在生产: " + num);
            // 通知其他线程,我生产完毕了
            cond.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 释放锁
            lock.unlock();
        }

    }

    // 消费者消费商品
    public void decrement() throws InterruptedException {
        // 加锁
        lock.lock();
        try {
            while (num==0) {
                // 等待生产
                cond.await();
            }
            num--;
            System.out.println(Thread.currentThread().getName() + "正在消费: " + num);
            // 通知其他线程,我消费完毕了
            cond.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 释放锁
            lock.unlock();
        }
    }
}

我们可以看到,他是随机的,我们不可控制的
在这里插入图片描述

有顺序的(多线程环境下)

我们假设有3个线程 : c1,c2,c3
我们需要c1线程跑完执行c2线程,c2线程跑完执行c3线程,c3线程跑完执行c1线程

那么我们需要怎么实现?

通过Condition来实现 --> 它可以精准的 ,唤醒通知线程
Lock lock = new ReentrantLock();
Condition c1 = lock.newCondition();
Condition c2 = lock.newCondition();
Condition c3 = lock.newCondition();
我们通过lock来实现3个监听器
通过flag状态来精准的选择,通知下一步执行那个线程

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author 码农天宇
 * @Version 1.0
 */

public class PC {

    public static void main(String[] args) {
        /*
                A执行完调用B,B执行完调用C,C执行完调用A
         */
        Data da = new Data();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                da.printA();
            }
        },"c1").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                da.printB();
            }
        },"c2").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                da.printC();
            }
        },"c3").start();
    }
}

class Data{

    Lock lock = new ReentrantLock();
    Condition c1 = lock.newCondition();
    Condition c2 = lock.newCondition();
    Condition c3 = lock.newCondition();
    // 状态 flag
    int flag = 1;

    // 顺序执行
    public void printA(){
        lock.lock();
        try {
            while ( flag != 1) {
                c1.await();
            }
            System.out.println(Thread.currentThread().getName() + ": AAA");
            // 唤醒指定的线程 B
            flag = 2;
            c2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printB(){
        lock.lock();
        try {
            while (flag != 2) {
                c2.await();
            }
            System.out.println(Thread.currentThread().getName() + ": BBB");
            flag = 3;
            // 唤醒线程c3
            c3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printC(){
        lock.lock();
        try {
            while (flag != 3) {
                c3.await();
            }
            System.out.println(Thread.currentThread().getName() + ": CCC");
            flag = 1;
            // 唤醒指定线程 c1
            c1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值