Click to See Complete Forum and Search --> : Repeating a SwingWorker
Steve25
May 6th, 2010, 09:25 AM
I'm using a SwingWorker to seperate a loop in my code which plays a midi sound. However I need to use the SwingWorker more than once and there's no way of telling how many times it will need to be repeated as its executed on a button click.
Here is the method which uses the SwingWorker:
public void playTune() {
//Play tune swing worker
SwingWorker<TunePlayer, Void> playWorker = new SwingWorker<TunePlayer, Void>() {
@Override
protected TunePlayer doInBackground() throws Exception {
while (!stopTune) {
if (!player.isPlaying()) {
player.play(tune);
}
}
return null;
}
};
playWorker.execute();
}
I understand that doInBackground() can only be executed once but here I am making a new object every time that button is pressed (playTune() is fired when the button is clicked). Equally I should mention that there is a stop button which turn stopTune to true when it is clicked. Any help would be much appreciated
keang
May 6th, 2010, 11:13 AM
Why are you using SwingWorker to provide the background execution?
The advantage of using a SwingWorker over creating your own background thread is it simplifies the task of updating the GUI from the background thread. You don't appear to be updating the GUI from the background thread so you probably don't need to use SwingWorker.
Steve25
May 6th, 2010, 12:32 PM
The program does use Swing for GUI and as said these are triggered on a button click. I've tried using ordinary threads with this but as soon as you press play the whole program freezes. The loop works as you can hear the sound looping round but obviously you can't do anything and you can't even press the stop button.
SwingWorker seems to do fine as it does exactly what I want it to except for the fact that I can't run it more than once.
keang
May 6th, 2010, 12:40 PM
SwingWorker provides a mechanism for you to interact with the GUI from the background thread - you are not doing this hence you do not need a SwingWorker. What you are doing is running a time consuming task and don't want the GUI to freeze whilst the task it is running, therefore you need to run it on a background thread ie not the Event Dispatch Thread. You do not need a SwingWorker to do this, you just need to create your own thread and run the task on that.
Steve25
May 6th, 2010, 12:46 PM
I guess I understand that but I have already tried creating my own thread and running it from there and it still freezes the application. I've still got the code for the thread class if you need to see what I did?
keang
May 6th, 2010, 12:54 PM
Ok, by all means post the code so we can tell you what is wrong with it.
What I would do is create a Runnable object to play the sound, create a single thread ExecutorService object by calling Executors.newSingleThreadExecutor() and then every time I wanted to play the sound submit the Runnable to the ExecutorService object.
Steve25
May 6th, 2010, 01:04 PM
Ok here is the thread class that I created a while back. In the other class where the SwingWorker is it was replaced with:
Thread playerThread = new Thread(new LoopsPlayer(tune, player));
playerThread.run();
The thread class code is:
public class LoopsPlayer implements Runnable {
private boolean stopTune;
private Tune tune;
private TunePlayer player;
public LoopsPlayer(Tune tune, TunePlayer player) {
this.tune = tune;
this.player = player;
}
public void setStopTune(boolean stop) {
stopTune = stop;
}
@Override
public void run() {
while (!stopTune) {
if (!player.isPlaying()) {
player.play(tune);
}
}
}
}
keang
May 6th, 2010, 01:15 PM
Your code is creating a background thread with a Runnable object but is then directly calling the run() method from the Event Dispatch Thread ie you aren't using the background thread the play the sound. To use the background thread you need to call it's start() method and not the run() method.
This of course will still be a one shot object because you can't restart a thread once it has completed. That's why I suggested you create an ExecutorService.
Steve25
May 6th, 2010, 01:18 PM
So basically what you're saying is its going to be no different from what I'm trying to do now? I've never actually heard of an ExecutorService, looks like I'll need to look that one up
keang
May 6th, 2010, 01:28 PM
Look in the java.util.concurrent package
keang
May 6th, 2010, 01:35 PM
You would use it as follows:
ExecutorService exec = Executors.newSingleThreadExecutor();
Runnable player = new Runnable()
{
@Override
public void run()
{
// play sound code
}
};
...
// place this line in your action listener to play the sound every time it is executed
exec.submit(player);
Steve25
May 6th, 2010, 01:37 PM
Ok thanks I'll give this a try
Steve25
May 6th, 2010, 01:57 PM
Worked perfectly and does everything I wanted, many thanks for your help! I only have one last question, when the loop ends and it exits the run() method is the thread cleared up as normal? In other words if I click start, stop then start again I'm not using 2 different threads am i? I don't want it to end up creating like 20 threads or anything silly
dlorde
May 6th, 2010, 02:45 PM
The thread stops when the run() method finishes. It's the approved way of stopping it.
I think there is a world market for maybe five computers...
T. J. Watson (IBM)
keang
May 6th, 2010, 03:09 PM
If you are using an ExecutorService then a single thread (or pool of threads depending on the type of Executor service you have created), is kept alive by the ExecutorService object to run the tasks you submit to it, until you call one of its shutdown methods.
So to answer your question, if you keep the same ExecutorService alive to play the sounds as and when you need them then it will reuse the thread(s) it has, it will not start new threads each time you submit a task to it.
I didn't mention shutdown before but you should shutdown the service once you no longer need it to release its resources.
codeguru.com
Copyright Internet.com Inc., All Rights Reserved.