不用等待唤醒机制实现的生产者与消费者
代码
package com.hust.juc;
/*
* 生产者和消费者案例
*/
public class TestProductorAndConsumer {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor pro = new Productor(clerk);
Consumer cus = new Consumer(clerk);
new Thread(pro, "生产者 A").start();
new Thread(cus, "消费者 B").start();
/*
* new Thread(pro, "生产者 C").start(); new Thread(cus, "消费者 D").start();
*/
}
}
// 店员
class Clerk {
private int product = 0;
// 进货
public synchronized void get() {// 循环次数:0
if (product >= 1) {
System.out.println("产品已满!");
/*try {
this.wait();
} catch (InterruptedException e) {
}*/
} else {
System.out.println(Thread.currentThread().getName() + " : "
+ ++product);
//this.notifyAll();
}
}
// 卖货
public synchronized void sale() {// product = 0; 循环次数:0
if (product <= 0) {
System.out.println("缺货!");
/*try {
this.wait();
} catch (InterruptedException e) {
}*/
} else {
System.out.println(Thread.currentThread().getName() + " : "
+ --product);
//this.notifyAll();
}
}
}
// 生产者
class Productor implements Runnable {
private Clerk clerk;
public Productor(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
clerk.get();
}
}
}
// 消费者
class Consumer implements Runnable {
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
clerk.sale();
}
}
}
运行结果
只要线程得到了CPU就会运行,缺货也会一直去缺货,很不科学,所以这个时候我们得用等待唤醒机制。
使用等待唤醒机制
代码
就是把上面的代码中await和notify的注释打开
运行结果
这个时候会发现都运行完了,但是程序并没有结束,这就是等待唤醒机制遗留问题。
解决等待唤醒机制的问题
我们分析一下,很容易想到是因为,await被唤醒之后是从await的地方继续执行,那么直接从else出走掉了,假如那是消费者的最后一轮循环,那么那个最后一轮循环的线程就结束了,生产者还有一轮或者多伦循环没有结束,生产者线程生产出的商品就没有线程去消费就会一直等待。
代码去掉else就解决了这个问题
package com.hust.juc;
/*
* 生产者和消费者案例
*/
public class TestProductorAndConsumer {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor pro = new Productor(clerk);
Consumer cus = new Consumer(clerk);
new Thread(pro, "生产者 A").start();
new Thread(cus, "消费者 B").start();
/*
* new Thread(pro, "生产者 C").start(); new Thread(cus, "消费者 D").start();
*/
}
}
// 店员
class Clerk {
private int product = 0;
// 进货
public synchronized void get() {// 循环次数:0
if (product >= 1) {
System.out.println("产品已满!");
try {
this.wait();
} catch (InterruptedException e) {
}
}
System.out
.println(Thread.currentThread().getName() + " : " + ++product);
this.notifyAll();
}
// 卖货
public synchronized void sale() {// product = 0; 循环次数:0
if (product <= 0) {
System.out.println("缺货!");
try {
this.wait();
} catch (InterruptedException e) {
}
}
System.out
.println(Thread.currentThread().getName() + " : " + --product);
this.notifyAll();
}
}
// 生产者
class Productor implements Runnable {
private Clerk clerk;
public Productor(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
clerk.get();
}
}
}
// 消费者
class Consumer implements Runnable {
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
clerk.sale();
}
}
}
多线程下的虚假唤醒
说白了其实这还是“单线程“,因为只有一个线程生产一个线程消费,如果现在有两个线程去生产,两个线程去消费,那么就会出现虚假唤醒的情况。
虚假唤醒代码
把上面的代码中CD线程注释掉的打开就行。
运行截图
虚假唤醒问题分析
仔细考虑一下,假如现在消费者线程C执行,此时没有商品,消费者线程C等待,这个时候消费者线程D也拿到了CPU,也是没有商品的状态,也等待。这个时候一个生产者生产出了一个商品,此时唤醒了两个等待的消费者,往下执行,那么很自然就产生了-1等负数的情况。
解决办法就是把wait放在循环里
package com.hust.juc;
/*
* 生产者和消费者案例
*/
public class TestProductorAndConsumer {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor pro = new Productor(clerk);
Consumer cus = new Consumer(clerk);
new Thread(pro, "生产者 A").start();
new Thread(cus, "消费者 B").start();
new Thread(pro, "生产者 C").start();
new Thread(cus, "消费者 D").start();
}
}
// 店员
class Clerk {
private int product = 0;
// 进货
public synchronized void get() {// 循环次数:0
while (product >= 1) {
System.out.println("产品已满!");
try {
this.wait();
} catch (InterruptedException e) {
}
}
System.out
.println(Thread.currentThread().getName() + " : " + ++product);
this.notifyAll();
}
// 卖货
public synchronized void sale() {// product = 0; 循环次数:0
while (product <= 0) {
System.out.println("缺货!");
try {
this.wait();
} catch (InterruptedException e) {
}
}
System.out
.println(Thread.currentThread().getName() + " : " + --product);
this.notifyAll();
}
}
// 生产者
class Productor implements Runnable {
private Clerk clerk;
public Productor(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
clerk.get();
}
}
}
// 消费者
class Consumer implements Runnable {
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
clerk.sale();
}
}
}
运行截图
这个才会得到解决。