http://hubingforever.blog.163.com/blog/static/17104057920108281216994/
读写锁ReentrantReadWriteLock
2010-09-28 13:02:16| 分类: JAVA线程安全 |字号 订阅
ReentrantReadWriteLock是ReentrantLock 类似语义的 ReadWriteLock 实现。
此类具有以下属性:
* 获取顺序
此类不会将读取者优先或写入者优先强加给锁访问的排序。但是,它确实支持可选的公平 策略。
非公平模式(默认)
当非公平地(默认)构造时,未指定进入读写锁的顺序,受到 reentrancy 约束的限制。
连续竞争的非公平锁可能无限期地推迟一个或多个 reader 或 writer 线程,但吞吐量通常要高于公平锁。
公平模式
当公平地构造线程时,线程利用一个近似到达顺序的策略来争夺进入。当释放当前保持的锁时,
可以为等待时间最长的单个 writer 线程分配写入锁,如果有一组等待时间大于所有正在等待的 writer 线程 的 reader 线程,将为该组分配写入锁。
如果保持写入锁,或者有一个等待的 writer 线程,则试图获得公平读取锁(非重入地)的线程将会阻塞。
直到当前最旧的等待 writer 线程已获得并释放了写入锁之后,该线程才会获得读取锁。
当然,如果等待 writer 放弃其等待,而保留一个或更多 reader 线程为队列中带有写入锁自由的时间最长的 waiter,
则将为那些 reader 分配读取锁。
试图获得公平写入锁的(非重入地)的线程将会阻塞,除非读取锁和写入锁都自由(这意味着没有等待线程)。
(注意,非阻塞 ReentrantReadWriteLock.ReadLock.tryLock() 和 ReentrantReadWriteLock.WriteLock.tryLock() 方法不会遵守此公平设置,并将获得锁(如果可能),不考虑等待线程)。
注意1:reader线程除了要获得读取锁外,还必须要写入锁没分配给其他任何线程,写入是完全自由的。读取锁可以同时分配给多个reader线程,
注意2:writer线程除了要获得写入锁外,还必须要读取锁没分配给任何线程,读取锁必须是完全自由。写入锁只能分配给一个writer线程。
注意3:当释放当前保持的写入锁时,会为等待最长的线程分配写入锁。可能是非显式的分配给reader线程,也可能是显式的分配给writer线程。
如果是分配给reader线程的话,应该是分配给所有等待的reader线程。
注意4:"重入"就是递归的意思
* 重入(递归)
此锁允许 reader 和 writer 按照 ReentrantLock 的样式重新获取读取锁或写入锁。
对于非重入reader,在写入线程保持的所有写入锁都已经释放后,才允许使用它们。
此外,writer可以获取读取锁,但是这种做法是不推荐的。
在其他应用程序中,当在调用或回调那些在读取锁状态下执行读取操作的方法期间保持写入锁时,重入很有用。
如果reader试图获取写入锁,那么将永远不会获得成功。
writer可以获取读取锁(即先获取写锁,再获得读锁),虽然不推荐。
比如:
public int getValue()
{
wl.lock();
rl.lock();
try{
return value;
}finally
{
wl.unlock();
rl.unlock();
}
}
reader无法获取写入锁(即先获取读锁,再获得写锁)。
这种显式获得的写锁和自动获得的写锁是不一样的。
reader无法获取写入锁(即先获取读锁,再获得写锁)
比如下面的代码在@1处,就永远不能返回。
public int getValue()
{
rl.lock();
wl.lock();//@1
try{
return value;
}finally
{
wl.unlock();
rl.unlock();
}
}
注意3:不仅读锁的是可递归的,写锁也是可递归的。这点和POSIX标准不一样
而在POSIX标准中,读锁是递归锁(即可重入),写锁是非递归锁(即不可重入)。
关于POSIX标准中的递归锁详细请参见《可递归锁与非递归锁》
public int increment()
{
wl.lock();
try{
return ++value;
}finally
{
wl.unlock();
}
}
public void incrementTen()
{
wl.lock();
try{
for(int i=0;i<10;i++)
increment();
}finally
{
wl.unlock();
}
}
注意4:总的来说无论读取锁何写锁都不支持同时在reader和writer之间共享。
但是读取锁支持在reader之间共享,它也可以在writer之间共享。写锁只能在reader中共享,它在writer中式独占的。
* 锁降级
重入还允许从写入锁降级为读取锁,其实现方式是:先获取写入锁,然后获取读取锁,最后释放写入锁。但是,从读取锁升级到写入锁是不可能的。
注意1:"从读取锁升级到写入锁是不可能",因为reader无法获取写入锁(即先获取读锁,再获得写锁)
* 锁获取的中断
读取锁和写入锁都支持锁获取期间的中断。
注意1:读取锁和写入锁都支持锁获取期间的中断,即InterruptedException。
* Condition 支持
写入锁提供了一个 Condition 实现,对于写入锁来说,该实现的行为与 ReentrantLock.newCondition() 提供的 Condition 实现对 ReentrantLock 所做的行为相同。当然,此Condition 只能用于写入锁。
读取锁不支持 Condition,readLock().newCondition() 会抛出 UnsupportedOperationException。
注意1:只有写锁提供了Condition 实现,而读取锁没有提供。
* 监测
此类支持一些确定是保持锁还是争用锁的方法。这些方法设计用于监视系统状态,而不是同步控制。
此类行为的序列化方式与内置锁的相同:反序列化的锁处于解除锁状态,无论序列化该锁时其状态如何。
示例用法。下面的代码展示了如何利用重入来执行升级缓存后的锁降级(为简单起见,省略了异常处理):
class CachedData {
Object data;
volatile boolean cacheValid;
final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
void processCachedData() {
rwl.readLock().lock();
if (!cacheValid) {
// Must release read lock before acquiring write lock
rwl.readLock().unlock();
rwl.writeLock().lock();
try {
// Recheck state because another thread might have
// acquired write lock and changed state before we did.
if (!cacheValid) {
data = ...
cacheValid = true;
// Downgrade by acquiring read lock before releasing write lock
rwl.readLock().lock();
} finally {
rwl.writeLock().unlock(); // Unlock write, still hold read
}
}
try {
use(data);
} finally {
rwl.readLock().unlock();
}
}
}}
在使用某些种类的 Collection 时,可以使用 ReentrantReadWriteLock 来提高并发性。
通常,在预期 collection 很大,读取者线程访问它的次数多于写入者线程,并且 entail 操作的开销高于同步开销时,这很值得一试。
例如,以下是一个使用 TreeMap 的类,预期它很大,并且能被同时访问。
class RWDictionary {
private final Map m = new TreeMap();
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();
public Data get(String key) {
r.lock();
try { return m.get(key);
finally { r.unlock(); }
}
public String[] allKeys() {
r.lock();
try { return m.keySet().toArray(); }
finally { r.unlock(); }
}
public Data put(String key, Data value) {
w.lock();
try { return m.put(key, value); }
finally { w.unlock(); }
}
public void clear() {
w.lock();
try { m.clear(); }
finally { w.unlock(); }
}
}}
实现注意事项:
此锁最多支持 65535 个递归写入锁和 65535 个读取锁。试图超出这些限制将导致锁方法抛出 Error
主要函数:
protected Thread getOwner()
返回当前拥有写入锁的线程,如果没有这样的线程,则返回 null。当通过不是所有者的线程调用此方法时,返回值反映当前锁状态的最接近近似值。例如,即使存在试图获得锁的线程,但是在它还没有获得前,所有者可能暂时为 null。设计此方法是为了便于构造提供更多扩展的锁监视设施的子类。
返回:
所有者;如果没有所有者,则返回 null
public int getReadLockCount()
查询为此锁保持的读取锁数量。此方法设计用于监视系统状态,而不是同步控制。
返回:
所保持的读取锁数量。
注意1:这里是指所有线程保持读取锁的数量。测试用例见例1
public boolean isWriteLocked()
查询是否某个线程保持了写入锁。此方法设计用于监视系统状态,而不是同步控制。
返回:
如果某个线程保持写入锁,则返回 true;否则返回 false
public boolean isWriteLockedByCurrentThread()
查询当前线程是否保持了写入锁。
返回:
如果当前线程保持写入锁,则返回 true;否则返回 false
public int getWriteHoldCount()
查询当前线程在此锁上保持的重入写入锁数量。对于与解除锁操作不匹配的每个锁操作,writer 线程都会为其保持一个锁。
返回:
当前线程保持的写入锁数量,如果当前线程从未保持过写入锁,则返回 0
注意1:只是当前线程保持的写入锁数量。测试用例见例1。
public int getReadHoldCount()
查询当前线程在此锁上保持的重入读取锁数量。对于与解除锁操作不匹配的每个锁操作,reader 线程都会为其保持一个锁。
返回:
当前线程保持的读取锁数量;如果当前线程从未保持过读取锁,则返回 0
注意2:只是当前线程保持的读取锁数量。测试用例见例1
protected Collection<Thread> getQueuedWriterThreads()
返回一个 collection,它包含可能正在等待获取写入锁的线程。因为在构成此结果的同时,实际的线程 set 可能不断发生变化,
所以返回的 collection 仅是尽力而为获得的估计值。所返回 collection 的元素没有特定的顺序。
设计此方法是为了便于构造提供更多扩展的锁监视器设施的子类。
返回:
线程的 collection
protected Collection<Thread> getQueuedReaderThreads()
返回一个 collection,它包含可能正在等待获取读取锁的线程。因为在构成此结果的同时,实际的线程 set 可能不断发生变化,
所以返回的 collection 仅是尽力而为获得的估计值。所返回 collection 的元素没有特定的顺序。
设计此方法是为了便于构造提供更多扩展的锁监视器设施的子类。
返回:
线程的 collection
public final boolean hasQueuedThreads()
查询是否所有的线程正在等待获取读取或写入锁。注意,因为随时可能发生取消操作,所以返回 true 并不保证任何其他线程将获取锁。
此方法主要用于监视系统状态。
返回:
如果有其他线程正等待获取锁,则返回 true
public final boolean hasQueuedThread(Thread thread)
查询是否给定线程正在等待获取读取或写入锁。注意,因为随时可能发生取消操作,所以返回 true 并不保证此线程将获取锁。
此方法主要用于监视系统状态。
参数:
thread - 线程
返回:
如果将给定的线程加入等待此锁的队列,则返回 true
抛出:
NullPointerException - 如果线程为 null
public final int getQueueLength()
返回等待获取读取或写入锁的线程估计数目。因为在此方法遍历内部数据结构时,可以动态地更改线程数,所以该值只能是一个估计值。
此方法设计用于监视系统状态,而不是同步控制。
返回:
正在等待此锁的线程估计数目
protected Collection<Thread> getQueuedThreads()
返回一个 collection,它包含可能正在等待获取读取或写入锁的线程。因为在构造此结果的同时,实际的线程 set 可能不断发生变化,
所以返回的 collection 仅是尽力而为获得的估计值。所返回 collection 中的元素没有特定的顺序。
此方法用于加快子类的构造速度,提供更多的监视设施。
返回:
线程的 collection
public boolean hasWaiters(Condition condition)
查询是否有些线程正在等待与写入锁有关的给定条件。注意,因为随时可能发生超时和中断,所以返回 true 并不保证将来某个 signal 将唤醒任何线程。此方法主要用于监视系统状态。
参数:
condition - 条件
返回:
如果有等待的线程,则返回 true
抛出:
IllegalMonitorStateException - 如果没有保持此锁
IllegalArgumentException - 如果给定的条件与此锁无关
NullPointerException - 如果条件为 null
public int getWaitQueueLength(Condition condition)
返回正等待与写入锁相关的给定条件的线程估计数目。注意,因为随时可能发生超时和中断,所以只能将估计值作为实际等待线程数的上限。此方法设计用于监视系统状态,而不是同步控制。
参数:
condition - 条件
返回:
等待线程的估计数
抛出:
IllegalMonitorStateException - 如果没有保持此锁
IllegalArgumentException - 如果给定的条件与此锁无关
NullPointerException - 如果条件为 null
protected Collection<Thread> getWaitingThreads(Condition condition)
返回一个 collection,它包含可能正在等待与写入锁相关的给定条件的那些线程。因为在构造此结果的同时,实际的线程 set 可能不断发生变化,所以返回的 collection 仅是尽力而为获得的估计值。所返回 collection 中的元素没有特定的顺序。此方法用于加快子类的构造速度,提供更多的条件监视设施。
参数:
condition - 条件
返回:
线程的 collection
抛出:
IllegalMonitorStateException - 如果没有保持此锁
IllegalArgumentException - 如果给定 condition 与此锁无关
NullPointerException - 如果条件为 null
例1:
final static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
final static ReadLock rl=rwl.readLock();
final static WriteLock wl=rwl.writeLock();
static void LockDemo()
{
Counter counter=new Counter();
counter.getValues();
wl.lock();
wl.lock();
System.out.println("getReadLockCount:"+rwl.getReadLockCount());
System.out.println("getWriteHoldCount:"+rwl.getWriteHoldCount()+"getReadHoldCount:"+rwl.getReadHoldCount());
wl.unlock();
wl.unlock();
rl.lock();
rl.lock();
System.out.println("------------------");
System.out.println("getReadLockCount:"+rwl.getReadLockCount());
System.out.println("getWriteHoldCount:"+rwl.getWriteHoldCount()+"getReadHoldCount:"+rwl.getReadHoldCount());
rl.unlock();
rl.unlock();
for(int i=0;i<10;i++)
{
new LockDemoThread(counter).start();
}
}
class LockDemoThread extends Thread
{
Counter c=null;
LockDemoThread(Counter c)
{
this.c=c;
}
public void run()
{
ReentrantReadWriteLock rwl = c.rwl;
int k=0;
while(k++<100)
{
int v=c.getValue();
int readLockCount=rwl.getReadLockCount();
int readHoldCount=rwl.getReadHoldCount();
System.out.println("readLockCount:"+readLockCount+"readHoldCount:"+readHoldCount);
Thread.yield();
}
}
}
class Counter {
private int value;
final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
ReadLock rl=rwl.readLock();
WriteLock wl=rwl.writeLock();
public int getValue()
{
rl.lock();
rl.lock();
try{
Thread.sleep(100);
System.out.println("getReadLockCount:"+rwl.getReadLockCount());
System.out.println("getWriteHoldCount:"+rwl.getWriteHoldCount()+"getReadHoldCount:"+rwl.getReadHoldCount());
return value;
} catch(InterruptedException e)
{
return value;
}
finally
{
rl.unlock();
rl.unlock();
}
}
public void getValues()
{
rl.lock();
try{
for(int i=0;i<10;i++)
getValue();
}finally
{
rl.unlock();
}
}
public int increment()
{
wl.lock();
try{
return ++value;
}finally
{
wl.unlock();
}
}
public void incrementTen()
{
wl.lock();
try{
for(int i=0;i<10;i++)
increment();
}finally
{
wl.unlock();
}
}
public int decrement() {
wl.lock();
try{
return --value;
}finally
{
wl.unlock();
}
}
}
分享到:
相关推荐
读写锁ReentrantReadWriteLock&StampLock详解_e读写锁ReentrantReadWriteLock&StampLock详解_e读写锁ReentrantReadWriteLock&StampLock详解_e读写锁ReentrantReadWriteLock&StampLock详解_e读写锁...
根据提供的文件信息,本文将详细解析读写锁`ReentrantReadWriteLock`以及`StampLock`在Java并发编程中的应用场景及其实现原理。 ### 一、读写锁介绍 #### 1.1 读写锁的基本概念 读写锁是一种特殊的锁机制,它可以...
在Java的`java.util.concurrent.locks`包中,`ReentrantReadWriteLock`类实现了读写锁的功能。这个锁允许多个读取者同时访问资源,但在有写入者时,所有读取者和写入者都会被阻塞,以确保数据的一致性。下面我们将...
在Java中,`java.util.concurrent.locks.ReentrantReadWriteLock`是标准的读写锁实现。这个类提供了可重入的特性,意味着一个线程在获得读锁或写锁后,可以再次请求相同的锁而不会被阻塞。这在处理递归调用时特别...
在Java中,`java.util.concurrent.locks`包下的`ReadWriteLock`接口提供了读写锁的抽象定义,具体实现由`ReentrantReadWriteLock`类提供。`ReentrantReadWriteLock`实现了`ReadWriteLock`接口,提供了`readLock()`和...
本文将深入探讨Java中的两种读写锁:ReentrantReadWriteLock和StampedLock,并分析它们的工作原理、特点以及如何在实际开发中进行应用。 一、ReentrantReadWriteLock(可重入读写锁) 1. **简介**: ...
本文主要介绍了 Java 读写锁实现原理浅析,包括读写锁的定义、读写锁的实现原理、ReentrantReadWriteLock 的实现机制等。 读写锁的定义 读写锁是一种机制,允许多个线程安全地访问共享资源。读写锁分为读锁和写锁...
读写锁的概念源于操作系统理论,但在许多编程语言中都有实现,如Java的`java.util.concurrent.locks.ReentrantReadWriteLock`和C++的`std::shared_timed_mutex`。下面将详细探讨读写锁的工作原理、优缺点以及如何在...
本文将深入探讨标题和描述中提及的各种锁,包括乐观锁、悲观锁、分布式锁、可重入锁、互斥锁、读写锁、分段锁、类锁以及行级锁。 1. **乐观锁**:乐观锁假设多线程环境中的冲突较少,所以在读取数据时不加锁,只有...
在Java中,`java.util.concurrent.locks.ReentrantReadWriteLock`类提供了可重入的读写锁实现。而在C++中,标准库中的`std::shared_mutex`和`std::unique_lock`可以用来实现读写锁。这些库提供了灵活的接口,允许...
在Java多线程并发编程中,ReentrantReadWriteLock(可重入读写锁)是一个重要的同步工具,它属于Java并发包(java.util.concurrent.locks)中的一个类。这个锁提供了比标准的synchronized关键字更细粒度的控制,允许...
ReentrantReadWriteLock是Java并发包中的一个核心类,它提供了读写锁的实现,使得多个线程可以并发读取共享资源,但写操作是互斥的,即同一时间只能有一个线程进行写操作。这种设计大大提高了多线程环境下对共享资源...
读写锁的实现可以使用ReentrantReadWriteLock类,该类提供了读锁和写锁的实现。 读写锁的使用场景是当读操作远远大于写操作时,读写锁可以提供比排它锁更好的并发量和吞吐量。例如,在一个队列中,读操作远远大于写...
Java 5开始,`java.util.concurrent.locks`包提供了`ReentrantReadWriteLock`类来实现可重入的读写锁。 1. **读/写锁的Java实现**: `ReentrantReadWriteLock`类提供了读锁(`ReadLock`)和写锁(`WriteLock`)两...
Java并发编程之重入锁与读写锁 在 Java 并发编程中,重入锁和读写锁是两个非常重要的概念。重入锁是指支持重进入的锁,也就是说,一个线程可以多次获取同一个锁,而不会被阻塞。读写锁则是维护了一对相关的锁,一...
在Java中,`java.util.concurrent.locks`包提供了`ReentrantReadWriteLock`类来实现可重入的读写锁。这个类包含两个核心方法:`readLock()`和`writeLock()`,分别用于获取读锁和写锁。读锁和写锁都是可重入的,这...
Java的`java.util.concurrent.locks.ReentrantReadWriteLock`类提供了可重入的读写锁功能。下面我们将详细探讨读写锁的使用以及其在示例中的应用。 读写锁的核心特性: 1. **读锁(共享锁)**:多个线程可以同时...
ReentrantLock//互斥锁 class CachedData { Object data; volatile boolean cacheValid; ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
读写锁:在多线程环境下,读写锁(如Java中的ReentrantReadWriteLock)提供了一种优化方案。它允许多个线程同时读取数据,但仅允许一个线程写入。当写锁被持有时,其他写操作和读操作都会被阻塞,确保数据的一致性。...