【Java并发编程】线程中断机制(辅以常见案例)

这篇具有很好参考价值的文章主要介绍了【Java并发编程】线程中断机制(辅以常见案例)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


本文由浅入深介绍了中断机制、中断的常见案例和使用场景。

1. 为什么需要

因为一些原因需要取消原本正在执行的线程。我们举几个栗子:

  • 假设踢足球点球时,A队前4轮中了4个球,B队前4轮只中了2个球,此时胜负已分,第5轮这个点球就不用踢了,此时需要停止A队的线程和B队的线程(共5个点球)。
  • 假设用户从网络上下载一个50M的文件,如果网络很慢,用户等的不耐烦,点了取消下载操作,此时需要停止下载的线程。

2. 如何理解

中断可以理解为线程的一个内部标识位属性,它表示一个运行中的线程是否被其他线程进行了中断操作。

中断就是其他线程通过interrupt方法给该线程发一个信号,该线程收到信号后可以选择响应或不响应信号,中断它只是一种协商机制。响应信号通常会结束自身run()方法的执行退出线程,不响应则继续执行无任何影响。

线程通过检测自身是否被中断来进行响应,检测方法有2种,分别是public static boolean interrupted()public boolean isInterrupted(),静态的方法会清除中断标识位属性,非静态方法不会清除中断标识位属性。

3. 如何使用

3.1. 中断相关API

Thread源码中关于中断的方法:

public class Thread implements Runnable {
    
    // 仅仅只是设置中断状态
    public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }
    
	// 会清除中断状态
	public static boolean interrupted() {
		return currentThread().isInterrupted(true);
	}
    
	// 不会清除中断状态
	public boolean isInterrupted() {
		return isInterrupted(false);
	}
    
    private native void interrupt0();
    
    private native boolean isInterrupted(boolean ClearInterrupted);
}

绘制成表格:

API 作用
public void interrupt() 中断this线程
public static boolean interrupted() 测试当前线程是否被中断,且线程的中断状态会被清除
public boolean isInterrupted() 测试this线程是否中断。 中断状态不会被这个方法影响

特别需要注意的是:静态interrupted方法会清除线程的中断状态。

3.2. 中断正常状态下的线程

中断一个线程非常简单,只需要在其他线程中对目标线程调用interrupt()方法,目标线程需要反复检测自身状态是否是interrupted状态,如果是,就立刻结束运行。

public class Main extends Thread {
    @Override
    public void run() {
        int i = 0;
        while (!Thread.currentThread().isInterrupted()) {
            i++;
            System.out.println();
        }
        System.out.println("done");
    }
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Main();
        t.start();
        t.sleep(1000);
        t.interrupt();
    }
}

main线程通过调用t.interrupt()中断t线程。但需要注意,interrupt()方法仅仅是给t线程发送了"中断请求",只是把t线程的标识属性设置为中断,至于t线程是否响应或处理该中断请求则要看t线程的具体代码。

这里,t线程在while中循环检测中断请求(isInterrupted()),如果检测到线程被中断则跳出while循环结束线程的run()方法。

3.3. 中断特殊状态下的线程

特殊状态其实在官网Thread中有好几种,这里只列举最常见的一种

如果线程

  • 被阻塞在Object类的wait()wait(long)wait(long, int)方法
  • 或被阻塞在Thread类的join()join(long)join(long, int)sleep(long)sleep(long, int)

如果此时调用线程的中断方法interrupt(),线程的中断状态会被清除且抛出InterruptedException异常。这种情况的一般处理方法是:捕获InterruptedException异常,然后重新设置中断状态,即调用Thread.currentThread().interrupt();

我们来看示例代码:

public class Main extends Thread {
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                // 模拟任务代码
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // ... 清理操作
                System.out.println(isInterrupted());//false
                // 重设中断标志位为true
                Thread.currentThread().interrupt();
            }
        }
        System.out.println(isInterrupted());//true
    }

    public static void main(String[] args) {
        Main t = new Main();
        t.start();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
        t.interrupt();
    }
}

4. 如何安全的停止线程

前面提到中断状态只是线程的一个标识位,而中断操作是一种简便的线程间交互方式,而这种交互方式最适合用来取消或停止任务。除了中断以外,还可以利用一个boolean变量来控制是否需要停止任务并终止该线程。 一般的,可以用以下方式来安全的停止线程:

  • 中断方式
  • volatile修饰的boolean变量方式

代码举例如下:

public class Shutdown {
	public static void main(String[] args) throws Exception {
		Runner one = new Runner();
		Thread t = new Thread(one, "t");
		t.start();
		// 睡眠1秒,让Runner线程充分运行,随后能够感知中断而结束
		TimeUnit.SECONDS.sleep(1);
		t.interrupt();
        
        
		Runner two = new Runner();
		t = new Thread(two, "t");
		t.start();
		// 睡眠1秒,让Runner线程充分运行,随后能够感知on为false而结束
		TimeUnit.SECONDS.sleep(1);
		two.cancel();
	}

