一、前言

大家肯定都使用过 Java 线程开发(Thread / Runnable),启动一个线程的做法通常是:

new Thread(new Runnable(

@Override

public void run() {

// todo sth...

}

)).start();

然而线程退出,大家是如何做的呢?一般做法可能不外乎以下两种:

设置一个标志位:true / false 来退出;

强制退出:thread.stop;(我相信,现在应该没人会使用这种方式了,因为JDK也很早就废弃了该方法)

可能还会有人提出,我可以用中断来退出线程! 我只能说:Too Young Too Simple!中断并不会使得线程结束而退出,中断(interrupt)只是唤醒被阻塞的线程而已。

本篇,我们就来好好的聊聊:线程中断,以及如何正确的使用线程中断,和正确的线程退出。

二、为何 Thread.stop 被废弃

This method is inherently unsafe. Stopping a thread with Thread.stop causes it to unlock all of the monitors that it

has locked (as a natural consequence of the unchecked ThreadDeath exception propagating up the stack). If any of the

objects previously protected by these monitors were in an inconsistent state, the damaged objects become visible to

other threads, potentially resulting in arbitrary behavior. Many uses of stop should be replaced by code that simply

modifies some variable to indicate that the target thread should stop running. The target thread should check this

variable regularly, and return from its run method in an orderly fashion if the variable indicates that it is to stop

running. If the target thread waits for long periods (on a condition variable, for example), the interrupt method

should be used to interrupt the wait.

以上是官方 JDK 中的源码注释说明,其含义如下:

**Thread.stop 方法天生就不安全。**使用该方法来停止线程,将会导致其它因为监视器锁『监视器我们在 synchronized 中就讲过,是 Java 的内置锁』而被锁住的线程全部都解锁!(本质的后果是:没有检查的 ThreadDeath 异常会在栈中传播,因而使得监视器锁解锁)。如果任何一个被监视器锁给锁住的对象处于一个不一致的状态,那么其被解锁后将会被其它线程可见,潜在的结果是产生任何后果。**我们应该使用一个变量来代替使用 stop 方法,告诉目标线程退出『这里就是我们开头所说的第一种方法,设置一个标志位』。**目标线程应该周期性的检查这个变量,并根据这个变量来正确的退出 run 方法。如果目标线程处于阻塞/休眠状态(如:使用 wait、sleep、yield 方法后,线程让出了 CPU 使用权,进而阻塞/休眠),此时,该标志位变量将不会起作用,那么,应该使用 interrupt 方法来中断目标线程的阻塞/休眠状态,将其唤醒!

对于 ThreadDeath 对象,官方还有补充:

线程可以在几乎任何地方抛出 ThreadDeath 异常。由于这一点,所有的同步方法和(代码)块将必须被考虑得事无巨细。

线程在清理第一个 ThreadDeath 异常的时候(在 catch 或 finally 语句中),可能会抛出第二个。清理工作将不得不重复直到到其成功。保障这一点的代码将会很复杂。

所以,我们也别想着去 try-catch ThreadDeath Exception!

同样,被废弃的还有 Thread.resume 和 Thread.suspend。这俩方法有造成死锁的危险:

使用suspend时,并不会释放锁;

如果存在某种情况要先获取该锁,再进行resume,那么就造成死锁了;

取代这两方法的正确方式是:Object.wait 和 Object.notify :

因为 Object.wait 进入阻塞时,会释放锁。

三、线程中断的含义

Thread 中有三个与中断相关的方法:

成员方法 interrupt():设置线程中断标志为 true ;

成员方法 isInterrupted():获取线程的中断状态,默认为 false,调用 interrupt() 后,该方法返回 true;

静态方法 Thread.interrupted():获取线程的中断状态,并且清除中断状态(设置为 false);

注:如果线程中断后,连续两次调用 Thread.interrupted(),第一次是 true & 清除状态,第二次结果是 false。

3.1、初步了解

我们先来通过一个例子来初步了解 thread.interrupt :

public class InterruptDemo implements Runnable {

@Override

public void run() {

while (true) {

System.out.println("Thread running...");

}

}

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

Thread thread = new Thread(new InterruptDemo(), "InterruptDemo");

System.out.println("start thread");

thread.start();

Thread.sleep(50);

System.out.println("interrupt thread");

thread.interrupt();

Thread.sleep(50);

System.out.println("thread's status = " + thread.isInterrupted());

}

}

输出结果:

start thread

Thread running...

Thread running...

......

interrupt thread

Thread running...

Thread running...

......

thread's status = true

Thread running...

......

我们可以看到,即便我们调用了 thread.interrupt 方法,线程也并没有退出,仍旧继续运行。因此,这个例子证明了一点:我们并不能通过"我们所认为的"中断来试图"结束"正在运行的线程。

3.2、中断即唤醒阻塞/休眠的线程

同样,我们再来看一个例子:

