返回

JUC学习笔记 - 04AQS源码解析

后端

AbstractQueuedSynchronizer:Java并发编程中的基石

概览

AbstractQueuedSynchronizer (AQS) 是Java并发包中一个至关重要的基础类,它提供了一种灵活且可扩展的机制来管理同步锁。AQS充当同步锁的框架,支持各种锁实现,包括ReentrantLock、Semaphore和CountDownLatch。理解AQS的内部机制对于掌握Java并发编程至关重要。

AQS的本质

AQS是一个抽象类,定义了同步锁的基本操作和行为。它通过一个队列机制来管理等待获取锁的线程。当一个线程尝试获取锁时,如果锁可用,它将立即获取锁。否则,该线程将进入队列并等待锁可用。

AQS支持两种类型的锁:

  • 独占锁: 仅允许一个线程持有该锁。
  • 共享锁: 允许多个线程同时持有该锁。

AQS还提供了两种锁的实现方式:

  • 公平锁: 保证线程获取锁的顺序与它们进入队列的顺序一致。
  • 非公平锁: 不保证获取锁的顺序。

AQS的实现

AQS的核心数据结构是一个队列,用于存储等待获取锁的线程。队列中的元素是Node 对象,它包含指向线程的引用、获取锁的方式(独占或共享)以及线程等待状态(例如,WAITING或SIGNAL)。

AQS使用state 字段来表示锁的状态。state是一个整型值,表示当前持有的锁数或等待队列中线程的数量。当state为0时,表示锁可用。

AQS的操作

AQS提供了以下主要操作:

  • acquire(): 尝试获取锁。如果锁不可用,则线程将进入队列并等待。
  • release(): 释放锁。如果队列中有等待的线程,则其中一个线程将被唤醒。
  • tryAcquire(): 尝试获取锁,如果锁不可用,则返回false。
  • tryRelease(): 尝试释放锁,如果锁已释放,则返回false。
  • isHeldExclusively(): 判断当前线程是否独占持有锁。
  • hasQueuedThreads(): 判断是否有线程在等待获取锁。
  • getQueueLength(): 获取等待获取锁的线程数。
  • getQueuedThreads(): 获取等待获取锁的线程列表。

AQS的使用

AQS可以通过直接继承或间接继承的方式使用。直接继承AQS 可以实现自定义的同步锁。间接继承AQS 可以通过使用AQS的内置锁实现,例如:

  • ReentrantLock: 一个可重入的互斥锁。
  • Semaphore: 一个允许指定数量的线程同时执行的信号量。
  • CountDownLatch: 一个等待所有线程完成特定任务的计数器。

代码示例:使用ReentrantLock

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {

    private ReentrantLock lock = new ReentrantLock();

    public void method() {
        lock.lock();
        try {
            // 执行受保护的代码
        } finally {
            lock.unlock();
        }
    }

}

结论

AQS是Java并发编程中一个极其重要的类,它为同步锁提供了强大的框架和灵活的机制。深入理解AQS的内部机制对于编写高效且可靠的多线程应用程序至关重要。

常见问题解答

  1. AQS和synchronized有什么区别?
    AQS是一个抽象类,而synchronized是一个。AQS提供了一种更灵活和可扩展的同步机制,而synchronized只能用于方法或代码块。

  2. 公平锁和非公平锁有什么区别?
    公平锁保证线程获取锁的顺序与它们进入队列的顺序一致,而非公平锁不提供这种保证。

  3. 如何选择合适的锁实现?
    锁实现的选择取决于应用程序的具体需求,例如并发级别、公平性要求和性能考虑。

  4. AQS中的state字段有什么作用?
    state字段表示锁的状态,包括当前持有的锁数或等待队列中线程的数量。

  5. AQS是否支持可重入锁?
    是的,AQS通过ReentrantLock类支持可重入锁,它允许一个线程多次获取同一把锁。