如何停止通过实现Runnable接口创建的线程?

73

我通过实现Runnable接口创建了一个类,并在项目的另一个类中创建了许多线程(近10个)。
如何停止其中的一些线程?

6个回答

86

最简单的方法是使用interrupt()方法,这会导致Thread.currentThread().isInterrupted()返回true,并且在某些情况下还可能抛出InterruptedException异常,例如当线程处于等待状态时,例如Thread.sleep()otherThread.join()object.wait()等。

run()方法中,您需要捕获该异常和/或定期检查Thread.currentThread().isInterrupted()的值,并执行某些操作(例如,退出)。

注意:虽然Thread.interrupted()看起来与isInterrupted()相同,但它具有一个令人讨厌的副作用:调用interrupted()清除中断标志,而调用isInterrupted()则不会。

其他不涉及中断的方法涉及使用运行中的线程监视的“停止”(volatile)标志。


1
我的线程中没有while,但有时我仍然需要在run方法内部处理所有行之前停止它。 - user25

43

如何停止实现Runnable接口创建的线程?

有许多方法可以停止线程,但所有方法都需要特定的代码来执行。通常停止线程的一种方式是创建一个 volatile boolean shutdown 字段,线程会定期检查该字段:

  // set this to true to stop the thread
  volatile boolean shutdown = false;
  ...
  public void run() {
      while (!shutdown) {
          // continue processing
      }
  }

你还可以中断线程,导致sleep()wait()和其他一些方法抛出InterruptedException异常。你还应该使用类似以下代码的内容检查线程中断标志:

  public void run() {
      while (!Thread.currentThread().isInterrupted()) {
          // continue processing
          try {
              Thread.sleep(1000);
          } catch (InterruptedException e) {
              // good practice
              Thread.currentThread().interrupt();
              return;
          }
      }
  }
注意,使用 interrupt() 中断线程并不一定会立即导致抛出异常。只有在可中断的方法中调用时才会抛出 InterruptedException 异常。
如果您想为实现了 Runnable 接口的类添加一个 shutdown() 方法,应该像下面这样定义您自己的类:
public class MyRunnable implements Runnable {
    private volatile boolean shutdown;
    public void run() {
        while (!shutdown) {
            ...
        }
    }
    public void shutdown() {
        shutdown = true;
    }
}

在我的类中,我编写了一个方法来将标志(例如您的示例中的shutdown)设置为true。但是我无法从其他需要停止线程的类中访问此方法。 - svkvvenky
是的,你需要以某种方式公开它。 我通常会做一些类似于 public class MyClass implements Runnable { volatile boolean shutdown; public void run() { if (! shutdown) { ... } } } 的东西。 你可以添加一个 shutdown() 方法来设置关机标记。 然后你将执行 new Thread(new MyClass()).start(); 来运行它。 - Gray
我的线程中没有while,但有时我仍然需要在run方法内部处理所有行之前停止它。 - user25
然后你可以使用一个 if。如果你想要更多的帮助,我建议你花时间提出一个完整的问题。 - Gray

13

使用Thread.stop()方法中途停止线程不是一个好的实践。更好的方式是通过编程让线程返回。让Runnable对象在run()方法中使用共享变量。每当你想要停止线程时,将该变量用作标志。

编辑:示例代码

class MyThread implements Runnable{
    
    private volatile Boolean stop = false;
    
    public void run(){
        
        while(!stop){
            
            //some business logic
        }
    }
    public Boolean getStop() {
        return stop;
    }

    public void setStop(Boolean stop) {
        this.stop = stop;
    }       
}

public class TestStop {
    
    public static void main(String[] args){
        
        MyThread myThread = new MyThread();
        Thread th = new Thread(myThread);
        th.start();
        
        //Some logic goes there to decide whether to 
        //stop the thread or not. 
        
        //This will compell the thread to stop
        myThread.setStop(true);
    }
}

在我的类中,我编写了一个将标志设置为false的方法。但是我无法从另一个类中访问此方法,在那里我需要停止线程。 - svkvvenky
假设创建线程的类负责停止它,那么在该类中应该维护对Runnable实例的引用,并且使用这个引用来调用设置标志位的方法。 - Santosh
如果您不介意的话,能否给出一个例子以便更加清晰明了。 - svkvvenky
6
你应该将stop变量声明为volatile。 - Jakub Kotowski
2
如果我想在“某些业务逻辑”中间取消,那么这不会使其仍然运行“整个业务逻辑”吗?因为标记将在while循环的下一次迭代中被检查。 - ANewGuyInTown

6
如果您使用ThreadPoolExecutor,并且使用submit()方法,它会返回一个Future。您可以调用返回的Future上的cancel()以停止您的Runnable任务。

5
取消尝试取消执行,但不能保证一定成功。 - Rohit

2

不建议在中途停止(杀死)线程。实际上,该API已被弃用。

然而,您可以在此处获取更多详细信息,包括解决方法:如何在Java中终止线程?


-2
Thread.currentThread().isInterrupted()非常有效,但此代码仅暂停计时器。 此代码停止并重置线程计时器。 h1是处理程序名称。 此代码添加在您的按钮单击侦听器内部。 w_h =分钟 w_m =毫秒 i=计数器。
 i=0;
            w_h = 0;
            w_m = 0;


            textView.setText(String.format("%02d", w_h) + ":" + String.format("%02d", w_m));
                        hl.removeCallbacksAndMessages(null);
                        Thread.currentThread().isInterrupted();


                        }


                    });


                }`

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接