如何在不同ActionListener中调用同一线程实现音乐播放/停止控制
你猜测的「停止音乐需要在播放按钮运行的同一线程中调用对应方法」的思路不完全正确,stop方法可以跨线程调用,无法停止的核心原因是:
- 你的
cl.play()方法是阻塞执行的,内部没有定期检查停止、暂停的状态标记,即使其他线程修改了停止标记,播放逻辑也感知不到,会继续执行。 - 你每次点击播放都会新建独立线程,没有统一管理播放线程,多次点击播放后会同时存在多个播放线程,更难统一控制。
实现方案
1. 修改播放控制类,增加线程可见的状态标记
给cl对应的类添加volatile修饰的状态标记,保证多线程修改的可见性,播放逻辑中循环检查状态响应控制指令:
public class 你的CL类名 { // 播放、暂停状态标记,volatile保证多线程修改后其他线程立即可见 private volatile boolean isPlaying = false; private volatile boolean isPaused = false; public void play() { isPlaying = true; isPaused = false; // 播放主循环,每次循环都检查状态 while (isPlaying) { if (isPaused) { // 暂停状态休眠10ms,避免空转占用CPU try { Thread.sleep(10); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } continue; } // 你原有读取音频帧、输出音频的播放逻辑写在这里 // 注意单次循环执行时间不要太长,否则停止/暂停响应会有延迟 } // 退出循环后执行资源释放,比如关闭音频流、重置播放进度 releaseResource(); } public void stop() { isPlaying = false; isPaused = false; } public void pause() { isPaused = true; } public void resume() { isPaused = false; } private void releaseResource() { // 你的资源释放逻辑 } }
2. 统一管理播放线程,避免重复创建
把播放线程提为GUI类的成员变量,避免每次点击播放都新建线程:
// GUI类的成员变量,统一持有播放线程实例 private Thread playThread; ActionListener bl2 = new ActionListener() { // 播放按钮 @Override public void actionPerformed(ActionEvent e) { System.out.println("listener1" + Thread.currentThread().getName()); System.out.println("beep"); // 已有播放线程在运行就跳过,避免重复播放 if (playThread != null && playThread.isAlive()) { return; } Runnable runnable = () -> cl.play(); playThread = new Thread(runnable); playThread.start(); } }; ActionListener bl3 = new ActionListener() { // 停止按钮 @Override public void actionPerformed(ActionEvent e) { cl.stop(); // 等待播放线程正常退出,避免资源泄漏 if (playThread != null) { try { playThread.join(); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } playThread = null; } } }; // 暂停按钮的事件监听实现 ActionListener bl4 = new ActionListener() { // 暂停按钮 @Override public void actionPerformed(ActionEvent e) { cl.pause(); } }; // 恢复播放按钮的事件监听实现,可与暂停按钮做二合一切换 ActionListener bl5 = new ActionListener() { // 恢复播放按钮 @Override public void actionPerformed(ActionEvent e) { cl.resume(); } };
注意事项
- 不要使用Java原生废弃的
Thread.stop()方法强制终止线程,该方法会导致资源无法正常释放,用状态标记通知线程主动退出的方式更安全。 - 如果你使用Java Sound API的
Clip类实现播放,直接在cl.stop()内部调用clip.stop()、clip.close()即可停止,无需自己实现状态循环。 - 所有Swing GUI的修改操作必须在EDT事件调度线程执行,不要在自定义的播放线程中修改界面元素。
内容的提问来源于stack exchange,提问作者Angry birds
相关产品推荐
相关产品推荐

