java 线程中断和退出

169 阅读4分钟

1、中断

线程中断即线程运行过程中被其他线程给打断了,它与 stop 最大的区别是:stop 是由系统强制终止线程,而 线程中断则是给目标线程发送一个中断信号,如果目标线程没有接收线程中断的信号并结束线程,线程则不会终止,具体是否退出或者执行其他逻辑由目标线程决定。
我们来看下线程中断最重要的 3 个方法,它们都是来自 Thread 类!

1、java.lang.Thread#interrupt
中断目标线程,给目标线程发一个中断信号,线程被打上中断标记。

2、java.lang.Thread#isInterrupted()
判断目标线程是否被中断,不会清除中断标记。

3、java.lang.Thread#interrupted
判断目标线程是否被中断,会清除中断标记。

2、退出线程执行

1、使用退出标志

  当run方法执行完后,线程就会退出。但有时run方法是永远不会结束的,如在服务端程序中使用线程进行监听客户端请求,或是其他的需要循环处理的任务。

  在这种情况下,一般是将这些任务放在一个循环中,如while循环。如果想使while循环在某一特定条件下退出,最直接的方法就是设一个boolean类型的标志,并通过设置这个标志为true或false来控制while循环是否退出。

//退出标志,volatile 保证可见性
public static volatile boolean exit =false;

public static void main(String[] args) throws InterruptedException {
   // 启动一个线程
   new Thread(()->{
      while (!exit) {
          try {
              Thread.sleep(200);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      }
       System.out.println("线程退出了");
   }).start();

   System.out.println("I am main thred");
   System.out.println("start to exit work thread");
   exit = true;
   Thread.sleep(200);
}

输出如下:

I am main thred
start to exit work thread
线程退出了

2、使用 interrupt 方法

// 启动一个线程
Thread thread = new Thread(() -> {
    // 判断中断标志是否已经被标志
    while (!Thread.currentThread().isInterrupted()) {
    }
    System.out.println("线程退出了");
});

// 启动线程
thread.start();
System.out.println("开始中断线程");
// 中断
thread.interrupt();

执行结果如下:

开始中断线程
线程退出了

使用 interrupt() + InterruptedException来中断线程

线程处于阻塞状态,如Thread.sleep、wait、IO阻塞等情况时,调用interrupt方法后,sleep等方法将会抛出一个InterruptedException:异常后,中断标志位会被清除

public static void main(String[] args) throws InterruptedException {
    // 启动一个线程
    Thread thread = new Thread(() -> {
        // 判断中断标志是否已经被标志
        while (!Thread.currentThread().isInterrupted()) {

            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                System.out.println("被中断了");
                System.out.println("当前中断标志位为:" + Thread.currentThread().isInterrupted());
            }
        }
        System.out.println("线程退出了");
    });

    // 启动线程
    thread.start();
    System.out.println("开始中断线程");
    // 模拟主线程干活,保证,thread在 Sleep 中
    Thread.sleep(200);
    // 中断
    thread.interrupt();
}

输出结果如下: 程序无法进行关闭

image.png

使用中断,正确退出方式如下:

public static void main(String[] args) throws InterruptedException {
    // 启动一个线程
    Thread thread = new Thread(() -> {
        // 判断中断标志是否已经被标志
        while (!Thread.currentThread().isInterrupted()) {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                // 异常后,中断标志会被清除。
                System.out.println("被中断了");
                System.out.println("当前中断标志位为:" + Thread.currentThread().isInterrupted());
                // 再次中断。来退出程序
                Thread.currentThread().interrupt();
            }
        }
        System.out.println("线程退出了");
    });

    // 启动线程
    thread.start();
    System.out.println("开始中断线程");
    // 模拟主线程干活,保证,thread在 Sleep 中
    Thread.sleep(200);
    // 中断
    thread.interrupt();

输出结果如下:

开始中断线程
被中断了
当前中断标志位为:false
线程退出了

3、使用原子变量

public static void main(String[] args) throws InterruptedException {
    // 停止标志
    AtomicBoolean exit = new AtomicBoolean(false);
    // 启动一个线程
    Thread thread = new Thread(() -> {
        // 判断标志是否退出
        while (!exit.get()) {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                // 异常后,中断标志会被清除。
                System.out.println("被中断了");
                System.out.println("当前中断标志位为:" + Thread.currentThread()
                .isInterrupted());
            }
        }
        System.out.println("线程退出了");
    });

    // 启动线程
    thread.start();
    // 模拟主线程干活,保证,thread在 Sleep 中
    Thread.sleep(200);
    //cas 先取值,再set
    exit.compareAndSet(false,true);
}

输出结果如下:

线程退出了

3、interrupted()isInterrupted()方法

interrupted()是静态方法:内部实现是调用的当前线程的isInterrupted(),并且会重置当前线程的中断状态。

/**
 * Tests whether the current thread has been interrupted.  The
 * <i>interrupted status</i> of the thread is cleared by this method.  In
 * other words, if this method were to be called twice in succession, the
 * second call would return false (unless the current thread were
 * interrupted again, after the first call had cleared its interrupted
 * status and before the second call had examined it).
 *
 * <p>A thread interruption ignored because a thread was not alive
 * at the time of the interrupt will be reflected by this method
 * returning false.
 *
 * @return  <code>true</code> if the current thread has been interrupted;
 *          <code>false</code> otherwise.
 * @see #isInterrupted()
 * @revised 6.0
 */
public static boolean interrupted() {
    return currentThread().isInterrupted(true);
}

isInterrupted()是实例方法,是调用该方法的对象所表示的那个线程的isInterrupted(),不会重置当前线程的中断状态

/**
 * Tests whether this thread has been interrupted.  The <i>interrupted
 * status</i> of the thread is unaffected by this method.
 *
 * <p>A thread interruption ignored because a thread was not alive
 * at the time of the interrupt will be reflected by this method
 * returning false.
 *
 * @return  <code>true</code> if this thread has been interrupted;
 *          <code>false</code> otherwise.
 * @see     #interrupted()
 * @revised 6.0
 */
public boolean isInterrupted() {
    return isInterrupted(false);
}