java锁(2)ReentrantReadWriteLock

ReentrantReadWriteLock

1.        特点:ReentrantReadWriteLock是一个读写锁,它提供了一个读锁和一个写锁,读锁用于只读操作,而写锁用于写入操作,读操作可以并行进行,而写操作则是互斥的。读锁和写锁的分离在一些写少读多的应用中可以带来性能上的提升

2.        读锁与写锁的约束关系

1)        当任一线程持有写锁或读锁时,其他线程不能获得写锁;

2)        当任一线程持有写锁时,其他线程不能获取读锁;

3)        多个线程可以同时持有读锁。

3.        优点:实现并发的读和互斥的写,达到很多操作并行的目地,从而提高性能。

4.        使用:

1)        创建一个读锁和一个写锁


2)        通过读锁实现并发读


3)        通过写锁实现互斥写


锁的获取顺序

1.        非公平模式:

1)        非公平模式不按照线程请求锁的顺序分配锁,而是当前请求锁的线程和等待队列中的线程一起竞争锁。

2)        连续竞争的非公平锁可能会导致等待队列中的线程长时间等待,但吞吐量要高于公平锁。

3)        非公平模式中,如果等待队列的头节点是写者线程(当前执行的可能是一个写者线程或者是多个读者线程),那么新到的读者线程将进入等待队列中阻塞。

4)        队头的写者线程,在当前正在执行的一个写者线程或者多个读者线程执行完成后,就会得到执行(如果存在新到写者线程,则需要竞争),防止等待队列中的写者线程一直等待。

2.        公平模式:

1)        公平模式采用近似FIFO的策略获取锁

2)        当一个线程释放了锁后,等待队列中等待时间最长的线程(单个写线程或者多个读线程)将获取锁(写锁或者读锁)。

3)        由于公平模式采用FIFO的策略获取锁,因此不存在写线程一直等待的问题。

重入

允许写者和读者按照ReentrantLock的方式多次获取读锁或写锁

1.        获取写锁的线程可以再次获取读锁:即写锁可以降级为读锁

2.        获取读锁的线程不能再次获取写锁:即读锁不可以升级为写锁

Condition支持

1.        读锁不支持Condition:readLock().newCondition()会抛出Unsupported OperationException

2.        写锁支持Condition:对于写入锁来说,该实现的行为与 ReentrantLock.newCondition()提供的Condition 实现对ReentrantLock所做的行为相同

ReentrantReadWriteLock的实现

1.        同步状态


1)        ReentrantReadWriteLock是将AQS中同步状态的整型变量分为了两个部分来实现的

Ø  低位的16位用于保存写锁状态;

Ø  高位的16位用于保存读锁状态;

2)        sharedCount:用于获取共享锁(读锁)数量,因为读锁是共享锁;

3)        exclusiveCount:用于获取排它锁(写锁)数量,因为写锁是排它锁。

2.        读锁与写锁

1)        读锁


a)        Lock():调用的是AQS中的acquireShared方法。

b)        Unlock():调用的是AQS中的releaseShared方法。

c)        采用的是AQS的共享模式,且不支持newCondition操作

2)        写锁


a)        Lock():调用AQS的acquire方法;

b)        Release():调用AQS的忍了阿萨我方法;

c)        写锁采用的是AQS的排它模式,且支持newCondition 操作;

3.        非公平模式

1)        写锁:获取锁


1.        获取当前同步状态c

2.        通过同步状态C获取写锁的数量

3.        C!=0 表示所以被占用(可能是写锁,也可能是读锁)

4.        W=0 表当前占用的是读锁,返回false

Ø  若w!=0 表示当前占用的是写锁,需进一步判断,是否是自己占用了这个写锁。

Ø  若不是返回false

5.        检查是否超过锁的上限

6.        修改写锁数

7.        (写线程永不阻塞)C=0 表示当前没有锁被占用,故修改锁的状态、将锁的拥有者声明为自己。

2)        写锁:释放锁


a)        检测:判断是否是锁的拥有者

b)        修改锁的状态

c)        若锁为0,表示释放锁

d)        将锁的拥有者置为空

3)        读锁:获取锁


1.        第一步获得所得状态

2.        若已有排他锁(写锁)占用并且写锁拥有者不是当前线程,怎返回-1;

上面检查完后,至此说明当前占用的是读锁,读锁是可以并发的。

3.        获得共享锁的个数,即读锁的个数;

4.        由于读锁不应该让写锁始终等待,故需要判断是否应该阻塞。

5.        如果readerShouldBlock返回false,且读锁数量小于MAX_COUNT,就可以尝试将读锁数量修改到c + SHARED_UNIT

6.        如果上步骤4中的条件都通过了,就表示读锁获取成功了,接下来的操作就是设置计数信息了。

7.        如果步骤4中条件失败,表示获取读锁失败,阻塞并等待唤醒。

4.        公平模式

1)        写锁:与非公平锁类似,唯一的差异就在于在tryAcquire中对writerShouldBlock的判断:


hasQueuedPredecessors:用于判断等待队列中是否存在等待线程,如果存在等待线程,则厚道的线程将进入等待队列中阻塞等待。

2)        读锁:同上,在是否应阻塞的判断上不一样,也是唯一区别。

 

