返回

同步器入门:基于 AQS 实现的 ReentrantLock 与 ReentrantReadWriteLock

后端

AQS:Java 中同步机制的关键

摘要

同步是多线程编程中至关重要的一个概念。在 Java 中,AbstractQueuedSynchronizer (AQS) 担任着同步机制实现的关键角色。本文将深入探讨 AQS 的重要性、原理以及基于 AQS 构建的两个重要同步器:ReentrantLockReentrantReadWriteLock

AQS 的重要性

AQS 提供了一套通用的同步原语,为构建各种同步器提供了基础。除了 ReentrantLock 和 ReentrantReadWriteLock,AQS 还为信号量、倒计数闩锁、屏障等同步器提供了实现。理解 AQS 的原理至关重要,它能帮助你透彻了解同步机制的实现细节,并合理使用同步器。

ReentrantLock

ReentrantLock 是一种可重入锁 ,允许同一个线程多次获取同一个锁。它通过维护一个计数器来实现可重入性:当线程获取锁时计数器加 1,释放锁时计数器减 1。当计数器为 0 时,表示锁处于未持有状态。ReentrantLock 还支持公平锁和非公平锁两种模式,分别保证了线程获取锁的顺序与请求锁的顺序一致,或不作此保证。

代码示例:ReentrantLock

ReentrantLock lock = new ReentrantLock();

// 获取锁
lock.lock();

// 操作共享资源

// 释放锁
lock.unlock();

ReentrantReadWriteLock

ReentrantReadWriteLock 是一种读写锁 ,允许多个线程同时读取共享数据,但只能允许一个线程同时写入共享数据。它维护两个计数器:读锁计数器和写锁计数器。当线程请求读锁时,若没有写锁,则直接获取读锁;若有写锁,则需要等待写锁释放后再获取读锁。当线程请求写锁时,若有读锁或写锁,则需要等待所有读锁和写锁释放后再获取写锁。

代码示例:ReentrantReadWriteLock

ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
Lock readLock = lock.readLock();
Lock writeLock = lock.writeLock();

// 获取读锁
readLock.lock();

// 读取共享资源

// 释放读锁
readLock.unlock();

// 获取写锁
writeLock.lock();

// 写入共享资源

// 释放写锁
writeLock.unlock();

AQS 实现原理

AQS 基于一个队列来实现同步。该队列存储了所有正在等待获取锁的线程。当线程请求获取锁时,若锁未被持有,则直接获取锁;若锁被持有,则当前线程将加入队列并等待。当锁释放时,队列中的第一个线程将获取锁。

其他 AQS 功能

AQS 还提供了其他功能,如条件变量、超时获取锁等,这些功能都可以在队列的基础上实现。

结论

ReentrantLock 和 ReentrantReadWriteLock 是 Java 中至关重要的同步器,均基于 AQS 实现。理解 AQS 的原理,对于深入了解同步机制的实现细节以及合理使用同步器至关重要。

常见问题解答

  1. AQS 中队列的作用是什么?
    • 存储等待获取锁的线程。
  2. ReentrantLock 如何实现可重入性?
    • 通过维护一个计数器,记录线程获取锁的次数。
  3. ReentrantReadWriteLock 如何确保写操作的独占性?
    • 通过维护一个写锁计数器,确保只有当没有其他读锁或写锁时,才能获取写锁。
  4. AQS 中条件变量的用途是什么?
    • 允许线程等待某个条件满足后才能继续执行。
  5. 如何防止同步器死锁?
    • 小心地获取和释放锁,避免循环等待的情况。