Java并发框架——AQS中断的支持

本文涉及的产品
图片翻译,图片翻译 100张
语种识别,语种识别 100万字符
文档翻译,文档翻译 1千页
简介: 线程的定义给我们提供了并发执行多个任务的方式,大多数情况下我们会让每个任务都自行执行结束,这样能保证事务的一致性,但是有时我们希望在任务执行中取消任务,使线程停止。

线程的定义给我们提供了并发执行多个任务的方式,大多数情况下我们会让每个任务都自行执行结束,这样能保证事务的一致性,但是有时我们希望在任务执行中取消任务,使线程停止。在java中要让线程安全、快速、可靠地停下来并不是一件容易的事,java也没有提供任何可靠的方法终止线程的执行。回到第六小节,线程调度策略中有抢占式和协作式两个概念,与之类似的是中断机制也有协作式和抢占式。

历史上Java曾经使用stop()方法终止线程的运行,他们属于抢占式中断。但它引来了很多问题,早已被JDK弃用。调用stop()方法则意味着①将释放该线程所持的所有锁,而且锁的释放不可控。②即刻将抛出ThreadDeath异常,不管程序运行到哪里,但它不总是有效,如果存在被终止线程的锁竞争;第一点将导致数据一致性问题,这个很好理解,一般数据加锁就是为了保护数据的一致性,而线程停止伴随所持锁的释放,很可能导致被保护的数据呈现不一致性,最终导致程序运算出现错误。第二点比较模糊,它要说明的问题就是可能存在某种情况stop()方法不能及时终止线程,甚至可能终止不了线程。看如下代码会发生什么情况,看起来线程mt因为执行了stop()方法将停止,按理来说就算execut方法是一个死循环,只要执行了stop()方法线程将结束,无限循环也将结束。其实不然,因为我们在execute方法使用了synchronized修饰,同步方法表示在执行execute时将对mt对象进行加锁,另外,Thread的stop()方法也是同步的,于是在调用mt线程的stop()方法前必须获取mt对象锁,但mt对象锁被execute方法占用,且不释放,于是stop()方法永远获取不了mt对象锁,最后得到一个结论,使用stop()方法停止线程不可靠,它未必总能有效终止线程。

 

public class ThreadStop {

public static voidmain(String[] args) {

           Thread mt= new MyThread();

           mt.start();

           try {

                    Thread.currentThread().sleep(100);

           } catch(InterruptedException e) {

                    e.printStackTrace();

           }

           mt.stop();

}

 

static classMyThread extends Thread {

           publicvoid run() {

                    execute();

           }

           privatesynchronized void execute() {

                    while(true) {

                    }

           }

}

}

 

经历了很长时间的发展,Java最终选择用一种协作式的中断机制实现中断。协作式中断的原理很简单,其核心是先对中断标识进行标记,某线程设置某线程的中断标识位,被标记了中断位的线程在适当的时间节点会抛出异常,捕获异常后做相应的处理。实现协作中断有三个要点需要考虑:①是在Java层面实现轮询中断标识还是在JVM中实现;②轮询的颗粒度的控制,一般颗粒度要尽量小周期尽量短以保证响应的及时性;③轮询的时间节点的选择,其实就是在哪些方法里面轮询,例如JVM将Thread类的wait()、sleep()、join()等方法都实现中断标识的轮询操作。

中断标识放在哪里?中断是针对线程实例而言,从Java层面上看,标识变量放到线程中肯定再合适不过了,但由于由JVM维护,所以中断标识具体由本地方法维护。在Java层面仅仅留下几个API用于操作中断标识,如下,

public class Thread{

    public voidinterrupt() {……}

    public BooleanisInterrupted() {……}

    public static Booleaninterrupted() {……}

}

上面三个方法依次用于设置线程为中断状态、判断线程状态是否中断、清除当前线程中断状态并返回它之前的值。通过interrupt()方法设置中断标识,假如在非阻塞线程则仅仅只是改变了中断状态,线程将继续往下运行,但假如在可取消阻塞线程中,如正在执行sleep()、wait()、join()等方法的线程则会因为被设置了中断状态而抛出InterruptedException异常,程序对此异常捕获处理。

上面提到的三个要点,第一是轮询在哪个层面实现,这个没有特别的要求,在实际中只要不出现逻辑问题,在Java层面或JVM层面实现都是可以的,例如常用的线程睡眠、等待等操作是通过JVM实现,而AQS框架里面的中断则放到Java实现,不管在哪个层面上去实现,在轮询过程中都一定要能保证不会产生阻塞。第二是要保证轮询的颗粒度尽可能的小周期尽可能短,这关系到中断响应的速度。第三点是关于轮询的时间节点的选取。

