Java并发框架——AQS中断的支持
线程的定义给我们提供了并发执行多个任务的方式,大多数情况下我们会让每个任务都自行执行结束,这样能保证事务的一致性,但是有时我们希望在任务执行中取消任务,使线程停止。在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。但肯定者则认为,虽然协作式中断机制推迟了中断请求的处理,但它为开发人员提供更灵活的中断处理策略,响应性可能不及抢占式,但程序健壮性更强。
喜欢研究java的同学可以交个朋友,下面是本人的微信号:
Java并发框架——AQS中断的支持的更多相关文章
- Java并发框架??AQS中断的支持
线程的定义给我们提供了并发执行多个任务的方式,大多数情况下我们会让每个任务都自行执行结束,这样能保证事务的一致性,但是有时我们希望在任务执行中取消任务,使线程停止.在java中要让线程安全.快速.可靠 ...
- 深入理解Java并发框架AQS系列(一):线程
深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 一.概述 1.1.前言 重剑无锋,大巧不工 读j.u.c包下的源码,永远无法绕开的经典 ...
- 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念
深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 一.AQS框架简介 AQS诞生于Jdk1.5,在当时低效且功能单一的synchroni ...
- 深入理解Java并发框架AQS系列(四):共享锁(Shared Lock)
深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 深入理解Java并发框架AQS系列(三):独占锁(Exclusive Lock) 深入 ...
- 深入理解Java并发框架AQS系列(三):独占锁(Exclusive Lock)
一.前言 优秀的源码就在那里 经过了前面两章的铺垫,终于要切入正题了,本章也是整个AQS的核心之一 从本章开始,我们要精读AQS源码,在欣赏它的同时也要学会质疑它.当然本文不会带着大家逐行过源码(会有 ...
- Java并发框架——AQS超时机制
AQS框架提供的另外一个优秀机制是锁获取超时的支持,当大量线程对某一锁竞争时可能导致某些线程在很长一段时间都获取不了锁,在某些场景下可能希望如果线程在一段时间内不能成功获取锁就取消对该锁的等待以提高性 ...
- Java并发框架——AQS阻塞队列管理(三)——CLH锁改造
在CLH锁核心思想的影响下,Java并发包的基础框架AQS以CLH锁作为基础而设计,其中主要是考虑到CLH锁更容易实现取消与超时功能.比起原来的CLH锁已经做了很大的改造,主要从两方面进行了改造:节点 ...
- Java并发框架——AQS之如何使用AQS构建同步器
AQS的设计思想是通过继承的方式提供一个模板让大家可以很容易根据不同场景实现一个富有个性化的同步器.同步器的核心是要管理一个共享状态,通过对状态的控制即可以实现不同的锁机制.AQS的设计必须考虑把复杂 ...
- Java并发框架——AQS之原子性如何保证?
在研究AQS框架时,会发现这个类很多地方都使用了CAS操作,在并发实现中CAS操作必须具备原子性,而且是硬件级别的原子性,java被隔离在硬件之上,明显力不从心,这时为了能直接操作操作系统层面,肯定要 ...
随机推荐
- TensorFlow学习笔记(UTF-8 问题解决 UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte)
我使用VS2013 Python3.5 TensorFlow 1.3 的开发环境 UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff ...
- [HNOI2012]永无乡
题目描述 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达 ...
- Trie模版
struct Trie{ Trie* nxt[]; int v; Trie(){ ;i<;i++){ nxt[i]=NULL; } v=-; } void insert(char s[],int ...
- 51nod1295 XOR key(可持久化trie)
1295 XOR key题目来源: HackerRank基准时间限制:1.5 秒 空间限制:262144 KB 分值: 160 难度:6级算法题 给出一个长度为N的正整数数组A,再给出Q个查询,每个查 ...
- hihocoder 1249(2015ACM/ICPC北京)
题意: 给你一块正方形的土地,里面有矩形的草地,要求把土地分成两份,满足以下两个条件 1.两边的绿洲,左边>=右边,差值尽可能的小 2.在满足1的情况下分给左边的土地尽快能的多 而且绿洲不会出现 ...
- bzoj3223Tyvj 1729 文艺平衡树 splay
3223: Tyvj 1729 文艺平衡树 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 5644 Solved: 3362[Submit][Sta ...
- bzoj2434阿狸的自动机
转载自 http://www.cnblogs.com/zj75211/p/6934976.html ●BZOJ 2434: [Noi2011]阿狸的打字机 ●赘述题目 (题意就不赘述了) ●解法: ...
- c++中成员函数的参数名与成员变量名重合的问题
有一天写类的时候突然想到了这个问题,下面就来介绍如何解决这个问题. 定义一个类: class test{ public: void setnum(); void getnum(); private: ...
- PTA 社交网络图中结点的“重要性”计算(30 分)
7-12 社交网络图中结点的“重要性”计算(30 分) 在社交网络中,个人或单位(结点)之间通过某些关系(边)联系起来.他们受到这些关系的影响,这种影响可以理解为网络中相互连接的结点之间蔓延的一种相互 ...
- Linux学习之CentOS(九)-----文件与目录的默认权限与隐藏权限
文件与目录的默认权限与隐藏权限 一个文件有若干个属性, 包括读写运行(r, w, x)等基本权限,及是否为目录 (d) 与文件 (-) 或者是连结档 (l) 等等的属性! 要修改属性的方法在前面也约略 ...