这是我参与8月更文挑战的第23天,活动详情查看:8月更文挑战
生产者消费者问题
生产者消费者问题是线程通信的经典问题,两个角色生产者消费者,他们共用一块资源空间,当空间空的时候消费者阻塞,当空间满的时候生产者阻塞。
例子
一个初始值为0的变量,两个线程对它进行操作,一个加1一个减1交替进行
package com.lemon;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class lesson8_Consumer {
public static void main(String[] args) {
ResourceInfo resourceInfo = new ResourceInfo();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
resourceInfo.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "AAA").start();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
resourceInfo.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "BBB").start();
}
}
class ResourceInfo {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private int num;
public void increment() throws InterruptedException {
lock.lock();
try {
while (num != 0) {
condition.await(); //等待不能生产
}
num++;
System.out.println(Thread.currentThread().getName() + "\t num:" + num);
condition.signalAll(); //通知唤醒
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
while (num == 0) {
condition.await();
}
num--;
System.out.println(Thread.currentThread().getName() + "\t num:" + num);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
为什么判断num的值的时候用while而不是if?
虚假唤醒
当一个条件满足的时候,很多线程都被唤醒了,但是有些是无用的唤醒。如果说原来num为0,生产之后num为1,这样就会唤醒其他消费者来消费,但是实际上这个num=1只会被一个消费者消费。
while不会出现虚假唤醒: 线程被唤醒后会重新判断while中的条件,通过了while循环才会执行后面的代码
if会出现虚假唤醒: 唤醒后线程会从wait之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码
在jdk1.8文档中 Object - > wait ->可以看到这段话:
线程也可以唤醒,而不会被通知,中断或超时,即所谓的虚假唤醒 。 虽然这在实践中很少会发生,但应用程序必须通过测试应该使线程被唤醒的条件来防范,并且如果条件不满足则继续等待。 换句话说,等待应该总是出现在循环中,就像这样:
synchronized (obj) {
while (<condition does not hold>)
obj.wait(timeout);
... // Perform action appropriate to condition
}