针对三要点来看看AQS框架中是如何支持中断的,主要在等待获取锁的过程中提供中断操作,下面是伪代码。只需增加加红加粗部分逻辑即可实现中断支持,在循环体中每次循环都对当前线程中断标识位进行判断,一旦检查到线程被标记为中断则抛出InterruptedException异常,高层代码对此异常捕获处理即完成中断处理。总结起来就是ASQ框架获取锁的中断机制是在Java层面实现的,轮询时间节点选择在不断做尝试获取锁操作过程中,每个循环的颗粒度比较小,响应速度得以保证,且循环过程不存在阻塞风险,保证中断检测不会失效。

if(尝试获取锁失败) {

    创建node

    使用CAS方式把node插入到队列尾部

    while(true){

    if(尝试获取锁成功并且 node的前驱节点为头节点){

把当前节点设置为头节点

    跳出循环

}else{

    使用CAS方式修改node前驱节点的waitStatus标识为signal

    if(修改成功){

        挂起当前线程

        if(当前线程中断位标识为true)

           抛出InterruptedException异常

}

}

}

 

判断线程是否处于中断状态其实很简单,只需使用Thread.interrupted()操作,如果为true则说明线程处于中断位,并清除中断位。至此AQS实现了支持中断的获取锁操作。

此节从java发展过程分析了抢占式中断及协作式中断,由于抢占式存在一些缺陷现在已不推荐使用,而协作式中断作为推荐做法,尽管在响应时间较长,但其具有无可比拟的优势。协作式中断我们可以在JVM层面实现,同样也可以在Java层面实现,例如AQS框架的中断即是在Java层面实现,不过如果继续深究是因为Java留了几个API供我们操作线程的中断标识位,这才使Java层面实现中断操作得以实现。对于java的协作式中断机制有人肯定有人批评,批评者说java没有抢占式中断机制,且协作式中断机制迫使开发者必须维护中断状态,迫使开发者必须处理InterruptedException。但肯定者则认为,虽然协作式中断机制推迟了中断请求的处理,但它为开发人员提供更灵活的中断处理策略,响应性可能不及抢占式,但程序健壮性更强。



点击订购作者书籍《Tomcat内核设计剖析》




目录
相关文章
|
13天前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
|
22天前
|
人工智能 前端开发 Java
基于开源框架Spring AI Alibaba快速构建Java应用
本文旨在帮助开发者快速掌握并应用 Spring AI Alibaba,提升基于 Java 的大模型应用开发效率和安全性。
基于开源框架Spring AI Alibaba快速构建Java应用
|
22天前
|
消息中间件 Java 数据库连接
Java 反射最全详解 ,框架设计必掌握!
本文详细解析Java反射机制,包括反射的概念、用途、实现原理及应用场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
Java 反射最全详解 ,框架设计必掌握!
|
18天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
23天前
|
Java 数据库连接 数据库
如何构建高效稳定的Java数据库连接池,涵盖连接池配置、并发控制和异常处理等方面
本文介绍了如何构建高效稳定的Java数据库连接池,涵盖连接池配置、并发控制和异常处理等方面。通过合理配置初始连接数、最大连接数和空闲连接超时时间,确保系统性能和稳定性。文章还探讨了同步阻塞、异步回调和信号量等并发控制策略,并提供了异常处理的最佳实践。最后,给出了一个简单的连接池示例代码,并推荐使用成熟的连接池框架(如HikariCP、C3P0)以简化开发。
45 2
|
28天前
|
SQL Java 关系型数据库
java连接mysql查询数据(基础版,无框架)
【10月更文挑战第12天】该示例展示了如何使用Java通过JDBC连接MySQL数据库并查询数据。首先在项目中引入`mysql-connector-java`依赖,然后通过`JdbcUtil`类中的`main`方法实现数据库连接、执行SQL查询及结果处理,最后关闭相关资源。
|
24天前
|
缓存 Java 数据库连接
Hibernate:Java持久层框架的高效应用
通过上述步骤,可以在Java项目中高效应用Hibernate框架,实现对关系数据库的透明持久化管理。Hibernate提供的强大功能和灵活配置,使得开发者能够专注于业务逻辑的实现,而不必过多关注底层数据库操作。
13 1
|
17天前
|
存储 Java 开发者
Java中的集合框架深入解析
【10月更文挑战第32天】本文旨在为读者揭开Java集合框架的神秘面纱,通过深入浅出的方式介绍其内部结构与运作机制。我们将从集合框架的设计哲学出发,探讨其如何影响我们的编程实践,并配以代码示例,展示如何在真实场景中应用这些知识。无论你是Java新手还是资深开发者,这篇文章都将为你提供新的视角和实用技巧。
14 0
|
SQL Java 数据库连接
Java面试题日积月累(SSM框架面试题22道)
Java面试题日积月累(SSM框架面试题22道)
90 0
|
4月前
|
设计模式 存储 安全
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
62 1
下一篇
无影云桌面