[ACCEPTED]-Stopping/Destroying a Thread-service
The thread destroy
and stop
methods are inherently 34 deadlock prone and not safe. Their existence 33 also gives the illusion that there might 32 be some way of halting another thread immediately 31 when something else tells it to.
I understand 30 your thinking, from your point of view their 29 is one main thread, and when this thread 28 hasn't received a response from it's worker 27 thread in a while you'd like to kill it 26 and restart it, without caring what it's up to. But the reason those methods 25 are deprecated is you should care what the thread 24 is up to. A lot.
What if the thread has a 23 lock around a variable you need to use later? What 22 if a thread has a file handle open? In all 21 these cases, and many more, simply stopping 20 the thread at it's current operation would 19 leave things in mess -- quite likely your 18 application would just crash further down 17 the line.
So in order for a thread to be 16 interruptible or cancel-able or stoppable, it 15 has to manage this itself. If a thread or 14 operation provides no way for itself to 13 be interrupted, then you cannot interrupt 12 it - it is assumed to do so would be unsafe.
If 11 you runnable is literally
public void run() {
doSomething();
}
then there is 10 no way to interrupt it. One would hope that 9 if doSomething
were a long operation that there might 8 be a way to either interact with it incrementally 7 with something like
public void run() {
while (running) {
MyParser.parseNext();
}
}
or to be able to pass 6 in a variable by reference which indicates 5 whether the thread is interrupted or not, and 4 hopefully the method would interrupt itself 3 at suitable location.
Remember a blocking
operation 2 is blocking. There is no way to get around 1 that, you cannot cancel it part way through.
Alternative answer
Use the following code:
MyThread thread; // class field
Create and start 7 the thread as you do it right now.
thread = new MyThread();
thread.start();
When the 6 service is destroyed, "signal" the 5 thread to quit
public void onDestroy() {
// Stop the thread
thread.abort = true;
thread.interrupt();
}
Here is thread implementation
//another class or maybe an inner class
class MyThread extends Thread {
syncronized boolean abort = false;
//ugly, I know
public void run() {
try {
if(!abort) doA();
if(!abort) doB();
if(!abort) doC();
if(!abort) doD();
} catch (InterruptedException ex) {
Log.w("tag", "Interrupted!");
}
}
}
You might want to read the following:
- How do you kill a thread in Java?
- Thread Primitive Deprecation as already pointed by Claszen
- http://www.devx.com/tips/Tip/31728 - based my code from here, but there are some issues with the code!
I 4 think that you could rely on catching the 3 exception and not check abort but I decided 2 to keep it that way.
UPDATE
I've seen this sample 1 in codeguru:
public class Worker implements Runnable {
private String result;
public run() {
result = blockingMethodCall();
}
public String getResult() {
return result;
}
}
public class MainProgram {
public void mainMethod() {
...
Worker worker = new Worker();
Thread thread = new Thread(worker);
thread.start();
// Returns when finished executing, or after maximum TIME_OUT time
thread.join(TIME_OUT);
if (thread.isAlive()) {
// If the thread is still alive, it's still blocking on the methodcall, try stopping it
thread.interrupt();
return null;
} else {
// The thread is finished, get the result
return worker.getResult();
}
}
}
Did you check the Java Thread Primitive Deprecation Documentation which is 2 referenced in the Thread API JavaDoc. You will 1 find some hints to handle your problem.
why don't you use an AsyncTask?
A task can be cancelled 11 at any time by invoking cancel(boolean). Invoking this 10 method will cause subsequent calls to 9 isCancelled() to return true. After invoking 8 this method, onCancelled(Object), instead 7 of onPostExecute(Object) will be invoked after 6 doInBackground(Object[]) returns. To ensure 5 that a task is cancelled as quickly as 4 possible, you should always check the 3 return value of isCancelled() periodically 2 from doInBackground(Object[]), if possible (inside 1 a loop for instance.)
I like to take the following approach:
class MyHandler extends Handler {
final Semaphore stopEvent = new Semaphore(0);
@Override
public void handleMessage(Message msg) {
try {
while (!stopEvent.tryAcquire(0, TimeUnit.SECONDS)) {
doSomething();
if (stopEvent.tryAcquire(SLEEP_TIME, TimeUnit.MILLISECONDS)) {
break;
}
}
} catch (InterruptedException ignored) {
}
stopSelf();
}
}
On 1 service onDestroy just release the stopEvent:
@Override
public void onDestroy() {
myHandler.stopEvent.release();
myHandler = null;
super.onDestroy();
}
Better to use global variable stopThread, stop 1 thread once variable changed to true.
btnStop.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0){
stopThread = true;
}
});
public void run() {
while (!stopThread) {
//do something
}
}
I think the best way to create and communicate 7 with another thread is to use an AsyncTask. Heres 6 an example of one:
public class Task extends AsyncTask<Void, Void, Void> {
private static final String TAG = "Task";
private boolean mPaused;
private Runnable mRunnable;
public Task(Runnable runnable) {
mRunnable = runnable;
play();
}
@Override
protected Void doInBackground(Void... params) {
while (!isCancelled()) {
if (!mPaused) {
mRunnable.run();
sleep();
}
}
return null;
}
private void sleep() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Log.w(TAG, e.getMessage());
}
}
public void play() {
mPaused = false;
}
public void pause() {
mPaused = true;
}
public void stop() {
pause();
cancel(true);
}
public boolean isPaused() {
return mPaused;
}
}
You can now easily use 5 this class, and start the thread by writing:
Task task = new Task(myRunnable);
task.execute((Void) null);
Along 4 with this you can easily pause or stop the 3 thread from looping:
Example of pausing and 2 playing the thread:
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (task.isPaused()) {
task.play();
} else {
task.pause();
}
}
});
Example of stopping and 1 starting the thread:
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (task.isCancelled()) {
task = new Task(myRunnable);
task.execute((Void) null);
} else {
task.stop();
}
}
});
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.