Skip to main content

ReentrantReadWriteLock

ReentrantReadWriteLock 是 Java 中一个实现读写锁的同步工具,允许多个线程同时读取共享资源,但只允许一个线程写入。 这种机制提高了并发访问共享资源的效率,特别是在读操作远多于写操作的场景中。下面是 ReentrantReadWriteLock 的主要知识点

主要特性

  1. 分离读写操作:允许多个读线程同时访问,但保证写操作的独占性。
  2. 提高并发性能:适用于读多写少的并发场景,可以显著提高性能。
  3. 支持可重入:线程可以重复获取已持有的锁。
  4. 锁降级:支持从写锁降级为读锁。
  5. 公平选择:可选择公平锁或非公平锁。

示例代码

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReentrantReadWriteLockExample {
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
private final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
private int value = 0;

// 读操作
public int getValue() {
readLock.lock(); // 获取读锁
try {
return value;
} finally {
readLock.unlock(); // 释放读锁
}
}

// 写操作
public void incrementValue() {
writeLock.lock(); // 获取写锁
try {
value++;
} finally {
writeLock.unlock(); // 释放写锁
}
}

public static void main(String[] args) {
ReentrantReadWriteLockExample example = new ReentrantReadWriteLockExample();
// 示例:启动读写线程
}
}

分析

  • 创建读写锁:首先,我们实例化 ReentrantReadWriteLock,然后分别获取它的读锁和写锁。
  • 读操作使用读锁:在 getValue 方法中,我们通过调用 readLock.lock() 获取读锁,执行读操作后,使用 finally 块确保读锁被释放。这允许多个线程同时进行读操作。
  • 写操作使用写锁:在 incrementValue 方法中,我们通过调用 writeLock.lock() 获取写锁,执行写操作,然后释放写锁。写锁保证了写操作的独占性,当写锁被持有时,其他读写操作都会被阻塞。

锁降级

锁降级是指在持有写锁的情况下,获取读锁,然后释放写锁的过程。这样做可以保留对资源的读取权限,而不允许其他线程进行写操作。锁降级的代码示例:

writeLock.lock();
try {
// 修改共享资源
value++;
readLock.lock(); // 在释放写锁之前获取读锁
} finally {
writeLock.unlock(); // 释放写锁
}
// 此处仍持有读锁
try {
// 读取共享资源
} finally {
readLock.unlock(); // 释放读锁
}

注意事项

  • 锁获取顺序:遵循获取写锁、读锁的顺序,以避免死锁。
  • 公平性设置ReentrantReadWriteLock 在构造时可以选择是公平锁还是非公平锁。公平锁可以防止饥饿,但可能会影响性能。

实际案例

🟢 1. 读线程可并发执行

多个读线程会同时获取读锁,不会互相阻塞。 这是 ReadWriteLock 的最大优势 —— 读多写少场景下性能很高。

🔴 2. 写线程必须等所有读线程释放读锁后才能获取写锁

写操作是独占锁,必须等待其他线程释放读锁,它自己才能加上写锁。

所以,通常写线程的 “正在写入…” 会晚于所有读线程完成之后出现。

package com.jasper.juc.lock;

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockExample {
private int value = 0;
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

public int read() {
lock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " 正在读取...");
Thread.sleep(100); // 模拟耗时读取
return value;
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.readLock().unlock();
}
}

public void write(int newValue) {
lock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " 正在写入...");
Thread.sleep(200); // 模拟耗时写入
this.value = newValue;
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.writeLock().unlock();
System.out.println(Thread.currentThread().getName() + " 写入完成...");
}
}
}

public class TestReadWriteLock {
public static void main(String[] args) {
ReadWriteLockExample example = new ReadWriteLockExample();

// 启动多个读线程
for (int i = 0; i < 5; i++) {
new Thread(() -> {
System.out.println("读取结果:" + example.read());
}, "读线程-" + i).start();
}

// 启动一个写线程
new Thread(() -> {
example.write(42);
}, "写线程").start();
}
}