版权声明:本文为博主原创文章,转载请声明出处 http://blog.csdn.net/bug_moving https://blog.csdn.net/bug_moving/article/details/60765233
不用等待唤醒机制实现的生产者与消费者
代码
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();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
运行结果
只要线程得到了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();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
多线程下的虚假唤醒
说白了其实这还是“单线程“,因为只有一个线程生产一个线程消费,如果现在有两个线程去生产,两个线程去消费,那么就会出现虚假唤醒的情况。
虚假唤醒代码
把上面的代码中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();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
运行截图
这个才会得到解决。