How do I stop a thread correctly

1. Explain the principle

Principle: Use interrupts to inform, not enforce. What are the principles for stopping threads in Java?

In Java, the best way to stop a thread is to use interrupts, but this simply tells the terminated thread “it’s time for you to stop.” The terminated thread has its own discretion (whether and when to stop), depending on both the requestor and the terminated party following an agreed code specification.

Starting tasks and threads is easy. Most of the time, we let them run until the end, or let them stop themselves. However, sometimes we want to end a task or thread early because the user canceled an operation, or the service needs to be shut down quickly, or because the operation timed out or went wrong.

It is not easy to stop tasks and threads safely, quickly, and reliably. Java does not provide any mechanism for safely terminating threads. But it provides Interruption, which is a cooperative mechanism that can cause one thread to terminate the current work of another thread.

This collaborative approach is necessary, and we rarely want a task, thread, or service to stop immediately, because such an immediate stop would leave the shared data structure in an inconsistent state. Instead, tasks and services can be written in a collaborative manner by first clearing up the work currently being performed and then ending when it needs to be stopped. This provides greater flexibility because the code of the task itself knows better how to perform the cleanup than the code that makes the cancellation request.

End-of-life issues complicate the design and implementation of tasks, services, and programs, an important element of programming that is often overlooked. One of the main differences between well-behaved software and poorly functioning software is that well-behaved software handles failures, shutdowns, and cancellations perfectly.

This chapter describes various mechanisms for implementing cancellation and interrupts and how to write tasks and services that respond to cancellation requests.

2. Best practice: How do I stop threads correctly

2.1 When do threads usually stop

1. The run() method completes. 2. When the thread stops, the occupied resources are reclaimed by the JVM.

2.2 Correct way to stop: Interrupt

2.2.1 How to Stop it in general

package stopthreads;

