生产者消费者模型
生产者消费者模型是通过一个容器来解决生产者和消费者的强耦合问题。
生产者与消费者彼此之间不直接通讯,而是通过阻塞队列来进行通讯,所以生产者生产完数据之后不会等待消费者处理,直接扔给阻塞队列
同时消费者直接从阻塞队列里取数据,阻塞队列就相当于一个缓冲区,平衡生产者与消费者的数据处理能力
先来复习一下可以用到的一个重要方法
1、wait()-痴汉方法
wait()就是使线程停止运行,会释放对象锁。
I.wait()方法会使当前线程调用该方法后进行等待,并且将该线程置入锁对象的等待队列中,直到接到通知或被中断为止。
II.wait()方法只能在同步方法或同步代码块中调用,如果调用wait()时没有适当的锁,会抛出异常。
III.wait()方法执行后,当前线程释放锁,其他线程可以竞争该锁。
wait()之后的线程继续执行由两种方法:
I.调用该对象的notify()方法唤醒等待线程
II.线程等待时调用interrupt()中断该线程
wait(long time) : 如果到了预计时间还未被唤醒,线程将继续执行。
/使用wait()方法
public class Test {
public static void main(String[] args) throws InterruptedException {
Object object = new Object();
synchronized (object){
System.out.println("开始等待");
object.wait();//wait方法使线程停止运行
System.out.println("等待结束");
}
System.out.println("main方法结束");
}
}
2、notify()方法
I.notify()方法也必须在同步方法或同步代码块中调用,用来唤醒等待在该对象上的线程。如果有多个线程等待,则任意挑选一个线程唤醒。
II.notify()方法执行后,唤醒线程不会立刻释放对象锁,要等待唤醒线程全部执行完毕后才释放对象锁。
//使用notify()方法
class MyThread implements Runnable{
private boolean flag;
private Object obj;
public MyThread(boolean flag, Object obj) {
this.flag = flag;
this.obj = obj;
}
public void waitMethod(){
synchronized (obj){
try {
while (true){
System.out.println("wait方法开始"+Thread.currentThread().getName());
obj.wait();//线程睡眠
System.out.println("wait方法结束"+Thread.currentThread().getName());
return;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void notifyMethod(){
synchronized (obj){
try {
System.out.println("notify方法开始"+Thread.currentThread().getName());
obj.notify();//线程唤醒
System.out.println("notify方法结束"+Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void run() {
if(flag){
this.waitMethod();
}else {
this.notifyMethod();
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
Object object = new Object();
MyThread waitThread = new MyThread(true,object);
MyThread notifyThread = new MyThread(false,object);
Thread thread1 = new Thread(waitThread,"wait线程");
Thread thread2 = new Thread(notifyThread,"notify线程");
thread1.start();
Thread.sleep(100);
thread2.start();
System.out.println("main方法结束");
}
}
3、notifyAll()
唤醒所有在该对象上等待的线程。
//使用notifyAll()方法唤醒线程
class MyThread implements Runnable{
private boolean flag;
private Object obj;
public MyThread(boolean flag, Object obj) {
this.flag = flag;
this.obj = obj;
}
public void waitMethod(){
synchronized (obj){
try {
while (true){
System.out.println("wait方法开始"+Thread.currentThread().getName());
obj.wait();
System.out.println("wait方法结束"+Thread.currentThread().getName());
return;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void notifyMethod(){
synchronized (obj){
try {
System.out.println("notifyAll方法开始"+Thread.currentThread().getName());
obj.notifyAll();//线程同时唤醒
System.out.println("notifyAll方法结束"+Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void run() {
if(flag){
this.waitMethod();
}else {
this.notifyMethod();
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
Object object = new Object();
MyThread waitThread1 = new MyThread(true,object);
MyThread waitThread2 = new MyThread(true,object);
MyThread waitThread3 = new MyThread(true,object);
MyThread notifyThread = new MyThread(false,object);
Thread thread1 = new Thread(waitThread1,"wait线程1");
Thread thread2 = new Thread(waitThread2,"wait线程2");
Thread thread3 = new Thread(waitThread3,"wait线程3");
Thread thread4 = new Thread(notifyThread,"notify线程");
thread1.start();
thread2.start();
thread3.start();
Thread.sleep(100);
thread4.start();
System.out.println("main方法结束");
}
}
注意:唤醒线程不能过早,如果还没有线程在等待中时,过早地唤醒线程就会出现,先唤醒后等待的情况,此时也就没有必要运行wait方法了。
4、线程阻塞的情况
I.调用sleep()方法,主动放弃占有的cpu,不会释放对象锁
II.调用阻塞式IO方法(read()、write()),在该方法返回前,线程阻塞。
III.线程试图获取一个monitor,但该monitor被其他线程所持有导致阻塞。
IV.线程等待某个通知,即调用wait(),释放对象锁
V.调用线程suspend(),将线程挂起,容易导致死锁,已被废弃。
5、生产者消费者模型
生产者消费者需要第三方来解耦,所以我们模拟一个简单的商品的生产者与消费者,生产者线程生产一个商品后消费者线程开始消费。
首先创建一个商品类Goods,类中有商品数量以及生产和消费方法
//商品类
class Goods{
//商品名称
private String goodsName;
//商品剩余数量
private int count;
//生产方法
public synchronized void set(String goodsName){
this.goodsName = goodsName;
this.count = count + 1;
System.out.println(toString());
}
//消费方法
public synchronized void get(){
//每次都消费一个商品
this.count = this.count-1;
System.out.println(toString());
}
@Override
public String toString(){
return "Goods [goodsName="+goodsName+",count="+count+"]"+Thread.currentThread().getName();
}
}
//消费者以及生产者类
//生产者
class Producer implements Runnable{
private Goods goods;
public Producer(Goods goods) {
super();
this.goods = goods;
}
@Override
public void run() {
this.goods.set("棒棒糖");
}
}
//消费者
class Consumer implements Runnable{
private Goods goods;
public Consumer(Goods goods) {
super();
this.goods = goods;
}
@Override
public void run() {
this.goods.get();
}
}
//测试类
public class Test{
public static void main(String[] args) throws InterruptedException {
Goods goods = new Goods();
Thread produceThread = new Thread(new Producer(goods),"生产者线程");
Thread consumerThread = new Thread(new Consumer(goods),"消费者线程");
//启动生产者线程
produceThread.start();
Thread.sleep(100);
//启动消费者线程
consumerThread.start();
}
}
如果生产者线程和消费者线程启动的顺序发生变化,将导致商品数量出现负数,所有需要对商品数量进行判断,使用wait()和notify()方法
//商品类
class Goods{
//商品名称
private String goodsName;
//商品剩余数量
private int count;
//生产方法
public synchronized void set(String goodsName) throws InterruptedException {
//判断此时商品是否有剩余
if(this.count > 0){
System.out.println("商品还有剩余,等待消费者消费");
wait();
}
this.goodsName = goodsName;
this.count = count + 1;
Thread.sleep(100);
System.out.println(toString());
//生产商品之后通知消费者线程可以进行消费
notify();//唤醒消费者线程
}
//消费方法
public synchronized void get() throws InterruptedException {
//判断此时商品数量是否为0
if (this.count == 0){
System.out.println("现在没有商品了,等待生产者生产");
wait();
}
//每次都消费一个商品
this.count = this.count-1;
Thread.sleep(100);
System.out.println(toString());
//唤醒生产者线程
notify();
}
@Override
public String toString(){
return "Goods [goodsName="+goodsName+",count="+count+"]"+Thread.currentThread().getName();
}
}
//消费者以及生产者类
//生产者
class Producer implements Runnable{
private Goods goods;
public Producer(Goods goods) {
super();
this.goods = goods;
}
@Override
public void run() {
try {
this.goods.set("棒棒糖");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//消费者
class Consumer implements Runnable{
private Goods goods;
public Consumer(Goods goods) {
super();
this.goods = goods;
}
@Override
public void run() {
try {
this.goods.get();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//测试类
public class Test{
public static void main(String[] args) throws InterruptedException {
Goods goods = new Goods();
Thread produceThread = new Thread(new Producer(goods),"生产者线程");
Thread consumerThread = new Thread(new Consumer(goods),"消费者线程");
//启动消费者线程
consumerThread.start();
Thread.sleep(100);
//启动生产者线程
produceThread.start();
}
}
以上我们的生产者和消费者可以按正常顺序进行工作,但是一次只能生产并消费一个商品,如今我们需要
多个生产者以及多个消费者。
//商品类
class Goods{
//商品名称
private String goodsName;
//商品剩余数量
private int count;
//生产方法(多个生产者)
public synchronized void set(String goodsName) throws InterruptedException {
//判断此时商品是否有剩余
while (this.count > 0){
//System.out.println("现在还有剩余商品,等待消费者消费");
wait();//只要还有商品剩余,就不工作
}
this.goodsName = goodsName;
this.count = count + 1;
Thread.sleep(100);
System.out.println(Thread.currentThread().getName());
System.out.println(toString());
//生产商品之后通知消费者线程可以进行消费
notifyAll();
}
//消费方法
public synchronized void get() throws InterruptedException {
//判断此时商品数量是否为0
while (this.count == 0){
//System.out.println("现在没有商品了,等待生产者生产");
wait();
}
//每次都消费一个商品
this.count = this.count-1;
Thread.sleep(100);
System.out.println(Thread.currentThread().getName());
System.out.println(toString());
//唤醒生产者线程
notifyAll();
}
@Override
public String toString(){
return "Goods [goodsName="+goodsName+",count="+count+"]"+Thread.currentThread().getName();
}
}
//消费者以及生产者类
//生产者
class Producer implements Runnable{
private Goods goods;
public Producer(Goods goods) {
super();
this.goods = goods;
}
@Override
public void run() {
while (true){
try {
this.goods.set("棒棒糖");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//消费者
class Consumer implements Runnable{
private Goods goods;
public Consumer(Goods goods) {
super();
this.goods = goods;
}
@Override
public void run() {
while (true){
try {
this.goods.get();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//测试类
public class Test{
public static void main(String[] args) throws InterruptedException {
Goods goods = new Goods();
//存储生产者、消费者线程
List<Thread> threadList = new ArrayList<>();
//10个生产者线程
for (int i=0; i<10; i++){
Thread produceThread = new Thread(new Producer(goods));
produceThread.setName("生产者线程"+i);
threadList.add(produceThread);
}
//5个消费者线程
for (int i=0; i<5; i++){
Thread consumeThread = new Thread(new Consumer(goods));
consumeThread.setName("消费者线程"+i);
threadList.add(consumeThread);
}
//启动所有线程
for (Thread thread : threadList){
thread.start();
}
}
}
注意:
请解释sleep()与wait()的区别:
1、sleep()是Thread类中定义的方法,使用该方法后到了一定时间后线程自动唤醒,不会释放对象锁
2、wait()是Object类中定义的方法,使用该方法后必须使用notify()或notifyAll()方法将线程唤醒,会释放对象锁