Java 下实现锁无关数据结构

来源:互联网 发布:删除开机引导软件 编辑:程序博客网 时间:2024/06/05 15:23

通常在一个多线程环境下,我们需要共享某些数据,但为了避免竞争条件引致数据出现不一致的情况,某些代码段需要变成原子操作去执行。这时,我们便需要利用各种同步机制如互斥(Mutex)去为这些代码段加锁,让某一线程可以独占共享数据,避免竞争条件,确保数据一致性。但可惜的是,这属于阻塞性同步,所有其他线程唯一可以做的就是等待。基于锁(Lock based)的多线程设计更可能引发死锁、优先级倒置、饥饿等情况,令到一些线程无法继续其进度。

锁无关(Lock free)算法,顾名思义,即不牵涉锁的使用。这类算法可以在不使用锁的情况下同步各个线程。对比基于锁的多线程设计,锁无关算法有以下优势:

  • 对死锁、优先级倒置等问题免疫:它属于非阻塞性同步,因为它不使用锁来协调各个线程,所以对死锁、优先级倒置等由锁引起的问题免疫;
  • 保证程序的整体进度:由于锁无关算法避免了死锁等情况出现,所以它能确保线程是在运行当中,从而确保程序的整体进度;
  • 性能理想:因为不涉及使用锁,所以在普遍的负载环境下,使用锁无关算法可以得到理想的性能提升。

自 JDK 1.5 推出之后,当中的 java.util.concurrent.atomic 的一组类为实现锁无关算法提供了重要的基础。本文介绍如何将锁无关算法应用到基本的数据结构中,去避免竞争条件,允许多个线程同时存取和使用集合中的共享数据。如果一个数据结构本身并非是线程安全的,一旦在多线程环境下使用这个数据结构,必须施加某种同步机制,否则很可能会出现竞争条件。我们即将设计的锁无关数据结构是线程安全的,所以使用时无需再编写额外代码去确保竞争条件不会出现。

数据结构的设计

本文会由浅入深,先提出锁无关栈(Stack)的实现方法,为读者提供必须的基础知识,栈是一个先入后出(Last in first out)的基本数据结构。当读者掌握必要的技术之后,我们便会着手设计相对复杂的链表(Linked List)数据结构,链表是很多其他数据结构的基础组成部分。不过,对比起栈,链表可以面对更棘手的线程同步问题。

在开始设计之前,我们需要理解一个十分重要的原语 Compare-and-swap (CAS) ,Herlihy 证明了 CAS 是实现锁无关数据结构的通用原语, CAS 可以原子地比较一个内存位置的内容及一个期望值,如果两者相同,则用一个指定值取替这个内存位罝里的内容,并且提供结果指示这个操作是否成功。很多现代的处理器已经提供了 CAS 的硬件实现,例如在 x86 架构下的 CMPXCHG8 指令。而在 Java 下,位于 java.util.concurrent.atomic 内的 AtomicReference<V> 类亦提供了 CAS 原语的实现,并且有很多其他的扩展功能。 CAS 操作将会是稍后实现的锁无关数据算法无可缺少的指令。

栈能以数组或者链表作为底下的储存结构,虽然采取链表为基础的实现方式会占用多一点空间去储存代表元素的节点,但却可避免处理数组溢出的问题。故此我们将以链表作为栈的基础。

首先,我们分析一下一个非线程安全的版本。为了清楚表达和集中于文章的主题,代码没有包含对异常及不正当操作的处理,读者请留意。它的代码如下:



本文转自IBM Developerworks中国

        请点击此处查看全文

 
原创粉丝点击