mirror of
https://github.com/LingCoder/OnJava8.git
synced 2026-08-23 14:33:28 +08:00
附录 并发底层原理 翻译至 无锁集合
This commit is contained in:
@@ -1637,11 +1637,29 @@ public class PriorityBlockingQueueDemo {
|
||||
|
||||
**Producer** 和 **Consumer** 通过 **PriorityBlockingQueue** 相互连接。因为阻塞队列的性质提供了所有必要的同步,因为阻塞队列的性质提供了所有必要的同步,请注意,显式同步是并不需要的 — 从队列中读取数据时,你不用考虑队列中是否有任何元素,因为队列在没有元素时将阻塞读取。
|
||||
|
||||
### Lock-Free Collections
|
||||
### 无锁集合
|
||||
|
||||
#### The Copying Strategy
|
||||
[集合](./12-Collections.md) 章节强调集合是基本的编程工具,这也要求包含并发性。因此,早期的集合比如 **Vector** 和 **Hashtable** 有许多使用 **synchronized** 机制的方法。当这些集合不是在多线程应用中使用时,这就导致了不可接收的开销。在 Java 1.2 版本中,新的集合库是非同步的,而给 **Collection** 类赋予了各种 **static** **synchronized** 修饰的方法来同步不同的集合类型。虽然这是一个改进,因为它让你可以选择是否对集合使用同步,但是开销仍然基于同步锁定。 Java 5 版本添加新的集合类型,专门用于增加线程安全性能,使用巧妙的技术来消除锁定。
|
||||
|
||||
#### Compare-And-Swap (CAS)
|
||||
无锁集合有一个有趣的特性:只要读取者仅能看到已完成修改的结果,对集合的修改就可以同时发生在读取发生时。这是通过一些策略实现的。为了让你了解它们是如何工作的,我们来看看其中的一些。
|
||||
|
||||
#### 复制策略
|
||||
|
||||
使用“复制”策略,修改是在数据结构一部分的单独副本(或有时是整个数据的副本)上进行的,并且在整个修改过程期间这个副本是不可见的。仅当修改完成时,修改后的结构才与“主”数据结构安全地交换,然后读取者才会看到修改。
|
||||
|
||||
在 **CopyOnWriteArrayList** ,写入操作会复制整个底层数组。保留原来的数组,以便在修改复制的数组时可以线程安全地进行读取。当修改完成后,原子操作会将其交换到新数组中,以便新的读取操作能够看到新数组内容。 **CopyOnWriteArrayList** 的其中一个好处是,当多个迭代器遍历和修改列表时,它不会抛出 **ConcurrentModificationException** 异常,因此你不用就像过去必须做的那样,编写特殊的代码来防止此类异常。
|
||||
|
||||
**CopyOnWriteArraySet** 使用 **CopyOnWriteArrayList** 来实现其无锁行为。
|
||||
|
||||
**ConcurrentHashMap** 和 **ConcurrentLinkedQueue** 使用类似的技术来允许并发读写,但是只复制和修改集合的一部分,而不是整个集合。然而,读取者仍然不会看到任何不完整的修改。**ConcurrentHashMap** **不会抛出concurrentmodificationexception** 异常。
|
||||
|
||||
#### 比较并交换 (CAS)
|
||||
|
||||
在 比较并交换 (CAS) 中,你从内存中获取一个值,并在计算新值时保留原始值。然后使用 CAS 指令,它将原始值与当前内存中的值进行比较,如果这两个值是相等的,则将内存中的旧值替换为计算新值的结果,所有操作都在一个原子操作中完成。如果原始值比较失败,则不会进行交换,因为这意味着另一个线程同时修改了内存。在这种情况下,你的代码必须再次尝试,获取一个新的原始值并重复该操作。
|
||||
|
||||
如果内存仅轻量竞争,CAS操作几乎总是在没有重复尝试的情况下完成,因此它非常快。相反,**synchronized** 操作需要考虑每次获取和释放锁的成本,这要昂贵得多,而且没有额外的好处。随着内存竞争的增加,使用 CAS 的操作会变慢,因为它必须更频繁地重复自己的操作,但这是对更多资源竞争的动态响应。这确实是一种优雅的方法。
|
||||
|
||||
最重要的是,许多现代处理器的汇编语言中都有一条 CAS 指令,并且也被 JVM 中的 CAS 操作(例如 **Atomic** 类中的操作)所使用。CAS 指令在硬件层面中是原子性的,并且与你所期望的操作一样快。
|
||||
|
||||
<!-- Summary -->
|
||||
## 本章小结
|
||||
|
||||
Reference in New Issue
Block a user