博客
关于我
ReentrantLock读写锁
阅读量:794 次
发布时间:2023-02-27

本文共 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线程在读锁释放后,才能获取写锁并执行写操作。这充分体现了读写锁的相互阻塞特性。

读写锁的条件变量限制

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

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

读写锁的应用场景

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

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

转载地址:http://hevfk.baihongyu.com/

你可能感兴趣的文章
oracle 行转列
查看>>
Oracle 表
查看>>
Oracle 递归
查看>>
oracle 逻辑优化,提升高度,综合SQL上下文进行逻辑优化
查看>>
oracle 闪回关闭,关闭闪回即disable flashback的操作步骤
查看>>
oracle--用户,权限,角色的管理
查看>>
oracle00205报错,Oracle控制文件损坏报错场景
查看>>
Oracle10g EM乱码之快速解决
查看>>
Oracle10g下载地址--多平台下的32位和64位
查看>>
Oracle10g安装了11g的ODAC后,PL/SQL连接提示TNS:无法解析指定的连接标识符
查看>>
Oracle11G基本操作
查看>>
Oracle11g服务详细介绍及哪些服务是必须开启的?
查看>>
Oracle11g静默安装dbca,netca报错处理--直接跟换操作系统
查看>>
oracle12安装软件后安装数据库,然后需要自己配置监听
查看>>
Oracle——08PL/SQL简介,基本程序结构和语句
查看>>
Oracle——distinct的用法
查看>>
Oracle、MySQL、SQL Server架构大对比
查看>>
oracle下的OVER(PARTITION BY)函数介绍
查看>>
Oracle中DATE数据相减问题
查看>>
Oracle中merge into的使用
查看>>