记ReentrantLock-Condition await被异常唤醒的问题

在实现生产者消费者模型时,使用ReentrantLock和Condition的await方法出现异常情况。在某些环境下,消费者从await中因异常被唤醒,但未调用signal,导致同步问题。分析官方文档发现,await方法在异常唤醒时,异常会被系统处理,类似于Object的wait方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近一个朋友在写一个简单的生产者和消费者模型的时候,遇到了一个诡异的问题,他实用ReentrantLock 的lock去锁住生成者和消费者,并且通过Condition的await方法使生成者在临界值的时候,处于睡眠状态。但是在他的电脑上居然出现了消费者同步的问题,经过分析是其中一个消费者从await中异常被唤醒,而代码此时并没有调用signal

代码如下:

package com.thread;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyBlocking {

	private static  List<Object> list = new ArrayList<>();

	private static Lock lock = new ReentrantLock();

	private static Condition producer = lock.newCondition();
	private static Condition consumer = lock.newCondition();

	public static void main(String[] args) {
		ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(10);
		for (int i = 0; i < 1; i++) {
			Producer producer = new Producer();
			newFixedThreadPool.execute(producer);
		}
		for (int i = 0; i < 10; i++) {
			Consumer consumer = new Consumer();
			newFixedThreadPool.execute(consumer);
		}

	}

	static class Producer implements Runnable {
		public void run() {
			while (true) {
				try {
					lock.lock();
					if (list.size() >= 1) {
						try {
							// 释放锁,当前线程挂起
							producer.await();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}

					// 生产数据
					list.add("1");
					Thread.sleep(1000);
					System.out.println(Thread.currentThread().getName() + "生产了一个产品,当前队列的长度=" + list.size());
					// 通知其他线程争抢cpu资源`
					consumer.signal();
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					// 解除锁
					lock.unlock();
				}
  
			}
		}

	}

	static class Consumer implements Runnable {
		public void run() {
			while (true) {
				try {
					lock.lock();
					int size = list.size();
					if (size == 0) {
						System.out.println("消费者线程挂起..." + Thread.currentThread().getName());
						consumer.await();
						System.out
								.println("消费者线程复活..." + Thread.currentThread().getName() + ",list.size=" + list.size());
					}

					int size1 = list.size();
					list.remove(size1 - 1);
					System.out
							.println("消费者线程" + Thread.currentThread().getName() + "正常消费一个...,list.size=" + list.size());
					producer.signal();
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					lock.unlock();
				}
			}
		}
	}

}

代码比较简单就不做特别的描述了,就是实现了一个生产者和消费者同步的一个过程,这里因为使用了ReentrantLock对生产者和消费者作用一个lock 因此这里不需要考虑集合的线程安全性。

但是就是这么一段代码居然在不同的机器上表现出不同的问题,在我的设备上运行一切正常,但是在他的机器上确没执行多久就在这段代码上出现了问题,消费者消费完成以后 从集合里面删除内容。

list.remove(size1 - 1);

按照正常的思考来说,这个问题是不应该发生的因为,前面有对集合size的判断,当size为0的时候 所有消费者都进入了await(线程进入睡眠释放lock锁

经过分析-就是在awiat的休眠时,当一个消费者别挂起的时候,另外一个消费者这时候被唤醒;我们常规的可能认为消费者别唤醒在这里只有生成者那边生成完成以后 调用signal通知消费者进行FIFO的被唤醒,其实不然这里就出了异常

那既然知道是await的问题,那我们就看下官方文档的描述如下

在这里插入图片描述

看完文档我们就恍然大悟了,还真有异常唤醒的这样的关键字。这就让我们想到了Object的wait方法一样,只是wait方法被异常唤醒的时候会执行InterruptedException,而这里await被异常唤醒的时候异常被系统处理了而已

 public final void wait() throws InterruptedException {
        wait(0);
    }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值