AQS 与 CAS

本文深入探讨了CAS(Compare-and-Swap)机制及其在Java中的应用,特别是在java.util.concurrent.atomic包下类的实现中。此外,还详细解析了AQS(AbstractQueuedSynchronizer)的工作原理及其实现,包括其在Semaphore和CountDownLatch等类中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一 . CAS

CAS(compare and swap),是解决多线程情况下锁性能损耗的机制。在java中,sun.misc.Unsafe 类提供了硬件级别的原子操作来实现CAS。

二. CAS典型应用

java.util.concurrent.atomic 包下的类大多是使用CAS操作来实现的:AtomicInteger

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();

    private volatile int value;// 初始int大小
    // 省略了部分代码...

    // 带参数构造函数,可设置初始int大小
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }
    // 不带参数构造函数,初始int大小为0
    public AtomicInteger() {
    }

    // 获取当前值
    public final int get() {
        return value;
    }

    // 设置值为 newValue
    public final void set(int newValue) {
        value = newValue;
    }

    //返回旧值,并设置新值为 newValue
    public final int getAndSet(int newValue) {
        /**
        * 这里使用for循环不断通过CAS操作来设置新值
        * CAS实现和加锁实现的关系有点类似乐观锁和悲观锁的关系
        * */
        for (;;) {
            int current = get();
            if (compareAndSet(current, newValue))
                return current;
        }
    }

    // 原子的设置新值为update, expect为期望的当前的值
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    // 获取当前值current,并设置新值为current+1
    public final int getAndIncrement() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

    // 此处省略部分代码,余下的代码大致实现原理都是类似的
}

在竞争不是非常激烈的情况下,该包下的原子操作性能比使用 synchronized 关键字的方式高效的多。

三. AQS

AQS 是Java.util.concurrent包的核心类。
这里写图片描述
整个包中很多类的结构都是如此,比如Semaphore,CountDownLatch都有一个内部类Sync,而所有的Sync都是继承自AbstractQueuedSynchronizer。 所以说想要读懂Java并发包的代码,首先得读懂这个类。
AQS的核心是通过一个共享变量来同步状态,变量的状态由子类去维护。AQS所做的工作主要有两部分:
(1)线程阻塞队列的维护
(2)线程的阻塞与唤醒
共享变量的修改都是通过Unsafe类提供的CAS操作完成的。AbstractQueuedSynchronizer类的主要方法是acquire和release,它们是典型的模板方法。
acquire方法用来获取锁,返回true说明线程获取成功继续执行,一旦返回false则线程加入到等待队列中,等待被唤醒,release方法用来释放锁。 下面这4个方法由子类去实现

//独占模式获取
protected boolean tryAcquire(int arg)
//独占模式释放
protected boolean tryRelease(int arg)
//共享模式获取
protected int tryAcquireShared(int arg)
//共享模式释放
protected boolean tryReleaseShared(int arg)
四. AQS的用法

下面的SimpleLock类实现了一个最简单非重入的互斥锁的功能。

class SimpleLock extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = -7316320116933187634L;

    public SimpleLock() {

    }

    protected boolean tryAcquire(int unused) {
        if (compareAndSetState(0, 1)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }

    protected boolean tryRelease(int unused) {
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }

    public void lock() {
        acquire(1);
    }

    public boolean tryLock() {
        return tryAcquire(1);
    }

    public void unlock() {
        release(1);
    }

    public boolean isLocked() {
        return isHeldExclusively();
    }
} 

public static void main(String[] args) throws InterruptedException {
    final SimpleLock lock = new SimpleLock();
    lock.lock();

    for (int i = 0; i < 10; i++) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                System.out.println(Thread.currentThread().getId() + " acquired the lock!");
                lock.unlock();
            }
        }).start();
        // 简单的让线程按照for循环的顺序阻塞在lock上
        Thread.sleep(100);
    }

    System.out.println("main thread unlock!");
    lock.unlock();
} 
五. AQS源码分析

http://zhanjindong.com/2015/03/10/java-concurrent-package-aqs-overview

### Java AQSCAS 机制详解 #### 1. AQS(AbstractQueuedSynchronizer)机制 AQS 是 Java 并发包中的核心类之一,用于构建锁和其他同步器的基础框架。它的设计基于两个关键思想:状态管理和队列管理[^5]。 - **状态管理** AQS 使用一个 `volatile` 类型的整数变量来表示同步状态。这个状态可以被多个线程安全地访问和修改。通过使用 CAS 操作(Compare And Swap),AQS 确保了对状态的原子性更新[^5]。例如,在 ReentrantLock 中,状态值表示当前锁的持有次数。 - **队列管理** AQS 使用一个 FIFO 队列来管理等待获取锁的线程。当一个线程尝试获取锁但失败时,它会被封装为一个节点并加入到队列中。一旦锁被释放,AQS 会从队列中唤醒下一个等待的线程[^5]。 以下是 AQS 的典型用法示例,以 ReentrantLock 为例: ```java import java.util.concurrent.locks.ReentrantLock; public class AqsExample { private final ReentrantLock lock = new ReentrantLock(); public void accessResource() { lock.lock(); // 获取锁 try { System.out.println(Thread.currentThread().getName() + " is accessing the resource."); } finally { lock.unlock(); // 释放锁 } } public static void main(String[] args) { AqsExample example = new AqsExample(); Thread t1 = new Thread(example::accessResource, "Thread-1"); Thread t2 = new Thread(example::accessResource, "Thread-2"); t1.start(); t2.start(); } } ``` #### 2. CAS(Compare And Swap)机制 CAS 是一种无锁算法,用于实现多线程环境下的原子操作。它的核心思想是:在更新某个变量之前,先检查该变量是否预期值相等。如果相等,则更新;否则,不进行任何操作。 - **CAS 的优点** - 不需要阻塞线程,避免了上下文切换的开销。 - 提高了并发性能,特别是在高竞争环境下。 - **CAS 的缺点** - 存在 ABA 问题:即在 CAS 操作期间,变量可能经历了多次变化,最终回到了初始值。为了解决这个问题,Java 提供了 `AtomicStampedReference` 类。 - 循环时间长:如果 CAS 操作失败,线程会不断重试,可能导致高 CPU 消耗。 以下是一个简单的 CAS 示例: ```java import java.util.concurrent.atomic.AtomicInteger; public class CasExample { private AtomicInteger atomicInteger = new AtomicInteger(0); public void increment() { int currentValue; int newValue; do { currentValue = atomicInteger.get(); newValue = currentValue + 1; } while (!atomicInteger.compareAndSet(currentValue, newValue)); } public static void main(String[] args) { CasExample example = new CasExample(); for (int i = 0; i < 10; i++) { new Thread(() -> example.increment()).start(); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Final value: " + example.atomicInteger.get()); } } ``` #### 3. AQSCAS 的关系 AQS 内部依赖 CAS 来实现对同步状态的安全更新。例如,在 ReentrantLock 中,AQS 使用 CAS 操作来尝试获取锁的状态。如果当前线程成功将状态从 0 更新为 1,则表示该线程成功获取了锁[^5]。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值