Java中javax.sound.sampled.Clip运行一段时间后停止工作
问题分析与解决方案
核心原因
你遇到的问题本质是音频线路资源耗尽,而非文件格式问题。每次调用AudioSystem.getClip()都会创建一个新的Clip实例,而系统能同时打开的音频线路数量是有限的。短时间内高频调用(每秒两次)会快速耗尽可用线路,此时再创建新Clip就会抛出LineUnavailableException,且由于资源不足的异常上下文混乱,会误报成“格式不支持”。
即使缓存了音频数据,只要每次都创建新的Clip实例且不及时释放资源,问题依然会出现——因为消耗的是音频线路资源,不是文件加载的内存资源。
解决方案
1. 播放结束后主动释放Clip资源
给Clip添加LineListener,在播放完成(STOP事件)后调用close()释放线路资源,及时回收资源避免耗尽:
public static void playSound(String path) { logger.info("Playing " + path); Clip clip = null; try { clip = AudioSystem.getClip(); AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new BufferedInputStream(new FileInputStream(path))); clip.open(audioInputStream); // 添加监听器,播放结束后关闭Clip clip.addLineListener(event -> { if (event.getType() == LineEvent.Type.STOP) { clip.close(); } }); clip.start(); } catch (LineUnavailableException e) { System.err.println("音频线路资源耗尽,无法播放 " + path); e.printStackTrace(); } catch (UnsupportedAudioFileException | IOException e) { e.printStackTrace(); } }
2. 复用Clip实例(针对重复播放的音频)
如果某些音频需要重复播放,可以提前初始化并缓存Clip实例,播放前重置状态而非创建新实例:
// 缓存已初始化的Clip private static final Map<String, Clip> CLIP_CACHE = new ConcurrentHashMap<>(); public static void playCachedSound(String path) throws IOException, UnsupportedAudioFileException, LineUnavailableException { Clip clip = CLIP_CACHE.computeIfAbsent(path, key -> { try { Clip newClip = AudioSystem.getClip(); AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new File(key)); newClip.open(audioInputStream); return newClip; } catch (Exception e) { throw new RuntimeException(e); } }); // 重置到起始位置并播放 clip.stop(); clip.setFramePosition(0); clip.start(); }
3. 控制并发播放数量
如果需要同时播放多个不同音频,可以使用信号量限制并发的Clip数量,避免超过系统线路上限:
// 限制最多同时播放2个音频(根据系统实际情况调整) private static final Semaphore SEMAPHORE = new Semaphore(2); public static void playSoundWithLimit(String path) { logger.info("Playing " + path); new Thread(() -> { try { SEMAPHORE.acquire(); Clip clip = AudioSystem.getClip(); AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new BufferedInputStream(new FileInputStream(path))); clip.open(audioInputStream); clip.addLineListener(event -> { if (event.getType() == LineEvent.Type.STOP) { clip.close(); SEMAPHORE.release(); } }); clip.start(); } catch (Exception e) { SEMAPHORE.release(); e.printStackTrace(); } }).start(); }
额外注意事项
- Ubuntu下的PulseAudio默认对同时打开的音频流有数量限制,及时释放资源是关键。
- OpenJDK的AudioSystem实现依赖系统底层音频服务,可通过
pulseaudio --check命令验证服务是否正常运行。
内容的提问来源于stack exchange,提问作者dVNE
相关产品推荐
相关产品推荐

