Before we start, let's look at what's wrong with the following code?
public class ThreadStopExample { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { try { System.out.println("Subthread start execution"); // Simulation business processing Thread.sleep(1000); } catch (Exception e) { } // Pseudocode: an important business method System.out.println("Important business methods of sub thread"); }); t1.start(); // Let the child thread run a little business first Thread.sleep(100); // Terminate child thread t1.stop(); // Wait for a period of time to ensure that the child thread "finishes executing" Thread.sleep(3000); System.out.println("Main thread execution completed"); } }
You may have found that the above code uses Thread.stop() to terminate the thread, which is not allowed in Java programs. What? Why can't you ask?
First of all, IDE will despise you. It will prevent you from using Thread.stop()!
What? You don't believe it. So look at this picture:
Well, then why can't you use it like this? Do you have to give me a perfunctory reason?
Problem 1: the integrity of the program is destroyed
In fact, for the code at the beginning of the article, its execution result is:
Subthread start execution
Main thread execution completed
We found a big problem. The most important pseudo code was not executed, as shown in the following figure:
It can be seen that after using stop() to terminate the thread, some of the remaining code of the thread will give up execution, which will cause a serious and hard to find big Bug. If the code that is not executed is the code that releases system resources, or the main logic processing code of this program. This destroys the integrity of the basic logic of the program, leading to unexpected problems, and it is also very secret, not easy to find and repair.
Some people say it's not easy. I'll add a finally?
This? There are all kinds of bar essence, especially this year.
OK, since this can't persuade you, let's look down.
Problem 2: breaking atomic logic
We know that synchronized in Java is an exclusive reentrant pessimistic lock. If we use it to decorate the code, it's OK to properly multithread, but if we encounter the stop() method, it's not necessarily. Let's look at the code directly.
public class ThreadStopExample { public static void main(String[] args) throws InterruptedException { MyThread myThread = new MyThread(); Thread t2 = new Thread(myThread); // Open thread t2.start(); for (int i = 0; i < 10; i++) { Thread t = new Thread(myThread); t.start(); } // End thread t2.stop(); } /** * Custom atomic test thread */ static class MyThread implements Runnable { // Counter int num = 0; @Override public void run() { // Synchronous code block to ensure atomic operation synchronized (MyThread.class) { // Self increment num++; try { // Thread sleep 0.1 seconds Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } // Self decrement num--; System.out.println(Thread.currentThread().getName() + " | num=" + num); } } } }
The execution results of the above procedures are as follows:
Thread-5 | num=1
Thread-4 | num=1
Thread-2 | num=1
Thread-1 | num=1
Thread-8 | num=1
Thread-6 | num=1
Thread-9 | num=1
Thread-3 | num=1
Thread-7 | num=1
Thread-10 | num=1
From the results, we can see that the code above is modified by synchronized + + and -- operations, and the final printed result num is not 0, but 1.
This is because the stop() method will release all locks in this thread, causing program execution disorder and destroying the atomic operation logic of the program.
The above problems lead to JDK abandoning the stop() method. Its source code is as follows:
/** * Forces the thread to stop executing. * <p> * If there is a security manager installed, its <code>checkAccess</code> * method is called with <code>this</code> * as its argument. This may result in a * <code>SecurityException</code> being raised (in the current thread). * <p> * If this thread is different from the current thread (that is, the current * thread is trying to stop a thread other than itself), the * security manager's <code>checkPermission</code> method (with a * <code>RuntimePermission("stopThread")</code> argument) is called in * addition. * Again, this may result in throwing a * <code>SecurityException</code> (in the current thread). * <p> * The thread represented by this thread is forced to stop whatever * it is doing abnormally and to throw a newly created * <code>ThreadDeath</code> object as an exception. * <p> * It is permitted to stop a thread that has not yet been started. * If the thread is eventually started, it immediately terminates. * <p> * An application should not normally try to catch * <code>ThreadDeath</code> unless it must do some extraordinary * cleanup operation (note that the throwing of * <code>ThreadDeath</code> causes <code>finally</code> clauses of * <code>try</code> statements to be executed before the thread * officially dies). If a <code>catch</code> clause catches a * <code>ThreadDeath</code> object, it is important to rethrow the * object so that the thread actually dies. * <p> * The top-level error handler that reacts to otherwise uncaught * exceptions does not print out a message or otherwise notify the * application if the uncaught exception is an instance of * <code>ThreadDeath</code>. * * @exception SecurityException if the current thread cannot * modify this thread. * @see #interrupt() * @see #checkAccess() * @see #run() * @see #start() * @see ThreadDeath * @see ThreadGroup#uncaughtException(Thread,Throwable) * @see SecurityManager#checkAccess(Thread) * @see SecurityManager#checkPermission * @deprecated This method is inherently unsafe. Stopping a thread with * Thread.stop causes it to unlock all of the monitors that it * has locked (as a natural consequence of the unchecked * <code>ThreadDeath</code> exception propagating up the stack). If * any of the objects previously protected by these monitors were in * an inconsistent state, the damaged objects become visible to * other threads, potentially resulting in arbitrary behavior. Many * uses of <code>stop</code> should be replaced by code that simply * modifies some variable to indicate that the target thread should * stop running. The target thread should check this variable * regularly, and return from its run method in an orderly fashion * if the variable indicates that it is to stop running. If the * target thread waits for long periods (on a condition variable, * for example), the <code>interrupt</code> method should be used to * interrupt the wait. * For more information, see * <a href="{@docRoot}/../technotes/guides/concurrency/threadPrimitiveDeprecation.html">Why * are Thread.stop, Thread.suspend and Thread.resume Deprecated?</a>. */ @Deprecated public final void stop() { SecurityManager security = System.getSecurityManager(); if (security != null) { checkAccess(); if (this != Thread.currentThread()) { security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION); } } // A zero status value corresponds to "NEW", it can't change to // not-NEW because we hold the lock. if (threadStatus != 0) { resume(); // Wake up thread if it was suspended; no-op otherwise } // The VM can handle all thread states stop0(new ThreadDeath()); }
It can be seen that the stop() method is modified by the @ Deprecated annotation, and the code modified by this annotation is represented as an obsolete method, which is not recommended. As can be seen from the remarks of stop (), stop () is not officially recommended as an unsafe method.
Thread terminated correctly
How to terminate a thread? Here are 2 correct methods:
- Set the exit ID to exit the thread;
- Use the interrupt() method to terminate the thread.
1. User defined exit ID
We can customize a boolean variable to identify whether to exit the thread. The implementation code is as follows:
// Custom exit ID exit thread static class FlagThread extends Thread { public volatile boolean exit = false; public void run() { while (!exit) { // Perform normal business logic } } }
We can see that we use the keyword volatile to decorate the thread, so as to ensure the safety of multi-threaded execution. When we need to let the thread exit, we only need to assign the variable exit to true.
2.interrupt terminate thread
When we use the interrupt() method, the execution results of the above two examples are normal. The execution code is as follows:
public class ThreadStopExample { public static void main(String[] args) throws InterruptedException { // Problem 1: the integrity of the program is destroyed Thread t1 = new Thread(() -> { try { System.out.println("Subthread start execution"); // Simulation business processing Thread.sleep(1000); } catch (Exception e) { } // Pseudo code: important business method System.out.println("Important business methods of sub thread"); }); t1.start(); // Let the child thread run a little business first Thread.sleep(100); // Terminate child thread t1.interrupt(); // Wait for a period of time to ensure that the child thread "finishes executing" Thread.sleep(3000); System.out.println("Main thread execution completed"); // Problem 2: breaking atomic logic MyThread myThread = new MyThread(); Thread t2 = new Thread(myThread); // Open thread t2.start(); for (int i = 0; i < 10; i++) { Thread t = new Thread(myThread); t.start(); } // End thread t2.interrupt(); } /** * Custom atomic test thread */ static class MyThread implements Runnable { // Counter int num = 0; @Override public void run() { // Synchronous code block to ensure atomic operation synchronized (MyThread.class) { // Self increment num++; try { // Thread sleep 0.1 seconds Thread.sleep(100); } catch (InterruptedException e) { System.out.println(e.getMessage()); } // Self decrement num--; System.out.println(Thread.currentThread().getName() + " | num=" + num); } } } }
The execution results of the above procedures are as follows:
Subthread start execution
Important business methods of sub thread
Main thread execution completed
sleep interrupted
Thread-1 | num=0
Thread-9 | num=0
Thread-10 | num=0
Thread-7 | num=0
Thread-6 | num=0
Thread-5 | num=0
Thread-4 | num=0
Thread-2 | num=0
Thread-3 | num=0
Thread-11 | num=0
Thread-8 | num=0
It can be seen that the above execution is in line with our expectation, which is the right way to terminate the thread.
summary
In this article, we talked about three ways to terminate a thread: the way to customize the exit ID, the way to use stop() or the way to interrupt(). The stop () method will lead to the problem that the integrity and atomicity of the program will be destroyed, and this method is identified as the expired method by JDK, which is not recommended, and the interrupt () method is undoubtedly the most suitable way for us to terminate the thread.
For more highlights, please pay attention to "Java Chinese community"