<think>我们正在讨论Java中的ReentrantReadWriteLock读写。用户要求了解其使用方法和原理。根据之前的回答和引用,我们可以总结如下: 使用方面: - 读写适用于读多写少的场景,通过分离读(共享)和写(独占)提高并发性能。 - 基本使用:创建ReentrantReadWriteLock对象,然后获取读(readLock)和写(writeLock),在操作前后加和解。 - 降级:允许持有写的线程获取读,然后释放写,从而降级为读。但升级(持有读再获取写)是不允许的,因为可能导致死。 原理方面: - 的状态通过一个32位的整数(state)来维护,高16位表示读的持有数量,低16位表示写的重入次数。 - 读是共享,可以有多个线程同时持有;写是独占,同一时间只能有一个线程持有。 - 非公平模式下,写可以插队以减少写线程的等待时间;公平模式下则按照请求顺序分配。 根据引用[1][2][3]: - 引用[1]提到了读写实现缓存、读和写的获取与释放,以及降级(不能升级)。 - 引用[2]强调读写在读多写少场景下比排他(如synchronized)效率高。 - 引用[3]解释了不支持升级的原因:保证数据的可见性。如果多个线程持有读,其中一个线程升级为写并更新数据,这个更新对其他读线程是不可见的,因为其他读线程仍然持有读,无法感知到更新。 因此,我们的回答应该包括: 1. 读写的基本概念和适用场景。 2. 读写的使用方法(示例代码)。 3. 读写的核心原理(状态表示、获取和释放的过程)。 4. 降级的操作和原因,以及为什么不支持升级。 5. 注意事项(如避免升级、公平性选择等)。 此外,我们还要注意用户要求参考站内引用,所以回答中要自然地引用这些内容。 结构安排: - 一、读写概述(包括适用场景) - 二、基本使用方法(代码示例) - 三、核心原理(状态表示、读和写的获取与释放) - 四、降级与升级问题 - 五、注意事项 - 六、总结 最后,生成相关问题。 注意:在回答中,对于原理部分,我们可以用文字描述,必要时用公式表示状态分割(如state的高16位和低16位)。</think>### Java `ReentrantReadWriteLock` 使用与原理详解 `ReentrantReadWriteLock` 是 Java 并发包中的高性能读写,专为**读多写少**的场景设计。通过分离读(共享)和写(独占),显著提升并发性能[^2]。 --- #### 一、核心使用场景与基础用法 **适用场景**:缓存系统、配置中心、资源注册表等读操作占比超 80% 的场景[^2]。 ```java import java.util.concurrent.locks.ReentrantReadWriteLock; public class ThreadSafeCache { private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); private final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock(); private final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock(); private Object data; // 读操作(共享) public Object getData() { readLock.lock(); // 多个线程可同时获取 try { return data; } finally { readLock.unlock(); } } // 写操作(独占) public void updateData(Object newData) { writeLock.lock(); // 仅一个线程可获取 try { data = newData; } finally { writeLock.unlock(); } } } ``` --- #### 二、核心实现原理 1. **状态表示** 通过 32 位 `state` 字段管理状态: - **高 16 位**:读持有数量(最大 65,535) - **低 16 位**:写重入次数(最大 65,535)[^1] ```java // 状态拆分示例 static final int SHARED_SHIFT = 16; static int sharedCount(int c) { return c >>> SHARED_SHIFT; } // 读数量 static int exclusiveCount(int c) { return c & 0xFFFF; } // 写重入次数 ``` 2. **获取规则** - **读**:当无写持有且无写等待时,多个线程可同时获取 - **写**:仅当无任何(读/写)持有时可获取 3. **公平性控制** ```java // 非公平模式(默认,吞吐量高) ReentrantReadWriteLock nonFairLock = new ReentrantReadWriteLock(); // 公平模式(按请求顺序分配ReentrantReadWriteLock fairLock = new ReentrantReadWriteLock(true); ``` --- #### 三、降级机制 **降级**:持有写的线程可获取读(避免其他写线程干扰),再释放写实现降级: ```java writeLock.lock(); try { // 1. 写操作 readLock.lock(); // 降级关键:写→读 } finally { writeLock.unlock(); // 释放写,保留读 } // 此时仍持有读 ``` **禁止升级**(读→写): ```java readLock.lock(); try { writeLock.lock(); // 死!当前线程阻塞等待自身释放读 } finally { ... } ``` **原因**:多个读共存时,若允许升级会导致数据不一致(其他读线程无法感知写操作)[^3]。 --- #### 四、底层同步机制 1. **读同步** - 使用 `Sync` 内部类的 `shared state` 管理 - 通过 `tryAcquireShared()` 实现 CAS 获取读 2. **写同步** - 基于 AQS(AbstractQueuedSynchronizer)的独占模式 - `tryAcquire()` 检查读/写状态 3. **重入实现** ```java // 写重入 writeLock.lock(); writeLock.lock(); // 允许同一线程重复获取 try { ... } finally { writeLock.unlock(); // 需两次解 writeLock.unlock(); } ``` --- #### 五、使用注意事项 1. **避免泄漏** 始终在 `finally` 块解: ```java readLock.lock(); try { /* 操作 */ } finally { readLock.unlock(); } // 确保解 ``` 2. **死预防** - 禁止升级 - 避免嵌套顺序不一致 3. **性能监控** ```java System.out.println(rwLock.toString()); // 输出示例: [Read locks = 3, WriteLocked by thread-1] ``` 4. **替代方案选择** 当写操作频繁时(>20%),优先使用 `ReentrantLock` 或 `synchronized`[^2]。 > **最佳实践**:读写在**读操作占比 ≥80%** 时性能优势显著,否则可能劣于独占[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值