终止线程一般建议采用的方法是让线程自行结束,进入Dead(死亡)状态,就是执行完run()方法。即如果想要停止一个线程的执行,就要提供某种方式让线程能够自动结束run()方法的执行。比如设置一个标志来控制循环是否执行,通过这种方式让线程离开run()方法。

第一种 使用Thread类提供的stop()方法或suspend()方法强制终止线程(不安全,不要用)

第二种 使用volatile标记位退出线程

第三种 使用 interrupt()方法终止线程

由于其他原因导致线程停滞(如 I/O),进入非运行状态,停止线程的基本思路也是触发一个异常,而这个异常与导致线程停滞的原因相关。


第一种 使用Tread类提供的stop()方法或suspend()方法强制终止线程(不安全,不要用)

在当前线程中使用Thread类提供的stop()方法来终止其它线程,它会释放已经锁定的所有监视资源。如果当前任何一个受到这些监视资源保护的对象处于一个不一致的状态,其他的线程将会看到这个不一致的状态,这可能会导致程序执行的不确定性,且这种问题很难被定位。

在当前线程中使用Thread类提供的suspend()方法来挂起其它线程,然后使用resume()方法恢复。该方法容易引起死锁。线程在调用suspend()方法时不会释放锁,这就会导致一个问题:如果在当前线程中使用一个suspend()挂起一个持有锁的线程,如果当前线程也试图取得同样的对象锁,程序就会发生死锁。

鉴于以上两种方法的不安全性,Java语言已经不建议使用以上两种方法来终止线程。

第二种 使用volatile标记位退出线程

就是在现线程定义中设置一个boolean型的标记位,在线程的run()方法中根据这个标记位是true还是false来判断是否退出。这种情况一般是将任务放在run()方法中的一个while循环中执行。

 package com.test01.stopThread;

 class MyThread extends Thread {
private volatile boolean exit = false; // volatile保证exit的同步 public void exit(){
exit = true; // 标记位
} @Override
public void run() {
while (!exit){ // 通过标记位控制while循环中的任务
System.out.println("This is a thread.");
}
}
} public class ThreadFlag {
public static void main(String [] args) throws Exception{
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
thread1.start(); // 启动线程thread1
thread2.start(); // 启动线程thread2
Thread.sleep(1000); // 当前线程睡眠5秒钟
thread1.exit(); // 终止thread1
thread2.exit(); // 终止thread2
thread1.join(); // 当前线程等待thread1执行完
thread2.join(); // 当前线程等待thread2执行完
System.out.println(" Thread had been exited");
}
}

第三种 使用 interrupt ()方法终止线程

使用interrupt()方法来终止来终止线程分为两种情况:

1)线程处于阻塞状态,如使用了sleep,同步锁的wait,socket中的receiver,accept等方法时,会使线程处于阻塞状态。当调用线程的interrupt()方法时,会抛出InterruptException异常。阻塞中的那个方法抛出这个异常,通过代码捕获该异常,然后break跳出循环状态,从而让我们有机会结束这个线程的执行。通常很多人认为只要调用interrupt方法线程就会结束,实际上是错的, 一定要先捕获InterruptedException异常之后通过break来跳出循环,才能正常结束run方法。

2)线程未处于阻塞状态,使用isInterrupted()判断线程的中断标志来退出循环。当使用interrupt()方法时,中断标志就会置true,和使用自定义的标志来控制循环是一样的道理。

为什么要区分进入阻塞状态和和非阻塞状态两种情况了,是因为当阻塞状态时,如果有interrupt()发生,系统除了会抛出InterruptedException异常外,还会调用interrupted()函数,调用时能获取到中断状态是true的状态,调用完之后会复位中断状态为false,所以异常抛出之后通过isInterrupted()是获取不到中断状态是true的状态,从而不能退出循环,因此在线程未进入阻塞的代码段时是可以通过isInterrupted()来判断中断是否发生来控制循环,在进入阻塞状态后要通过捕获异常来退出循环。因此使用interrupt()来退出线程的最好的方式应该是两种情况都要考虑。

阻塞时抛出异常的情景

  package com.test01.stopThread;

  class MyThread01 extends Thread{
@Override
public void run(){
try {
sleep(50000); // sleep()是Thread类的静态方法
System.out.println("线程正在执行。");
} catch (InterruptedException e){ // 阻塞时sleep()方法会抛出异常
System.out.println("线程终止。");
System.out.println(e.getMessage());
break; // 捕获异常之后,退出循环
}
}
} public class Interrupt1 {
public static void main(String [] args)throws Exception {
MyThread01 thread01 = new MyThread01();
thread01.start();
System.out.println("在50秒内按任意键终止线程");
System.in.read();
thread01.interrupt(); // 中断线程thread01
thread01.join(); // 当前线程等待thread01执行完
System.out.println("线程已经结束");
}
}

非阻塞不抛出异常的情景

 package com.test01.stopThread;

 class MyThread01 extends Thread{
@Override
public void run(){
while(!isInterrupted()){ // 可以替换成Thread.interrupted()
System.out.println("线程执行中");
}
}
} public class Interrupt1 {
public static void main(String [] args)throws Exception {
MyThread01 thread01 = new MyThread01();
thread01.start();
Thread.sleep(10);
thread01.interrupt(); // 中断线程thread01
thread01.join(); // 当前线程等待thread01执行完
System.out.println("线程已经结束");
}
}

