生产者-消费者模型(等待与唤醒机制)
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
使用synchronized + wait()/notify()实现生产者消费者模型:
wait()/notify()必须在同步代码块或同步方法中执行(所以肯定获取到了锁)
调用线程进入等待队列,释放锁。
任意一个锁对象只有一个等待队列,所有线程对象调用wait()都置入同一个等待队列,调用notify或notifyAll()会唤醒不该唤醒的线程,无端浪费CPU资源。
调用wait():当前线程释放锁,进入等待队列,线程与其它线程竞争重新获取锁。
notify():只唤醒一个线程,且是随机唤醒一个。
notifyAll():一次唤醒所有等待线程。
class Goods {
//商品名称
private String goodsName;
//商品库存
private int count;
//生产方法
public synchronized void set(String goodsName) throws InterruptedException {
//此时还有商品没被消费,等待消费者消费
while (this.count > 0) {
wait();
}
this.goodsName = goodsName;
this.count = count + 1;
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName());
System.out.println("生产" + toString());
System.out.println("-------------------------");
//生产完商品后通知消费者线程可以消费了
notifyAll();
}
class Producer implements Runnable {
private Goods goods;
public Producer(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
while (true) {
try {
this.goods.set("迪奥999");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//消费方法
public synchronized void get() throws InterruptedException {
//此时还没有商品,等待生产者生产商品
while (this.count == 0) {
wait();
}
//每次消费一个商品
this.count = this.count - 1;
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName());
System.out.println("消费" + toString());
System.out.println("-------------------");
//消费完告知生产者线程可以继续生产了
notifyAll();
}
class Consumer implements Runnable {
private Goods goods;
public Consumer(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
try {
this.goods.get();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public String toString() {
return "Goods [goodsName=" + goodsName + ",count=" + count + "]";
}
}
/**
* 生产者类
*/
class Producer implements Runnable {
private Goods goods;
public Producer(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
try {
this.goods.set("迪奥999");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 消费者类
*/
class Consumer implements Runnable {
private Goods goods;
public Consumer(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
try {
this.goods.get();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class SynchronizedTest {
public static void main(String[] args) {
Goods goods = new Goods();
//存储生产者、消费者线程
List<Thread> threadList = new ArrayList<>();
//10个生产者线程
for (int i = 0; i < 10; i++) {
Thread producerThread = new Thread(new Producer(goods));
producerThread.setName("生产者线程" + i);
threadList.add(producerThread);
}
//6个消费者线程
for (int i = 0; i < 6; i++) {
Thread consumerThread = new Thread(new Consumer(goods));
consumerThread.setName("消费者线程" + i);
threadList.add(consumerThread);
}
//启动所有线程
for (Thread thread : threadList) {
thread.start();
}
}
}
用while不用if原因:
当商品数量大于0,所有线程都得阻塞,消费者消费之后,调用notifyAll( )唤醒所有线程,包括消费者自己(多个消费者),如果使用if,代码卡在wait,唤醒后继续执行下面的代码,就有可能把一个商品多消耗了几次。
使用Lock+Condition实现多生产者-消费者模型:
class Goods {
//商品名称
private String name;
//当前商品数量
private int count;
//商品最大数量
private int maxCount;
public Goods(int maxCount) {
this.maxCount = maxCount;
}
private Lock lock = new ReentrantLock();
//消费者队列
private Condition consumer = lock.newCondition();
//生产者队列
private Condition producer = lock.newCondition();
/**
* 生产方法
*
* @param name 生产的商品名称
*/
public void setGoods(String name) {
lock.lock();
try {
//当商品数量达到了大值时阻塞生产者线程
while (count == maxCount) {
System.out.println(Thread.currentThread().getName() +
"商品已达到最大数量,等待消费者消费"
);
producer.await();
}
Thread.sleep(200);
//生产商品
this.name = name;
count++;
System.out.println(Thread.currentThread().getName() + "生产" + toString());
//唤醒消费者进程
consumer.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
* 消费方法
*/
public void getGoods() {
lock.lock();
try {
//商品数为0时阻塞消费者线程
while (count == 0) {
System.out.println(Thread.currentThread().getName() +
"商品已被消费完,等待生产者生产"
);
consumer.await();
}
Thread.sleep(200);
//消费商品
count--;
System.out.println(Thread.currentThread().getName() + "消费" + toString());
//唤醒生产者线程
producer.signalAll();
} catch (InterruptedException e) {
} finally {
lock.unlock();
}
}
@Override
public String toString() {
return "Goods{" + "name='" + name + '\'' + "count=" + count + "}";
}
}
class Producer implements Runnable {
private Goods goods;
public Producer(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
while (true) {
this.goods.setGoods("迪奥999");
}
}
}
class Consumer implements Runnable {
private Goods goods;
public Consumer(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
while (true) {
this.goods.getGoods();
}
}
}
public class ConditionTest {
public static void main(String[] args) {
List<Thread> list = new ArrayList<>();
Goods goods = new Goods(10);
Producer producer = new Producer(goods);
Consumer consumer = new Consumer(goods);
//创建10个消费者模型
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(consumer, "消费者" + i);
list.add(thread);
}
//创建5个生产者线程
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(producer, "生产者" + i);
list.add(thread);
}
for (Thread th : list) {
th.start();
}
}
}
考点:sleep()和wait()的区别:
1)sleep()是Thread类中定义的方法,到了一定的时间后该线程自动唤醒,不会释放对象锁。
2)wait()是Object类中定义的方法,要想唤醒必须使用notify()、notifyAll()方法才能唤醒,会释放对象锁 。