前言
相关系列
- 《Java & Lock & 目录》
- 《Java & AQS & 目录》
- 《Java & Lock/AQS & ReentrantReadWriteLock & 源码》
- 《Java & Lock/AQS & ReentrantReadWriteLock & 总结》
- 《Java & Lock/AQS & ReentrantReadWriteLock & 问题》
涉及内容
- 《Java & Lock & 总结》
- 《Java & AQS & 总结》
- 《Java & Lock & ReadWriteLock & 总结》
- 《Java & Condition & 总结》
- 《Java & Condition/AQS & ConditionObject & 总结》
- 《Java & synchronized & 总结》
概述
简介
ReentrantReadWriteLock @ 可重入读写锁类是ReadWriteLock @ 读写锁接口的唯一实现类。读写锁接口在API层面定义了读写锁的概念,而可重入读写锁类则实现了这种概念。可重入读写锁类遵循读写锁接口的设计在内部实现了读/写锁两个锁接口实现类用于对读/写操作进行区分管理。这其中读锁类是共享特性的实现,因为读操作并不会对“目标”资源造成修改,因此读写锁允许多线程共同持有读锁及并发访问资源以提升性能;而写锁类则是独占特性的实现,由于写操作会修改“目标”资源的原因,读写锁只允许一条线程持有写锁,并同时阻止其它线程对包含读锁在内一切锁的加锁以确保资源访问的绝对安全。因此如果从这个角度来看,读锁类也是兼具独占特性的锁接口实现类。该知识点会在下文讲解读/写锁时详述。
可重入读写锁类仅能在多核处理器中提升性能。读写锁相对于独占锁的优势在于其解放了读操作的并发性能,而由于单核处理器的并发本质是串行分片执行,因此可重入读写锁在单核处理器中相比独占锁而言并不能取得良好的性能提升。
可重入读写锁类支持公平/非公平两种访问策略。可重入读写锁类支持在创建实例时指定公平策略,从而令线程可以按访问顺序加锁可重入读写锁以满足现实开发中的公平需求。如果在创建时不指定,那么按照默认规则可重入读写锁将是非公平的。非公平可重入读写锁虽然无法保证线程“先到先得”,但整体性能相对于公平可重入读写锁来说却有质的提升。因此如果没有公平需求,那么非公平可重入读写锁应当是实际开发中的首选,该知识点会在下文讲解公平/非公平时详述。
可重入读写锁类同时基于AQS类的独占/共享模式实现。可重入读写锁类虽然是读写锁接口的直接实现类,但实际上却是基于AQS类实现的。所谓基于AQS类实现,是指可重入读写锁类对读/写锁类中锁接口定义方法的重写都是对AQS类内部字段/方法/机制的赋值/重写/调用。但需要注意的是:可重入读写锁类并不是AQS类的子类,大多数基于AQS类实现的API都采用在内部定义/实现单/多个AQS类子类并调用实例的方式来实现自身设计,可重入读写锁类也不例外,该知识点会在下文讲解AQS时详述。
使用
创建
-
public ReentrantReadWriteLock(boolean fair) —— 创建指定访问策略的可重入读写锁。
-
public ReentrantReadWriteLock() —— 创建非公平策略的可重入读写锁,其效果等价于在ReentrantReadWriteLock(boolean fair)方法中传入false。
读/写锁
-
public Lock readLock() —— 读锁 —— 获取当前可重入读写锁的读锁。
-
public Lock writeLock() —— 写锁 —— 获取当前可重入读写锁的写锁。
检查
-
public final boolean isFair() —— 是否公平 —— 判断当前可重入读写锁是否为公平策略,是则返回true;否则返回false。
-
protected Thread getOwner() —— 获取拥有者 —— 获取当前可重入读写锁写锁的独占持有线程。由于实际独占持有线程可能在获取期间发生变化,因此该方法的返回值无法作为准确的判断依据,除非调用线程是独占持有线程本身。
-
public int getReadLockCount() —— 获取读锁总数 —— 获取当前可重入读写锁的读锁持有总数。由于实际读锁持有总数可能在获取期间发生变化,因此该方法的返回值无法作为准确的判断依据。
-
public int getReadHoldCount() —— 获取读锁持有总数 —— 获取当前线程对当前可重入读写锁的读锁持有总数。
-
public boolean isWriteLocked() —— 是否写锁 —— 判断当前可重入读写锁的写锁是否已被独占持有,是则返回true;否则返回false。
-
public boolean isWriteLockedByCurrentThread() —— 是否被当前线程写锁 —— 判断当前可重入读写锁的写锁是否已被当前线程独占持有,是则返回true;否则返回false。
-
public int getWriteHoldCount() —— 获取写锁持有总数 —— 获取当前线程对当前可重入读写锁的写锁持有总数。
-
protected Collection getQueuedThreads() —— 获取排队线程集 —— 获取等待持有当前可重入读写锁的线程估计集。由于线程的实际等待状态可能在获取期间发生变化,因此该方法的返回值无法作为准确的判断依据。
-
protected Collection getQueuedWriterThreads() —— 获取排队写线程集 —— 获取等待独占持有当前可重入读写锁写锁的线程估计集。由于线程的实际等待状态可能在获取期间发生变化,因此该方法的返回值无法作为准确的判断依据。
-
protected Collection getQueuedReaderThreads() —— 获取排队读线程集 —— 获取等待共享持有当前可重入读写锁读锁的线程估计集。由于线程的实际等待状态可能在获取期间发生变化,因此该方法的返回值无法作为准确的判断依据。
-
public final boolean hasQueuedThreads() —— 存在排队线程 —— 判断是否存在线程正在等待持有当前可重入读写锁,是则返回true;否则返回false。由于线程的实际等待状态可能在判断期间发生变化,因此该方法的返回值无法作为准确的判断依据。
-
public final boolean hasQueuedThread(Thread thread) —— 存在排队线程 —— 判断指定线程是否正在等待持有当前可重入读写锁,是则返回true;否则返回false。由于线程的实际等待状态可能在判断期间发生变化,因此该方法的返回值无法作为准确的判断依据。
-
public final int getQueueLength() —— 获取队列长度 —— 获取等待持有当前可重入读写锁的线程总数估计值。由于线程的实际等待状态可能在获取期间发生变化,因此该方法的返回值无法作为准确的判断依据。
-
public boolean hasWaiters(Condition condition) —— 存在等待者 —— 判断在指定条件中是否存在线程正在等待恢复当前可重入读写锁写锁的独占持有,是则返回true;否则返回false。由于线程的实际等待状态可能在判断期间发生变化,因此该方法的返回值无法作为准确的判断依据。此外如果当前可重入读写锁的写锁未被独占持有则方法会抛出非法监视器异常;如果指定条件为null则方法会抛出空指针异常;如果指定条件不基于当前可重入读写锁写锁创建则方法会抛出非法参数异常。
-
public int getWaitQueueLength(Condition condition) —— 获取等待队列长度 —— 获取在指定条件中等待恢复对当前可重入读写锁写锁的独占持有的线程总数估计值。由于线程的实际等待状态可能在判断期间发生变化,因此该方法的返回值无法作为准确的判断依据。此外如果当前可重入读写锁的写锁未被独占持有则方法会抛出非法监视器异常;如果指定条件为null则方法会抛出空指针异常;如果指定条件不基于当前可重入读写锁写锁创建则方法会抛出非法参数异常。
-
protected Collection getWaitingThreads(Condition condition) —— 获取等待线程集 —— 获取在指定条件中等待恢复当前可重入读写锁独占持有的线程估计集。由于线程的实际等待状态可能在判断期间发生变化,因此该方法的返回值无法作为准确的判断依据。此外如果当前可重入读写锁的写锁未被独占持有则方法会抛出非法监视器异常;如果指定条件为null则方法会抛出空指针异常;如果指定条件不基于当前可重入读写锁写锁创建则方法会抛出非法参数异常。
实现
读/写锁
可重入读写锁类内部实现了ReadLock/WriteLock @ 读锁/写锁两个Lock @ 锁接口实现类。可重入读写锁类遵循读写锁接口的设计在内部实现了读/写锁两个锁接口实现类用于对读/写操作进行区分管理。这其中读锁类是共享特性的实现,因为读操作并不会对“目标”资源造成修改,因此读写锁允许多线程共同持有读锁及并发访问资源以提升性能;而写锁类则是独占特性的实现,由于写操作会修改“目标”资源的原因,读写锁只允许一条线程持有写锁,并同时阻止其它线程对包含读锁在内一切锁的加锁以确保资源访问的绝对安全。因此如果从这个角度来看,读锁类也是兼具独占特性的锁接口实现类。
public class ReentrantReadWriteLock implements ReadWriteLock, Serializable {
...
/**
* The lock returned by method {@link ReentrantReadWriteLock#readLock}.
* 该锁通过ReentrantReadWriteLock#readLock方法返回。
*/
public static class ReadLock implements Lock, Serializable {