生产者与消费者问题无疑是线程间通信协作的经典案例。下面来看一看”生产者/消费者“的一个简单案例:
首先,我们定义一个缓冲区Buffer,这个缓冲区用来存取数据,但缓冲区大小为1,即每次只能存取一个数据。
public class Buffer {
private ArrayList<Integer> list = new ArrayList<>(1); //定义一个大小为1的数组线性表,用于存放数据
//写入数据
public void write(int value) {
try {
synchronized (this) { //给对象加锁
while (list.size() == 1) {
System.out.println("等待清空缓存...");
wait(); //如果缓冲区已有数据,则线程进入阻塞状态,并释放锁
}
list.add(value); //添加数据
notify(); //唤醒等待等待中的线程
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public int read() {
try {
synchronized (this) {
while (list.size() == 0) { //给对象加锁
System.out.println("等待写入数据...");
wait(); //如果缓冲区没有数据,则线程进入阻塞状态,并释放锁
}
int value = list.get(0); //获取数据
list.clear(); //清空缓存
notify(); //唤醒等待等待中的线程
return value;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return 0;
}
}
然后,分别设定生产者和消费者来存取数据:
1.生产者
public class Producer implements Runnable{
private Buffer buffer;
public Producer(Buffer buffer) {
this.buffer = buffer; //设定缓存区
}
@Override
public void run() {
int i = 0;
while (true) { //不断循环往缓冲区写入数据
System.out.println("Producer写入" + i);
buffer.write(i++); //往缓存中写入数据i
try {
Thread.sleep((long) (Math.random() * 1000)); //每写入一个数据,休息一段时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2.消费者
public class Consumer implements Runnable{
private Buffer buffer;
public Consumer(Buffer buffer) {
this.buffer = buffer;
}
@Override
public void run() {
while (true) {
System.out.println("Consumer读出" + buffer.read()); //不断从缓冲区读出数据
try {
Thread.sleep((long) (Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
最后,我们写一个主类作为测试:
public class ConsumerProducer {
private Buffer buffer = new Buffer(); //新建缓冲区
private Producer producer;
private Consumer consumer;
private ExecutorService executors = Executors.newFixedThreadPool(2); //创建大小为2的线程池
public ConsumerProducer() {
producer = new Producer(buffer); //新建生产者
consumer = new Consumer(buffer); //新建消费者
executors.execute(producer); //添加任务
executors.execute(consumer); //添加任务
executors.shutdown(); //停止接收任务
}
public static void main(String[] args) {
new ConsumerProducer();
}
}
好的,一个生产者/消费者模型已经搭建好了!我们来看看结果吧:
结果表明,当缓存中存在数据时,消费者需要等待存入数据后才能取出;同理,缓存中没有数据时,生产者需要取出数据后才能存入。
最后,画一个图来说明一下生产者与消费者之间线程之间的关系吧!