带指定间隔的生产者/消费者实时场景代码问题及优化方案问询
优化异步队列消费与定时播放的实现方案
先聊聊你现有代码里的核心问题:
持有锁时调用
Thread.sleep()的危害:你在synchronized(q)代码块里调用了Thread.sleep(3000),这会导致队列的锁被Consumer线程持续持有3秒。而你的add方法也是用synchronized(q)加锁的,这意味着在这3秒内,所有试图往队列里加元素的线程都会被阻塞,根本没法往队列里塞数据——这就是为什么“队列持续被事件填充时声音停止播放”的原因,因为新元素根本加不进去,Consumer处理完当前元素后,队列还是空的,就会一直wait,直到锁被释放后add线程才能添加元素,但这时候播放已经停了。队列空时的wait逻辑没问题,但锁的范围太大:现有代码把播放和sleep都放在锁里,完全没必要,锁只需要覆盖队列的修改和读取操作即可。
针对你的业务场景(异步填充队列、依次消费、多元素时每隔X秒取一个播放),我给出以下优化方案:
优化后的代码实现
队列填充方法(基本不变,锁范围合理)
Queue<Wrapper> q = new ConcurrentLinkedQueue<>(); // 填充队列 void add(A obj) { synchronized (q) { q.add(obj); q.notify(); // 通知消费者队列有新元素了 } }
优化后的消费者线程
class Consumer extends Thread { private static final long PLAY_INTERVAL = 3000; // 每隔3秒播放一次 @Override public void run() { while (!Thread.currentThread().isInterrupted()) { A w = null; // 仅在操作队列时持有锁,避免阻塞其他线程 synchronized (q) { try { while (q.isEmpty()) { q.wait(); // 队列空时等待,主动释放锁 } w = q.remove(); // 取出队列头部元素 } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 恢复中断状态,保证线程优雅退出 break; } } // 释放锁后再处理播放和等待,不影响队列的填充操作 if (w != null) { try { playSomeSoundForXsec(w); // 执行播放声音操作 // 播放完成后,等待指定间隔再取下一个元素 Thread.sleep(PLAY_INTERVAL); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } } } }
优化点说明
- 缩小锁的范围:把队列的操作(判断空、取元素)限制在
synchronized块内,播放和等待逻辑放在锁外。这样Consumer在播放和等待时,add线程可以正常获取锁往队列里添加元素,彻底解决了“队列持续填充时声音停止”的问题。 - 正确处理中断:当线程被中断时,恢复中断状态并退出循环,保证线程可以优雅停止,避免资源泄漏。
- 贴合业务逻辑:每次处理完一个元素后,严格等待X秒再取下一个,不管队列里有没有新元素,都能保证间隔播放;如果队列空了,消费者会进入wait状态,直到有新元素被添加进来再立即处理,处理完成后仍会保持间隔等待。
内容的提问来源于stack exchange,提问作者basu
相关产品推荐
相关产品推荐

