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

Java中结合BasicPlayer的线程问题:实时获取播放器状态

我来帮你解决这个线程同步和BasicPlayer状态监控的问题,先梳理下你当前代码里的核心问题,再给你两种可行的解决方案:

核心问题分析

你当前代码里最致命的问题是:在play()方法中,你启动了一个全新的Player实例线程——new Thread(new Player()).start(),这个新线程和正在播放音频的Player完全不是同一个对象,自然监控不到正确的播放状态。这不是单纯加同步方法能解决的,得先把实例统一起来。

另外,直接轮询状态不是最高效的方式,BasicPlayer本身提供了监听器接口,可以主动监听播放完成事件,比轮询更优雅。下面分两种方式给你修复:

方式一:修复轮询线程的实例问题

先调整代码,让监控线程属于当前Player实例,确保它监控的是正在播放的同一个BasicPlayer对象:

import javazoom.jl.player.BasicPlayer;
import javazoom.jl.player.BasicPlayerException;
import java.net.MalformedURLException;
import java.net.URL;

public class Player implements Runnable {
    private String song;
    private boolean comp = false;
    private final String path;
    private final BasicPlayer player;

    public Player() {
        player = new BasicPlayer();
        path = System.getProperty("user.dir") + "/src/Music/";
    }

    public void setSongName(String songName) {
        song = songName;
    }

    public String getSongName() {
        return song;
    }

    public void play() {
        try {
            player.open(new URL("file:///" + path + song));
            player.play();
            // 启动当前实例的监控线程,而不是新建Player对象
            new Thread(this).start();
        } catch (BasicPlayerException | MalformedURLException e) {
            e.printStackTrace();
        }
    }

    // 把状态监控逻辑放到run方法里
    @Override
    public void run() {
        try {
            // 循环检查状态,直到播放完成
            while (!isComplete()) {
                Thread.sleep(500); // 每隔500ms检查一次,避免过度占用CPU
            }
            setComp(true);
            // 这里可以直接添加自动播放下一首的逻辑
            System.out.println("当前歌曲播放完成,准备播放下一首");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    private boolean isComplete() {
        // 同步player对象,确保状态读取的线程安全
        synchronized (player) {
            // 推荐用BasicPlayer的常量代替硬编码的2,可读性更强
            return player.getStatus() == BasicPlayer.STOPPED;
        }
    }

    public synchronized boolean getComplete() {
        return comp;
    }

    public synchronized void setComp(boolean comp) {
        this.comp = comp;
    }
}

关键修改点:

  • 把new Thread(new Player()).start()改成new Thread(this).start(),用当前Player实例作为Runnable,确保监控的是同一个播放实例
  • 把状态检查逻辑移到run()方法中,避免重复创建对象
  • 用BasicPlayer.STOPPED常量代替硬编码的2,代码更易维护
  • 添加Thread.sleep(500)减少轮询的CPU消耗

方式二:使用BasicPlayerListener监听播放事件(更优雅)

轮询不是最优解,BasicPlayer支持监听器模式,可以主动通知播放完成事件,代码更简洁高效:

import javazoom.jl.player.BasicPlayer;
import javazoom.jl.player.BasicPlayerEvent;
import javazoom.jl.player.BasicPlayerException;
import javazoom.jl.player.BasicPlayerListener;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;

public class Player implements BasicPlayerListener {
    private String song;
    private boolean comp = false;
    private final String path;
    private final BasicPlayer player;

    public Player() {
        player = new BasicPlayer();
        // 给Player添加监听器,监听播放事件
        player.addBasicPlayerListener(this);
        path = System.getProperty("user.dir") + "/src/Music/";
    }

    public void setSongName(String songName) {
        song = songName;
    }

    public void play() {
        try {
            player.open(new URL("file:///" + path + song));
            player.play();
        } catch (BasicPlayerException | MalformedURLException e) {
            e.printStackTrace();
        }
    }

    // 监听播放完成事件(EOM = End of Media)
    @Override
    public void stateUpdated(BasicPlayerEvent event) {
        if (event.getCode() == BasicPlayerEvent.EOM) {
            setComp(true);
            System.out.println("当前歌曲播放完成,准备播放下一首");
            // 在这里直接调用下一首的播放逻辑即可
        }
    }

    // 下面是监听器的其他默认实现方法,按需调整
    @Override
    public void opened(Object stream, Map properties) {}

    @Override
    public void progress(int bytesread, long microseconds, byte[] pcmdata, Map properties) {}

    @Override
    public void setController(BasicPlayer controller) {}

    public synchronized boolean getComplete() {
        return comp;
    }

    public synchronized void setComp(boolean comp) {
        this.comp = comp;
    }
}

优势:

  • 不需要额外的轮询线程,由BasicPlayer主动通知播放完成
  • 代码更简洁,CPU消耗更低
  • 能精准捕捉到播放结束的时机

主程序同步示例

在你的Scanner输入线程中,只需要和这个Player实例交互即可。比如:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Player player = new Player();
        Scanner scanner = new Scanner(System.in);
        
        while (true) {
            System.out.println("输入歌曲名播放(输入exit退出):");
            String input = scanner.nextLine();
            if ("exit".equalsIgnoreCase(input)) {
                break;
            }
            player.setSongName(input);
            player.play();
            // 如果用轮询方式,可以在这里等待播放完成
            // while (!player.getComplete()) {
            //     try {
            //         Thread.sleep(100);
            //     } catch (InterruptedException e) {
            //         e.printStackTrace();
            //     }
            // }
        }
        scanner.close();
    }
}

内容的提问来源于stack exchange,提问作者Archishmaan P

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 03:56:43