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;