注意:在Thread类中有两个方法可以判断线程是否被中断。

一个是Thread类的静态方法interrupted(),用来判断当前线程是否被中断。

一个是非静态方法isInterrupted(),判断调用这个方法的线程是否被中断,即可以在当前线程中判断其他线程是否被中断。

Java中终止线程的三种方法的更多相关文章

  1. java中终止线程的三种方式

    在java中有三种方式可以终止线程.分别为: 1.  使用退出标志,使线程正常退出,也就是当run方法完成后线程终止.  2.  使用stop方法强行终止线程(这个方法不推荐使用,因为stop和sus ...

  2. JAVA中创建线程的三种方法及比较

    JAVA中创建线程的方式有三种,各有优缺点,具体如下: 一.继承Thread类来创建线程 1.创建一个任务类,继承Thread线程类,因为Thread类已经实现了Runnable接口,然后重写run( ...

  3. Java中创建线程的三种方法以及区别

    Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例.Java可以用三种方式来创建线程,如下所示: 1)继承Thread类创建线程 2)实现Runnable接口创建线 ...

  4. Java中创建线程的三种方式以及区别

    在java中如果要创建线程的话,一般有3种方法: 继承Thread类: 实现Runnable接口: 使用Callable和Future创建线程. 1. 继承Thread类 继承Thread类的话,必须 ...

  5. java中创建线程的3种方法

    1.继承Thread类优点:可以直接使用Thread类中的方法,代码比较简单.缺点:继承Thread类之后不能继承其他类. 2.实现Runable接口优点:实现接口,比影响继承其他类或实现接口.缺点: ...

  6. java中创建线程的几种方法及区别

    1,实现Runnable接口创建线程 特点: A:将代码和数据分开,形成清晰的模型 B:线程体run()方法所在的类可以从其它类中继承一些有用的属性和方法 C:有利于保持程序风格的一致性 2,继承Th ...

  7. Java中创建线程的三种方式及其优缺点

    1.自定义一个继承Thread的类,由于Java的单继承特性,限制了该类的扩展性. 2.实现Runnable接口,重写run()方法. 3.实现Callable接口,重写call方法.线程执行体可以有 ...

  8. (转)Java结束线程的三种方法

    背景:面试过程中问到结束线程的方法和线程池shutdown shutdownnow区别以及底层的实现,当时答的并不好. Java结束线程的三种方法 线程属于一次性消耗品,在执行完run()方法之后线程 ...

  9. Java结束线程的三种方法(爱奇艺面试)

    线程属于一次性消耗品,在执行完run()方法之后线程便会正常结束了,线程结束后便会销毁,不能再次start,只能重新建立新的线程对象,但有时run()方法是永远不会结束的.例如在程序中使用线程进行So ...

随机推荐

  1. 2018面向对象程序设计(Java)第3周学习指导及要求

    2018面向对象程序设计(Java) 第3周学习指导及要求(2018.9.11-2018.9.16)   学习目标 适应老师教学方式,能按照自主学习要求完成本周理论知识学习: 掌握Java Appli ...

  2. linux服务器设置只允许密钥登陆

    首先需要修改一些配置文件 vim /etc/ssh/sshd_config 进入sshd_config文件后需要更改几个地方 PubkeyAuthentication yes #启用公告密钥配对认证方 ...

  3. Java多态的向上转型和向下转型

    Java多态的向上转型和向下转型 向上转型:(子类转为父类,自动转型) 格式 :父类类型 变量名 = new 子类类型(); 注:子类赋值给父类,那这时这个父类就能调用子类的方法 向下转型:(父类转为 ...

  4. 仿造mongodb的存储方式存一些假数据

    //存入数据 $data = json_encode($row); // 过滤 $data = addslashes($data); //读取数据 $falseData = stripslashes( ...

  5. VBox添加虚拟磁盘挂载

    1. 关闭虚拟机,然后在设置里面选择添加虚拟硬盘 2.lsblk检查存在10G sdb虚拟磁盘 fdisk -l 检查 /dev/sdb 尚没有分区 3.磁盘分区 4.检查分区状况lsblk 5.格式 ...

  6. http4e eclipse plugin 插件介绍

    感谢作者的分享: http://blog.csdn.net/wiker_yong/article/details/10066905 以及作者的破解jar.目前看网站留言说已经git了. 官网链接地址: ...

  7. Oracle 入门

    一.安装Oracle 11g 服务端 服务端安装教程:https://jingyan.baidu.com/article/363872eccfb9266e4aa16f5d.html 二.安装客户端 客 ...

  8. Appium1.6 定位iOS元素和操作元素

    元素定位方式  第一种:通过Appium1.6的Inspector来查看 具体安装方式前面的随笔已经介绍了:http://www.cnblogs.com/meitian/p/7360017.html ...

  9. 解决谷歌浏览器频繁出现adobe flash player因过期而遭到阻止的问题(转自知乎)

    作者:在战争中链接:https://www.zhihu.com/question/32223811/answer/128088278来源:知乎著作权归作者所有,转载请联系作者获得授权. 很多新用户在安 ...

  10. Dedecms织梦内容页获取当前页面顶级栏目名称方法

    Dedecms织梦做站的时候,需要在当前页面调用顶级栏目名称的时候,织梦默认{dede:field name='typename' /} 可以获取当前栏目页上一级栏目的名称,而不是当前栏目顶级栏目名称 ...