Skip to content

# JUC 并发

AQS

相比原来的 syncornizedReentrantLock 解决了防死锁、公平、超时控制、精准唤醒的问题。

  • 一个双向链表
  • 一个 volatile 修饰的保证多线程可见性的同步状态 state,代表当前对象锁是否被占用和重入次数
  • 线程尝试加锁,加锁失败的入队列,公平锁下保证 FIFO,非公平状态允许插队

AQS 是 JUC 包的基石,它内部维护了一个 volatile int state 代表同步状态(通过 CAS 更新),以及一个 FIFO 线程等待队列(CLH 双向链表)。

当线程请求共享资源时:

  1. 调用 tryAcquire() 尝试获取资源,成功则直接返回。
  2. 若失败,将当前线程封装为 Node 节点 加入等待队列尾部。
  3. 入队后再次判断:如果前驱是 head,则再次尝试 tryAcquire()
  4. 若仍失败,调用 LockSupport.park() 挂起线程
  5. 当资源释放时,唤醒队列中的后继节点,被唤醒的线程回到步骤 3。

公平锁 vs 非公平锁的区别:

  • 公平锁:新线程在 tryAcquire() 前会检查队列中是否有等待者(hasQueuedPredecessors()),有则放弃,严格按队列顺序。
  • 非公平锁:新线程直接 CAS 抢锁,不管队列中是否有等待者;被唤醒的线程可能竞争失败,需重新排队。

可重入性

同一个线程可以多次获取同一把锁,而不会被阻塞或死锁

这个例子中通过递归调用在临界区内多次尝试获得同一把锁,由于 ReentrantLock 支持可重入,这里不会导致死锁,计数器 +1 即可,递归回溯的时候则 -1。

java
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockBasic {
    private final ReentrantLock lock = new ReentrantLock();
    private int count = 0;

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public void recursiveIncrement(int depth) {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 获取锁,深度: " + depth);
            if (depth > 0) {
                recursiveIncrement(depth - 1);
            }
            count++;
        } finally {
            lock.unlock();
            System.out.println(Thread.currentThread().getName() + " 释放锁, 深度: " + depth);
        }
    }

    public static void main(String[] args) {
        ReentrantLockBasic demo = new ReentrantLockBasic();

        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                demo.recursiveIncrement(3);
            }
        }, "Thread-1");

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                demo.recursiveIncrement(3);
            }
        }, "Thread-2");

        Thread thread3 = new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                demo.recursiveIncrement(3);
            }
        }, "Thread-3");

        thread1.start();
        thread2.start();
        thread3.start();

        try {
            thread1.join();
            thread2.join();
            thread3.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("Final count: " + demo.count);
    }
}

避免死锁

通过 tryLock 尝试获取锁,在获取不到锁的时候可以做其他事情,避免阻塞。

公平锁

通过 new ReentrantLock(true); 配置公平锁,保证队首节点尝试获取锁的时候不会被新进线程抢占。

其他AQS容器

特性CountDownLatchSemaphoreCyclicBarrier
核心作用线程等待事件(计数归零)控制并发数量(许可证)线程互相等待(集结点)
可重用性一次性(计数归零后失效)可重用(释放许可证后可再次获取)可循环使用(计数器自动重置)
线程关系主线程等待多个子线程线程间竞争资源线程间互相等待
典型场景资源加载、并发测试连接池限流、服务降级并行计算、多阶段任务

CAS

比较并交换。