What is an interrupt?
There are interruptions everywhere in the computer world, and any work can not be separated from interruptions. It can be said that the whole computer system is driven by interruptions. So what is an interrupt? In simple terms, the CPU stops the current task to do something else, and then comes back to the same task. This process is called an interrupt!
The difference between responding interrupts and non-responding interrupts
Each Thread has an interrupt state of type Boolean. When the Thread is interrupted, the interrupt state of the Thread is set to true. The Thread contains the interrupt Thread and the method to query the interrupt Thread, as follows:
public class Thread{
public void interrupt(a){... }public boolean isInterrupted(a){... }public static boolean interrupted(a){... }}Copy the code
Interrupt () interrupts the target thread, while isInterrupted() returns the interrupted status of the target thread. The static interrupted() method clears the current interrupted status and returns its previous value, which is the only way to clear the interrupted status.
Calling the interrupt() method does not immediately stop what the target thread is doing, but merely passes a message requesting an interruption.
Let’s look at an example to understand these three methods:
public class InterruptTest {
public static void main(String[] args) throws InterruptedException {
Thread threadOne = new Thread(new Runnable() {
@Override
public void run(a) {
boolean isInterrupted = Thread.currentThread().isInterrupted();
System.out.println(isInterrupted);
if (isInterrupted) {
System.out.println("hao");
}
System.out.println(Thread.currentThread().getName() + " threadOne isInterrupt1 " + Thread.currentThread().isInterrupted());
while(! Thread.interrupted()){ System.out.println("-- -- -- -- -- -- -- --");
}
System.out.println(Thread.currentThread().getName() + " threadOne isInterrupt2 "+ Thread.currentThread().isInterrupted()); }},"t1");
// Start the thread
threadOne.start();
// Set the interrupt flag
threadOne.interrupt();
System.out.println("Interrupt completes execution...");
threadOne.join();
System.out.println("main over... ");
System.out.println(Thread.currentThread().getName() + "IsInterrupted3."+ threadOne.isInterrupted()); }}Copy the code
Output result:
Interrupt completes execution... true hao t1 threadOne isInterrupt1 true t1 threadOne isInterrupt2 false main over... The main isInterrupted3: falseCopy the code
Thread execution order is not the same print results are not the same!
False t1 threadOne isInterrupt1 false -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- interrupt is done... t1 threadOne isInterrupt2 false main over... The main isInterrupted3: falseCopy the code
The third output result
False T1 threadOne isInterrupt1 True T1 threadOne isInterrupt2 false Interrupt Complete... main over... The main isInterrupted3: falseCopy the code
The main thread prints isInterrupted3, which is always false because there are no threads to interrupt the main thread. Take a good look at the results of the above 3 types of printing can understand the role of these 3 methods;
When a thread calls wait(),sleep(), or join(), it will throw an exception if it receives an interrupt request (interrupt() is invoked) or if it finds an interrupt state when it begins execution.
public class InterruptTest {
@SneakyThrows
public static void main(String[] args) {
Thread threadOne = new Thread(new Runnable() {
@Override
@SneakyThrows
public void run(a) {
while (true) {
System.out.println("+ + + + + + + + + + +");
Thread.sleep(3000);
System.out.println("-- -- -- -- -- -- -- -- -- --"); }}},"t1");
InterruptTest t = new InterruptTest();
threadOne.start();
Thread.sleep(200); threadOne.interrupt(); }}Copy the code
Print result:
+++++++++++
Exception in thread "t1" java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at cn.haoxy.use.lock.reenlock.InterruptTest$1.run(InterruptTest.java:25)
at java.lang.Thread.run(Thread.java:748)
Copy the code
Interrupt (),isInterrupted(),interrupted(),interrupted(); Let’s look at what are response interrupts and non-response interrupts.
Response interrupts and non-response interrupts in AQS
In fact, the above exception is a way of responding to the interrupt; Let’s take Lock as an example:
public static void main(String[] args) {
Lock lock = new ReentrantLock();
The purpose of the lock is to allow the following lock.lockInterruptibly() method to enter the doAcquireInterruptibly() method
lock.lock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run(a) {
try {
// Notice that the lockInterruptibly() method is used - to respond to interrupts
lock.lockInterruptibly();
System.out.println("lockInterruptibly....");
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " interrupted."); e.printStackTrace(); }}},"t1");
t1.start();
Thread.sleep(1000);
// Call the interrupt() method to break it
t1.interrupt();
Thread.sleep(5000);
}
Copy the code
Print the result
t1 interrupted.
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at cn.haoxy.use.lock.reenlock.InterruptTest$3.run(InterruptTest.java:92)
at java.lang.Thread.run(Thread.java:748)
Copy the code
Lock.lockinterruptibly () is called to attempt to acquire the lock, and t1.interrupt() is called to set the T1 thread to an interrupt flag while attempting to acquire the lock, tracing the source code when the code comes to the doAcquireInterruptibly() method:
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null;
failed = false;
return;
}
// 1 Determines if it really needs to be suspended, and if so, the parkAndCheckInterrupt() method is called to suspend it
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
throw newInterruptedException(); }}finally {
if(failed) cancelAcquire(node); }}Copy the code
Here the focus is on 1 code shouldParkAfterFailedAcquire (p, node) && parkAndCheckInterrupt (), Other code we have in JUC AbstractQueuedSynchronizer (AQS) source – plus unlock explained in detail in the analysis, it would not be in too much here!
Because the lock.lock() method is called in the main thread before the lock.lockInterruptibly() method is called, the thread is suspended and waiting. So the locksupport.park (this) method in parkAndCheckInterrupt() is executed to suspend the thread! Lock. Lock (). The main thread releases the lock, and it’s t1’s turn to acquire the lock. The T1 thread wakes up where it was suspended:
private final boolean parkAndCheckInterrupt(a) {
// Where to hang
LockSupport.park(this);
// Wake up and continue down
return Thread.interrupted();
}
Copy the code
After being interrupted(), the method t1.interrupt() will interrupt the T1 Thread when thread.interrupted () is interrupted. Thread. Interrupted () returns true; Throw new InterruptedException(); The T1 thread is interrupted to acquire the lock; This is the Lock response to interrupt Lock acquisition process;
There are response interrupts, and of course there are non-response interrupts:
public final void acquire(int arg) {
if(! tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null;
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
interrupted = true; }}finally {
if(failed) cancelAcquire(node); }}Copy the code
If you read my previous article, the code above must look familiar!
The difference between this and responding to an interrupt to acquire a lock is:
// Do not respond to interrupt
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()){
interrupted = true;
}
// The response is interrupted
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()){
throw new InterruptedException();
}
Copy the code
If you don’t respond to interrupts you simply set the flag bit to interrupted = true;
If interrupted = true (acquireQueued(final Node Node, int arg) returns true, selfInterrupt() will follow; Methods;
static void selfInterrupt(a) {
// Set the current thread to interrupt again
Thread.currentThread().interrupt();
}
Copy the code
selfInterrupt(); Method calls the interrupt() method; What is the purpose? I personally feel that since thread.interrupted () is called in parkAndCheckInterrupt() when the Thread wakes up, it changes the flag of the Thread. When thread.interrupted () returns true, but changes the interrupt flag to false, so thread.currentthread () is called in the selfInterrupt() method to set the currentThread to interrupted A); That is to restore the user’s flag, the user determines the thread state to decide how to deal with;