Home  >  Article  >  Java  >  How to terminate a thread in Java

How to terminate a thread in Java

WBOY
WBOYforward
2023-04-26 08:58:201037browse

The mystery of Thread.stop being disabled

Asked how to terminate a thread, probably most people know that you can call the Thread.stop method.

But this method is no longer recommended after jdk1.2. Why is it not recommended?

Let’s first look at the definition of this method:

  @Deprecated(since="1.2")
    public final void stop() {
        @SuppressWarnings("removal")
        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());
    }

We can see from the code that the stop method first detects whether there is thread access permission. If you have permission, determine whether the current thread is a newly created thread. If not, then call the resume method to release the suspended state of the thread.

Finally call the stop0 method to end the thread.

Resume and stop0 are two native methods, and the specific implementation will not be discussed here.

It seems that the stop method is very reasonable and there is no problem. So why is this method unsafe?

Next let’s look at an example.

We create a NumberCounter class. This class has a safe method increaseNumber, which is used to add one to the number:

public class NumberCounter {
    //要保存的数字
    private volatile int number=0;
    //数字计数器的逻辑是否完整
    private volatile boolean flag = false;

    public synchronized int increaseNumber() throws InterruptedException {
        if(flag){
            //逻辑不完整
            throw new RuntimeException("逻辑不完整,数字计数器未执行完毕");
        }
        //开始执行逻辑
        flag = true;
        //do something
        Thread.sleep(5000);
        number++;
        //执行完毕
        flag=false;
        return number;
    }
}

In fact, in actual work, such a method may need to perform comparison It takes a long time, so here we simulate this time-consuming operation by calling Thread.sleep.

Here we also have a flag parameter to mark whether the increaseNumber method has been successfully executed.

Okay, next we call the method of this class in a thread and see what happens:

    public static void main(String[] args) throws InterruptedException {
        NumberCounter numberCounter= new NumberCounter();
        Thread thread = new Thread(()->{
            while (true){
                try {
                    numberCounter.increaseNumber();
                } catch (InterruptedException e) {
                   e.printStackTrace();
                }
            }
        });
        thread.start();
        Thread.sleep(3000);
        thread.stop();
        numberCounter.increaseNumber();
    }

Here, we create a thread and wait for this thread to run for 3 seconds After that, the thread.stop method was called directly, and we found the following exception:

Exception in thread "main" java.lang.RuntimeException: The logic is incomplete and the digital counter has not been executed
at com.flydean.NumberCounter.increaseNumber(NumberCounter.java:12)
at com.flydean.Main.main(Main.java:18)

This is because of the thread.stop method The running of the thread is directly terminated, resulting in mberCounter.increaseNumber not being completed.

But this unfinished state is hidden. If you use the thread.stop method to terminate the thread, it is likely to lead to unknown results.

So, we say thread.stop is unsafe.

How to be safe

So, if the thread.stop method is not called, how can we terminate the thread safely?

The so-called safety means that the logic in the thread needs to be completely executed, not half executed.

In order to achieve this effect, Thread provides us with three similar methods, which are interrupt, interrupted and isInterrupted.

interrupt is to set the interrupt flag for the thread; interrupted is to detect the interrupt and clear the interrupt status; isInterrupted only detects the interrupt. Another important point is that interrupted is a class method that acts on the current thread. interrupt and isInterrupted act on this thread, that is, the thread represented by the instance that calls this method in the code.

interrupt is the interrupt method. Its workflow is as follows:

  • If the current thread instance is calling wait(), wait(long) or wait( long, int) method or the join(), join(long), join(long, int) method, or the Thread.sleep(long) or Thread.sleep(long, int) method is called in this instance and is blocking status, its interrupted status will be cleared and an InterruptedException will be received.

  • If this thread is blocked during an I/O operation on the InterruptibleChannel, the channel will be closed, the thread's interrupt status will be set to true, and the thread A java.nio.channels.ClosedByInterruptException exception will be received.

  • If this thread is blocked in java.nio.channels.Selector, the interrupt status of the thread will be set to true, and it will return immediately from the select operation .

  • If none of the above conditions are true, set the interrupt status to true.

In the above example, in the increaseNumber method of NumberCounter, we called the Thread.sleep method, so if the interrupt method of thread is called at this time, the thread will throw An InterruptedException.

We change the above call example to the following:

    public static void main(String[] args) throws InterruptedException {
        NumberCounter numberCounter = new NumberCounter();

        Thread thread = new Thread(() -> {
            while (true) {
                try {
                    numberCounter.increaseNumber();
                } catch (InterruptedException e) {
                    System.out.println("捕获InterruptedException");
                    throw new RuntimeException(e);
                }
            }
        });

        thread.start();
        Thread.sleep(500);
        thread.interrupt();
        numberCounter.increaseNumber();
    }

Try again after running:

Exception in thread "main" Exception in thread "Thread-0" java.lang.RuntimeException: 逻辑不完整,数字计数器未执行完毕
    at com.flydean.NumberCounter.increaseNumber(NumberCounter.java:12)
    at com.flydean.Main2.main(Main2.java:21)
java.lang.RuntimeException: java.lang.thread.interrupt: sleep interrupted
    at com.flydean.Main2.lambda$main$0(Main2.java:13)
    at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.InterruptedException: sleep interrupted
    at java.base/java.lang.Thread.sleep(Native Method)
    at com.flydean.NumberCounter.increaseNumber(NumberCounter.java:17)
    at com.flydean.Main2.lambda$main$0(Main2.java:10)
    ... 1 more
捕获InterruptedException

可以看到,我们捕获到了这个InterruptedException,并且得知具体的原因是sleep interrupted。

捕获异常之后的处理

从上面的分析可以得知,thread.stop跟thread.interrupt的表现机制是不一样的。thread.stop属于悄悄终止,我们程序不知道,所以会导致数据不一致,从而产生一些未知的异常。

而thread.interrupt会显示的抛出InterruptedException,当我们捕捉到这个异常的时候,我们就知道线程里面的逻辑在执行的过程中受到了外部作用的干扰,那么我们就可以执行一些数据恢复或者数据校验的动作。

在上面的代码中,我们是捕获到了这个异常,打印出异常日志,然后向上抛出一个RuntimeException。

正常情况下我们是需要在捕获异常之后,进行一些处理。

那么自己处理完这个异常之后,是不是就完美了呢?

答案是否定的。

因为如果我们自己处理了这个InterruptedException, 那么程序中其他部分如果有依赖这个InterruptedException的话,就可能会出现数据不一致的情况。

所以我们在自己处理完InterruptedException之后,还需要再次抛出这个异常。

怎么抛出InterruptedException异常呢?

有两种方式,第一种就是在调用Thread.interrupted()清除了中断标志之后立即抛出:

   if (Thread.interrupted())  // Clears interrupted status!
       throw new InterruptedException();

还有一种方式就是,在捕获异常之后,调用Thread.currentThread().interrupt()再次中断线程。

public void run () {
  try {
    while (true) {
      // do stuff
    }
  }catch (InterruptedException e) {
    LOGGER.log(Level.WARN, "Interrupted!", e);
    // Restore interrupted state...
    Thread.currentThread().interrupt();
  }
}

这两种方式都能达到预想的效果。

The above is the detailed content of How to terminate a thread in Java. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact [email protected] delete