(非原创)
中断
JAVA中并没有好的中断线程的方式,早期引入的Thead.stop()和Thread.resume()容易导致死锁(参考:http://docs.oracle.com/javase/6/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html),已经不推荐使用。
JAVA线程提供了协作式中断,何为协作是中断,是相对抢占式中断而言的,简单来讲就是设置一个中断标志位,不停地检查这个标志位的状态,如果检查到线程中断,就中断线程。JVM线程内部维护着一个中断标志,程序员不能直接操作这个中断标志位,只能通过线程的以下几个方法设置中断位:
- public void interrupt()
- public static boolean interrupted()
- private native boolean isInterrupted(boolean ClearInterrupted);
- public boolean isInterrupted()
AQS中提供了支持中断的方法
- private void doAcquireInterruptibly(int arg) throws InterruptedException;
- private void doAcquireSharedInterruptibly(int arg) throws InterruptedException;
- private boolean doAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException;
这几个方法都抛出了InterruptedException,这些方法都会先出处中断异常,处理的代码如下:
- if (Thread.interrupted())
- throw new InterruptedException();
我们还看到有些方法并没有申请抛出InterruptedException,当它被中断时,设置了线程的中断位。
- private static void selfInterrupt() {
- Thread.currentThread().interrupt();
- }
超时
AQS与JVM内置锁的一个不同点在于AQS中提供了超时机制,即线程在等待一定时间后会立即返回。下面以doAcquireNanos为例来分析:
- private boolean doAcquireNanos(int arg, long nanosTimeout)
- throws InterruptedException {
- long lastTime = System.nanoTime();
- final Node node = addWaiter(Node.EXCLUSIVE);
- boolean failed = true;
- try {
- for (;;) {
- final Node p = node.predecessor();
- if (p == head && tryAcquire(arg)) {
- setHead(node);
- p.next = null; // help GC
- failed = false;
- return true;
- }
- if (nanosTimeout <= 0)
- return false;
- if (shouldParkAfterFailedAcquire(p, node) &&
- nanosTimeout > spinForTimeoutThreshold)
- LockSupport.parkNanos(this, nanosTimeout);
- long now = System.nanoTime();
- nanosTimeout -= now - lastTime;
- lastTime = now;
- if (Thread.interrupted())
- throw new InterruptedException();
- }
- } finally {
- if (failed)
- cancelAcquire(node);
- }
- }
2、如果剩余时间>0,就用总时间减去一次循环耗费的时间,继续阻塞;
3、如果在这期间线程被中断,就抛出中断异常,如果有其他异常产生,就取消这次获取。
取消
取消获取的逻辑比较复杂,下面来分析一下:
- private void cancelAcquire(Node node) {
- // Ignore if node doesn't exist
- if (node == null)
- return;
- node.thread = null;
- // Skip cancelled predecessors
- Node pred = node.prev;
- while (pred.waitStatus > 0)
- node.prev = pred = pred.prev;
- // predNext is the apparent node to unsplice. CASes below will
- // fail if not, in which case, we lost race vs another cancel
- // or signal, so no further action is necessary.
- Node predNext = pred.next;
- // Can use unconditional write instead of CAS here.
- // After this atomic step, other Nodes can skip past us.
- // Before, we are free of interference from other threads.
- node.waitStatus = Node.CANCELLED;
- // If we are the tail, remove ourselves.
- if (node == tail && compareAndSetTail(node, pred)) {
- compareAndSetNext(pred, predNext, null);
- } else {
- // If successor needs signal, try to set pred's next-link
- // so it will get one. Otherwise wake it up to propagate.
- int ws;
- if (pred != head &&
- ((ws = pred.waitStatus) == Node.SIGNAL ||
- (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
- pred.thread != null) {
- Node next = node.next;
- if (next != null && next.waitStatus <= 0)
- compareAndSetNext(pred, predNext, next);
- } else {
- unparkSuccessor(node);
- }
- node.next = node; // help GC
- }
- }
2、将当前结点设置为取消状态;
3、如果当前结点是队尾结点,则将当前结点从队尾移除;否则执行4;
4、找到当前结点的继任结点,前趋的next指针指向继任结点(pred->next=current->next);
5、当前结点的next指针指向自己,前面提到这一方面为了回收,一方面为了使isOnSyncQueue方法简单。
其他
AQS还提供了一些线程监控的方法:
- //获取哪些线程在等待
- protected final Collection<Thread> getWaitingThreads();
- //获取等待队列的长度
- protected final int getWaitQueueLength();
- //是否有线程在等待
- protected final boolean hasWaiters()
- //是否拥有同步器
- final boolean isOwnedBy(AbstractQueuedSynchronizer sync)
- //是否在同步队列中
- final boolean isOnSyncQueue(Node node)
- //支持共享模式的线程
- public final Collection<Thread> getSharedQueuedThreads()
- //支持独占模式的线程
- public final Collection<Thread> getExclusiveQueuedThreads();
参考资料:
相关推荐
本文档主要内容如下: 1、线程安全和锁 Synchronized 底层实现原理 2、可重入锁与 Synchronized 的其他特性 3、ThreadLocal 的底层实现与使用 ...11、AQS源码分析 12、CAS原理分析和使用场景 13、.....
Java 并发编程中的 JUC(java.util.concurrent)库以及其核心组件 AQS(AbstractQueuedSynchronizer)在构建高性能、可伸缩性的多线程应用方面具有重要的地位。 AQS 是 JUC 中的核心组件,它提供了一个框架,让...
Java并发编程解析 | 解析AQS基础同步器的设计与实现
java锁底层实现,AQS源码分析。我在公司内部分享写的,如果想进一步了解,可以私聊
课程内容包括了JAVA手写线程池,UC线程池API详解,线程安全根因详解,锁与原子类,分布式锁原理与实现方式,并发编程-AQS等等针对性非常强的JAVA编程开发教程,这其中的内容对JAVA开发技能的拔尖,非常的有帮助。...
JAVA并发编程与高并发解决方案-并发编程四之J.U.C之AQS.docx
课程内容包括了JAVA手写线程池,UC线程池API详解,线程安全根因详解,锁与原子类,分布式锁原理与实现方式,并发编程-AQS等等针对性非常强的JAVA编程开发教程,这其中的内容对JAVA开发技能的拔尖,非常的有帮助。...
线程的初始化,中断以及其源码讲解.mp4 多种创建线程的方式案例演示(一)带返回值的方式.mp4 多种创建线程的方式案例演示(二)使用线程池.mp4 Spring对并发的支持:Spring的异步任务.mp4 使用jdk8提供的lambda进行...
第4节学习并发的四个阶段并推荐学习并发的资料 [免费观看] 00:09:13分钟 | 第5节线程的状态以及各状态之间的转换详解00:21:56分钟 | 第6节线程的初始化,中断以及其源码讲解00:21:26分钟 | 第7节多种创建线程的...
Java并发之AQS详解.pdf
第4节学习并发的四个阶段并推荐学习并发的资料 [免费观看] 00:09:13分钟 | 第5节线程的状态以及各状态之间的转换详解00:21:56分钟 | 第6节线程的初始化,中断以及其源码讲解00:21:26分钟 | 第7节多种创建线程的...
超全的多线程与高并发的编程笔记,从JVM&JMM角度讲多线程,synchronized优化原理,AQS和线程池等等,需要的童鞋请自行下载!
aqs-并发编程笔记.pdf
第4节学习并发的四个阶段并推荐学习并发的资料 [免费观看] 00:09:13分钟 | 第5节线程的状态以及各状态之间的转换详解00:21:56分钟 | 第6节线程的初始化,中断以及其源码讲解00:21:26分钟 | 第7节多种创建线程的...
java并发编程 基础知识,守护线程与线程, 并行和并发有什么区别? 什么是上下文切换? 线程和进程区别 什么是线程和进程? 创建线程有哪几种方式?,如何避免线程死锁 线程的 run()和 start()有什么区别? 什么是 ...
Java并发编程实战 本书深入浅出地介绍了Java线程和并发,是一本完美的Java并发参考手册。书中从并发性和线程安全性的基本概念出发,介绍了如何使用类库提供的基本并发构建块,用于避免并发危险、构造线程安全的类及...
java并发包详解,condition重入锁;Semaphore信号量;ReadWriteLock读写锁;CountDownLatch计时器;CyclicBarrier循环栅栏; 重⼊锁可以完全替代synchronized关键字。在JDK5.0的早期版本中,重⼊锁的性能远远好于 ...
20.9.24aqs-并发编程笔记.pdf
基本概念:介绍并发编程的基本概念,如进程、线程、并行与并发的区别等。 同步机制:讲解如何使用锁、信号量、原子操作等同步机制来避免竞态条件和死锁。 并发模型:介绍不同的并发模型,如生产者-消费者模型、管道...