多线程基础学习篇之停止线程

在java中有3种停止线程的方法

  1. 线程正常退出,也就是当前线程的run方法正常执行完毕后线程终止
  2. 使用stop方法强行终止线程 不过这个方法已经被废弃
  3. 使用interrupt()方法中断线程

接下来介绍这些方法的优缺点

首先介绍interrupt()方法
如果仅仅在当前线程调用interrupt()方法其实并不会马上停止该线程,只是在当前线程打了一个终止的标记。
可以看以下演示:

    // 创建t1线程
    Thread t1 = new Thread(() -> {
          for (int i = 0; i < 5000; i++) {
              System.out.println("t1线程:" + i);
          }
      });
    // 执行t1线程
    t1.start();
    Thread.sleep(20);
    // t1调用interrupt()方法
    t1.interrupt();
    System.out.println("t1调用interrupt()方法");
   }

执行结果
在这里插入图片描述
我们可以发现:先让主线程休息20ms,t1 线程执行到 i =1796 时 调用了t1.interrupt()。但是之后t1线程并没有停止还在继续执行。

那怎么用interrupt()方法实现停止线程呢? 我们先来看看怎么判断线程已经中断

Thread类中提供了2个方法来判断线程是否中断
在这里插入图片描述
在这里插入图片描述
可以看到 interrupted() 是静态方法,如果调用Thread.interrupted(),这就表示判断执行该方法的当前线程 是否中断。
而 isInterrupted() 是实例方法,必须通过线程对象来调用,这个方法表示判断调用该方法的线程 是否中断。

  // 创建t1线程
  Thread t1 = new Thread(() -> {
      for (int i = 0; i < 5000; i++) {
          System.out.println("t1线程:" + i);
      }
  });
  // 执行t1线程
  t1.start();
  Thread.sleep(20);
  // t1调用interrupt()方法
  t1.interrupt();
  System.out.println("线程是否已经中断:" + t1.interrupted());
  System.out.println("线程是否已经中断:" + t1.interrupted());

在这里插入图片描述
可以看到打印了2个false,这当然是可以预见的,因为上述代码调用的是静态方法
interrupted() 。这个方法表示正在执行该方法的当前线程是否中断
虽然是用t1线程调用的 但是当前线程是主线程 它可从来没有调用interrupt()方法 所以当然打印的是false。这也告诉我们静态方法尽量用类名调用,不然容易产生误会。

如果我们把上述的判断中断代码改成这样

System.out.println("线程是否已经中断:" + t1.isInterrupted());
System.out.println("线程是否已经中断:" + t1.isInterrupted());

在这里插入图片描述
可以看到因为调用了t1.interrupt()方法 所以返回了true 而且是2个

接下来我们看看静态方法interrupted()的正确使用

Thread.currentThread().interrupt();
System.out.println("线程是否已经中断:" + Thread.interrupted());
System.out.println("线程是否已经中断:" + Thread.interrupted());

在这里插入图片描述
首先第一个true
是因为我们先使当前线程调用了interrupt()中断方法
然后利用Thread.interrupted()判断当前线程是否中断 理所当然是true
那为什么第二次判断是false呢?

这是因为Thread.interrupted()这个静态方法在第一次调用之后 第二次调用会清除中断状态
而对象.isInterrupted()则不会

总结一下这2个方法

Thread.interrupted():测试当前线程是否已经中断,连续执行会将中断标志清除
对象.isInterrupted():测试该线程对象是否已经中断,连续执行不会将中断标志清除

那怎么利用interrupt() 和interrupted() 结合终止线程呢?

// 创建t1线程
Thread t1 = new Thread(() -> {
    for (int i = 0; i < 5000; i++) {
        if(Thread.interrupted()) {
            System.out.println("线程已经终止");
            break;
        }
        System.out.println("t1线程:" + i);
    }
    System.out.println("当前run方法还可以继续执行");
});
// 执行t1线程
t1.start();
Thread.sleep(20);
// t1调用interrupt()方法
t1.interrupt();

在这里插入图片描述
我们只需要调用interrupt()方法 然后在线程中判断是否已经中断 如果中断就在里面进行相关处理

可以看到这里输出的线程终止只是代表for循环里面的代码不再执行
因为我们调用了break方法只是跳出当前循环

如果我们想让以后代码都不再执行 可以直接调用return

// 创建t1线程
Thread t1 = new Thread(() -> {
    for (int i = 0; i < 5000; i++) {
        if(Thread.interrupted()) {
            System.out.println("线程已经终止");
            return;
        }
        System.out.println("t1线程:" + i);
    }
    System.out.println("当前run方法还可以继续执行");
});
// 执行t1线程
t1.start();
Thread.sleep(20);
// t1调用interrupt()方法
t1.interrupt();

