生产者和消费者模型:
通过一个容器来解决生产者和消费者的强耦合问题。
生产者消费者彼此之间不直接通讯,而是通过阻塞队列来进行通讯,生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列中里取,阻塞队列相当于一个缓冲区,平衡了生产者和消费者的处理能力
阻塞队列就是用来给生产者和消费者解耦的。
线程间通信使用的API:
- wait():让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁.
- notify():唤醒当前对象上等待的线程(唤醒单个线程)
- notifyAll():唤醒所有的线程
这三种方法都是Object类的方法;
先对对象加锁,才能调用上面的三个方法(调用这三个方法必须是在synchronized代码块中)
wait()方法:使线程停止运行。
- 使当前线程进行等待。用来及那个当前线程置入"阻塞队列"中,并且在wait()所在的代码处停止执行,直到接到通知或被中断为止。
- wait()方法只能在同步方法中或同步代码块中调用
- 如果调用wait()时,没有持有适当的锁,会抛出异常。
- wait()方法执行后,当前线程释放锁,线程与其他线程竞争重新获取锁。
notify()方法:使停止的线程继续运行
- 用来通知那些可能等待该对象的对象锁的其他线程,对其发出notify,并使它们重新获取该对象的对象锁。如果有多个线程等待,则由线程规划器随机挑选出一个呈wait状态的线程。
- 在notify()后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行完,退出同步代码块之后才会释放对象锁。
notifyAll():唤醒所有等待线程
线程间通信的过程:
消费者一直消费
生产者一直生产
20个消费者线程,消费者每次消费一个
10个生产者线程,生产者每次生产三个
最大库存为100
public class BreadShop1 {
private static int COUNT;//表示库存数,线程共享的
//消费者
public static class Consumer implements Runnable{
private String name;
public Consumer(String name) {
this.name = name;
}
@Override
public void run() {
try {
//一直消费
while (true){
synchronized (BreadShop1.class) {//加同一把锁(消费者和生产者使用的是同一把锁)
//库存到达下限,不能继续消费,需要阻塞等待
if (COUNT == 0){
BreadShop1.class.wait();//库存数等于0,当前线程阻塞等待,释放对象锁
}else{
//库存> 0,允许消费
System.out.printf("消费者 %s 消费了1个面包\n",name);
COUNT--;
//通知BreadShop1.class.wait();代码进入阻塞的线程
BreadShop1.class.notifyAll();//唤醒进入阻塞队列的线程
//模拟线程的耗时
// Thread.sleep(100);
}
//优化
Thread.sleep(100);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//生产者
public static class Producer implements Runnable{
private String name;
public Producer(String name) {
this.name = name;
}
@Override
public void run() {
try {
while (true){
synchronized (BreadShop1.class) {
//库存到达上限
if (COUNT + 3 > 100){
//到了最大库存数,阻塞
BreadShop1.class.wait();//释放对象锁
}else{
System.out.printf("生产者 %s 生产了3个面包\n",name);
COUNT = COUNT +3;
BreadShop1.class.notifyAll();
}
}
Thread.sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
//模拟20个消费者线程,同时启动20个消费者线程
Thread[] consumer = new Thread[20];
for (int i =0;i< 20;i++){
consumer[i] = new Thread(new Consumer(String.valueOf(i)));
}
for (Thread thread : consumer){
thread.start();
}
//模拟10个生产者线程,同时启动10个生产者线程
Thread[] producers = new Thread[10];
for (int i =0;i< 10;i++){
producers[i] = new Thread(new Producer(String.valueOf(i)));
}
for (Thread thread : producers){
thread.start();
}
}
}
改变需求:
10个消费者,每次消费的数量为5
5个生产者,每天的生产次数是10,每次生产的个数是3
最大库存量为100
public class BreadShop进阶版 {
private static int 消费者数量 = 10;
private static int 每次消费的面包数 = 5;
private static int 生产者数量 = 5;
private static int 生产次数 = 10;
private static int 每次生产的面包数 = 3;
private static int 最大库存数 = 100;
private static int 面包店库存;
private static int 生产面包的总数;
public static class Consumer implements Runnable{
private String name;
public Consumer(String name) {
this.name = name;
}
@Override
public void run() {
//一直消费
try {
while (true){
synchronized (BreadShop进阶版.class){
if(生产面包的总数 == 生产者数量 * 生产次数 * 每次生产的面包数){
break;
}
//库存到达下限,不能继续消费,需要阻塞等待
if(面包店库存 - 每次消费的面包数 < 0){
BreadShop进阶版.class.wait();//释放对象锁
}else {
//库存满足消费条件,允许消费
面包店库存 -= 每次消费的面包数;
System.out.printf("消费者 %s 消费了%s个面包, 库存%s\n", name, 每次消费的面包数, 面包店库存);
//通知BreadShop.class.wait();代码进入阻塞的线程
BreadShop进阶版.class.notifyAll();
//模拟消费的耗时
Thread.sleep(200);
}
}
Thread.sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//生产者
public static class Producer implements Runnable{
private String name;
public Producer(String name) {
this.name = name;
}
@Override
public void run() {
try {
//达到生产次数,满足生产次数才会退出
for (int i = 0; i< 生产次数; i++){
synchronized (BreadShop进阶版.class){
//库存到达上限,不能继续生产,需要阻塞等待
while(面包店库存 + 每次生产的面包数 > 最大库存数){
BreadShop进阶版.class.wait();//释放对象锁
}
面包店库存 += 每次生产的面包数;
生产面包的总数 += 每次生产的面包数;//生产面包的总数要更改
//库存满足生产条件
System.out.printf("生产者 %s 生产了%s次, 库存%s, 生产的数量%s\n", name, i+1, 面包店库存, 生产面包的总数);
//通知BreadShop.class.wait();代码进入阻塞的线程
BreadShop进阶版.class.notifyAll();
//模拟消费的耗时
Thread.sleep(200);
}
Thread.sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
//同时启动消费者线程
Thread[] consumers = new Thread[消费者数量];
for(int i = 0; i< 消费者数量; i++){
consumers[i] = new Thread(new Consumer(String.valueOf(i)));
}
//同时启动生产者线程
Thread[] producers = new Thread[生产者数量];
for(int i = 0; i< 生产者数量; i++){
producers[i] = new Thread(new Producer(String.valueOf(i)));
}
for(Thread t : consumers){
t.start();
}
for(Thread t : producers){
t.start();
}
}
}