/** * Description: Stop the thread if there are no sleep() and wait() methods in the run() method. * /

public class RightStopThreadWithoutSleep implements Runnable{

    public static void main(String[] args) {
        Thread thread = new Thread(new RightStopThreadWithoutSleep());
        thread.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt();

    }
    @Override
    public void run(a) {
        int num = 0;
        while (num <= Integer.MAX_VALUE / 2) {if(! Thread.currentThread().isInterrupted() && num %10000= =0) {
                System.out.println(num + It's a multiple of 10,000.);
            }
            num++;
        }
        System.out.println("The mission is over."); }}Copy the code

Note: thread. Interrupt (); A thread that cannot be forced to interrupt must have the cooperation of the thread to be interrupted. Add the following code to the child thread:! Thread.currentThread().isInterrupted()

2.2.2 How to Stop a Thread if it is Blocked

package stopthreads;

import static java.lang.Thread.*;

public class RightStopThreadWithSleep {
    
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = ()->{
            int num = 0;
            while (num <= 300) {if (num % 100= =0 && !currentThread().isInterrupted()){
                    System.out.println(num + It's an integer multiple of 100.);
                }
                num++;
            }
            try {
                sleep(1000);
            } catch(InterruptedException e) { e.printStackTrace(); }}; Thread thread =new Thread(runnable);
        thread.start();
        Thread.sleep(500); thread.interrupt(); }}Copy the code

The results are as follows:

2.2.3 If the thread blocks after each iteration

package stopthreads;

import static java.lang.Thread.currentThread;
import static java.lang.Thread.sleep;

/** * Description: If sleep() or wait() is called each time the loop is executed, then... * /
public class rightStopThreadWithSleepEveryLoop {

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = ()->{
            int num = 0;
            try {
                while (num <= 10000) {if (num % 100= =0 && !currentThread().isInterrupted()){
                        System.out.println(num + It's an integer multiple of 100.);
                    }
                    num++;
                    sleep(10); }}catch(InterruptedException e){ e.printStackTrace(); }}; Thread thread =new Thread(runnable);
        thread.start();
        Thread.sleep(5000); thread.interrupt(); }}Copy the code

Try-catch problem in a while:

package stopthreads;

import static java.lang.Thread.currentThread;
import static java.lang.Thread.sleep;

public class CantInterrupt {

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = ()->{
            int num = 0;

                while (num <= 10000) {if (num % 100= =0 && !currentThread().isInterrupted()){
                        System.out.println(num + It's an integer multiple of 100.);
                    }
                    num++;
                    try {
                        sleep(10);
                    } catch(InterruptedException e) { e.printStackTrace(); }}}; Thread thread =new Thread(runnable);
        thread.start();
        Thread.sleep(5000); thread.interrupt(); }}Copy the code

Change the try-catch position and the result is completely different:

Note:
&& !currentThread().isInterrupted()



The reason:

2.3 Two best practices in actual development

2.3.1 Best Practice 1: Preference: Pass interrupt (exception thrown on method signature)

Let’s add an InterruptException catch by incorrectly handling an exception in the called method, which swallows the exception in the lower-level method and makes upper-level calls unaware of the exception. Instead, throw an exception, and the actual handling of the exception should be called by the function that called it. The error code is as follows:

package stopthreads;

/** * Description: Priority after catching InterruptionException: throw an exception in the method signature. * Then, try-catch is enforced in run(). * /
public class RightWayStopThreadInProduction implements Runnable {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadInProduction());
        thread.start();
        thread.sleep(1000);
        thread.interrupt();
    }

    @Override
    public void run(a) {
        while(true){
            System.out.println("go"); throwInMethod(); }}private void throwInMethod(a) {

        /** * Error: This is like eating the exception, so that the upper level of the call is not aware of the exception * The correct way is to throw an exception, and the actual handling of the exception is to call the function that called it. * /
        try {
            Thread.sleep(2000);
        } catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code

The thread cannot be stopped because of an error handling exception:


The right approach: Throw an exception, and the actual handling of the exception should be left to the calling function. Low-level methods, throwing exceptions, callers, just Surround with try/catch.

The correct code is as follows:

package stopthreads;

import static java.lang.Thread.sleep;

/** * Description: Priority after catching InterruptionException: throw an exception in the method signature. * Then, try-catch is enforced in run(). * /
public class RightWayStopThreadInProduction implements Runnable {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadInProduction());
        thread.start();
        sleep(1000);
        thread.interrupt();
    }

    @Override
    public void run(a) {
        while(true){
            System.out.println("go"); throwInMethod(); }}private void throwInMethod(a) throws InterruptedException {

        /** * Error: This is like eating the exception, so that the upper level of the call is not aware of the exception * The correct way is to throw an exception, and the actual handling of the exception is to call the function that called it. * /
       /* try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } * /

       sleep(2000); }}Copy the code

Conclusion:

2.3.2 Best Practice 2: Do not or cannot deliver: Restore interrupt (manually restore interrupt again)

Try-catch is possible in low-level methods, but be sure to add thread.currentThread ().interrupt();

package stopthreads;

import static java.lang.Thread.sleep;

public class RightWayStopThreadInProduction2 implements Runnable{

    @Override
    public void run(a) {

        while(true) {if (Thread.currentThread().isInterrupted()) {
                System.out.println("Thread interrupt");
                break; } reInterrupt(); }}private void reInterrupt(a) {

        try {
            sleep(2000);
        } catch(InterruptedException e) { Thread.currentThread().interrupt(); e.printStackTrace(); }}public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadInProduction2());
        thread.start();
        sleep(1000); thread.interrupt(); }}Copy the code

Results:

If you do not want to or cannot pass InterruptedException(for example, do not throw InterruptedException when using the RUN method), Choose to restore the interrupt state by calling Thread.currentThread() interrupt() in the catch clause so that subsequent executions can still detect that the interrupt occurred. See the code above, where the thread is interrupted during a sleep, caught by a catch, and resets the interrupted state so that it can detect the interrupted state during the next loop and exit normally.

Interrupts should not be masked

2.4 Benefits of proper stop

3. Wrong way to stop a thread

3.1 Error Stop 1: Deprecated stop, suspend, and resume methods

Using stop() to stop a thread will cause it to suddenly stop in the middle of running, unable to complete a basic unit operation (a company in the code), and resulting in dirty data (some companies get too much equipment or too little equipment).

package threadcoreknowledge.createthreads.stopthreads;

/** * Description: Wrong stop method: stopping a thread with stop() will cause the thread to stop in the middle of running, unable to complete a basic unit operation (a company), resulting in dirty data (some companies receive too much or too little equipment). * /
public class StopThread implements Runnable{

    @Override
    public void run(a) {
        // Simulation command of the army: there are 5 companies, each company of 10 people, in the company as a unit, issued weapons and ammunition, soldiers called to pick up
        for (int i = 0; i < 5; i++) {
            System.out.println("Company" + i + "Start collecting weapons.");
            for (int j = 0; j < 10; j++) {
                System.out.println(j);
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("Company"+i+"All collected"); }}public static void main(String[] args) {
        Thread thread = new Thread(new StopThread());
        thread.start();
        try {
            Thread.sleep(1000);
        } catch(InterruptedException e) { e.printStackTrace(); } thread.stop(); }}Copy the code

There is also a false theory that using stop() will not release the monitor lock, causing the program to freeze. Stop () will release the monitor.

3.2 Stop Error two: Set Boolean flag bits with volatile

A plausible

package threadcoreknowledge.stopthreads.volatiledemo;

/** * description: demonstrate the limitations of using volatile: part1 seems feasible */
public class WrongWayVolatile implements Runnable {

    private volatile boolean canceled = false;

    @Override
    public void run(a) {
        int num = 0;
        try {
            while (num <= 100000 && !canceled) {
                if (num % 100= =0) {
                    System.out.println(num + "It's a multiple of 100.");
                }
                num++;
                Thread.sleep(1); }}catch(InterruptedException e) { e.printStackTrace(); }}public static void main(String[] args) throws InterruptedException {
        WrongWayVolatile r = new WrongWayVolatile();
        Thread thread = new Thread(r);
        thread.start();
        Thread.sleep(5000);
        r.canceled = true; }}Copy the code

How to not

storage.put(num); Is blocked and cannot enter a new while() loop to say,! Canceled is impossible to determine

Code demonstration: Java

package threadcoreknowledge.createthreads.wrongway.volatiledemo;

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

public class WrongWayVolatileCantStop {

    public static void main(String[] args) throws InterruptedException {
        ArrayBlockingQueue storage = new ArrayBlockingQueue(10);
        Producer producer = new Producer(storage);
        Thread thread = new Thread(producer);
        thread.start();
        Thread.sleep(1000);

        Consumer consumer = new Consumer(storage);
        while (consumer.needMoreNums()) {
            System.out.println(storage.take()+"Consumed");
            Thread.sleep(100);
        }
        System.out.println("Consumers don't need more data."); /** * Once the consumption does not need more data, we should let the producer stop, * but the actual situation, in storage.put(num); Blocked, unable to access the new levelwhile() loop judgment,! Canceled = is also impossible to determine */ producer. Canceled =true;
        System.out.println(producer.canceled);

    }
}
class Producer implements Runnable{

    public volatile boolean canceled = false;
    BlockingQueue storage;

    public Producer(BlockingQueue storage) {
        this.storage = storage;
    }

    @Override
    public void run() { int num = 0; Try {/ / canceledtrue, cannot enterwhile(num <= 100000 && ! canceled) {if (num % 100 == 0) {
                    storage.put(num);
                    System.out.println(num + "It's a multiple of 100. It's in storage.");
                }
                num++;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("Producer ends run");
        }
    }

}

class Consumer {

    BlockingQueue storage;

    public Consumer(BlockingQueue storage) {
        this.storage = storage;
    }

    public boolean needMoreNums() {
        if(Math. The random () > 0.95) {return false;
        }
        return true; }}Copy the code

Result: The program is not over.

To repair

Using interrupt: code to demonstrate:

package threadcoreknowledge.createthreads.wrongway.volatiledemo;

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

public class WrongWayVolatileFixed  {

    public static void main(String[] args) throws InterruptedException {
        ArrayBlockingQueue storage = new ArrayBlockingQueue(10);
        WrongWayVolatileFixed body = new WrongWayVolatileFixed();
        Producer producer = body.new Producer(storage);
        Thread producerThread = new Thread(producer);
        producerThread.start();
        Thread.sleep(1000);

        Consumer consumer = body.new Consumer(storage);
        while (consumer.needMoreNums()) {
            System.out.println(storage.take()+"Consumed");
            Thread.sleep(100);
        }
        System.out.println("Consumers don't need more data.");
        /** * Once the consumption does not need more data, we should let the producer stop, * but the actual situation, in storage.put(num); Is blocked and cannot enter a new while() loop to say,! Canceled also makes it impossible to tell about */
        producerThread.interrupt();

    }
    class Producer implements Runnable{

        BlockingQueue storage;

        public Producer(BlockingQueue storage) {
            this.storage = storage;
        }

