CAS和AQS

注意
本文最后更新于 2023-08-21,文中内容可能已过时。

在Java并发编程中,CAS(Compare-And-Swap)和AQS(AbstractQueuedSynchronizer)是两个非常重要的概念和工具。它们分别代表了两种不同的同步机制,用于解决多线程并发访问共享资源时的数据一致性问题。

CAS概述

CAS(Compare-And-Swap)是一种无锁算法,它通过原子操作来保证数据的一致性,而不需要使用传统的锁机制。CAS操作包含三个操作数:内存位置(V)、期望的原值(A)和新值(B)。CAS操作会检查内存位置V的值是否等于期望的原值A,如果是,则将内存位置V的值更新为新值B,这个过程是原子的。

在Java中,java.util.concurrent.atomic包下的类(如AtomicIntegerAtomicLong等)就是基于CAS实现的。这些类提供了一系列原子操作,如incrementAndGet()decrementAndGet()等,它们能够在多线程环境下安全地更新共享变量的值。

CAS的优点是性能高,因为它避免了线程挂起和唤醒的开销。但是,CAS也存在一些问题,如ABA问题和自旋开销等。ABA问题是指,如果一个变量原来是A,后来被一条线程改为B,最后又被改回A,则CAS检查时会认为这个变量从来没有被改变过。自旋开销是指,当CAS操作失败时,线程会不断重试,这可能会导致CPU资源的浪费。

AQS概述

AQS(AbstractQueuedSynchronizer)是Java并发包java.util.concurrent.locks中的一个核心组件,它是一个用于构建锁和同步器的框架。AQS使用一个int类型的变量来表示同步状态,并提供了一系列的方法来操作这个状态。AQS将请求共享资源的线程封装成排队的节点并加入同步队列中,通过维护一个FIFO(先进先出)的队列来管理等待获取锁的线程。

AQS的设计是基于模板方法模式的,它定义了一些需要被继承者实现的方法来管理同步状态。例如,tryAcquire方法尝试获取锁,tryRelease方法尝试释放锁。这些方法需要由具体的同步器来实现。

Java中的ReentrantLockSemaphoreCountDownLatchCyclicBarrier等同步工具类都是基于AQS实现的。这些类通过继承AQS并实现相关的方法来管理同步状态。

总的来说,CAS和AQS都是Java并发编程中解决多线程同步问题的重要工具。CAS通过原子操作实现无锁化的数据访问,适用于简单的数据更新场景;而AQS则提供了一个灵活的框架来构建各种复杂的锁和同步器,适用于需要更精细控制同步状态的场景。

CAS实现类

Java中常见的CAS实现类主要包括以下几个:

  1. AtomicInteger

    • AtomicInteger类提供了一个支持原子操作的整型变量。它包含了一系列的方法,如incrementAndGet(), decrementAndGet(), addAndGet(int delta), getAndSet(int newValue)以及核心的compareAndSet(int expect, int update),这些方法都利用了CAS操作来确保原子性。
  2. AtomicLong

    • 与AtomicInteger类似,AtomicLong为长整型(long)数值提供了原子操作。它也包含了如incrementAndGet(), decrementAndGet(), addAndGet(long delta), getAndSet(long newValue)compareAndSet(long expect, long update)等方法,适用于需要原子性操作长整型数值的场景。
  3. AtomicReference

    • AtomicReference类提供了对对象引用的原子操作。通过这个类,你可以实现对象引用的原子更新。它提供了如compareAndSet(V expect, V update)等方法来确保对象引用的更新是原子的。
  4. AtomicBoolean

    • AtomicBoolean类为布尔值提供了原子操作。它允许你以原子方式设置、获取和比较布尔值,常用方法有set(boolean newValue), get(), compareAndSet(boolean expect, boolean update)等。
  5. AtomicStampedReferenceAtomicMarkableReference

    • 这两个类解决了CAS操作中的ABA问题。AtomicStampedReference和AtomicMarkableReference分别通过版本戳和标记来确保值在被其他线程修改后,CAS操作能够识别出来,从而避免错误地更新值。
  6. AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, AtomicReferenceFieldUpdater

    • 这些类提供了对指定类的指定字段进行原子更新的能力。它们允许你对某个类的实例中的某个字段进行原子性的更新操作,而无需对整个对象加锁。

这些类都位于java.util.concurrent.atomic包中,利用底层的Unsafe类或者直接利用硬件支持的CAS指令来实现原子操作,从而提供了高效且无锁的并发控制机制。

AQS实现类

在Java中,AQS(AbstractQueuedSynchronizer)是一个用于构建锁和同步器的框架,许多同步类都是基于AQS来实现的。以下是一些Java中常见的基于AQS实现的类:

  1. ReentrantLock

    • ReentrantLock是一个可重入的互斥锁,它基于AQS实现。ReentrantLock提供了比synchronized更灵活的锁机制,包括定时锁等待、可中断的锁等待以及尝试锁等功能。
  2. Semaphore

    • Semaphore(信号量)也是基于AQS实现的。它用于控制对多个共享资源的访问,可以设定允许同时访问共享资源的线程数量。
  3. CountDownLatch

    • CountDownLatch是一个同步辅助类,它允许一个或多个线程等待直到在其他线程中进行的一组操作完成。这也是基于AQS实现的,通常用于确保某些初始化操作在所有其他操作之前完成。
  4. CyclicBarrierCountDownLatch 的部分实现:

    • 尽管CyclicBarrier的主要逻辑并非直接基于AQS,但它内部的计数器是通过AQS的同步状态(state)来实现的,用于控制多个线程的相互等待。
    • CountDownLatch则直接使用AQS的state来表示计数,当计数减至零时,所有等待的线程将被释放。
  5. ReadWriteLock 的实现类 ReentrantReadWriteLock

    • ReentrantReadWriteLock是读写锁的一个实现,它允许多个读线程并行执行,但只允许一个写线程执行。这个锁也是基于AQS实现的,通过AQS的state来表示读锁和写锁的持有情况。
  6. StampedLock

    • StampedLock是Java 8中引入的一个新的读写锁,它提供了更复杂的读/写锁策略,包括乐观读锁和写锁。虽然其内部实现较为复杂,但也部分依赖于AQS的某些机制。

这些类都利用了AQS提供的同步状态管理、线程排队和阻塞/唤醒机制来实现各自的同步需求。AQS为这些同步类提供了一个强大且灵活的框架,使得实现复杂的同步逻辑变得更加容易和高效。