深入理解 JUC:PriorityBlockingQueue

优先级队列 PriorityQueue 应该是大家都比较熟悉的一个集合组件,本文将要介绍的 PriorityBlockingQueue 是 PriorityQueue 的线程安全版本。PriorityBlockingQueue 底层依赖于数组作为存储结构,最大容量上限是 Integer.MAX_VALUE - 8,所以几乎可以将其视为无界的。同 PriorityQueue 一样,PriorityBlockingQueue 同样引入了堆数据结构来编排队列元素的优先级,默认使用最小堆结构。

阅读全文

深入理解 JUC:LinkedBlockingQueue

上一篇我们分析了无界非阻塞线程安全队列 ConcurrentLinkedQueue 的设计与实现,本篇我们继续分析线程安全队列 LinkedBlockingQueue 的实现机制。由 Blocking 字样可以推断出 LinkedBlockingQueue 是阻塞队列,前面我们介绍过阻塞队列和非阻塞队列在实现上的区别,知道阻塞队列一般是基于锁机制来保证线程安全,本文我们就一起来分析一下 LinkedBlockingQueue 是如何基于锁构建线程安全队列的。

阅读全文

深入理解 JUC:ConcurrentLinkedQueue

ConcurrentLinkedQueue 是线程安全的无界非阻塞队列。在 JUC 包中,线程安全的队列按照实现方式可以分为阻塞队列和非阻塞队列两大类,前者基于锁来保证线程安全,而后者则基于 CAS 机制保证线程安全,阻塞队列一般在类名中都带有 Blocking 的字样。

阅读全文

深入理解 JUC:CopyOnWriteArrayList

CopyOnWriteArrayList 是线程安全的 List 实现,底层依赖于数组作为存储结构,并基于 写时复制(CoW: Copy-on-Write)机制 保证线程安全性。CopyOnWriteArrayList 在执行修改操作时会将底层数组复制一份,并在副本上实施修改,最后再更新回底层数组。虽然这样的实现比较消耗内存,但却带来了较高的执行效率,属于拿空间换时间。

阅读全文

深入理解 JUC:Semaphore

前面我们分析了同步器 CountDownLatch 和 CyclicBarrier 的设计和实现,这两个同步器在使用上都有一个共同的特点,就是在构造时需要指定参与的线程数目,然后对计数器执行减值操作。本文将要介绍的 Semaphore 信号量同样在构造时需要指定一个 int 类型 permits 参数,不过该参数并不用于指定参与的线程数目,相反,Semaphore 并不限制参与的线程数,该参数用于限制同一时间最大允许执行的线程数目上限。

阅读全文

深入理解 JUC:CyclicBarrier

上一篇我们分析了 CountDownLatch 同步器的设计与实现,在日常开发中还有另外一个同步器组件 CyclicBarrier 常常令我们容易混淆。在文章 理清 CountDownLatch 与 CyclicBarrier 的区别 中我们曾经总结过二者的区别,并通过示例演示了各自的应用场景。上一篇,我们分析了 CountDownLatch 的实现原理,本文我们继续从源码层面来分析 CyclicBarrier 的设计与实现。

阅读全文

深入理解 JUC:CountDownLatch

CountDownLatch 是一个同步辅助工具,用于阻塞当前一个或多个线程以等待其它线程中的操作完成。在构造 CountDownLatch 对象时,我们需要指定一个非负的 count 值,一般情况下,调用 CountDownLatch#await 方法的线程需要阻塞等待该 count 值变为 0 时才能够继续往下执行。具体示例可以参考 理清 CountDownLatch 与 CyclicBarrier 的区别

阅读全文

深入理解 JUC:ReentrantReadWriteLock

上一篇我们分析了 ReentrantLock 锁的设计与实现,对于大部分并发场景来说已经能够满足同步加锁的需求,但是相对于本文将要介绍的 ReentrantReadWriteLock 锁而言,ReentrantLock 主要存在的不足在于不区分读操作和写操作。考虑读多写少的场景,如果将读操作和写操作一视同仁的对待,那么线程之间读操作是互斥的,同一时间只允许一个线程读取数据,势必会影响系统的吞吐性能,并且读操作并不会导致数据的不一致性,这种情况下应该允许多个线程对同一份数据执行并发读取。

阅读全文

深入理解 JUC:ReentrantLock

上一遍我们深入分析了 AQS 的设计与实现,了解到 AQS 是 JUC 包实现的基础支撑,本文我们就来分析一个基于 AQS 实现的 JUC 组件,即 ReentrantLock。

ReentrantLock 译为可重入锁,我们在使用时总是将其与 synchronized 关键字进行对比,实际上 ReentrantLock 与 synchronized 关键字在使用上具备相同的语义,区别仅在于 ReentrantLock 相对于 synchronized 关键字留给开发者的可操作性更强,所以在使用上更加灵活,当然凡事都有两面,灵活的背后也暗藏着更加容易出错的风险。

阅读全文

深入理解 JUC:AQS 队列同步器

AbstractQueuedSynchronizer 简称 AQS,可能我们几乎不会直接去使用它,但它却是 JUC 的核心基础组件,支撑着 java 锁和同步器的实现,例如 ReentrantLock、ReentrantReadWriteLock、CountDownLatch,以及 Semaphore 等。大神 Doug Lea 在设计 JUC 包时希望能够抽象一个基础且通用的组件以支撑上层模块的实现,AQS 应运而生。

AQS 本质上是一个 FIFO 的双向队列,线程被包装成结点的形式,基于自旋机制在队列中等待获取资源(这里的资源可以简单理解为对象锁)。AQS 在设计上实现了两类队列,即 同步队列条件队列 ,其中同步队列服务于线程阻塞等待获取资源,而条件队列则服务于线程因某个条件不满足而进入等待状态。条件队列中的线程实际上已经获取到了资源,但是没有能够继续执行下去的条件,所以被打入条件队列并释放持有的资源,以让渡其它线程执行,如果未来某个时刻条件得以满足,则该线程会被从条件队列转移到同步队列,继续参与竞争资源,以继续向下执行。

阅读全文