	private static class Runner implements Runnable {
		private long i;

		private volatile boolean on = true;

		@Override
		public void run() {
			while (on && !Thread.currentThread().isInterrupted()) {
				i++;
			}
			System.out.println("Count i = " + i);
		}

		public void cancel() {
			on = false;
		}
	}
}

5. 参考资料

《Java并发编程的艺术》,可联系作者无套路下载可复制的电子版pdf书籍

官方Thread类的源码和注释文章来源地址https://www.toymoban.com/news/detail-643031.html

到了这里,关于【Java并发编程】线程中断机制(辅以常见案例)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Java 并发编程】一文读懂线程、协程、守护线程

    在 Java 线程的生命周期一文中提到了 就绪状态的线程在获得 CPU 时间片后变为运行中状态 ,否则就会在 可运行状态 或者 阻塞状态 ,那么系统是如何分配线程时间片以及实现线程的调度的呢?下面我们就来讲讲线程的调度策略。 线程调度是指系统为线程分配 CPU 执行时间片

    2023年04月08日
    浏览(44)
  • Java面试_并发编程_线程基础

    进程是正在运行程序的实例, 进程中包含了线程, 每个线程执行不同的任务 不同的进程使用不同的内存空间, 在当前进程下的所有线程可以共享内存空间 线程更轻量, 线程上下文切换成本一般上要比进程上下文切换低(上下文切换指的是从一个线程切换到另一个线程) 并发是单个

    2024年02月07日
    浏览(40)
  • 【Java并发编程】变量的线程安全分析

    1.成员变量和静态变量是否线程安全? 如果他们没有共享,则线程安全 如果被共享: 只有读操作,则线程安全 有写操作,则这段代码是临界区,需要考虑线程安全 2.局部变量是否线程安全 局部变量是线程安全的 当局部变量引用的对象则未必 如果给i对象没有逃离方法的作用

    2024年02月08日
    浏览(36)
  • 【Java 并发编程】Java 线程本地变量 ThreadLocal 详解

    先一起看一下 ThreadLocal 类的官方解释: 用大白话翻译过来,大体的意思是: ThreadLoal 提供给了 线程局部变量 。同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本。这里有几点需要注意: 因为每个 Thread 内有自己的实例副本,且 该副本只能由当前 Thread 使用 。

    2024年02月04日
    浏览(44)
  • java并发编程:多线程基础知识介绍

    最初的计算机只能接受一些特定的指令,用户每输入一个指令,计算机就做出一个操作。当用户在思考或者输入时,计算机就在等待。这样效率非常低下,在很多时候,计算机都处在等待状态。 后来有了 批处理操作系统 ,把一系列需要操作的指令写下来,形成一个清单,一次

    2024年02月07日
    浏览(36)
  • java语法(二)线程并发、Juit单元测试、反射机制、注解、动态代理、XML解析、JVM

    正则表达式验证网站 1、 ? :表示前边这个字符可以出现0次或者1次。例如下边 /used? 既可以匹配 use 也可以匹配 used 。 2、 * :匹配0个或者多个字符, * 号代表前边这个字符可以出现0次或者多次。例如 /ab*c 可以匹配 ac、abc、abbbbc 3、 + :与 * 号不同的是, + 需要前面这个字符

    2024年02月06日
    浏览(30)
  • Java并发编程第6讲——线程池(万字详解)

    Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池,本篇文章就详细介绍一下。 定义:线程池是一种用于管理和重用线程的技术(池化技术),它主要用于提高多线程应用程序的性能和效率。 ps:线程池、连接池、内存池

    2024年02月11日
    浏览(26)
  • Java并发编程学习18-线程池的使用(下)

    上篇介绍了 ThreadPoolExecutor 配置和扩展相关的信息,本篇开始将介绍递归算法的并行化。 还记得我们在《Java并发编程学习11-任务执行演示》中,对页面绘制程序进行一系列改进,这些改进大大地提供了页面绘制的并行性。 我们简单回顾下相关的改进过程: 第一次新增时,页

    2024年02月12日
    浏览(26)
  • Java并发编程学习16-线程池的使用(中)

    上篇分析了在使用任务执行框架时需要注意的各种情况,并简单介绍了如何正确调整线程池大小。 本篇将继续介绍对线程池进行配置与调优的一些方法,详细如下: ThreadPoolExecutor 为 Executors 中的 newCachedThreadPool 、 newFixedThreadPool 和 newScheduledThreadExecutor 等工厂方法返回的 Exe

    2024年02月10日
    浏览(28)
  • Java并发编程学习笔记(一)线程的入门与创建

    认识 程序由指令和数据组成,简单来说,进程可以视为程序的一个实例 大部分程序可以同时运行多个实例进程,例如记事本、画图、浏览器等 少部分程序只能同时运行一个实例进程,例如QQ音乐、网易云音乐等 一个进程可以分为多个线程,线程为最小调度单位,进程则是作

    2024年02月16日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包