public class InterruptDemo implements Runnable {

@Override

public void run() {

while (true) {

System.out.println("Thread will sleep 10s ------------------------- running");

long timestamp = System.currentTimeMillis();

try {

Thread.sleep(10000);

} catch (InterruptedException e) {

System.out.println("thread interrupted...");

}

timestamp = System.currentTimeMillis() - timestamp;

System.out.println("Thread run, total sleep = " + timestamp + "(ms)");

}

}

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

Thread thread = new Thread(new InterruptDemo(), "InterruptDemo");

System.out.println("start thread");

thread.start();

Thread.sleep(3000);

System.out.println("interrupt thread");

thread.interrupt();

System.out.println("main exit");

}

}

输出结果:

start thread

Thread will sleep 10s ------------------------- running

interrupt thread

main exit

thread interrupted...

Thread run, total sleep = 3002(ms)

Thread will sleep 10s ------------------------- running

Thread run, total sleep = 10002(ms)

Thread will sleep 10s ------------------------- running

我们可以看到,线程启动后,进入睡眠(10s),3秒后被中断唤醒,执行完一个 while 后再次进入第二次睡眠(10s),然后周而复始。

3.3、一般标志位法退出线程

public class InterruptDemo implements Runnable {

private static final AtomicBoolean running = new AtomicBoolean(true);

@Override

public void run() {

while (running.get()) {

long timestamp = System.currentTimeMillis();

timestamp = System.currentTimeMillis() - timestamp;

System.out.println("Thread run, total sleep = " + timestamp + "(ms)");

}

System.out.println("Thread exit");

}

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

Thread thread = new Thread(new InterruptDemo(), "InterruptDemo");

System.out.println("start thread");

thread.start();

Thread.sleep(100);

System.out.println("interrupt thread");

thread.interrupt();

running.set(false);

System.out.println("main exit");

}

}

输出结果:

start thread

.......

Thread run, total sleep = 0(ms)

interrupt thread

Thread run, total sleep = 0(ms)

Thread run, total sleep = 0(ms)

Thread run, total sleep = 0(ms)

main exit

Thread exit

我们通过使用一个 AtomicBoolean 变量来当作标志位,使得我们的线程能正常退出。 我们也可以判断线程是否被中断而选择性的退出。

3.4、线程中断退出

public class InterruptDemo implements Runnable {

@Override

public void run() {

while (!Thread.currentThread().isInterrupted()) {

long timestamp = System.currentTimeMillis();

timestamp = System.currentTimeMillis() - timestamp;

System.out.println("Thread run, total sleep = " + timestamp + "(ms)");

}

System.out.println("Thread exit");

}

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

Thread thread = new Thread(new InterruptDemo(), "InterruptDemo");

System.out.println("start thread");

thread.start();

Thread.sleep(100);

System.out.println("interrupt thread");

thread.interrupt();

System.out.println("main exit");

}

}

输出结果:

start thread

.......

Thread run, total sleep = 0(ms)

interrupt thread

Thread run, total sleep = 0(ms)

Thread run, total sleep = 0(ms)

Thread run, total sleep = 0(ms)

main exit

Thread exit

3.5、标志位 + 线程中断结合

public class InterruptDemo implements Runnable {

private static final AtomicBoolean running = new AtomicBoolean(true);

@Override

public void run() {

while (running.get()) {

System.out.println("Thread will sleep 10s ------------------------- running");

long timestamp = System.currentTimeMillis();

try {

Thread.sleep(10000);

} catch (InterruptedException e) {

System.out.println("Interrupted... Todo other things then exit......");

running.set(false);

continue;

}

timestamp = System.currentTimeMillis() - timestamp;

System.out.println("Thread run, total sleep = " + timestamp + "(ms)");

}

System.out.println("Thread exit");

}

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

Thread thread = new Thread(new InterruptDemo(), "InterruptDemo");

System.out.println("start thread");

thread.start();

Thread.sleep(3000);

System.out.println("interrupt thread");

thread.interrupt();

System.out.println("main exit");

}

}

输出结果:

start thread

Thread will sleep 10s ------------------------- running

interrupt thread

main exit

Interrupted... Todo other things then exit......

Thread exit

四、总结

本文我们分析了线程的中断,并让大家了解了中断的含义:只是告诉该线程,你被『中断』了,至于你想干嘛,还是由你自己来决定。同时,我们也简单分析了几个废弃的方法的原因。希望大家学习了本文之后,能正确且合理的设计,线程如何安全的退出。

五、附录

Object.wait:阻塞当前线程,释放持有的锁;

Object.notify:唤醒当前对象上被阻塞的线程,使其进入就绪状态;

Object.notifyAll:唤醒所有线程;

Thread.sleep:指定当前线程休眠一定时间,让出CPU,但不会释放同步资源锁;

Thread.yield:让出CPU使用权,让自己和其它线程来争夺使用CPU的机会,因此,使用此方法后,并不能保证该线程又再次拿到CPU而恢复运行(使用此方法后,优先级高的线程拿到CPU的概率较大,但优先级低的线程也有概率拿到CPU而执行),同理不会释放同步资源锁;

