You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaZoom Player多线程关闭触发NullPointerException问题求助

解决JavaZoom播放器关闭时的NullPointerException问题

嘿,我一眼就揪出问题所在了——你完全找错了要停止的播放器对象!来给你拆解原因+修复方案:

问题根源分析

  1. 空指针的直接原因:当isRunning为true时,你新建了一个Main m实例,然后直接调用m.play.close()。但这个新实例的play字段还没被初始化(play是在run()方法里才会赋值的,而这个新线程此时还没启动),自然触发空指针异常!
  2. 逻辑错误:你根本没去停止正在运行的那个播放线程,反而创建了新的对象,相当于在和空气打架。
  3. 废弃方法风险: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:35:52