Skip to content

Commit

Permalink
附录 并发底层原理 翻译至 无锁集合
Browse files Browse the repository at this point in the history
  • Loading branch information
AlfredAlan committed Aug 4, 2019
1 parent 1863ae1 commit 293ebe3
Showing 1 changed file with 21 additions and 3 deletions.
24 changes: 21 additions & 3 deletions docs/book/Appendix-Low-Level-Concurrency.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 -->
## 本章小结
Expand Down

0 comments on commit 293ebe3

Please sign in to comment.