How to prematurely terminate a thread?
I've created a button that plays a song in the background of one of my Gui applets -- the first obstacle is that the gui program would 'freeze' while the song played, then unfreeze when it finished. I counteracted this effect by making a 2nd thread when I play the song - so both programs would run at the same time. I was successful. However, I would like a 2nd button that would terminate that created thread.. lets say if the user wants to turn 'off' that sound.. Here is how I've been making a thread that plays a song: http://pastebin.com/tvAfJj34
Well, I am not an expert on threads, but I know there are issues with talking to them once started that you have to be aware of. I skimmed this example set and it might help: http://www.javaworld.com/article/2078679/java-concurrency/modern-threading-for-not-quite-beginners.html
Thanks -- the link is quite helpful. I'll let you know how it goes when I get around to it.
It depends heavily on your thread implementation. Ideally the thread will have conditions that let it know it's time to stop, and you simply have to regenerate those conditions. Typically is it undesirable to kill a thread via another thread because a thread generally wants to know when and why it needs to stop.
Thanks wio -- that's a similar conclusion that I've reached.. and I've devised a way so that the song-playing thread does Thread.sleep() in a loop - in many small increments as opposed to one big Thread.sleep() -- this way I can have it check a boolean flag variable -- and if I set that boolean variable to true, then the loop will prematurely terminate. Though I'm not too familiar with multithreaded communication.. I initialize the thread like this: ``` Thread t1; private class buttonForMusicListener implements ActionListener { public void actionPerformed(ActionEvent e) { String buttonPressed = e.getActionCommand(); if(buttonPressed.equals("Play Song")) { t1 = new Thread(new PlayMusicClass()); t1.start(); } else if(buttonPressed.equals("End Song")) { //how to call a method of the instance of the PlayMusicClass() //playMusicClassObject.callMethodSettingBooleanFlagVar(); } } } ```
But how would I call a method of the PlayMusicClass object that's inside the t1 thread? I'm thinking within that method, I would set the boolean flag variable to stop the Thread.sleep() loop. -thanks @wio
What is PlayMusciClass?
Keep track of PlayMusicClass
That is, store it. Then tell it when to stop, if you can.
Basically, I can't help you without knowing more about that class and how it works.
Sure, should only take a moment to write up-
``` import javax.sound.sampled.*; public class PlayMusicClass implements Runnable { private boolean keepPlaying; private File file; private Clip clip; public PlayMusicClass() { //constructor keepPlaying = true; file = new File("SongToPlay.WAV"); } public void run() { //called method upon t1.start(); try{ clip = AudioSystem.getClip(); clip.open(AudioSystem.getAudioInputStream(file)); clip.start(); int songLengthMilli = (int)(clip.getMicrosecondLength()/1000)+1000; int smallParts = (int)(songLengthMilli/10000); int accumulator = 0; for(; accumulator < 10000 && keepPlaying; accumulator++) { Thread.sleep(smallParts); } }catch(Exception e){} } public void stopThread() { keepPlaying = false; } } ``` If I could call the stopThread() method from the original thread somehow, I could stop the thread cleanly. I'm not terribly familiar with the Clip class nor AudioSystem -- I just know the combination of code that lets a song be played - though that should be somewhat irrelevant in this particular problem.
As long as the thread is delaying / sleeping -- the song will continue. So essentially, the code above will enable me to stop the sleep cycle if I flip the boolean variable 'keepPlaying'.
Okay, need to figure out more about the Clip class and how it works.
Is Clip.start() a synchronous or asynchronous method?
Not sure.. I suppose I'm just doing guess and check here. What would being a synchronous vs asynchronous method imply?
A synchronous method is one which will not execute the next line until it is done. An asynchronous method is one which will do what it is doing (as if it is in its own thread) and the next line will execute immediately.
Your code is written as if it is asynchronous.
If I had to guess.. I'd guess asynchronous.. otherwise I don't think there would be much of a point to the Thread.sleep().. though it's unfortunate that I'm not at my computer to test it out. That small block of code that plays audio was taken from a well-rated youtube video - I suppose the next logical thing for me to try is whether replacing Thread.sleep(songLength); with Thread.sleep(5000); would cut the song down to the first 5 seconds or not.. I'll try that tomorrow
The thing is, if it is asynchronous, then you don't need to put it in its own thread to begin with.
Allright -- thanks for all the help wio; I don't happen to be on the computer with the code right now - but I'll be sure to see if the song plays without the Thread.sleep(), if that's the case, then I blame the youtube video :p. I'll post an update tomorrow.
Well, there are two options here
It is synchronous, in which case it will finish playing and your sleep code will do nothing. It won't even prevent it from playing. It is asynchronous, and you might as well just directly use it in the UI thread because it won't block.
thanks - I'll keep them in mind
Actually, this is part of a bigger project involving an arduino and capacitive sensors (they sense when you touch conductive material like cans / aluminum foil) so I found a way to link the arduino to java -- I'm looking forward to making a project where different sounds play when different cans / conductive 'somethings are touched :D. Using the existing code, I can already make something like that -- though if I were to add a GUI options that would play background 'beats' or something, then knowing what we're discussing here will make the puzzle pieces fit together. With the code as it is now - something interesting I noticed is that pressing it multiple times will create the same song playing over itself -- that's a good thing for the project I think. Anyway, not much I can do for testing until tomorrow.
A more formal way of stopping thread execution in java is to interrupt the thread and have a custom interrupt handler inside the tread (which in your case would terminate the tread). http://docs.oracle.com/javase/tutorial/essential/concurrency/interrupt.html This is a good idea for another reason and that is because there are lots of other classes in java thread management which relies on the interrupt function to perform therad management. http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html#shutdownNow() for example. Finally I recommend you to use some kind of thread pool (or other thread managment tool) if you are planing on creating lots threads. http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html
Thanks for all the info on threads Lyrae - I plan on exploring them. Though for the purpose of the song playing situation -- I made the mistake of assuming that the Thread.sleep() after clip.start() was necessary - which it isn't :p derp. In which case I don't need to create a 2nd thread in the first place. Using ArrayList of Clip objects, I can create as many simultaneous playings of the same clip, and then using another button, I can call clip.get(clip.size()-1).stop(); to stop the most recently made clip, and then clip.remove(clip.size()-1); to remove it from the arraylist. It works great for stopping the clips. If I can do that- I should be able to move forward without any obstacles & manipulate the sound options as I see fit -- I'll post an update on the project in not too long.
Here is a jpg with a small music player that I made vv It plays a song when you click the 'play' button, the combo box has about 5 options for song choice so far. When you click play a 2nd time, it will stop the song, and create a new clip to start from the beginning. And clicking play on a different song while it's still playing a different song will have the program terminate the existing clip, then move on to the new clip. bit of code: ``` private class MusicListener implements ActionListener { public void actionPerformed(ActionEvent e) { String pickeds = e.getActionCommand(); if(pickeds.equals("Play")) { String songChoice = (String)jcb.getSelectedItem(); //combo box try{ if(clip.size() > 0) { clip.get(0).stop(); clip.remove(0); } if(songChoice.equals("Kiara by Bonobo")) { clip.add(AudioSystem.getClip()); File musicFile = new File("C:\\Arduino\\Random\\Audio\\AudioFiles\\kiara.WAV"); clip.get(clip.size()-1).open(AudioSystem.getAudioInputStream(musicFile)); clip.get(clip.size()-1).start(); } else if(songChoice.equals("Summer's Gone by PL")) { clip.add(AudioSystem.getClip()); File musicFile = new File("C:\\Arduino\\Random\\Audio\\AudioFiles\\SummersGone.WAV"); clip.get(clip.size()-1).open(AudioSystem.getAudioInputStream(musicFile)); clip.get(clip.size()-1).start(); } else if(songChoice.equals("The Time Has Come by PL")) { clip.add(AudioSystem.getClip()); File musicFile = new File("C:\\Arduino\\Random\\Audio\\AudioFiles\\TimeHasCome.WAV"); clip.get(clip.size()-1).open(AudioSystem.getAudioInputStream(musicFile)); clip.get(clip.size()-1).start(); } else if(songChoice.equals("Almost Familiar by PL")) { clip.add(AudioSystem.getClip()); File musicFile = new File("C:\\Arduino\\Random\\Audio\\AudioFiles\\AlmostFamiliar.WAV"); clip.get(clip.size()-1).open(AudioSystem.getAudioInputStream(musicFile)); clip.get(clip.size()-1).start(); } }catch(Exception ei){} } else if(pickeds.equals("Stop Song")) { if(clip.size() > 0) { clip.get(0).stop(); clip.remove(0); } } } } ```
note -- clip is an ArrayList object holding 1 object at a time of type Clip.
Rather than going though a bunch of `if` and `else if` checks, what you could do is create a `Map` (such as `HashMap`), which is like an array but it allows string indices. Then just look up the file name in the `Map` via the `songChoice`.
``` Map<String, String> filenames = new HashMap<String, String>(); filenames.put( "Kiara by Bonobo", "C:\\Arduino\\Random\\Audio\\AudioFiles\\kiara.WAV"); filenames.put( "Summer's Gone by PL", "C:\\Arduino\\Random\\Audio\\AudioFiles\\SummersGone.WAV"); // etc ``` Then the `if` part becomes: ``` clip.add(AudioSystem.getClip()); File musicFile = new File(filenames.get(songChoice)); clip.get(clip.size()-1).open(AudioSystem.getAudioInputStream(musicFile)); clip.get(clip.size()-1).start(); ```
Thanks for teaching me the Map / HashMap code! I'll use it to further simplify programs in the future.
I assume that String, String can be substituted for any other 'objects' too? like perhaps Integer, Integer.
All that is necessary is that the K class have a hashCode() function that works, and since all classes extend the Object class, they all should have one.
For custom classes, you may need to override the K class's `hashCode()` method the same way you would need to override its `equals()` method. I've never had to worked with a case where there K class was not a predefined class (such as String) though.
I'm a bit confused as to why you are using an ArrayList of clips. It doesn't seem to have a point
You're kinda right. I have it successfully running without the arraylist now -- with clip.stop() being called before each song change and at each press of the stop song button. and after the clip.stop() in the song play button, reinitialize clip to clip = AudioSystem.getClip(); The arraylist was able to tell whether there was a clip object in existence, since when I'm done with the object I remove it from the arraylist -- though certainly isn't needed for what I'm doing.
Join our real-time social learning platform and learn together with your friends!