解决生产者消费者问题一共有三种方法
方法1:**
使用wait()和notify(),当容器满了之后,生产者需要暂时停止生产,直到容器非满,当容器空了之后,消费者需要暂时停止消费,直到容器非空,缓冲区为满和为空时都调用wait()方法等待,直到生产者生产了一个产品或者消费者消费了一个产品之后才会唤醒所有线程。
方法2:
await()与signal()方法,即线程锁的方式。
可重入锁(ReentrantLock)的实现。
可重入锁,也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响。
简单来说,该锁维护这一个与获取锁相关的计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,函数调用结束计数器就减1,然后锁需要被释放两次才能获得真正释放。
已经获取锁的线程进入其他需要相同锁的同步代码块不会被阻塞。
方法3:
阻塞队列(BlockingQueue)的实现。
被阻塞的情况主要有如下两种:
1.当队列满了的时候进行入队列操作
2.当队列空了的时候进行出队列操作
当一个线程对已经满了的阻塞队列进行入队操作时会阻塞,除非有另外一个线程进行了出队操作,当一个线程对一个空的阻塞队列进行出队操作时也会阻塞,除非有另外一个线程进行了入队操作。 所以,阻塞队列是线程安全的。
下面是方法2的代码示例
package com.wah.fundation.thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author wah
* @Date 2020/7/17 13:31
*/
//定义一个资源类
class Resource{
private int id;
private String name;
private int sourceCount; //sourceCount == 10 表示资源满, sourceCount == 0 表示没有资源
private final String[] names = new String[10]; //用于存放资源的资源数组
private int proPtr,custPtr;
private static final Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private Condition condition1 = lock.newCondition();
//生产资源
public void product(String name){
lock.lock(); //获取锁
try {
//如果资源容器已满,则等待
while (sourceCount == names.length){
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.id++; //id++
this.name = name + id;
names[proPtr] = this.name; //将生产的资源放入容器
System.out.println(Thread.currentThread().getName() + "生产了" + this.name);
if(++proPtr == names.length) proPtr = 0;
sourceCount++; // 表示生产了一个资源
condition1.signal(); //唤醒所有线程
}finally {
lock.unlock(); //释放锁
}
}
//消费资源
public void cust(){
lock.lock();
try {
while (sourceCount == 0){
try {
condition1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "消费了" + names[custPtr]);
if(++custPtr == names.length) custPtr = 0;
sourceCount--;
condition.signal();
}finally {
lock.unlock();
}
}
}
/**
* 定义一个生产者类
*/
class Product implements Runnable{
private Resource resource;
public Product(Resource resource){
this.resource = resource;
}
@Override
public void run() {
while(true){
this.resource.product("方便面");
}
}
}
class Customer implements Runnable{
private Resource resource;
public Customer(Resource resource){
this.resource = resource;
}
@Override
public void run() {
while (true)
this.resource.cust();
}
}
public class ProductAndCustomerPro {
public static void main(String[] args) {
Resource resource = new Resource();
Product product = new Product(resource);
Customer customer = new Customer(resource);
new Thread(product).start();
new Thread(customer).start();
new Thread(product).start();
new Thread(customer).start();
}
}