简体   繁体   中英

Java how to stop Threads running concurrently

I am trying to make a text to speech thread stop whenever the talk(String text, boolean voiceEnabled) method is called from an ActionEvent using buttons.

When these buttons are pressed different text Strings are passed to the method, which runs the audio on a new thread. If the current thread is still running but a new ActionEvent occurs I need the current thread to stop (ie the text-to-speech) so that the new text-to-speech audio can be played without the current audio clip and new clip playing over the top of eachother.

This is what I currently have but the TTS audio are playing over the top of eachother. I need the current TTS to stop as soon as a new TTS is triggered. I believe my main problem is that a new Thread is being made each time the method is called.

Any help greatly appreciated. Thanks!

public void talk(String text, boolean voiceEnabled) {
    System.out.println(text);

    // Create a new Thread as JLayer is running on the current Thread and will
    // make the application lag
    Thread thread = new Thread(() -> {
        try {
            // Create a JLayer instance
            AdvancedPlayer player = new AdvancedPlayer(synthesizer.getMP3Data(text));
            if (voiceEnabled) {
                player.play(); //Plays the TTS audio
                System.out.println("Successfully retrieved synthesizer data");
            }
            else {
            }
        } catch (IOException | JavaLayerException e) {

            e.printStackTrace();
        }
    });
    // We don't want the application to terminate before this Thread terminates
    thread.setDaemon(false);
    // Start the Thread
    thread.start();
}

You appear to be burying key references inside of anonymous inner classes, and I don't see how you can get to them when and if needed. Why do this? Why not create an instance of a non-anonymous class, one with an AdvancedPlayer field, one whose reference is held by some collection, perhaps a List<...> or a HashMap, or by a variable if only one to two are running, where you can extract the object, get its AdvancedPlayer field and call .stop() on it?

eg,

public class RunnablePlayer implements Runnable {
    private AdvancedPlayer player;
    private String text;
    private boolean voiceEnabled;

    public RunnablePlayer(String text, boolean voiceEnabled) {
        this.text = text;
        this.voiceEnabled = voiceEnabled;
    }

    @Override
    public void run() {
        try {
            // Create a JLayer instance
            player = new AdvancedPlayer(synthesizer.getMP3Data(text));
            if (voiceEnabled) {
                player.play(); //Plays the TTS audio
                System.out.println("Successfully retrieved synthesizer data");
            } 
        } catch (IOException | JavaLayerException e) {
            e.printStackTrace();
        }
    }

    public AdvancedPlayer getPlayer() {
        return player;
    }

    public void stop() {
        // perhaps do a null check here first?
        if (player != null) {
            player.stop();
        }
    }
}

Then you could have a field of the class, something like:

// field of the class
private RunnablePlayer runnablePlayer;

and use this in your talk method:

public void talk(String text, boolean voiceEnabled) {
    if (runnablePlayer != null) {
        runnablePlayer.stop();  // not calling this on a Thread
    }

    runnablePlayer = new RunnablePlayer(text, voiceEnabled);
    Thread thread = new Thread(runnablePlayer);
    //.....
    thread.start();
}

Code not compiled or tested, but is presented to just give a general idea.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM