Java thread state and correct posture for thread shutdown

1. Thread status and switch

There are six states of threads in Java, which are implemented by enumerating classes in Thread. As follows, I explain each state to some extent.

  public enum State {
        /** Indicates that a thread is not enabled (that is, the start method is not called)*/
        NEW,

        /**
         * JVM All threads executed in are in this state, but it is not necessary to execute in the JVM.
         * That is, only this state is eligible to be scheduled by the JVM for timeslice execution.
         */
        RUNNABLE,

        /**
         * The thread is waiting to acquire the lock resource to enter the blocking state.
         * In this state, it always monitors the lock dynamics and is ready to seize the lock at any time
         * If the lock resource is obtained, enter the RUNNABLE state again
         */
        BLOCKED,

        /**
         * When calling the park method of Object.wait, Thread.join or LockSupport class, the thread enters this state.
         * If no other thread wakes up actively in this state, it will wait indefinitely.
         * Wake up methods include: Object.notify (wake up random one), Object.notifyAll (wake up all threads).
         * The awakened thread enters the RUNNABLE state again
         */
        WAITING,

        /**
         * It is the same as WAITING state, but the difference is that the method called is limited by time.
         * For example: Object.wait(10), Thread.sleep(10), Thread.join(10), LockSupport.parkNanos(10), LockSupport.parkUntil(10)
         * There are two ways to wake up:
         *     1,Time expired.
         *     2,Other threads called notify or notifyAll
         *  It also enters the RUNNABLE state after waking up
         */
        TIMED_WAITING,

        /** End of thread (normal death or termination)*/
        TERMINATED;
    }

In addition to NEW and TERMINATED, other states can be converted to each other. The conversion process is shown in the figure below.

 

Here we will talk about the runnable state in particular. In this state, the thread does not necessarily execute the program. Only the thread scheduled by the JVM can obtain the execution time slice, and only the thread in this state can obtain the time slice. In other words, the thread scheduled by the JVM and obtaining the time slice only belongs to the thread in the runnable state. In order to facilitate understanding, runnable can be divided into two states: runnable and Running (of course, you can also change it to other states, here I just understand it for myself). Then the above thread transition diagram is transformed into the following one (refer to the thread state diagram in the art of Java Concurrent programming):

 

About the example of thread state transition, you can deepen your understanding through the following code

public class Test {
    public static void main(String[] args) {
        Test test = new Test();
        // 1.NEW state
        Thread thread = new Thread(() -> {
            // 3.Conduct test Object lock contention. If the lock is seized, continue to execute, otherwise enter BLOCKED Status monitoring the lock, access after re acquisition RUNNABLE 
            synchronized (test) {
                try {
                    // 4.Get into TIMED_WAITING Status, 100 ms Back in RUNNABLE State scramble time slice 
                    Thread.sleep(100);
                    // 5.After regaining the time slice, enter WAITING state 
                    test.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 6.normal run()After the method is executed, the thread ends and enters TERMINATED 
        });
        // 2.call start()Method, thread in RUNNABLE state
        thread.start();
    }
}

Note: the order of code execution is the serial number of the comment.

2. End a thread correctly

In the above example, we can see that after the normal execution of the run method of a thread, the thread will normally die and enter the TERMINATED state. So if we need to stop the thread halfway, how can we end a thread correctly?

  1. Use interrupt() method: inside the thread, it defines a variable to identify whether the current thread is in the interrupted state. Calling interrupt() method makes the state true. We use this method with exception handling to end a thread.
      public static void main(String[] args) {
            Thread thread = new Thread(() -> {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    // There return It is necessary. The reason is explained later.
                    return;
                }
                System.err.println("thread interrupt test...");
            });
            thread.start();
            thread.interrupt();
            System.out.println("main thread end...");
        }

    // Result graph: the statement following the exception will not print


      Here's aboutInterrupt ID variable in thread (followed by interrupt Name)It should be noted that its status will be reset under certain circumstances.
       1,Inside the thread catch After the exception interrupt The status of will be reset to false.
    2,Thread called Thread.interrupted()After the method, interrupt The status of will be reset to false. If you need to determine whether the thread is interrupted, you can use the object method. isInterrupted(),This method does not reset.
    So we need to add in the code just now. return To end the thread, otherwise the thread will continue to execute, as shown in the following figure


    Use isInterrupted()Realization:
    public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(() -> {
    while (!Thread.currentThread().isInterrupted()) {
    int k = 0;
    while (k++ < 10) {
    System.out.println("do something..." + k);
    }
    }
    System.err.println("thread end...");
    });
    thread.start();
    Thread.sleep(1);
    // The main thread process is finished, and the thread needs to be stopped.
    thread.interrupt();
    }

      

  2. Use identity bit to implement: define a variable to identify whether the thread is terminated, and exit the run method if it is terminated. It's the same as the implementation of isInterrupted() above, but it's just a volatile variable.
    public class Test {
    
        public static volatile boolean interrupted = false;
    
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(() -> {
                while (!interrupted) {
                    int k = 0;
                    while (k++ < 10) {
                        if (interrupted) {
                            System.err.println("thread invoke end....");
                            return;
                        }
                        System.out.println("do something..." + k);
                    }
                }
            System.err.println("thread end...");
            });
            thread.start();
            Thread.sleep(1);
            // The main thread process is finished, and the thread needs to be stopped.
            interrupted = true;
        }
    }
    // Result diagram

     

stop() method -- incorrect thread interrupt method

In the method provided by the thread, there is another method to force the thread to shut down -- stop(). This method can be said to be quite overbearing, giving a sense of "I don't care, I want you to die immediately (referring to thread)", and it will release all lock resources of thread, which may lead to data inconsistency and thread insecurity, as shown in the following example.

public class Test {

        public static volatile boolean flag = false;
        public int state = 0;

        public static void main(String[] args) throws InterruptedException {
            Test test = new Test();
            Thread thread = new Thread(() -> {
                synchronized (test) {
                    try {
                        test.state = 1;
                        Thread.sleep(100);
                        if (flag) {
                            test.state = 2;
                        }
                        System.err.println("thread execute finished...");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            thread.start();
            Thread.sleep(1);
            thread.stop();
            flag = true;
            System.out.println("state Status:" + test.state);
        }
}
// In this code, state is set to 1 by default when entering the thread, and then state is set to 2 if a specific condition is triggered after a period of time, but before the specific condition is triggered, the thread is terminated. Although the specific condition is met, it cannot be executed, resulting in data inconsistency.
// Result diagram
  

Therefore, we should use the above two correct ways instead of stop() to terminate the thread. In addition, if the stop() method is executed before the thread start(), it will die immediately when the thread starts.

 

 

If there's something wrong, I hope you can give me some advice (free anyway, right).

Keywords: Java jvm Programming

Added by nickmagus on Thu, 17 Oct 2019 21:35:38 +0300