问题描述
有一个缓冲区,生产者往里生产东西,消费者从里消费东西。
缓冲区满时,生产者不能再生产;缓冲区空时,消费者不能再消费。
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();
}
}
}