There are four ways to stop a thread
Method 1: determine the exit thread through volatile identification
public class VolatileCanStop implements Runnable{ private static volatile boolean canceled = false; @Override public void run() { int i = 0; while (!canceled&&i<=1000){ i++; try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" "+i+" execution..."); } System.out.println(Thread.currentThread().getName()+" is stop..."); } public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new VolatileCanStop()); thread.start(); TimeUnit.SECONDS.sleep(10); canceled = true; } }
Method 2 uses the stop() method to exit the thread
public class StopWay implements Runnable{ @Override public void run() { int i = 0; while (i <= 1000){ i++; System.out.println(Thread.currentThread().getName()+" "+i+" execution..."); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+" is stop..."); } public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new StopWay()); thread.start(); TimeUnit.SECONDS.sleep(10); thread.stop(); } }
The third method is suspend() and resume() to suspend and resume the thread
public class SuspendWay implements Runnable{ @Override public void run() { int i = 0; while (true){ System.out.println(Thread.currentThread().getName()+" "+i+" execution..."); try { i++; TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new SuspendWay()); thread.start(); TimeUnit.SECONDS.sleep(10); thread.suspend(); System.out.println(thread.getName()+" pause 5 seconds..."); TimeUnit.SECONDS.sleep(5); thread.resume(); System.out.println(thread.getName()+" recover..."); } }
Method 4 uses the interrupt method to interrupt the thread.
public class InterruptWay implements Runnable{ @Override public void run() { int i = 0; while (!Thread.currentThread().isInterrupted()&&i<=100){ System.out.println(Thread.currentThread().getName()+" "+i+" execution..."); try { i++; TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { Thread.currentThread().interrupt(); e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new InterruptWay()); thread.start(); TimeUnit.SECONDS.sleep(10); thread.interrupt(); } }
What is the best way to stop a thread?
First of all, we need to understand a principle: thread stopping should be based on notification and cooperation, rather than forced stopping.
Based on this principle, we can actually exclude the use of stop() and suspend() methods.
-
In the stop method, it is violent to forcibly stop the thread, which is inherently unsafe. When it stops the thread, it will throw a ThreadDeath exception, which may lead to the incomplete business process of the thread. Forcibly closing the thread may cause us to be unable to know when the sub thread is closed, At this time, if you need to do resource recovery and other operations on the thread, you can't do it.
-
As for the suspend() method, this method only plays the role of pausing the thread, and the thread execution can be resumed later through resume. There really seems to be nothing wrong with this combination, but if it is not used properly, it is easy to cause the monopoly of public objects, so that other threads cannot access public objects. That is, threads stopped through the suspend() method will not release the lock. In addition to not releasing resources, it may also lead to asynchronous data in threads.
In fact, in Java, the official has abandoned the stop, suspend and resume methods to remind us that there are better and safer methods to use.
stop and suspend cannot be used. Now we have only two methods left. One is to judge the exit thread through the volatile ID, and the other is to interrupt the thread by using the interrupt method.
At first glance, it seems quite reliable to judge the exiting thread through the volatile identifier. It seems that satisfaction is to end the thread through notification and cooperation. Can it be used in all application scenarios?
The answer is No. in the above example, it can be used without problems, but let's look at the following scenario.
producer
public 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 { while (num <= 100000 && !canceled) { //If it is a multiple of 50, it will be put into the warehouse if (num % 50 == 0) { storage.put(num); System.out.println(num + "Is a multiple of 50,Put it in the warehouse."); } num++; } } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println("Producer end run"); } } }
consumer
public class Consumer { BlockingQueue storage; public Consumer(BlockingQueue storage) { this.storage = storage; } public boolean needMoreNums() { return !(Math.random() > 0.97); } }
Main method
public class ExecutorMain { public static void main(String[] args) throws InterruptedException { ArrayBlockingQueue storage = new ArrayBlockingQueue(8); Producer producer = new Producer(storage); Thread producerThread = new Thread(producer); //Start executing the producer and collect all numbers that are multiples of 50 from 100000 producerThread.start(); Thread.sleep(500); //Start executing consumer Consumer consumer = new Consumer(storage); //Judge whether the random number is greater than 0.97, jump out if it is greater than, and execute consumption if it is less than while (consumer.needMoreNums()) { System.out.println(consumer.storage.take() + "Consumed"); Thread.sleep(100); } System.out.println("Consumers don't need more data."); //Once consumption doesn't need more data, we should let producers stop, but the actual situation can't stop producer.canceled = true; System.out.println(producer.canceled); } }
First, let's take a look at the execution process. First, the execution producer will find out the multiple of 50 from 100000 numbers and put them into the queue. After waiting for 500 milliseconds, the producer will have enough time to fill the warehouse. When the warehouse reaches the capacity, it will not continue to fill the data. At this time, the producer will block and the consumer will be created after 500 milliseconds, Determine whether consumption is required by the way that the random number is greater than 0.97, and then sleep for 100 milliseconds after each consumption. When the random number is greater than 0.97, the consumer no longer needs data, will jump out of the cycle, and then set the cancelled flag to true, and print out the end of the producer's operation.
After understanding the execution process, it seems that there is nothing wrong with the logic. It can be found after several times of execution. Although cancelled has been set to true at the end of execution, the producer still does not stop, because in this case, the producer is executing storage When the put (Num) is blocked, there is no way to enter the next cycle to judge the cancelled value before he is awakened. Therefore, in this case, there is no way to stop the producer with volatile.
At this time, you need to use interrupt to play. The only difference between interrupt and volatile marking is that interrupt can still feel the interrupt signal and respond even if the producer is blocked.
In the use of interrupt, if interrupt listens to an interrupt signal, it can be judged through the isInterrupted() method or handled by capturing the InterruptedException exception.
What we need to pay attention to is
- When throwing an exception again, don't swallow the exception raw.
- If you can handle this exception, exit after cleaning up.
- If you do not handle this exception and do not continue to execute the task, you need to throw it again.
- If you continue to execute the task without handling this exception, you need to recover the interrupt flag after capturing the exception (leave it to the subsequent program to check the interrupt)
summary
When using multithreading, it is necessary to stop threads gracefully through interrupt, because it can stop threads in the way of notification and cooperation, which can also enable us to have comprehensive control over the life cycle of threads.