[Multithreading]-How to stop a thread gracefully

1. When do we need to interrupt a thread

In actual development, there are many scenarios that require us to interrupt a running thread, such as:

  1. When we use the ticket grabbing software, one of the channels has already grabbed a train ticket. At this time, we need to notify other threads to stop working.
  2. When we want to get the task result within a limited time, we also need to close it when the task times out

But Java cannot simply exit the thread running like the break in the code block. The API methods provided by Java itself always feel unsatisfactory. There is no way to perfectly solve our needs. So how do we stop a thread gracefully?

Two, how to terminate a thread

1) Stop violence

1. Use the stop method to stop the thread

Java provides a stop method to stop the running of threads. First, we write a piece of code to demonstrate how the stop method terminates the thread:

package xiao.thread.stop;

public class StopThread {
    
    
	private static int count;

	public static void main(String[] args) throws InterruptedException {
    
    
		// first :create a thread task
		Thread thread = new Thread(() -> {
    
    
			while (true) {
    
    
				++count;
				// 为了减少打印数 增加个计数判断 
				//也可调用sleep方法进行休眠
				if (count % 100 == 0) {
    
    
					System.out.println("此时线程依旧存活" + count);
				}
			}
		}, "thread_task for stopThread");
		thread.start();
		// 如果直接终止 很有可能thread的run方法还没有开始执行 所以建议让main线程休眠一段时间来观察效果
		Thread.currentThread().sleep(1_000);
		thread.stop();
	}
}

First of all, we can clearly observe that when we call stop, the thread is violently stopped. Although this method is simple, the Java language itself does not encourage us to do so. The stop() method is in a strikethrough state, which indicates that this method has been deprecated.

2. Why not advocate using the stop method

In fact, the reason why the stop method is not recommended is simple, because the termination of the optimal state of the thread can only commit suicide and cannot be killed . Specifically, when we call the stop method through other threads, we don't know where the killed thread has been executed at this moment. For example, we are doing an operation to add data to the collection, and we cannot know where the data is added at this time. When we call the stop method, the killed thread will immediately release the lock held by itself , and other threads can see the unprocessed data at this time, causing thread safety issues and destroying the consistency of the object .

2) Catch exception method

1. Interrupt mechanism

Interruption is well understood. It does not have the ability to interrupt itself, but is just a cooperative mechanism. In Java, there is no grammatical implementation of interruption, and we can only use the existing interruption flag to remember the business process to achieve the purpose of interruption.

2. Related API

Insert picture description here
When implementing the interrupt function, our commonly used API is mainly three methods:

1.
Calling this method by public void interrupt will set the interruption flag of the thread to true
2.
Calling this method by public boolean isInterrupt will return the interruption flag that is available at the moment
3. Public static boolean interruped
This method can only be called by Thread.interrupted() , He will do two operations. The
first step is to return to the interrupt status of the current thread. The
second step is to set the current interrupt flag to false.

3. Verify that interrupt does not have the ability to stop threads

First, let's give a small example to prove that when we call the interrupt method, jvm cannot interrupt the target thread for us:

public class InterruptThread {
    
    

	private static int count;

	public static void main(String[] args) {
    
    
		Thread thread = new Thread(() -> {
    
    
			while (true) {
    
    
				System.out.println("线程依旧存活:_此时计数器状态为_" + ++count);
				try {
    
    
					Thread.currentThread().sleep(1_000);
				} catch (InterruptedException e) {
    
    
				}

			}
		},"测试线程");
		thread.start();
		thread.interrupt();
	}

}

We look at the output:
Insert picture description here
While we start method is called immediately after the call to interrupt, but the goal of testing every second thread still print the counter status in the console, and no actual interrupt thread through this example we show that in When we call interrupt, JVM does not immediately stop the target thread for us . What if we want to stop the thread after calling interrupt?

4. Exception handling method to stop the thread

Now that we have confirmed that JVM does not actively interrupt the thread for us, then we need to use the interrupt flag mentioned in the interrupt method to do something. First we need to prove a new argument:

When the blocking method receives an interrupt signal, an InterruptedException will be thrown

Then we modify the above code:

public class InterruptThread {
    
    
	private static int count;
	public static void main(String[] args) throws InterruptedException {
    
    
		Thread thread = new Thread(() -> {
    
    
			while (true) {
    
    
				System.out.println(Thread.currentThread().getName()+"正在运行~");
				try {
    
    
					Thread.currentThread().sleep(2_500);
				} catch (InterruptedException e) {
    
    
					// TODO Auto-generated catch block
					e.printStackTrace();
					System.out.println(Thread.currentThread().getName()+"接收到中断异常");
				}
			}

		}, "测试线程");
		thread.start();
		Thread.currentThread().sleep(1_000);
		thread.interrupt();
	}
}

