同步器入门:基于 AQS 实现的 ReentrantLock 与 ReentrantReadWriteLock
2022-12-01 22:38:07
AQS:Java 中同步机制的关键
摘要
同步是多线程编程中至关重要的一个概念。在 Java 中,AbstractQueuedSynchronizer (AQS) 担任着同步机制实现的关键角色。本文将深入探讨 AQS 的重要性、原理以及基于 AQS 构建的两个重要同步器:ReentrantLock 和 ReentrantReadWriteLock 。
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 的原理,对于深入了解同步机制的实现细节以及合理使用同步器至关重要。
常见问题解答
- AQS 中队列的作用是什么?
- 存储等待获取锁的线程。
- ReentrantLock 如何实现可重入性?
- 通过维护一个计数器,记录线程获取锁的次数。
- ReentrantReadWriteLock 如何确保写操作的独占性?
- 通过维护一个写锁计数器,确保只有当没有其他读锁或写锁时,才能获取写锁。
- AQS 中条件变量的用途是什么?
- 允许线程等待某个条件满足后才能继续执行。
- 如何防止同步器死锁?
- 小心地获取和释放锁,避免循环等待的情况。