Java并发包源码学习之AQS框架(三)LockSupport和interrupt
接着上一篇文章今天我们来介绍下LockSupport
和Java中线程的中断(interrupt)
。
其实除了LockSupport,Java之初就有Object
对象的wait和notify方法可以实现线程的阻塞和唤醒。那么它们的区别 是什么呢?
主要的区别应该说是它们面向的对象不同。阻塞和唤醒是对于线程来说的,LockSupport的park/unpark更符合这个语义,以“线程”作为方法的参数, 语义更清晰,使用起来也更方便。而wait/notify的实现使得“线程”的阻塞/唤醒对线程本身来说是被动的,要准确的控制哪个线程、什么时候阻塞/唤醒很困难, 要不随机唤醒一个线程(notify)要不唤醒所有的(notifyAll)。
wait/notify
最典型的例子应该就是生产者/消费者了:
class BoundedBuffer1 {private int contents;final Object[] items = new Object[100];int putptr, takeptr, count;public synchronized void put(Object x) {while (count == items.length) {try {wait();} catch (InterruptedException e) {}}items[putptr] = x;if (++putptr == items.length)putptr = 0;++count;notifyAll();}public synchronized Object take() {while (count == 0) {try {wait();} catch (InterruptedException e) {}}Object x = items[takeptr];if (++takeptr == items.length)takeptr = 0;--count;notifyAll();return x;}public static class Producer implements Runnable {private BoundedBuffer1 q;Producer(BoundedBuffer1 q) {this.q = q;new Thread(this, "Producer").start();}int i = 0;public void run() {int i = 0;while (true) {q.put(i++);}}}public static class Consumer implements Runnable {private BoundedBuffer1 q;Consumer(BoundedBuffer1 q) {this.q = q;new Thread(this, "Consumer").start();}public void run() {while (true) {System.out.println(q.take());}}}public static void main(String[] args) throws InterruptedException {final BoundedBuffer1 buffer = new BoundedBuffer1();new Thread(new Producer(buffer)).start();new Thread(new Consumer(buffer)).start();} }
View Code
上面的例子中有一点需要知道,在调用对象的wait
之前当前线程必须先获得该对象的监视器(synchronized
),被唤醒之后需要重新获取到监视器才能继续执行。
//wait会先释放当前线程拥有的监视器 obj.wait(); //会re-acquire监视器
而LockSupport
并不需要获取对象的监视器。LockSupport机制是每次unpark
给线程1个“许可”——最多只能是1,而park
则相反,如果当前 线程有许可,那么park方法会消耗1个并返回,否则会阻塞线程直到线程重新获得许可,在线程启动之前调用park/unpark
方法没有任何效果。
// 1次unpark给线程1个许可 LockSupport.unpark(Thread.currentThread()); // 如果线程非阻塞重复调用没有任何效果 LockSupport.unpark(Thread.currentThread()); // 消耗1个许可 LockSupport.park(Thread.currentThread()); // 阻塞 LockSupport.park(Thread.currentThread());
因为它们本身的实现机制不一样,所以它们之间没有交集,也就是说LockSupport阻塞的线程,notify/notifyAll没法唤醒。
实际上现在很少能看到直接用wait/notify的代码了,即使生产者/消费者也基本都会用Lock
和Condition
来实现,我会在后面《Java并发包源码学习之AQS框架(五)ConditionObject源码分析》 文章中再回头看这个例子。
总结下LockSupport
的park/unpark
和Object
的wait/notify
:
- 面向的对象不同;
- 跟Object的wait/notify不同LockSupport的park/unpark不需要获取对象的监视器;
- 实现的机制不同,因此两者没有交集。
虽然两者用法不同,但是有一点,LockSupport
的park和Object的wait一样也能响应中断。
public static void main(String[] args) throws InterruptedException {final Thread t = new Thread(new Runnable() {@Overridepublic void run() {LockSupport.park();System.out.println("thread " + Thread.currentThread().getId() + " awake!");}});t.start();Thread.sleep(3000);// 2. 中断 t.interrupt(); }
thread 9 awake!
在我之前的一篇博客“如何正确停止一个线程”有介绍过Thread.interrupt()
Thread.interrupt()方法不会中断一个正在运行的线程。这一方法实际上完成的是,在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态。更确切的说,如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,那么,它将接收到一个中断异常(InterruptedException),从而提早地终结被阻塞状态。
LockSupport.park()
也能响应中断信号,但是跟Thread.sleep()
不同的是它不会抛出InterruptedException
, 那怎么知道线程是被unpark还是被中断的呢,这就依赖线程的interrupted status,如果线程是被中断退出阻塞的那么该值被设置为true, 通过Thread的interrupted
和isInterrupted
方法都能获取该值,两个方法的区别是interrupted
获取后会Clear,也就是将interrupted status重新置为false。
AQS和Java线程池中都大量用到了中断,主要的作用是唤醒线程、取消任务和清理(如ThreadPoolExecutor的shutdown方法),AQS中的acquire
方法也有中断和不可中断两种。 其中对于InterruptedException
如何处理最重要的一个原则就是Don't swallow interrupts,一般两种方法:
- 继续设置interrupted status
- 抛出新的InterruptedException
try {……… } catch (InterruptedException e) {// Restore the interrupted status Thread.currentThread().interrupt();// or thow a new//throw new InterruptedException(); }
AQS的acquire
就用到了第一种方法。
关于InterruptedException
处理的最佳实践可以看IBM的这篇文章。
最后按照惯例做下引申。上面BoundedBuffer1
类的put
和take
方法中的wait为什么要放在一个while循环里呢? 你如果去看Object.wait()
方法的Javadoc的话会发现官方也是建议下面这样的用法:
synchronized (obj) {while (<condition does not hold>)……obj.wait();…… }
StackOverflow上有一个问题里一个叫xagyg的回答解释的比较清楚,有兴趣的可以看下。 简单来说因为:
wait前会释放监视器,被唤醒后又要重新获取,这瞬间可能有其他线程刚好先获取到了监视器,从而导致状态发生了变化, 这时候用while循环来再判断一下条件(比如队列是否为空)来避免不必要或有问题的操作。 这种机制还可以用来处理伪唤醒(spurious wakeup),所谓伪唤醒就是no reason wakeup
,对于LockSupport.park()
来说就是除了unpark
和interrupt
之外的原因。
LockSupport
也会有同样的问题,所以看AQS的源码会发现很多地方都有这种re-check的思路,我们下一篇文就来看下AbstractQueuedSynchronizer
类的源码。
转载于:https://www.cnblogs.com/zhanjindong/p/java-concurrent-package-aqs-locksupport-and-thread-interrupt.html
Java并发包源码学习之AQS框架(三)LockSupport和interrupt相关推荐
- aqs clh java_Java并发包源码学习之AQS框架(二)CLH lock queue和自旋锁
上一篇文章提到AQS是基于CLH lock queue,那么什么是CLH lock queue,说复杂很复杂说简单也简单, 所谓大道至简: CLH lock queue其实就是一个FIFO的队列,队列 ...
- Java并发包源码学习系列:同步组件CountDownLatch源码解析
文章目录 CountDownLatch概述 使用案例与基本思路 类图与基本结构 void await() boolean await(long timeout, TimeUnit unit) void ...
- Java并发包源码学习系列:AbstractQueuedSynchronizer
文章目录 本篇学习目标 AQS概述 AbstractOwnableSynchronizer 同步队列与Node节点 同步状态state 重要方法分析 独占式获取与释放同步状态 共享式获取与释放同步状态 ...
- Java并发包源码学习系列:LBD双端阻塞队列源码解析
尝试将节点加入到first之前,更新first,如果插入之后超出容量,返回false. private boolean linkFirst(Node node) { // assert lock.is ...
- 深入java并发包源码(三)AQS独占方法源码分析
深入java并发包源码(一)简介 深入java并发包源码(二)AQS的介绍与使用 深入java并发包源码(三)AQS独占方法源码分析 AQS 的实现原理 学完用 AQS 自定义一个锁以后,我们可以来看 ...
- JAVA小项目实例源码—学习娱乐小助手
代码地址如下: http://www.demodashi.com/demo/11456.html 一.程序实现 项目目录: MyJFrame:实现项目界面样式: AppProcess:实现调用api或 ...
- Java 中 Integer 源码学习之缓存池了解
Java 中 Integer 源码学习之缓存池了解 面试题 new Integer(123) 与 Integer.valueOf(123) 的区别? new Integer(123) 每次都会新建一个 ...
- 通过weui-1.1.3源码学习前端开发(三)从example看起-src/index.html文件
通过weui-1.1.3源码学习前端开发(三)从example看起 前面已经看过了gulp的构建文件gulpfile.js,下面我们就从weui提供的一个example开始看看吧 先看看src/exa ...
- Java IO流源码学习之二(Buffered字节流)
本文记录BufferedInputStream,BufferedOutputStream两个带缓冲的输入输出流. BufferedInputStream继承于FilterInputStream,Buf ...
最新文章
- 页面可视化搭建工具前生今世
- 权限柜作用_超市条码寄存柜使用要点
- python改变turtle画笔方向的函数_哪个选项不能改变turtle画笔的运行方向?
- linux io函数,unix/Linux低级IO函数的用法有哪些? 爱问知识人
- PostgreSQL 9.5 pg_dump新特性 你是我的眼
- 静态页面放图片及居中
- php 到处excel 乱码,php导出Excel乱码怎么解决?
- cad打印去掉边框_CAD打印的时候如何去掉打印线框?
- linux蓝牙打印机驱动安装失败怎么办,打印机驱动安装失败怎么办?
- 华为云产品介绍—大数据
- 1400张正方形图片合集压缩包图片分辨率330*330
- 毕业论文开题报告怎么写
- Android 登录3D翻转动画效果
- 【报告分享】2020吃货人群图鉴-Mob研究院(附下载)
- 2022年软件评测师真题
- (株)FAU艾芙优连续五年荣获“韩国消费者评价第一”
- 进度记录【Day 12-13】Oracle索引、视图、函数、程序、异常处理、存储过程、事务
- unity塔防游戏怪物转向_野生防御塔游戏下载-野生防御塔游戏安卓版 v1.0
- 清理mstsc远程桌面连接的记录
- 源码/反码/补码 (转)
热门文章
- ASP.NET学习顺序(转摘)
- vue vuex 挂载_GitHub - BingoVue/vuex: 用Vue实现简版vuex
- docker执行容器内的shell_为什么不建议把数据库部署在docker容器内?
- php header详解
- .Net QQ互联教程 1
- 阅读笔记《梦断代码》其二
- 苹果电脑如何读写ntfs格式磁盘
- matlab故障识别,基于Matlab的电力系统故障分析与仿真(V2.1)最新版
- 三维球体换算到二维_三维制图讲义04 - 基础几何体
- word类型得实参和lpwstr类型的形参不兼容_前端测试题:(解析)关于JavaScript的数据类型,下面说法错误的是?...