012-多线程-基础-非阻塞算法相关技术简介
一、概述
1.1、Java 的多线程同步机制
在现代的多处理器系统中,提高程序的并行执行能力是有效利用 CPU 资源的关键。为了有效协调多线程间的并发访问,必须采用适当的同步机制来协调竞争。当前常用的多线程同步机制可以分为下面三种类型:
- volatile 变量:轻量级多线程同步机制,不会引起上下文切换和线程调度。仅提供内存可见性保证,不提供原子性。
- CAS 原子指令:轻量级多线程同步机制,不会引起上下文切换和线程调度。它同时提供内存可见性和原子化更新保证。
- 内部锁和显式锁:重量级多线程同步机制,可能会引起上下文切换和线程调度,它同时提供内存可见性和原子性。
1.2、多处理器系统对并发的支持
现代的多处理器系统大多提供了特殊的指令来管理对共享数据的并发访问,这些指令能实现原子化的读 - 改 - 写操作。现代典型的多处理器系统通常支持两种同步原语(机器级别的原子指令):CAS 和 LL/SC。Intel,AMD 和 SPARC 的多处理器系统支持“比较并交换”(compare-and-swap,CAS)指令。IBM PowerPC,Alpha AXP,MISP 和 ARM 的多处理器系统支持“加载链接 / 存储条件”(load-linked/store-conditional,LL/SC)指令。
JDK 为 concurrent.atomic 包中的原子类提供了 compareAndSet() 方法,compareAndSet() 方法使用上面这些机器级别的原子指令来原子化的更新值。java. concurrent 包中的这些原子类,为用非阻塞算法实现并发容器打下了基础。
1.3、非阻塞算法
从 Amdahl 定律我们可以知道,要想提高并发性,就应该尽量使串行部分达到最大程度的并行;也就是说:最小化串行代码的粒度是提高并发性能的关键。
与锁相比,非阻塞算法在更细粒度(机器级别的原子指令)的层面协调多线程间的竞争。它使得多个线程在竞争相同资源时不会发生阻塞,它的并发性与锁相比有了质的提高;同时也大大减少了线程调度的开销。同时,由于几乎所有的同步原语都只能对单个变量进行操作,这个限制导致非阻塞算法的设计和实现非常复杂。
一个线程的失败和挂起不会引起其他些线程的失败和挂起,这样的算法称为非阻塞算法。非阻塞算法通过使用底层机器级别的原子指令来取代锁,从而保证数据在并发访问下的一致性。
1.4、基于非阻塞算法实现的并发容器
在JDK 中基于非阻塞算法实现的并发容器。在 JDKUpdate23 的 util.concurrent 包中,基于非阻塞算法实现的并发容器包括:ConcurrentLinkedQueue,SynchronousQueue,Exchanger 和 ConcurrentSkipListMap。ConcurrentLinkedQueue 是一个基于链接节点的无界线程安全队列,本文接下来将结合 JDK 源代码,来探索它的非阻塞算法的具体实现机制。SynchronousQueue 是一个没有容量的阻塞队列,它使用双重数据结构 来实现非阻塞算法。Exchanger 是一个能对元素进行配对和交换的交换器。它使用 消除 技术来实现非阻塞算法 。ConcurrentSkipListMap 是一个可以根据 Key 进行排序的可伸缩的并发 Map。