Solution 1 :
worker.cancel
was called in the main thread. Therefore, Thread.currentThread()
inside the cancel method returns the main thread and therefore the interrupt
method was called by the main thread. You are not interrupting the worker thread in this case.
Problem :
I want to interrupt a thread with interrupt() method, however I experience a behaviour I cannot explain.
As indicated in the code, calling the worker method cancel() from MainActivity can’t interrupt the thread, where calling workerThread.interrupt() can.
If the worker calls cancel() by itself it’s interrupting.
Unless the current thread is interrupting itself, which is always permitted, the checkAccess method of this thread is invoked, which may cause a SecurityException to be thrown.
I thought it might be a security exception, cause the UIThread tries to interrupt the workerThread, but its not thrown. And if, it should also be thrown when workerThread.interrupt() is called?
Worker.java
package com.example.threadapp;
public class Worker implements Runnable {
public void cancel() {
Log.d("Threading", "call Thread.currentThread().interrupt()");
try {
Thread.currentThread().interrupt();
} catch (SecurityException e) {
e.printStackTrace();
Log.d("Threading", "SecurityException while interrupting");
}
}
@Override
public void run() {
//
while(!Thread.currentThread().isInterrupted()) {
Log.d("Threading", "Worker alive");
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
cancel();
}
//cancel(); //interrupts thread
//Thread.currentThread().interrupt(); //interrupts thread
}
//
Log.d("Threading", "Worker Thread closing");
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
private Worker worker;
private Thread workerThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
worker = new Worker();
workerThread = new Thread(worker);
workerThread.start();
(new Handler(Looper.getMainLooper())).postDelayed(new Runnable() {
@Override
public void run() {
Log.d("Threading", " call worker.cancel()");
worker.cancel(); //doesn't interrupt thread
//workerThread.interrupt(); //interrupts thread
}
}, 10000);
}
}
Comments
Comment posted by Seven
But isn’t workerThread.interrupt() also called from the main thread?
Comment posted by Loremar Marabillas
The interrupt method toggles a flag variable inside the worker thread. So when you call isInterrupted, it returns that value. Therefore it does not matter where you’re calling it. On the other hand, when you call mainThread.interrupt it toggles the flag variable of the main thread. isInterrupted will always return false in this case since it does not know the value from the main thread.
Comment posted by Seven
If it is only a flag, and it doesn’t matter from where it is set, why does ( called from MainThread) worker.cancel() won’t interrupt the thread and workerThread.interrupt(); will ?
Comment posted by Loremar Marabillas
Remember that the while loop is running on the worker thread, right? That happened when you called workerThread.start(). So when you call Thread.currentThread.isInterrupted, you’re actually calling workerThread.isInterrupted. That method asks for the value of the variable inside workerThread. That variable was changed when you called workerThread.interrupt. So it does not matter if you call it in the main thread, you will be able to change that variable. When you call mainThread.interrupt which happened when you call worker.cancel, that variable which was needed by isInterrupted didn’t change
Comment posted by Loremar Marabillas
Instead it changed the variable inside the mainThread. Remember, what was asked was the variable inside the workerThread. You can use Thread.currentThread.getName if you want to check which thread you are in. You’ll see that the code inside cancel method is indeed running in the main thread.