您的位置:首页 > 编程语言 > Java开发

Java多线程生产者消费者说明等待唤醒机制问题和虚假唤醒问题

2017-03-07 15:34 513 查看

不用等待唤醒机制实现的生产者与消费者

代码

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(cu
4000
s, "消费者 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();
}
}
}


运行截图



这个才会得到解决。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