Notes on eight core knowledge systems of multithreading - 3 Thread interrupt

Mind map link

Multithreading system learning notes link

Refer to the intensive course of Java concurrency core knowledge system on muke.com

Overview of thread interrupts

Common interview questions:

1. How to stop a thread

answer:
    1. Using the interrupt method to request instead of forcibly terminating the thread is a cooperative mechanism.

    In this way, the thread requested to interrupt can independently decide to process its own logic.

    The advantage of this is that it can ensure data security, clean up in time and ensure data integrity.

    1. If the interrupt method works, it needs to be used in many ways

      1 is the interrupt request sent by the requesting party

      2. The stopped party shall check the interrupt signal in each cycle or at an appropriate time,

      And process this signal when an InterruptException may be thrown

      3. If the thread is stopped by a sub method and the sub method is called, there are two best practices

      1) To pass an interrupt, i.e. give priority to throwing an exception upward at the sub method layer, pass the interrupt signal to the run method, and process the interrupt signal logic at the run method layer

      2) Resume interrupt, that is, after receiving the interrupt signal, the sub method sets the interrupt state again.

    1. If interrupt is not used, other methods will have certain disadvantages and consequences

      1) Stop will suddenly stop the thread. If the thread has no time to process the remaining data, the data will be incomplete

      2) Suspend and other methods will suspend the thread, not destroy the object, hold the lock and block, which will lead to deadlock

      3) Setting the boolean flag bit with volatile cannot handle long-term blocking, resulting in the thread being unable to stop

Relevant code certificate
There are two best practices for sub methods:
  • Delivery interruption:
package threadcoreknowledge.stopthreads;

import threadcoreknowledge.createthreads.ThreadStyle;

/**
 * Description: Best Practices 1:
 * catch Preferences after interruptiedexcetion:
 * If an exception is thrown in the method signature, try/catch will be forced in run()
 */
public class RightWayStopThreadInProd implements Runnable {
    @Override
    public void run() {
        while (true && !Thread.currentThread().isInterrupted()) {
            System.out.println("go");
            try {
                throwInMethod();
            } catch (InterruptedException e) { // The run method handles exception logic
                Thread.currentThread().interrupt();
                //Save log, stop program
                System.out.println("Save log");
                e.printStackTrace();
            }
        }
    }

    private void throwInMethod() throws InterruptedException { // Subprocess throw up exception
            Thread.sleep(2000);
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadInProd());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
}
  • Recovery interruption:
package threadcoreknowledge.stopthreads;

/**
 * Description: Best Practices 2:
 * Calling Thread. in the catch subclause currentThread(). Interrupt() to restore the set interrupt state,
 * So that the interruption just occurred can still be checked in the subsequent implementation
 * Go back to the RightWayStopThreadInProd just now, fill in the interrupt and let it jump out
 */
public class RightWayStopThreadInProd2 implements Runnable {
    @Override
    public void run() {
        while (true) {
            if (Thread.currentThread().isInterrupted()) { // Detection interrupt
                System.out.println("Interrupted´╝îEnd of program operation");
                break;
            }
            reInterrupt();
        }
    }

    private void reInterrupt() {
        try {
            Thread.sleep(2000); // The interrupt flag is cleared and becomes false
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // Interrupt again and set the flag bit to true
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadInProd2());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
}
Setting the boolean flag bit with volatile cannot handle long-term blocking
package threadcoreknowledge.stopthreads.volatiledemo;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * Description: demonstrates that volatile cannot stop a thread when its limited part2 is blocked
 * In this example, the production speed of producers is fast and the consumption speed of consumers is slow,
 * Therefore, when the blocking queue is full, producers will block and wait for consumers to consume further
 */
public class WrongWayVolatileCantStop {

    public static void main(String[] args) throws InterruptedException {
        // Define blocking queue
        ArrayBlockingQueue storage = new ArrayBlockingQueue(10);

        // Producer startup
        Producer producer = new Producer(storage);
        Thread producerThread = new Thread(producer);
        producerThread.start();
        Thread.sleep(1000);

        // Consumer startup
        Consumer consumer = new Consumer(storage);
        while (consumer.needMoreNums()) { // Normal consumption of consumers
            System.out.println(consumer.storage.take()+"Consumed");
            Thread.sleep(100);
        }
        // Consumers stop spending
        System.out.println("Consumers don't need more data.");

        //Once consumption does not need more data, we should stop producers, but the actual situation does not work
        producer.canceled=true;
        System.out.println(producer.canceled); // true
    }
}
// Producer logic
class Producer implements Runnable {
    public volatile boolean canceled = false; // Define flag bit
    BlockingQueue storage; // Blocking queue
    public Producer(BlockingQueue storage) {
        this.storage = storage;
    }

    @Override
    public void run() {
        int num = 0;
        try {
            while (num <= 100000 && !canceled) { // Flag bit cannot be blocked queue response
                if (num % 100 == 0) {
                    storage.put(num); // Adding elements to the blocking queue will respond to exceptions if interrupted
                    System.out.println(num + "Is a multiple of 100,Put it in the warehouse.");
                }
                num++;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("Producer end run");
        }
    }
}

// Consumer logic
class Consumer {
    BlockingQueue storage;
    public Consumer(BlockingQueue storage) {
        this.storage = storage;
    }
    // Set a random proportion so that consumers don't consume
    public boolean needMoreNums() {
        if (Math.random() > 0.95) {
            return false;
        }
        return true;
    }
}
  • Repair scheme
...
     // Interrupt with interrupt
        producerThread.interrupt();
    }

    class Producer implements Runnable {
        BlockingQueue storage;
        public Producer(BlockingQueue storage) {
            this.storage = storage;
        }   
        @Override
        public void run() {
            int num = 0;
            try {
                while (num <= 100000 && !Thread.currentThread().isInterrupted()) { // Interrupt detection
                    if (num % 100 == 0) {
                        storage.put(num); // Will respond to Interrupt signal
                        System.out.println(num + "Is a multiple of 100,Put it in the warehouse.");
                    }
                    num++;
                }
 ...

2. How to handle non interruptible blocking

answer:
    1. Unfortunately, there is no universal solution,
    2. Different solutions should be given for some locks or some IO S
    3. For example, for reentrantlock (reentrant lock), if the lock method is used and blocked during the lock process, there is no way to respond to the interrupt response, but this class provides the lockInterrupt method, which can respond to the interrupt response
    4. That is, according to the characteristic situation, use the characteristic method to make them respond to the interrupt

Keywords: Java

Added by zerogreen on Sat, 08 Jan 2022 12:03:44 +0200