ReentrantLock读写锁
发布日期:2025-05-02 05:20:40 浏览次数:13 分类:精选文章

本文共 3183 字,大约阅读时间需要 10 分钟。

ReentrantReadWriteLock 读写锁详解

读写锁的基本介绍

读写锁(ReentrantReadWriteLock)是一种并发控制机制,专为处理读多写少的场景设计。它允许多个线程同时持有读锁,从而提高系统性能。读写锁的核心思想是在读操作之间支持并发,而在写操作与读操作、写操作之间必须阻塞,直到冲突资源解除。

读写锁的核心优势在于其效率和灵活性。通过持有一个共享锁,读操作可以在不影响其他读操作的情况下进行,而写操作则需要独占资源,确保数据一致性。在实际应用中,读写锁常用于缓存管理、数据库连接池等场景。

读写锁的使用

读写锁通常通过数据容器类(DataContainer)来实现。在这种模式中,数据容器类提供了读锁和写锁两种操作:

DataContainer 类

package cn.knightzz.juc.reentrantlock.readwrite;
import lombok.extern.Slf4j;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@Slf4j
@SuppressWarnings("all")
public class DataContainer {
private Object data;
private ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
private ReentrantReadWriteLock.ReadLock readLock = rw.readLock();
private ReentrantReadWriteLock.WriteLock writeLock = rw.writeLock();
public Object read() {
log.debug("获取读取锁 ... ");
readLock.lock();
try {
log.debug("开始读取 ... ");
TimeUnit.SECONDS.sleep(1);
return data;
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
log.debug("释放读锁 ... ");
readLock.unlock();
}
}
public void write() {
log.debug("获取写入锁..");
writeLock.lock();
try {
log.debug("开始写入数据 ... ");
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
log.debug("释放写入锁 ... ");
writeLock.unlock();
}
}
}

读锁与写锁的行为

  • 读锁:支持多个线程同时持有,读操作之间不会阻塞。
  • 写锁:只允许一个线程持有,写操作与其他写操作和读操作之间会阻塞。
  • 重入锁:允许同一线程在持有读锁的情况下再次获取读锁,称为重入锁。写锁也支持重入,但在持有读锁的情况下无法获取写锁。

读写锁的测试与行为验证

读锁并发测试

在多线程环境中,读锁可以同时被多个线程获取。例如,在以下测试中:

public static void main(String[] args) throws IOException, InterruptedException {
DataContainer container = new DataContainer();
for (int i = 1; i < 5; i++) {
new Thread(() -> container.read(), "t" + i).start();
}
System.in.read();
}

可以看到多个线程同时获取读锁,并在不影响彼此的情况下执行读操作。这种特性使得读锁非常适合读密度高的场景。

写锁并发测试

在写锁场景中,写操作是独占的。以下测试展示了写锁的行为:

public static void main(String[] args) throws IOException, InterruptedException {
DataContainer container = new DataContainer();
for (int i = 1; i < 5; i++) {
new Thread(() -> container.write(), "t" + i).start();
}
System.in.read();
}

在这个测试中,写操作会相互阻塞,直到先前的写操作完成。可以看到,虽然多个线程尝试获取写锁,但只有一个线程能够真正执行写操作。

读写锁的相互阻塞

读写锁的独特之处在于,它不仅支持读写锁的相互阻塞,还允许在读锁持有的情况下,写锁无法获取。以下测试展示了这种行为:

public static void main(String[] args) throws IOException, InterruptedException {
DataContainer container = new DataContainer();
new Thread(() -> container.read(), "t1").start();
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
new Thread(() -> container.write(), "t2").start();
}

在这个测试中,t1线程首先获取读锁并执行读操作。t2线程在读锁释放后,才能获取写锁并执行写操作。这充分体现了读写锁的相互阻塞特性。

读写锁的条件变量限制

读写锁不支持条件变量,这意味着无法在持有锁的情况下进行条件检查。这种限制使得读写锁的实现更加简单,但也带来了使用上的局限性。例如,如果需要在持有读锁的情况下进行条件检查,读写锁无法满足需求。

此外,重入锁的行为也需要注意:在持有读锁的情况下,无法获取写锁。这意味着如果一个线程在持有读锁的情况下尝试获取写锁,将导致等待直到读锁被释放。

读写锁的应用场景

读写锁的主要应用场景在于缓存一致性管理。通过使用读写锁,可以确保在多线程环境中,缓存的读写操作能够高效且安全地进行。在实际应用中,读写锁可以用来保护共享资源,避免数据不一致的问题。

总结来说,读写锁是一种高效的并发控制机制,特别适用于读多于写的场景。通过合理使用读锁和写锁,可以在多线程环境中实现资源共享和数据保护。

上一篇:ReentrantLock的公平锁与非公平锁
下一篇:PHP mongoDB 操作

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2026年06月04日 09时44分17秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章