Operation result:
Insert picture description here
Here we can see that when we call the interrupt method, because the thread also calls the method sleep that may block the thread at this time, the target thread throws an interruptException when it receives our interrupt signal. , But because there is still code to be executed in the while method body, the thread is not over at this time, at this time we need to use the exception we caught to transform the code:

public class InterruptThread {
    
    
	private static int count;
	private static boolean mark = true ;
	public static void main(String[] args) throws InterruptedException {
    
    
		Thread thread = new Thread(() -> {
    
    
			while (mark) {
    
    
				System.out.println(Thread.currentThread().getName()+"正在运行~");
				try {
    
    
					Thread.currentThread().sleep(2_500);
				} catch (InterruptedException e) {
    
    
					// TODO Auto-generated catch block
					e.printStackTrace();
					mark = false ;
					System.err.println(Thread.currentThread().getName()+"接收到中断信号");
				}
			}
		}, "测试线程");
		thread.start();
		Thread.currentThread().sleep(1_000);
		thread.interrupt();
	}
}

Operation result: It
Insert picture description here
can be seen that the program does not continue to run after receiving our interrupt signal, and the thread stops. The change in the above code actually occurs in the judgment condition of while. We added a boolean variable mark, and the principle of stopping the thread by catching an exception is actually very simple: we think we set an interrupt variable and set the value to the code to run When we catch the exception, we change the value of the interrupt variable to achieve the purpose of jumping out of the loop to stop the thread.
Of course, there are many ways to achieve jumping out of the loop, and we can also use the return keyword to achieve the effect of jumping out of the loop:

public class InterruptThread {
    
    
	public static void main(String[] args) throws InterruptedException {
    
    
		Thread thread = new Thread(() -> {
    
    
			while (true) {
    
    
				System.out.println(Thread.currentThread().getName()+"正在运行~");
				try {
    
    
					Thread.currentThread().sleep(2_500);
				} catch (InterruptedException e) {
    
    
					e.printStackTrace();
					System.err.println(Thread.currentThread().getName()+"接收到中断信号");
					return ;
				}
			}
		}, "测试线程");
		thread.start();
		Thread.currentThread().sleep(1_000);
		thread.interrupt();
	}
}

Operation result:
Insert picture description here
You can also make good use of exceptions to end the thread's operation through return.

Of course, we still have a question about interruptException.

In actual development, we cannot end the thread by throwing an exception regardless. If you just want to record the log, then you should reset the interrupt flag to false at this time to avoid accidental interruption of the program

3) Realized by daemon thread (just understand)

We can also use the characteristics of the daemon thread to end the operation of a thread

If you don’t understand the characteristics of the daemon thread, you can read the introduction of thread in my previous article

Let's look at the code implementation first:

public class DaemonThreadInterrupt {
    
    
	public static Thread thread;
	public boolean flag = true;

	public void execut(Runnable task) {
    
    
		thread = new Thread(()->{
    
    
			Thread thread2 = new Thread(task,"任务线程");
			thread2.setDaemon(true);
			thread2.start();
			try {
    
    
				thread2.join();
			} catch (InterruptedException e) {
    
    
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		},"执行线程");
		thread.start();

	}

	public void stop() {
    
    
		thread.interrupt();
	}
	
	public static void main(String[] args) {
    
    
		DaemonThreadInterrupt daemonThreadInterrupt = new DaemonThreadInterrupt();
		daemonThreadInterrupt.execut(()->{
    
    
			while(true) {
    
    
				System.out.println("程序运行");
				try {
    
    
					thread.sleep(1_000);
				} catch (InterruptedException e) {
    
    
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		});
		daemonThreadInterrupt.stop();
	}
}

Implementation principle:
In the above code, I put the task thread that needs to be implemented into an execution thread. The execution thread is not responsible for the processing of any business logic, but is only responsible for starting the task thread. After the task thread is started, it is set as the daemon thread of the execution thread. Then let him join the thread of execution.
Then we used the characteristics of the join method and the characteristics of the daemon thread to design the stop method:
after the join method is executed, the execution thread will wait for the task thread to execute before executing, but when we call the interrupt method, the join method receives When the interrupt signal is reached, an exception will be thrown, and the execution thread is no longer waiting for the task thread to run. Here comes the important point: At this time the execution thread is finished, and the task thread as the daemon thread of the execution thread also ends its life cycle.
This method makes full use of the multiple characteristics of threads to stop a thread, and the end process is controllable, and a delay end can also be set, which is suitable for the realization of timing task threads.

Guess you like

Origin blog.csdn.net/xiaoai1994/article/details/110041270