不建议用return 我们可以用抛异常然后在catch中进行相关的处理

// 创建t1线程
Thread t1 = new Thread(() -> {
    try {
        for (int i = 0; i < 5000; i++) {
            if(Thread.interrupted()) {
                System.out.println("线程已经终止");
                throw new InterruptedException("线程终止");
            }
            System.out.println("t1线程:" + i);
        }
        System.out.println("当前run方法还可以继续执行");
    } catch (InterruptedException e) {
        System.out.println("我真的退出了");
        e.printStackTrace();
    }
});
// 执行t1线程
t1.start();
Thread.sleep(20);
// t1调用interrupt()方法
t1.interrupt();

在这里插入图片描述

可以看到线程真的退出了。。。

这里我们还要注意一点
如果线程处于以下情况,那么当线程被中断的时候,能自动感知到

  • 来自 Object 类的 wait()、wait(long)、wait(long, int),
  • 来自 Thread 类的 join()、join(long)、join(long,
    int)、sleep(long)、sleep(long, int)

这几个方法的相同之处是,方法上都有: throws InterruptedException

如果线程阻塞在这些方法上(我们知道,这些方法会让当前线程阻塞),这个时候如果其他线程对这个线程进行了中断,那么这个线程会从这些方法中立即返回,抛出 InterruptedException 异常,同时重置中断状态为 false。
对于以上 3 种情况是最特殊的,因为他们能自动感知到中断(这里说自动,当然也是基于底层实现),并且在做出相应的操作后都会重置中断状态为 false。

那是不是只有以上 3 种方法能自动感知到中断呢?不是的,如果线程阻塞在 LockSupport.park(Object obj) 方法,也叫挂起,这个时候的中断也会导致线程唤醒,但是唤醒后不会重置中断状态,所以唤醒后去检测中断状态将是 true。
在并发包中,有非常多的处理中断的例子,提供两个方法,分别为响应中断和不响应中断,对于不响应中断的方法,记录中断而不是丢失这个信息。如 Condition 中的两个方法就是这样的:

void await() throws InterruptedException;
void awaitUninterruptibly();

除了用interrupt() 我们还可以设置一个flag变量 注意必须用volatile关键字修饰 保证其可见性

public class StopThread {
    private static volatile boolean flag = true;
    public static void main(String[] args) throws InterruptedException {
        Person person = new Person();
        // 创建t1线程
        Thread t1 = new Thread(() -> {
            while (flag) {
                try {
                    Thread.sleep(500);
                    System.out.println("我正在运行");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
            System.out.println("我已经停止了");
        });
        // 执行t1线程
        t1.start();
        Thread.sleep(3000);
        System.out.println("主线程说:哈哈你马上就停止了");
        flag = false;

    }

}

使用stop强行停止线程

这个方法十分暴力 如果当前线程正在执行同步方法 那么stop方法会强行中断线程并 释放锁,这就可能导致数据的不一致性 并且在用stop方法终结线程 不能保证线程资源的正常释放
可以看以下代码

@Data
class Person {
    private String name = "wyy";
    private int age = 200;
}

Person person = new Person();
  // 创建t1线程
  Thread t1 = new Thread(() -> {
      person.setName("wmh");
      try {
          Thread.sleep(500);
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
      person.setAge(20);
  });
  // 执行t1线程
  t1.start();

  Thread.sleep(100);

  t1.stop();

  System.out.println(person);
}

在这里插入图片描述
所以强烈不建议用stop方法,他已经废弃

使用suspend 和 resume

这2个方法可以实现对线程的暂停和恢复 但和stop一样也被废弃
下面是对susspend resume stop 的演示

public static void main(String[] args) throws InterruptedException {
        Person person = new Person();
        // 创建t1线程
        Thread t1 = new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我在运行:" + new Date());
            }
        });
        // 执行t1线程
        t1.start();
        Thread.sleep(3000);
        t1.suspend();
        System.out.println("主线程在运行:" + new Date());
        Thread.sleep(3000);
        t1.resume();
        Thread.sleep(3000);
        t1.stop();

    }

结果如下
在这里插入图片描述
suspend和resume为什么会被抛弃呢?
因为suspend调用后 线程不会释放已经占用的资源(锁),而是占着资源进入睡眠状态,这样容易引发死锁。
关于暂停恢复操作 可以用等待/通知实现

参考:java多线程编程核心技术
https://www.javadoop.com/post/AbstractQueuedSynchronizer-2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值