0

One function in my program is to playback an Mp3 file. I'm using the JLayer Library (http://www.javazoom.net/javalayer/javalayer.html) to accomplish this.

Also, to prevent blocking of the program while the song plays, I want to use Threads. See this code snippet:

import javazoom.jl.player.Player;

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class P2PClient {

    private static Player player;

    public static void main (String[] args) {
        try {
            play(args[0]);
        } catch (FileNotFoundException | javazoom.jl.decoder.JavaLayerException e) {
            e.printStackTrace();
        }

        System.out.println("Hello");
        player.close();

    }

    private static void play(String track) throws FileNotFoundException, javazoom.jl.decoder.JavaLayerException {
         new Thread() {
            private Player player = new Player(new FileInputStream(track));

            @Override
            public void run() {
                try {
                    P2PClient.player = this.player;
                    player.play();
                } catch (javazoom.jl.decoder.JavaLayerException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
}

The playback works. Also, the "Hello" is printed, therefore I know that play(args[0]) is not blocking the current thread. What's not working is the player.close() line... Instead I get a NullPointerException in this line. I want to have a reference to player in the anonymous thread instance and still "autostart" the thread with the call to void play(String track). Therefore I resort to a little ugly method of declaring a private static Player variable in the P2PClient class.

I don't understand why I'm getting this NullPointerException... If this is an incorrect way of accomplishing what I want, how do I get a player reference without returning a Thread object?

de_dust
  • 291
  • 5
  • 13
  • This https://stackoverflow.com/questions/4934913/are-static-variables-shared-between-threads might help as well. – GhostCat Jan 07 '18 at 19:19
  • Why do you want to close the player immediately after creating it? You seem to understand that the `run()` method doesn't block the execution of the main thread. – Mick Mnemonic Jan 07 '18 at 19:29
  • @MickMnemonic: Just to test if the close() method is working, unfortunately it's not... The song continues to play while I get the exception – de_dust Jan 07 '18 at 19:30
  • If you only need a single player instance, I would refactor this so that you initialize it not within `run()`, but e.g. in the field declaration (`private static Player player = new Player()`) and then make `Player.play()` take the track as a method argument. The code is suffering from a race condition; the static field may not be initialized by the other thread before you call `close()` on it. – Mick Mnemonic Jan 07 '18 at 19:35
  • @MickMnemonic: I've thought of that, too. However, the Player() constructor takes a FileInputSteam for one specific mp3 file. So I'd have one Player instance for one mp3 file. So I can't have one Player instance for multiple mp3 files – de_dust Jan 07 '18 at 19:37
  • Sorry, didn't notice that you can't modify`Player`. Then you need to synchronize access to the player variable. – Mick Mnemonic Jan 07 '18 at 19:39
  • @MickMnemonic: I don't understand why it's a race condition. The `P2PClient.player = this.player` line comes before `player.play()`. And since the song plays, the `private static Player player` variable must be already assigned, or am I thinking incorrect? – de_dust Jan 07 '18 at 19:40
  • @MickMnemonic: How would I synchronize access to the player variable? – de_dust Jan 07 '18 at 19:41
  • The song is not yet playing when you call `close()` on the player; hence the NPE. Declaring the player field as `volatile` might actually be enough in your case. – Mick Mnemonic Jan 07 '18 at 19:47
  • @MickMnemonic: You are right, I put a breakpoint at the `player.close()` line and the song was not playing. But why? I thought the song will play when the program passes the `play(args[0])` line? Unfortunately declaring the variable volatile didn't help... – de_dust Jan 07 '18 at 19:51
  • @MickMnemonic: I thought, that the song starts immediately when the program calls `player.play` in the `private static void play()` method... – de_dust Jan 07 '18 at 19:52
  • Essentially, you should be calling `play()` and `stop()` in the same thread, because I don't think it makes sense otherwise. If you want to make this example work, you'd need to somehow make sure that the initializing thread has done its work before calling `close()`. You could precede the call in `main()` with a `while (player==null){sleep (100);}`, for example. – Mick Mnemonic Jan 07 '18 at 20:16
  • 1
    @MickMnemonic: Yup, solved it. The while loop did it. But instead of `sleep (100)` I put `Thread.onSpinWait()` inside. It's a new Java9 feature and gives more system resources to the other threads. Also, I made `player` volatile. Thanks for your help :-) – de_dust Jan 07 '18 at 20:18

0 Answers0