前言
多线程的经典问题就是生产者和消费者了,之前一直比较模糊,只知道大概在我的最初印象里面就是只有生产者生产了才能让消费者去消费。这只是一个笼统的概念,最近在学习《JAVA多线程编程核心技术》,自己动手写了一个。
代码
仓库:
package thread;
import java.util.LinkedList;
public class Storage {
private int MAX_SIZE = 100;
private LinkedList<Integer> list = new LinkedList<>();
public Storage(int num) {
for (int i = 0; i < num; i++) {
list.add(1);
}
}
public void produce(int num) throws InterruptedException {
synchronized (list) {
while ((list.size() + num) > MAX_SIZE) {
System.out.println("仓库容量不足");
System.out.println(Thread.currentThread().getName()+"被阻塞");
list.wait();
}
System.out.println(Thread.currentThread().getName()+"开始生产");
for (int i = 0; i < num; i++) {
list.add(1);
}
System.out.println("当前仓库库存:"+list.size());
list.notifyAll();
}
}
public void consume(int num) throws InterruptedException {
synchronized (list) {
while ((list.size() - num) < 0) {
System.out.println("库存不够了,暂停消费");
System.out.println(Thread.currentThread().getName()+"被阻塞");
list.wait();
}
System.out.println(Thread.currentThread().getName()+"开始消费");
for (int i = 0; i < num; i++) {
list.pop();
}
System.out.println("当前仓库库存:"+list.size());
list.notifyAll();
}
}
}
消费者:
package thread;
public class Customer extends Thread {
private Storage storage;
private int num;
public Customer(Storage storage, int num) {
this.storage = storage;
this.num = num;
}
public void consume() {
try {
this.storage.consume(this.num);
} catch (Exception e) {
// TODO: handle exception
}
}
@Override
public void run() {
consume();
}
}
生产者:
package thread;
public class Producer extends Thread{
private Storage storage;
private int num;
public Producer(Storage storage, int num) {
this.storage = storage;
this.num = num;
}
public void produce() {
try {
this.storage.produce(this.num);
} catch (Exception e) {
// TODO: handle exception
}
}
@Override
public void run() {
produce();
}
}
测试:
package thread;
public class Test {
public static void main(String[] args) {
Storage storage = new Storage(1);
int num = 10;
Customer[] cus = new Customer[num];
Producer[] pro = new Producer[num];
for (int i = 1; i < num; i++) {
cus[i] = new Customer(storage, i);
pro[i] = new Producer(storage, i);
cus[i].setName(i + "号消费者");
pro[i].setName(i + "号生产者");
}
for (int i = 0; i < 10; i++) {
try {
cus[i].start();
pro[i].start();
} catch (Exception e) {
// TODO: handle exception
}
}
}
}
----output----
1号消费者开始消费
当前仓库库存:0
3号生产者开始生产
当前仓库库存:3
3号消费者开始消费
当前仓库库存:0
2号生产者开始生产
当前仓库库存:2
库存不够了,暂停消费
6号消费者被阻塞
6号生产者开始生产
当前仓库库存:8
2号消费者开始消费
当前仓库库存:6
1号生产者开始生产
当前仓库库存:7
库存不够了,暂停消费
9号消费者被阻塞
8号生产者开始生产
当前仓库库存:15
8号消费者开始消费
当前仓库库存:7
6号消费者开始消费
当前仓库库存:1
7号生产者开始生产
当前仓库库存:8
7号消费者开始消费
当前仓库库存:1
5号生产者开始生产
当前仓库库存:6
5号消费者开始消费
当前仓库库存:1
4号生产者开始生产
当前仓库库存:5
4号消费者开始消费
当前仓库库存:1
9号生产者开始生产
当前仓库库存:10
9号消费者开始消费
当前仓库库存:1
几点注意:
1、使用while 而不是if,因为使用if可能消费者被唤醒时,之间执行remove然而list里面没有东西会抛出异常,while能让它再次进行一次判断。
2、notifyAll而不是notify,原因是防止同类之间的相互唤醒导致假死状态。比如消费者1 唤醒消费者2线程 生产者1 又唤醒 生产者2,最终会导致整个过程僵住
3、为了让实验比较清晰,笔者多加了几条输出。
补充:另一种await/signal实现。差不多,可以作为练习
package thread;
import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class MyService {
private ReentrantLock lock = new ReentrantLock();
private Condition conditionA = lock.newCondition();
private int MAX_SIZE = 100;
private LinkedList<Integer> holder = new LinkedList<>();
public void produce(int num) {
try {
lock.lock();
while (holder.size() + num >= MAX_SIZE) {
conditionA.await();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < num; i++) {
holder.add(1);
}
System.out.println("生产出了" + num + "个产品" + " 仓库容量:" + holder.size());
conditionA.signalAll();
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
public void consume(int num) {
try {
lock.lock();
while (holder.size() - num < 0) {
conditionA.await();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < num; i++) {
holder.remove();
}
System.out.println("消费了" + num + "个产品" + " 仓库容量:" + holder.size());
conditionA.signalAll();
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
package thread;
import java.util.ArrayList;
import java.util.Random;
public class Test5 {
public static void main(String[] args) {
MyService service = new MyService();
ArrayList<Thread> producerList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
producerList.add(new Thread(new Runnable() {
@Override
public void run() {
Random random = new Random();
service.produce(random.nextInt(10)+1);
}
}));
}
ArrayList<Thread> consumerList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
consumerList.add(new Thread(new Runnable() {
@Override
public void run() {
Random random = new Random();
service.consume(random.nextInt(10)+1);
}
}));
}
for (Thread thread : consumerList) {
thread.start();
}
for (Thread thread : producerList) {
thread.start();
}
}
}