In Java, it is used to terminate a running Thread. Instead of calling the stop method, it sets a flag bit by itself. It detects the flag bit at the safe point and decides whether to exit. However, it may also be unable to reach the flag bit because the Thread is suspended. Therefore, the java Thread provides an interrupt mechanism, and the Thread class provides a call method to interrupt the execution of the Thread: interrupt, which is used to interrupt the waiting suspended by the Thread. After calling the interrupt method, the Thread will wake up and continue to execute the interrupted code after the next cpu scheduling.
We often call Thread#sleep, Object#wait, Queue#poll and other methods and ask us to handle InterruptedException exceptions. So, after throwing InterruptedException, will the thread terminate?
If the InterruptedException is not caught, the thread will terminate because of the exception, not because it is interrupted. If an InterruptedException is caught, the thread will not terminate.
In fact, interrupt is only used by the jvm to wake up threads suspended due to lock contention, I/O operation and hibernation, and set an interrupt flag. We can use this flag to do some processing. For example, when we send a message to a remote server and sleep to wait for the result, if the thread is awakened and the interrupt flag is set, we can know that it is awakened by interrupt instead of waiting for the result to be awakened, and we can decide whether to continue waiting for the result or give up waiting.
XXL job provides the operation of canceling a task. Any running thread can only use the interrupt mechanism to end the thread task, so we want the task support to be cancelled. When writing a scheduled task, we must consider whether we should capture the InterruptedException and how to use the interrupt flag to end the task, otherwise the task cannot be cancelled.
Let's look at a case:
@Test public void test() { ExecutorService executorService = Executors.newSingleThreadExecutor(); Future<?> future = executorService.submit(() -> { while (true) { System.out.println( "rung....." ); ThreadUtils.sleep(1000); } }); ThreadUtils.sleep(1000); future.cancel(true); try { future.get(); } catch (InterruptedException | CancellationException | ExecutionException e) { e.printStackTrace(); } ThreadUtils.sleep(1000 * 60); }
This case creates a thread pool with only one thread and submits a dead sequential task that only calls ThreadUtils The sleep method enters sleep. Normally we call thread Sleep methods require whether to capture interrupt exceptions. In many cases, we will get rid of the trouble, so we use a tool class to provide sleep methods, and then capture interrupt exceptions, such as ThreadUtils:
public class ThreadUtils { public static void sleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException ignored) { } } }
In this case, because we caught the interrupt exception, the task will not be terminated, but the cancelationexception will be thrown when we call the get method of future, as shown in the following figure.
The task is still running
Therefore, in the actual development, if the Job we developed is the same, the Job will not be interrupted or cancelled until the Job execution is completed or restarted. When developing a Job, you should reasonably consider whether to catch interrupt exceptions.
If we want the task in the case to be terminated, we can deal with it as follows:
@Test public void test() { ExecutorService executorService = Executors.newSingleThreadExecutor(); Future<?> future = executorService.submit(() -> { while (true) { System.out.println( "rung....." ); try { Thread.sleep(1000); } catch (InterruptedException ex) { System.err.println( "interrupted" ); return; // Exit loop } } }); ThreadUtils.sleep(1000); future.cancel(true); try { future.get(); } catch (InterruptedException | CancellationException | ExecutionException e) { e.printStackTrace(); } ThreadUtils.sleep(1000 * 60); }
For the interrupt method of Thread, the general meaning described in the note is as follows:
- If the interrupted thread currently calls Object#wait, Thread#join and Thread#sleep methods, it will receive InterruptedException and clear the interrupt flag;
- If this thread is blocked during I/O operation (java nio), the channel calling interrupt method will be closed, the thread will receive a closedbyinteruptexception, and the interrupt flag will be set;
- ....
How to understand the interrupt flag?
"If the interrupted thread currently calls Object#wait, Thread#join and Thread#sleep methods, it will receive InterruptedException and clear the interrupt flag". The code in the case just meets this point. If we change the case code to the following:
@Test public void test() { ExecutorService executorService = Executors.newSingleThreadExecutor(); Future<?> future = executorService.submit(() -> { while (!Thread.interrupted()) { System.out.println( "rung....." ); try { Thread.sleep(1000); } catch (InterruptedException ex) { System.err.println( "interrupted" ); } } }); ThreadUtils.sleep(1000); future.cancel(true); try { future.get(); } catch (InterruptedException | CancellationException | ExecutionException e) { e.printStackTrace(); } ThreadUtils.sleep(1000 * 60); }
When you execute this code, you will find that the dead loop does not exit at all. It is precisely because the Thread#sleep method is interrupted. The JVM does not set the interrupt flag, but just throws an InterruptedException exception.
In other cases, the JVM will only set the interrupt flag and will not throw InterruptedException. If we do not process the interrupt signal, the interrupt signal will not affect the continued execution of the program.
@Test public void test2() { ExecutorService executorService = Executors.newSingleThreadExecutor(); Future<?> future = executorService.submit(() -> { int number = 0; while (!Thread.interrupted()) { number++; } System.out.println(number); }); ThreadUtils.sleep(1000); future.cancel(true); try { future.get(); } catch (InterruptedException | CancellationException | ExecutionException e) { e.printStackTrace(); } ThreadUtils.sleep(1000 * 60); }
There is no blocking caused by I/O operation in this case, because after calling the interrupt method, the thread only sets the interrupt flag. We use the interrupt flag as the sequential exit condition. Running this case, we will see that after the thread is interrupted, the task terminates. On the contrary, if we don't handle the interrupt flag, wait for the IDEA process card to drop.