多线程经典问题——生产者消费者精简总结(Java版)

问题描述

有一个缓冲区,生产者往里生产东西,消费者从里消费东西。
缓冲区满时,生产者不能再生产;缓冲区空时,消费者不能再消费。

Java实现关键点

1、实现时声明一个资源类,然后把生产和消费的方法都写里面,生产者和消费者线程里直接调用资源类对象的对应方法即可。不要把生产和消费的方法单独写在生产者类和消费者类中,否则无法控制共享资源的增减。

2、使用lambda表达式来造生产和消费线程,不要显式去造生产者和消费者的类。

3、判断时必须用while不能用if,否则存在虚假唤醒问题。
虚假唤醒(个人理解):多个同类正在wait,notify唤醒了同类。唤醒后的同类有可能会获得锁,则直接从wait开始继续往下执行了,而实际上此时可能并不满足生产或消费的条件,引发错误

4、两套东西,不能混用:

  • synchronized+wait+notify
  • lock+condition.await+condition.signal

synchronized代码实现

package thread;

/**
 * @author cjh
 * @description 完整的生产者消费者实例,synchronized版
 * @create 2021-09-23 16:27
 */
//synchronized同步,线程通信要使用wait和notify()
public class ProduceConsume1 {
    public static void main(String[] args) {
        Repository repository = new Repository();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                Product product = new Product(i);
                repository.produceProduct(product);
            }
        }, "producer1").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                repository.consumeProduct();
            }
        }, "consumer2").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                repository.consumeProduct();
            }
        }, "consumer1").start();



    }
}

class Product {
    int id;

    public Product(int id) {
        this.id = id;
    }
}

class Repository {

    Product[] products = new Product[10];

    private int count = 0;

    public synchronized void produceProduct(Product product) {
        //这里一定要用while,如果用if的话,会存在虚假唤醒问题。本来wait着的同类拿到了锁,继续往下走了,造成数组越界

        //比如当前P1和P2是wait状态,此时C1消费了一个产品后,唤醒了P1和P2,此时P1先拿到锁,又生产了一个产品,
        //此时产品个数为10,P2又拿到锁,接着wait这行往下运行,下面count == 10,数组会越界

        //多个生产者或多个消费者都在wait的时候,notify有可能会唤醒同类

        //除非只有一个生产者和一个消费者的情况下,才能用if
        while (count == 10) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        products[count] = product;
        count++;
        System.out.println(Thread.currentThread().getName() + "已生产完毕,生产的产品id为:" + product.id +
                ",当前资源数: " + count);
        //这里使用notify或者notifyAll没有区别,该继续wait的还是继续wait
        notifyAll();

    }

    public synchronized void consumeProduct() {
        //这里一定要用while,如果用if的话,当唤醒消费者线程时,直接往下走,数组会越界
        while (count == 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //数组索引比长度小1,所以要先--
        count--;
        Product product = products[count];
        System.out.println(Thread.currentThread().getName() + "已消费完毕,消费的产品id为:" + product.id +
                ",当前资源数: " + count);
        notifyAll();

    }
}

lock锁代码实现

package thread;

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

/**
 * @author cjh
 * @description 完整的生产者消费者实例,lock版
 * @create 2021-10-04 15:50
 */
//lock锁同步,线程通信要使用condition.await()和condition.signalAll()
public class ProduceConsume2 {
    public static void main(String[] args) {
        Repository2 repository = new Repository2();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                Product2 product = new Product2(i);
                repository.produceProduct(product);
            }
        }, "producer1").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                repository.consumeProduct();
            }
        }, "consumer1").start();
    }
}

class Product2 {
    int id;

    public Product2(int id) {
        this.id = id;
    }
}

class Repository2 {

    Lock lock = new ReentrantLock();

    Condition condition = lock.newCondition();

    Product2[] products = new Product2[10];

    private int count = 0;

    public void produceProduct(Product2 product) {
        //try-catch-finally快捷键 ctrl+alt+t
        lock.lock();
        try {
            while (count == 10) {
                condition.await();
            }
            products[count] = product;
            count++;
            System.out.println(Thread.currentThread().getName() + "已生产完毕,生产的产品id为:" + product.id +
                    ",当前资源数: " + count);
            //这里使用notify或者notifyAll没有区别,该继续wait的还是继续wait
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void consumeProduct() {
        lock.lock();
        try {
            while (count == 0) {
                condition.await();
            }
            count--;
            Product2 product = products[count];
            System.out.println(Thread.currentThread().getName() + "已消费完毕,消费的产品id为:" + product.id +
                    ",当前资源数: " + count);
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值