以上就是详解Java 线程中断的详细内容,更多关于Java 线程中断的资料请关注脚本之家其它相关文章!

java中断runnable_详解Java 线程中断相关推荐

  1. java lock unlock_详解Java中的ReentrantLock锁

    ReentrantLock锁 ReentrantLock是Java中常用的锁,属于乐观锁类型,多线程并发情况下.能保证共享数据安全性,线程间有序性 ReentrantLock通过原子操作和阻塞实现锁原 ...

  2. java system sleep_详解Java中的sleep()和wait()的区别

    详解Java中的sleep()和wait()的区别 对于sleep()方法,我们首先要知道该方法是属于Thread类中的.而wait()方法,则是属于Object类中的. sleep()方法导致了程序 ...

  3. java中priorityqueue_详解JAVA中priorityqueue的具体使用

    Java中PriorityQueue通过二叉小顶堆实现,可以用一棵完全二叉树表示.本文从Queue接口函数出发,结合生动的图解,深入浅出地分析PriorityQueue每个操作的具体过程和时间复杂度, ...

  4. java web ip_详解Java Web如何限制访问的IP的两种方法

    前一阵子因为在做项目时碰到了这个功能,现在好好总结一下,至于为什么要限制IP访问,我就不多说了.然后百度了一下,现在主要有两种方式去限制IP访问,第一种是最简单的方便的,第二种是通过过滤器来限制访问. ...

  5. java中implement_详解JAVA中implement和extends的区别

    详解JAVA中implement和extends的区别 extends是继承父类,只要那个类不是声明为final或者那个类定义为abstract的就能继承,Java中不支持多重继承,但是可以用接口来实 ...

  6. java中getclass_详解java中this.getClass()和super.getClass()的实例

    详解java中this.getClass()和super.getClass()的实例 前言: 遇到this.getClass()和super.getClass()的返回值感到疑惑,经过探索豁然开朗. ...

  7. java 重定向 redirect_详解Java从后台重定向(redirect)到另一个项目的方法

    (1)通过ModelAndView跳转 @RequestMapping("alipayforward") public ModelAndView alipayforward(Htt ...

  8. java ftp ftpclient_详解JAVA中使用FTPClient工具类上传下载

    详解JAVA中使用FTPClient工具类上传下载 在Java程序中,经常需要和FTP打交道,比如向FTP服务器上传文件.下载文件.本文简单介绍如何利用jakarta commons中的FTPClie ...

  9. 【Java核心】详解Java中断机制

    一.引言 我们知道,通过线程的start方法启动一个线程后,线程开始执行run方法,run方法运行结束后线程退出,那为什么还需要结束一个线程呢?有多种情况,比如说:        很多线程的运行模式是 ...

最新文章

  1. 【TarsosDSP】TarsosDSP 简介 ( TarsosDSP 功能 | 相关链接 | 源码和相关资源收集 | TarsosDSP 示例应用 | TarsosDSP 源码路径解析 )
  2. MongoDB同步到Oracle,MongoToOracleMongoDB数据库迁移工具
  3. Java 之 String 类型
  4. asp.net core 系列 20 EF基于数据模型创建数据库
  5. Atitit.ALT+TAB没反应车and 点击任务栏程序闪烁但是不能切换
  6. 针对口令的暴力破解攻击方式
  7. (模拟)HDU - 5857 Median
  8. 碰见参数错误咋解决?
  9. [完全版] Windows安装与配置Git cz (commitizen)
  10. win7 安装IE11 教程
  11. python 病毒 基因_#Python#提取基因对应的蛋白质名
  12. android平板改成电视盒子,【当贝市场】废旧手机改造成电视盒子详细教程
  13. 7.面向对象进阶(1)
  14. Migrando电子商务可以实现Iluria para o Shopify(Python的标准)
  15. win7系统(32位)安装CAD2004的方法
  16. 展望 | 2023年十大科技趋势
  17. Python爬虫项目之NBA球员可视化分析
  18. 【墨者学院】SQL手动注入 靶场初体验
  19. 计算机毕设——中文分词方法研究与实现
  20. openwrt 下编译ipk

热门文章

  1. java毕业设计网上订餐系统mybatis+源码+调试部署+系统+数据库+lw
  2. 盘点Python编程的简易版自动化工具——ADB史上全操作
  3. MSVCR110.dll缺失问题解决
  4. 成功跳槽字节跳动,详细的Java学习指南
  5. linux查看GCC版本
  6. react-高阶组件
  7. 思迈特软件Smartbi:人口热力图怎么做,这个热力图软件太方便了
  8. 力扣 827. 最大人工岛
  9. 华为鸿蒙汽车自动驾驶,华为鸿蒙车机OS现身,自动驾驶再进化
  10. 基于单片机家庭火灾报警系统设计-毕设课设资料