使用synchronized实现生产消费
虚假等待
虚假唤醒意味着线程在没有满足 wait()
条件的情况下被唤醒。由于 wait()
可能会由于多种原因(如操作系统调度等)发生虚假唤醒,线程被唤醒后可能发现它原本等待的条件并未改变
示例分析:
scss
public synchronized void produce() throws InterruptedException {
if (queue.size() == 1) {
wait(); // 如果缓冲区满了,生产者等待
}
queue.add(1); // 生产产品
notify(); // 唤醒消费者
}
public synchronized void consume() throws InterruptedException {
if (queue.isEmpty()) {
wait(); // 如果缓冲区空了,消费者等待
}
queue.remove(0); // 消费产品
notify(); // 唤醒生产者
}
这里使用了if
来判断是否(满/空);
假设一下步骤发生:
1.生产者线程add(1),然后唤醒其他线程;
2.由于某些原因(比如操作系统调度、虚假唤醒等),消费线程 被唤醒,即使缓冲区仍然为空, 它依然开始执行。
3.消费线程 执行判断queue.isEmpty()
,如果他被虚假唤醒,判断结果为true,然后线程会继续进入消费逻辑,尝试从一个空的缓冲区取数据。
反制生产者亦是。
因为 if
只能判断一次,如果被虚假唤醒消费线程后,就失效了。 使用 while
循环代替 if
可以解决这个问题,因为 while
会 在每次被唤醒时重新检查条件 ,确保条件符合才继续执行
scss
public synchronized void produce() throws InterruptedException {
wgile (queue.size() == 1) {
wait(); // 如果缓冲区满了,生产者等待
}
queue.add(1); // 生产产品
notify(); // 唤醒消费者
}
public synchronized void consume() throws InterruptedException {
wgile (queue.isEmpty()) {
wait(); // 如果缓冲区空了,消费者等待
}
queue.remove(0); // 消费产品
notify(); // 唤醒生产者
}
Lock版的生产者消费问题
与synchronized
的对比,三件套。

使用Lock
与Conditon
实现的生产消费,下边这个代码本质和synchronized
类似
csharp
public class ProducerConsumer {
public static void main(String[] args) {
Task task = new Task();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
task.inSale();
}
}, "生产者1").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
task.inSale();
}
}, "生产者2").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
task.deSale();
}
}, "消费者1").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
task.deSale();
}
}, "消费者2").start();
}
}
class Task {
public Integer count = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void inSale() {
lock.lock();
try {
while (count != 0) {
condition.await();
}
count++;
System.out.println(Thread.currentThread().getName() + "生产了一个产品,目前产品数量为:" + count);
condition.signalAll(); // 唤醒所有等待线程
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
public void deSale() {
lock.lock();
try {
while (count == 0) {
condition.await();
}
count--;
System.out.println(Thread.currentThread().getName() + "消费了一个产品,目前产品数量为:" + count);
condition.signalAll(); // 唤醒所有等待线程
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
}
但是你会发现他没有按照一定顺序生产消费

Condition实现精准通知和唤醒线程
使用Condition
可以精准唤醒你想要实现的线程 例如:A->C->B->D
csharp
public class ProducerConsumer {
public static void main(String[] args) {
Task task = new Task();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
task.printA();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
task.printB();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
task.printC();
}
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
task.printD();
}
}, "D").start();
}
}
class Task {
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
Condition condition4 = lock.newCondition();
//1=》A 2=》B 3=》C 4=》D
private int number = 1;
public void printA() {
lock.lock();
try {
while (number != 1) {
condition1.await();
}
number = 3;
System.out.println(Thread.currentThread().getName() + "生产了一个产品,唤醒C:" + number);
condition3.signal(); // 唤醒C线程
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
public void printB() {
lock.lock();
try {
while (number != 2) {
condition2.await();
}
number = 4;
System.out.println(Thread.currentThread().getName() + "生产了一个产品,唤醒D:" + number);
condition4.signal(); // 唤醒D线程
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
public void printC() {
lock.lock();
try {
while (number != 3) {
condition3.await();
}
number = 2;
System.out.println(Thread.currentThread().getName() + "生产了一个产品,唤醒B:" + number);
condition2.signal(); // 唤醒A线程
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
public void printD() {
lock.lock();
try {
while (number != 4) {
condition4.await();
}
number = 1;
System.out.println(Thread.currentThread().getName() + "生产了一个产品,唤醒A:" + number);
condition1.signal(); // 唤醒B线程
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
}
