在Java并发编程的世界中,锁是一种用于同步访问共享资源的机制。Java并发库(JUC)为我们提供了多种锁的实现,每种都有其特定的使用场景和优势。其中,ReentrantReadWriteLock是一种非常独特且实用的锁,它允许更高的并发性,特别适用于读多写少的场景。
ReentrantReadWriteLock,即可重入的读写锁,它维护了两把锁:读锁和写锁。读锁允许多个线程同时持有,从而允许多个线程同时读取共享资源,提高了并发读取的效率。而写锁则是独占的,同一时间只能被一个线程持有,用于保护写入共享资源的操作。
需要注意的是,读锁和写锁是互斥的。当写锁被持有时,其他线程无法获取读锁或写锁,从而保证了写入操作的独占性。而当读锁被持有时,虽然其他线程可以继续获取读锁,但无法获取写锁,这保证了在读取过程中共享资源不会被修改。
ReentrantReadWriteLock的实现机制相对复杂,涉及多个内部类和同步控制。下面我将简要概述其核心实现机制,并提供一些关键部分的源码分析。
ReentrantReadWriteLock的源码较长且复杂,这里给出部分关键代码辅助分析。
// ReentrantReadWriteLock的部分内部类定义
public class ReentrantReadWriteLock
implements ReadWriteLock, java.io.Serializable {
// 内部抽象类Sync继承自AbstractQueuedSynchronizer
abstract static class Sync extends AbstractQueuedSynchronizer {
// ... 省略其他方法和字段 ...
// 尝试获取写锁的方法(由WriteLock调用)
final boolean tryAcquire(int acquires) {
// ... 省略实现细节 ...
}
// 尝试释放写锁的方法(由WriteLock调用)
final boolean tryRelease(int releases) {
// ... 省略实现细节 ...
}
// 尝试获取读锁的方法(由ReadLock调用)
final int tryAcquireShared(int acquires) {
// ... 省略实现细节 ...
}
// 尝试释放读锁的方法(由ReadLock调用)
final boolean tryReleaseShared(int releases) {
// ... 省略实现细节 ...
}
// 判断是否处于读锁状态
final boolean isHeldExclusively() {
// ... 省略实现细节 ...
}
}
// 非公平锁的实现类NonfairSync继承自Sync
static final class NonfairSync extends Sync {
// ... 省略其他方法和字段 ...
}
// 公平锁的实现类FairSync继承自Sync
static final class FairSync extends Sync {
// ... 省略其他方法和字段 ...
}
// 读锁的实现类ReadLock实现Lock接口
public static class ReadLock implements Lock, java.io.Serializable {
// ... 省略其他方法和字段 ...
// 读锁的lock方法实现
public void lock() {
sync.acquireShared(1);
}
// 读锁的unlock方法实现
public void unlock() {
sync.releaseShared(1);
}
}
// 写锁的实现类WriteLock实现Lock接口
public static class WriteLock implements Lock, java.io.Serializable {
// ... 省略其他方法和字段 ...
// 写锁的lock方法实现
public void lock() {
sync.acquire(1);
}
// 写锁的unlock方法实现
public void unlock() {
sync.release(1);
}
}
// ... 省略其他方法和字段 ...
}
ReentrantReadWriteLock定义了两个内部类ReadLock和WriteLock来实现读锁和写锁的功能。它们内部都持有一个Sync对象,用于实际的锁操作。Sync是一个继承自AbstractQueuedSynchronizer的抽象类,它实现了锁的核心逻辑。具体的公平性和非公平性锁的实现则通过FairSync和NonfairSync两个类来完成,它们分别继承自Sync。
下面是一个使用ReentrantReadWriteLock
的代码,其中包含一个共享资源(一个简单的计数器),多个读线程和写线程将并发地访问这个资源。读线程只会读取计数器的值,而写线程会修改计数器的值。
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockExample {
// 共享资源:一个简单的计数器
private int counter = 0;
// 创建一个ReentrantReadWriteLock对象
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
// 获取读锁
private final ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
// 获取写锁
private final ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
// 读操作:增加读计数
public int readCounter() {
readLock.lock(); // 加读锁
try {
// 模拟读操作的耗时
Thread.sleep(10);
return counter;
} catch (InterruptedException e) {
e.printStackTrace();
return -1;
} finally {
readLock.unlock(); // 释放读锁
}
}
// 写操作:增加计数器的值
public void incrementCounter() {
writeLock.lock(); // 加写锁
try {
// 模拟写操作的耗时
Thread.sleep(10);
counter++;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
writeLock.unlock(); // 释放写锁
}
}
// 读线程类
class ReaderThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("读线程" + Thread.currentThread().getId() + "读取计数器值:" + readCounter());
}
}
}
// 写线程类
class WriterThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
incrementCounter();
System.out.println("写线程" + Thread.currentThread().getId() + "增加计数器值");
}
}
}
// 测试方法
public static void main(String[] args) {
ReadWriteLockExample example = new ReadWriteLockExample();
// 启动多个读线程和写线程
for (int i = 0; i < 3; i++) {
new Thread(example.new ReaderThread()).start();
new Thread(example.new WriterThread()).start();
}
}
}
我们定义了一个ReadWriteLockExample
类,它包含一个计数器counter
,一个ReentrantReadWriteLock
对象以及对应的读锁和写锁。我们定义了两个方法readCounter
和incrementCounter
来分别执行读操作和写操作,并在执行前后分别加锁和释放锁。
我们还定义了两个内部类ReaderThread
和WriterThread
来分别代表读线程和写线程。在main
方法中,我们创建了多个读线程和写线程并启动它们,以模拟并发访问共享资源的场景。
ReentrantReadWriteLock是Java并发库中一种非常实用的读写锁实现,它允许多个线程同时读取共享资源,提高了程序的并发性能。在使用时需要注意正确获取和释放锁,避免死锁等问题;同时根据实际需求选择合适的公平性策略也是非常重要的。通过深入了解和合理使用ReentrantReadWriteLock,我们可以编写出更高效、更安全的并发程序。