JavaZoom Player多线程关闭触发NullPointerException问题求助
解决JavaZoom播放器关闭时的NullPointerException问题
嘿,我一眼就揪出问题所在了——你完全找错了要停止的播放器对象!来给你拆解原因+修复方案:
问题根源分析
- 空指针的直接原因:当
isRunning为true时,你新建了一个Main m实例,然后直接调用m.play.close()。但这个新实例的play字段还没被初始化(play是在run()方法里才会赋值的,而这个新线程此时还没启动),自然触发空指针异常! - 逻辑错误:你根本没去停止正在运行的那个播放线程,反而创建了新的对象,相当于在和空气打架。
- 废弃方法风险:
Thread.stop()是Java早已废弃的方法,强制停止线程会导致资源泄漏、状态不一致等问题,绝对不能用。
修复方案
第一步:给Main类添加优雅停止的能力
修改Main类,添加停止标志位和统一的停止方法,同时优化播放逻辑支持中断:
import java.net.*; import java.util.Scanner; import java.io.*; import javazoom.jl.decoder.JavaLayerException; import javazoom.jl.player.Player; import javax.swing.ImageIcon; public class Main extends Thread { public static URL radiourl; public Player play; public static int art = 0; public static String radio = null; InputStream stream; // 用volatile保证多线程下的可见性,用于优雅中断播放 private volatile boolean isStopped = false; public void run() { int valuehere = RadioGui.value; switch (valuehere) { case 1: radio = "http://mp3-live.swr3.de/swr3_m.m3u"; art = 1; break; case 2: radio = "http://www.wdr.de/wdrlive/media/einslivedigi.m3u"; art = 1; break; case 3: radio = "http://www.wdr.de/wdrlive/media/wdr2.m3u"; art = 1; break; case 4: radio = "http://sunshinelive.hoerradar.de/sunshinelive-live-mp3-hq"; art = 2; break; default: return; // 未选中电台直接退出 } try { URL url; if (art == 1) { url = new URL(radio); Scanner scan = new Scanner(url.openStream()); String radioendurl = null; // 读取播放地址时也检查停止标志 while (scan.hasNext() && !isStopped) { String radiourlLine = scan.nextLine(); if (radiourlLine.contains(".mp3")) { radioendurl = radiourlLine; break; // 找到地址就停止读取,提升效率 } } if (radioendurl == null || isStopped) { return; } url = new URL(radioendurl); } else { // art ==2 url = new URL(radio); } stream = url.openStream(); play = new Player(stream); RadioGui.isRunning = true; System.out.println(RadioGui.isRunning); // 逐帧播放,每播放一帧就检查是否需要停止 while (!isStopped && play.play(1)) { // 空循环,仅用于检查停止标志 } } catch (IOException | JavaLayerException e) { e.printStackTrace(); } finally { // 播放结束或停止后统一清理资源 RadioGui.isRunning = false; if (play != null) { play.close(); } try { if (stream != null) { stream.close(); } } catch (IOException e) { e.printStackTrace(); } } } // 对外暴露的安全停止方法 public void stopPlayer() { isStopped = true; if (play != null) { play.close(); } } }
第二步:修改主窗体代码,保存正在运行的播放器实例
在RadioGui类中添加成员变量保存当前播放实例,修复点击事件逻辑:
// 在RadioGui类中添加成员变量,保存正在运行的播放器实例 private Main currentPlayer; // 修改鼠标点击事件逻辑 label.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent arg0) { sendername = (String)sender.getSelectedItem(); // 优化为else if,避免不必要的判断 if(sendername.equals("SWR 3")){ value = 1; bild.setIcon(new ImageIcon(RadioGui.class.getResource("/Images/swr 3.png"))); name.setText(sendername); } else if(sendername.equals("1 Live DIGGi")){ value = 2; bild.setIcon(new ImageIcon(RadioGui.class.getResource("/Images/1livediggi.png"))); name.setText(sendername); } else if(sendername.equals("WDR 2")){ value = 3; bild.setIcon(new ImageIcon(RadioGui.class.getResource("/Images/wdr2.gif"))); name.setText(sendername); } else if(sendername.equals("SUNSHINE LIVE")){ value = 4; bild.setIcon(new ImageIcon(RadioGui.class.getResource("/Images/sunshine.png"))); name.setText(sendername); } if(isRunning){ // 停止当前正在运行的播放器 if(currentPlayer != null){ currentPlayer.stopPlayer(); // 等待线程结束,避免快速切换出现异常 try { currentPlayer.join(500); } catch (InterruptedException e) { e.printStackTrace(); } } isRunning = false; } else { // 创建新的播放器并启动 currentPlayer = new Main(); new Thread(currentPlayer).start(); } } });
关键修改点说明
- 用
volatile标志位isStopped实现优雅中断,替代废弃的Thread.stop() - 播放时逐帧检查停止标志,确保停止请求能被及时响应
- 主窗体保存当前播放实例,确保操作的是正在运行的那个播放器,而不是新建的未初始化对象
- 统一在
finally块中清理资源,避免内存泄漏
内容的提问来源于stack exchange,提问作者Nireki
相关产品推荐
相关产品推荐

