读写锁ReentrantReadWriteLock(转)

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();
}
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值