/ Java  

Java Multithreading 7: Interrupt and end thread

This blog will introduce the interrupt() method and termination of threads. The contents include:

  1. interrupt() introduction
  2. The way to terminate the thread
    1. Terminate threads in the blocked state
    2. Terminate threads in the running state
  3. Example of thread termination
  4. The difference between interrupted() and isInterrupted()

interrupt() introduction

Regarding interrupt(), look at the java jdk11 documentation(https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Thread.html#interrupt()):

Interrupts this thread.
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.
If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.
If this thread is blocked in an I/O operation upon an InterruptibleChannel then the channel will be closed, the thread’s interrupt status will be set, and the thread will receive a ClosedByInterruptException.
If this thread is blocked in a Selector then the thread’s interrupt status will be set and it will return immediately from the selection operation, possibly with a non-zero value, just as if the selector’s wakeup method were invoked.
If none of the previous conditions hold then this thread’s interrupt status will be set.
Interrupting a thread that is not alive need not have any effect.

The way to terminate the thread

The stop() and suspend() methods in Thread have been suggested not to be used due to inherent insecurity!

Then, let’s first discuss the termination of threads in the blocked state and running state, and then summarize a general method.

Terminate threads in the blocked state

Normally, we terminate threads in blocked state by interrupting.

When the thread is blocked due to calling sleep(), wait(), join() etc, if the interrupt() method of the thread is called at this time, the thread’s interrupt flag is set to true. Due to the blocked state, the interrupt flag will be cleared and an InterruptedException exception will be generated. Put the InterruptedException at the appropriate place can terminate the thread.

1
2
3
4
5
6
7
8
9
10
@Override
public void run() {
try {
while (true) {
// execute tasks...
}
} catch (InterruptedException ie) {
// catch InterruptedException, exit while(true) loop, the thread exits
}
}

Continuously execute tasks in while(true). When the thread is blocked, the interrupt() of the calling thread generates an InterruptedException exception. The exception is captured outside of while(true), so that the while(true) loop is exited!

Note: the capture of InterruptedException is generally placed outside the body of the while(true) loop, so that when the exception occurs, the while(true) loop is exited. Otherwise, the InterruptedException is within the while(true) loop, and additional exit processing needs to be added.

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public void run() {
while (true) {
try {
// execute tasks...
} catch (InterruptedException ie) {
// InterruptedException inside while(true)
// when catch InterruptedException, while(true) is still running
break;
}
}
}

Terminate threads in the running state

Usually, we terminate the thread in running state by flagging, including interrupt flag and additional flag.

Use interrupt flag

1
2
3
4
5
6
@Override
public void run() {
while (!isInterrupted()) {
// execute task....
}
}

isInterrupted() will determine whether the thread’s interrupt flag is true. When the thread is running and we need to terminate it, we can call the thread’s interrupt() method to make the thread’s interrupt flag as true. Then isInterrupted() will return true. At this point, the while loop will exit.

Note: interrupt() does not terminate the thread in the running state! It will set the thread’s interrupt flag to true.

Use additional flag

1
2
3
4
5
6
7
8
9
10
11
private volatile boolean flag = true;
protected void stopTask() {
flag = false;
}

@Override
public void run() {
while (flag) {
// execute tasks....
}
}

There is a flag boolean in the thread, and its default value is true. We provide stopTask() to set the flag. When we need to terminate the thread, call the thread’s stopTask() method to let the thread exit the while loop.

Note: The flag is set as volatile to ensure the visibility of flag. That is, after other threads modify the flag through stopTask(), the current thread can see the value of the modified flag.

The more general form of termination thread is as follows, which can terminate thread in blocked state and running state:

1
2
3
4
5
6
7
8
9
10
11
@Override
public void run() {
try {
// 1. if the interrupt flag is true, the thread is terminated
while (!isInterrupted()) {
// execute tasks...
}
} catch (InterruptedException ie) {
// 2. When catch InterruptedException, thread is terminated
}
}

Example of thread termination

interrupt() is often used to terminate blocked threads.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class InterruptDemo {
public static void main(String[] args) {
try {
// create thread t1
Thread t1 = new MyThread("t1");
System.out.println(t1.getName() + " (" + t1.getState() + ") is new.");

// start thread t1
t1.start();
System.out.println(t1.getName() + " (" + t1.getState() + ") is started.");

// main thread sleep 300 ms, then interrupt t1
Thread.sleep(300);

t1.interrupt();
System.out.println(t1.getName() + " (" + t1.getState() + ") is interrupted.");

// main thread sleep 300 ms, then check t1 state
Thread.sleep(300);
System.out.println(t1.getName() + " (" + t1.getState() + ") is interrupted now.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}

static class MyThread extends Thread {
public MyThread(String name) {
super(name);
}

@Override
public void run() {
try {
int i = 0;
while (!isInterrupted()) {
Thread.sleep(100);

i++;

System.out.println(Thread.currentThread().getName() + " (" + this.getState() + ") loop " + i);
}
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " (" + this.getState() + ") catch InterruptedException.");
}
}
}
}

Results:

1
2
3
4
5
6
7
t1 (NEW) is new.
t1 (RUNNABLE) is started.
t1 (RUNNABLE) loop 1
t1 (RUNNABLE) loop 2
t1 (TIMED_WAITING) is interrupted.
t1 (RUNNABLE) catch InterruptedException.
t1 (TERMINATED) is interrupted now.

The main thread creates thread t1 through new MyThread("t1"), and then starts thread t1 through t1.start().

After t1 starts, it will constantly check its interrupt flag. If the interrupt flag is false, then sleep for 100ms.

After t1 sleeps, it will switch to the main thread. When the main thread runs again, it will execute t1.interrupt() to interrupt the thread t1. After t1 receives the interrupt instruction, it will set the interrupt flag of t1 to false and an InterruptedException will be thrown. In the run() method of t1, it is an exception caught outside the while of the loop body; therefore the loop is terminated.

We make a small modification to the above example and move the block of code that caught InterruptedException in the run() to the body of the while loop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class InterruptDemo2 {
public static void main(String[] args) {
try {
// create thread t1
Thread t1 = new MyThread("t1");
System.out.println(t1.getName() + " (" + t1.getState() + ") is new.");

// start thread t1
t1.start();
System.out.println(t1.getName() + " (" + t1.getState() + ") is started.");

// main thread sleep 300 ms, then interrupt t1

Thread.sleep(300);

t1.interrupt();
System.out.println(t1.getName() + " (" + t1.getState() + ") is interrupted.");

// main thread sleep 300 ms, then check t1 state
Thread.sleep(300);
System.out.println(t1.getName() + " (" + t1.getState() + ") is interrupted now.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}

static class MyThread extends Thread {
public MyThread(String name) {
super(name);
}

@Override
public void run() {
int i = 0;

while (!isInterrupted()) {
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
System.out.println(Thread.currentThread().getName() + " (" + this.getState() + ") catch InterruptedException.");
}

i++;
System.out.println(Thread.currentThread().getName() + " (" + this.getState() + ") loop " + i);
}
}
}
}

Results:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
t1 (NEW) is new.
t1 (RUNNABLE) is started.
t1 (RUNNABLE) loop 1
t1 (RUNNABLE) loop 2
t1 (RUNNABLE) catch InterruptedException.
t1 (RUNNABLE) loop 3
t1 (TIMED_WAITING) is interrupted.
t1 (RUNNABLE) loop 4
t1 (RUNNABLE) loop 5
t1 (RUNNABLE) loop 6
t1 (RUNNABLE) is interrupted now.
t1 (RUNNABLE) loop 7
t1 (RUNNABLE) loop 8
t1 (RUNNABLE) loop 9
t1 (RUNNABLE) loop 10
....

The program has entered an infinite loop!

Why is this so? This is because t1 is interrupted by interrupt() while in the blocked state. At this time, the interrupt flag will be cleared(isInterrupted () will return false), and an InterruptedException will be thrown. This exception is caught in the while loop. Therefore, t1 will naturally enter an endless loop.

To solve this problem, we need to additionally deal with the exit of the while loop when catching the exception. For example, adding break or return to MyThread‘s catch(InterruptedException).

The following is an example of terminate a thread by additional flag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class InterruptDemo3 {
public static void main(String[] args) {
try {
// create thread t1
MyThread3 t1 = new MyThread3("t1");
System.out.println(t1.getName() + " (" + t1.getState() + ") is new.");

// start thread t1
t1.start();
System.out.println(t1.getName() + " (" + t1.getState() + ") is started.");

// main thread sleep 300 ms, then interrupt t1

Thread.sleep(300);

t1.stopTask();
System.out.println(t1.getName() + " (" + t1.getState() + ") is interrupted.");

// main thread sleep 300 ms, then check t1 state
Thread.sleep(300);
System.out.println(t1.getName() + " (" + t1.getState() + ") is interrupted now.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}

static class MyThread3 extends Thread {
private volatile boolean flag = true;

public void stopTask() {
flag = false;
}

public MyThread3(String name) {
super(name);
}

@Override
public void run() {
synchronized (this) {
try {
int i = 0;
while (flag) {
Thread.sleep(100);

i++;
System.out.println(Thread.currentThread().getName() + " (" + this.getState() + ") loop " + i);
}
} catch (InterruptedException ie) {
System.out.println(Thread.currentThread().getName() + " (" + this.getState() + ") catch InterruptedException.");
}
}
}
}
}

Results:

1
2
3
4
5
6
7
t1 (NEW) is new.
t1 (RUNNABLE) is started.
t1 (RUNNABLE) loop 1
t1 (RUNNABLE) loop 2
t1 (TIMED_WAITING) is interrupted.
t1 (RUNNABLE) loop 3
t1 (TERMINATED) is interrupted now.

The difference between interrupted() and isInterrupted()

Both interrupted() and isInterrupted() can be used to detect the interrupt flag of an object.

The difference is that in addition to returning the interrupt flag, interrupted() will also clear the interrupt flag (that is, set the interrupt flag to false). isInterrupted() only returns the interrupt flag.