        @Override
        public void run(a) {
            int num = 0;
            try {
                Canceled is true
                while (num <= 100000 && !Thread.currentThread().isInterrupted()) {
                    if (num % 100= =0) {
                        storage.put(num);
                        System.out.println(num + "It's a multiple of 100. It's in storage."); } num++; }}catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println("Producer ends run"); }}}class Consumer {

        BlockingQueue storage;

        public Consumer(BlockingQueue storage) {
            this.storage = storage;
        }

        public boolean needMoreNums(a) {
            if (Math.random() > 0.95) {
                return false;
            }
            return true; }}}Copy the code

Result: The program ended normally.

Conclusion:

Stop thread important function source code parsing

4.1 Interrupt () source code analysis

4.2 Analysis of relevant methods to determine interruption

2 the static Boolean interrupted ()

The source code is as follows:

  /** * Tests whether the current thread has been interrupted. The * <i>interrupted status</i> of the thread is cleared by  this method. In * other words, if this method were to be called twice in succession, the * second call would return false (unless the current thread were * interrupted again, after the first call had cleared its interrupted * status and before the second call had examined it). * * <p>A thread interruption ignored because a thread was not alive * at the time of the interrupt will be reflected by this method * returning false. * *@return  <code>true</code> if the current thread has been interrupted;
     *          <code>false</code> otherwise.
     * @see #isInterrupted()
     * @revised6.0 * /
    public static boolean interrupted(a) {
        return currentThread().isInterrupted(true);
    }
Copy the code

This method calls private Native Boolean isInterrupted(Boolean ClearInterrupted); And pass ‘true'(‘true’ indicates whether the current state is cleared.) To determine the state of the thread and return true/false (true indicates that the thread has been interrupted, false indicates that the thread is continuing). And when it returns, the thread’s interrupt status is set directly to false. In other words, the thread interrupt state is directly cleared, this is the only way to clear the thread interrupt state. (because private native Boolean isInterrupted(Boolean ClearInterrupted); It is native and cannot be called.

4.2.2 Boolean isInterruted ()

And the static Boolean interrupted (); As such, the interrupted status of the current thread is returned, but the isInterrpted() method does not clear the interrupted status.

4.2.3 Thread.interrupted() Action object

Thread.interrupted() the effect object is actually the Thread that calls it. For example, after the interrupt () method returns the status of the Thread in which the interrupted() function is interrupted. After the interrupt () method returns the status of the Thread in which the interrupted function is interrupted.

Exercise: Think about what the following program will produce.

package threadcoreknowledge.createthreads.stopthreads;

public class RightWayInterrupted {

    public static void main(String[] args) throws InterruptedException {

        Thread threadOne = new Thread(new Runnable() {
            @Override
            public void run(a) {
                for(; ;) {}}});// Start the thread
        threadOne.start();
        // Set the interrupt flag
        threadOne.interrupt();
        // Get the interrupt flag
        System.out.println("isInterrupted: " + threadOne.isInterrupted());  //true
        // Get the interrupt flag and reset it
        System.out.println("isInterrupted: " + threadOne.interrupted()); // Clears the interrupt flag bit false
        // Get the interrupt flag and straighten it
        System.out.println("isInterrupted: " + Thread.interrupted());  // Clear true twice
        // Get the interrupt flag
        System.out.println("isInterrupted: " + threadOne.isInterrupted()); //ture
        threadOne.join();
        System.out.println("Main thread is over."); }}Copy the code

The running results are as follows:

5. Common interview questions

5.1 How do I Stop a Thread correctly?

It can be explained from the following three aspects:

  1. How it works: Using interrupts to ask a thread to stop, rather than forcing it to, is safe.
  2. Three-way cooperation: to stop the thread, the requestor, the stopped party and the submethod called should cooperate with each other: a. As the stopped party: check for interrupt signals in each loop or when appropriate, and handle the interrupt signal where an InterrupedException may be thrown; B. Requestor: send interrupt signal; C. Use a try-catch to throw an InterrupedException.
  3. Finally, the wrong method: Stop /suspend is deprecated, and volatile Boolean does not handle prolonged blocking

5.2 How to handle uninterruptible blocking (such as reentrantLock. lock() or Socket I/O failure to respond to interrupts, how to stop the thread?)

If a thread is blocked due to calling wait(), sleep(), or Join (), you can interrupt the thread and wake it up by throwing InterruptedException. Unfortunately, there is no universal solution for blocking that does not respond to InterruptedException. But we can make use of certain other can interrupt response method, such as: already. LockInterruptibly (), such as: close the socket to make threads returns immediately and other methods to achieve a goal.