前言

强制停止一个线程是不安全的,线程什么时候可以停止,只有线程自己知道,我们模拟主线程中启动一个工作线程,主线程睡眠3秒后去停止工作线程。

标志位

public class ThreadCloseGraceful {

  private static class Worker extends Thread {

    private volatile boolean running = true;

    public Worker(String name) {
      super(name);
    }

    @Override
    public void run() {
      String threadName = Thread.currentThread().getName();
      while (running) {
        System.out.println(threadName + " is running");
        try {
          Thread.sleep(1_000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
      System.out.println(threadName + " exit");
    }

    public void shutdown() {
      this.running = false;
    }
  }

  public static void main(String[] args) {
    Worker worker = new Worker("Worker");
    worker.start();
    try {
      Thread.sleep(3000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    worker.shutdown();
  }
}

使用volatile关键字保证内存可见性,主线程修改了,工作线程可以立刻知道running字段的内容变化。如果工作线程已经阻塞了,这种方法没有办法停止,这种情况可以使用线程的interrupt()方法。

Thread.interrupt()

public class ThreadCloseGraceful2 {

  private static class Worker extends Thread {


    public Worker(String name) {
      super(name);
    }

    @Override
    public void run() {
      String threadName = Thread.currentThread().getName();
      while (true) {
        System.out.println(threadName + " is running");
        try {
          Thread.sleep(10_000);
        } catch (InterruptedException e) {
          e.printStackTrace();
          break;
        }
      }
      System.out.println(threadName + " exit");
    }
  }

  public static void main(String[] args) {
    Worker worker = new Worker("Worker");
    worker.start();
    try {
      Thread.sleep(3000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    worker.interrupt();
  }
}

我们调用interrupt()方法后

  1. 如果工作线程被阻塞了,会抛出InterruptedException异常,我们可以捕获此异常,然后停止线程
  2. 如果工作线程在运行中,会将线程的中断标记设置为true,可以调用isInterrupted()方法,判断当前中断标记是否为true。

java中如何优雅的停止一个线程_java

apache工具包中的FileAlterationMonitor文件变化监控器就是通过标志位和中断来实现的。

强制停止

public class ThreadService {

  private Thread executeThread;

  private boolean finished = false;

  public void execute(Runnable task) {
    executeThread = new Thread(() -> {
      Thread runner = new Thread(task);
      runner.setDaemon(true);
      runner.start();
      try {
        runner.join();
        finished = true;
      } catch (InterruptedException e) {
        //ignore
      }
    });
    executeThread.start();
  }

  public void shutdown(long mills) {
    long currentTime = System.currentTimeMillis();
    while (!finished) {
      if ((System.currentTimeMillis() - currentTime) >= mills) {
        System.out.println("任务超时,需要结束他!");
        executeThread.interrupt();
        break;
      }
      try {
        Thread.sleep(1);
      } catch (InterruptedException e) {
        System.out.println("执行线程被打断!");
        break;
      }
    }
    finished = false;
  }
}

客户端调用

public class ThreadCloseForce {


  public static void main(String[] args) throws InterruptedException {

    ThreadService service = new ThreadService();
    long start = System.currentTimeMillis();
    service.execute(() -> {
      //load a very heavy resource.
      /*while (true) {

      }*/
      try {
        Thread.sleep(2000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    });
    service.shutdown(3000);
    System.out.println("main start");
    long end = System.currentTimeMillis();
    System.out.println(end - start);
  }
}

在工作线程内启动一个新的守护线程,当主线程停止工作线程时,会向工作线程发中断信号,就算工作线程在执行一个费时的操作,也会被停止。