基于ReentrantLock实现H2O持续生成线程报错问题排查
分析你的代码问题与解决方案
首先,我们来拆解你遇到的IllegalMonitorStateException异常以及代码中的其他逻辑问题:
一、异常的直接原因:锁的获取位置错误
你在hydrogenProducer和oxygenProducer方法中,把lock.lock()放在了try代码块内部:
try { lock.lock(); // ... 其他代码 } finally { lock.unlock(); }
这种写法存在风险:如果lock.lock()抛出异常(比如安全管理器阻止获取锁、或者极端情况下的JVM错误),线程并没有成功获取锁,但finally块仍然会执行lock.unlock()。而ReentrantLock的unlock()方法要求调用线程必须持有锁,否则就会抛出IllegalMonitorStateException。
正确的写法应该是在try块之外先获取锁,确保只有获取锁成功后才进入try块:
lock.lock(); // 先获取锁,失败则不会进入后续逻辑 try { // ... 业务逻辑 } finally { lock.unlock(); }
二、核心逻辑错误:未实现H₂O的生成规则
你的代码完全没有实现“每两个H和一个O组成一组”的需求,只是让三个线程各自打印字符,没有任何等待协调逻辑,具体问题包括:
- O线程调用错误方法:在main方法中,创建生成O的线程时,错误调用了
hydrogenProducer(),导致O线程也打印h,完全无法生成氧原子。 - 缺乏等待条件:没有维护H和O的计数,也没有让线程在条件不满足时等待(比如H不足两个时,O线程需要等待;O不足一个时,H线程需要等待),无法保证生成的是合法的H₂O分子。
三、修正后的完整代码
下面是使用Lock和生产者消费者模式实现的正确代码,确保每次生成两个H和一个O的组合:
package com.threads.reentrantlock.consumerproducer; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class H2OProducer { // 全局锁 private static final Lock lock = new ReentrantLock(true); // 等待条件:H的数量足够生成H2O private static final Condition hCondition = lock.newCondition(); // 等待条件:O的数量足够生成H2O private static final Condition oCondition = lock.newCondition(); // 记录当前已生成的未配对的H数量 private static int hCount = 0; public static void main(String[] args) { // 两个生成H的线程 Thread h1 = new Thread(() -> { try { hydrogenProducer(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); e.printStackTrace(); } }); Thread h2 = new Thread(() -> { try { hydrogenProducer(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); e.printStackTrace(); } }); // 一个生成O的线程 Thread o = new Thread(() -> { try { oxygenProducer(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); e.printStackTrace(); } }); h1.start(); h2.start(); o.start(); try { h1.join(); h2.join(); o.join(); } catch (InterruptedException e) { e.printStackTrace(); } } public static void hydrogenProducer() throws InterruptedException { while (true) { // 持续生成H lock.lock(); try { // 如果当前H数量已经是2,等待O线程消耗后再生成 while (hCount == 2) { hCondition.await(); } System.out.print("H"); hCount++; // 如果H数量达到2,唤醒O线程可以生成O了 if (hCount == 2) { oCondition.signal(); } } finally { lock.unlock(); } // 模拟生成间隔,避免太快输出 Thread.sleep(500); } } public static void oxygenProducer() throws InterruptedException { while (true) { // 持续生成O lock.lock(); try { // 如果当前H数量不足2,等待H线程生成足够的H while (hCount < 2) { oCondition.await(); } System.out.println("O"); // 打印O后换行,代表一个H2O分子生成完成 hCount = 0; // 重置H计数,准备下一个H2O // 唤醒H线程继续生成H hCondition.signalAll(); } finally { lock.unlock(); } // 模拟生成间隔 Thread.sleep(500); } } }
修正后的逻辑说明:
- 用
hCount记录当前已生成的未配对的H数量。 - H线程:当
hCount达到2时,进入等待,直到O线程消耗后重置计数。 - O线程:当
hCount不足2时,进入等待,直到H线程生成足够的H后唤醒它。 - 每次O生成后,重置
hCount并唤醒所有H线程,开始下一轮的H₂O生成。 - 所有线程持续运行(通过
while(true)实现持续生成)。
内容的提问来源于stack exchange,提问作者Ashish Banker
相关产品推荐
相关产品推荐

