/*public class ReentrantReadWriteLock extends Object implements ReadWriteLock, Serializable */
/*嵌套类摘要 static class ReentrantReadWriteLock.ReadLock readLock() 方法返回的锁。 static class ReentrantReadWriteLock.WriteLock writeLock() 方法返回的锁。 */
API的两个例子:
class CachedData { Object data; volatile boolean cacheValid; 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(); // 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(); rwl.writeLock().unlock(); // Unlock write, still hold read } use(data); rwl.readLock().unlock(); } }
class RWDictionary { private final Map<String, Data> m = new TreeMap<String, Data>(); 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(); } } }}
重入
此锁允许 reader 和 writer 按照 ReentrantLock 的样式重新获取读取锁或写入锁。在写入线程保持的所有写入锁都已经释放后,才允许重入 reader 使用它们。
此外,writer 可以获取读取锁,但反过来则不成立。在其他应用程序中,当在调用或回调那些在读取锁状态下执行读取操作的方法期间保持写入锁时,重入很有用。如果 reader 试图获取写入锁,那么将永远不会获得成功。
锁降级
重入还允许从写入锁降级为读取锁,其实现方式是:先获取写入锁,然后获取读取锁,最后释放写入锁。但是,从读取锁升级到写入锁是不可能的。
rwl.readLock().lock(); if (!cacheValid) { // Must release read lock before acquiring write lock rwl.readLock().unlock(); rwl.writeLock().lock(); if (!cacheValid) { data = ... cacheValid = true; } rwl.readLock().lock(); rwl.writeLock().unlock(); // Unlock write, still hold read }
锁获取的中断
读取锁和写入锁都支持锁获取期间的中断。
Condition 支持
写入锁提供了一个 Condition 实现,对于写入锁来说,该实现的行为与 ReentrantLock.newCondition() 提供的 Condition 实现对 ReentrantLock 所做的行为相同。当然,此 Condition 只能用于写入锁。
读取锁不支持 Condition,readLock().newCondition() 会抛出 UnsupportedOperationException。
JDK:http://www.cjsdn.net/Doc/JDK60/java/util/concurrent/locks/ReentrantReadWriteLock.html
ReentrantReadWriteLock竞争条件:
ReentrantReadWriteLock会使用两把锁来解决问题,一个读锁,一个写锁
线程进入读锁的前提条件:
没有其他线程的写锁,
没有写请求或者有写请求,但调用线程和持有锁的线程是同一个
线程进入写锁的前提条件:
没有其他线程的读锁
没有其他线程的写锁
如果【有一个线程加了写锁,那么其他线程就不能加写锁了】,同一时间只能允许一个线程加写锁。因为加了写锁就意味着有人要写一个共享数据,那同时就不能让其他人来写这个数据了。
同时【如果有线程加了写锁,其他线程就不能加读锁】了,因为既然都有人在写数据了,你其他人当然不能来读数据了!
如果【有一个线程加了读锁,别的线程是可以随意同时加读锁】的,因为只是有线程在读数据而已,此时别的线程也是可以来读数据的!
同理,如果【一个线程加了读锁,此时其他线程是不可以加写锁】的,因为既然有人在读数据,那就不能让你随意来写数据了!
说到ReentrantReadWriteLock,首先要做的是与ReentrantLock划清界限。它和后者都是单独的实现,彼此之间没有继承或实现的关系。然后就是总结这个锁机制的特性了:
(a).重入方面WriteLock可以获取ReadLock,但是反过来ReadLock想要获得WriteLock则永远都不要想。
(b).WriteLock可以降级为ReadLock,顺序是:先获得WriteLock再获得ReadLock,然后释放WriteLock,这时候线程将保持Readlock的持有。反过来ReadLock想要升级为WriteLock则不可能,为什么?参看(a),呵呵.
(c).ReadLock可以被多个线程持有并且在作用时排斥任何的WriteLock,而WriteLock则是完全的互斥。这一特性最为重要,因为对于高读取频率而相对较低写入的数据结构,使用此类锁同步机制则可以提高并发量。
(d).不管是ReadLock还是WriteLock都支持Interrupt,语义与ReentrantLock一致。
(e).WriteLock支持Condition并且与ReentrantLock语义一致,而ReadLock则不能使用Condition,否则抛出UnsupportedOperationException异常。
以上就是比较重要的,或者衡量是否使用ReentrantReadWriteLock的基础了。下面还是写个小例子说明部分内容:
- import java.util.HashMap;
- import java.util.Map;
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantReadWriteLock;
- /**
- * @author: yanxuxin
- * @date: 2010-1-7
- */
- public class ReentrantReadWriteLockSample {
- public static void main(String[] args) {
- testReadLock();
- // testWriteLock();
- }
- public static void testReadLock() {
- final ReadWriteLockSampleSupport support = new ReadWriteLockSampleSupport();
- support.initCache();
- Runnable runnable = new Runnable() {
- public void run() {
- support.get("test");
- }
- };
- new Thread(runnable).start();
- new Thread(runnable).start();
- new Thread(new Runnable() {
- public void run() {
- support.put("test", "test");
- }
- }).start();
- }
- public static void testWriteLock() {
- final ReadWriteLockSampleSupport support = new ReadWriteLockSampleSupport();
- support.initCache();
- new Thread(new Runnable() {
- public void run() {
- support.put("key1", "value1");
- }
- }).start();
- new Thread(new Runnable() {
- public void run() {
- support.put("key2", "value2");
- }
- }).start();
- new Thread(new Runnable() {
- public void run() {
- support.get("key1");
- }
- }).start();
- }
- }
- class ReadWriteLockSampleSupport {
- private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
- private final Lock readLock = lock.readLock();
- private final Lock writeLock = lock.writeLock();
- private volatile boolean completed;
- private Map<String,String> cache;
- public void initCache() {
- readLock.lock();
- if(!completed) {
- // Must release read lock before acquiring write lock
- readLock.unlock(); // (1)
- writeLock.lock(); // (2)
- if(!completed) {
- cache = new HashMap<String,String>(32);
- completed = true;
- }
- // Downgrade by acquiring read lock before releasing write lock
- readLock.lock(); // (3)
- writeLock.unlock(); // (4) Unlock write, still hold read
- }
- System.out.println("empty? " + cache.isEmpty());
- readLock.unlock();
- }
- public String get(String key) {
- readLock.lock();
- System.out.println(Thread.currentThread().getName() + " read.");
- startTheCountdown();
- try{
- return cache.get(key);
- }
- finally{
- readLock.unlock();
- }
- }
- public String put(String key, String value) {
- writeLock.lock();
- System.out.println(Thread.currentThread().getName() + " write.");
- startTheCountdown();
- try{
- return cache.put(key, value);
- }
- finally {
- writeLock.unlock();
- }
- }
- /**
- * A simple countdown,it will stop after about 5s.
- */
- public void startTheCountdown() {
- long currentTime = System.currentTimeMillis();
- for(;;) {
- long diff = System.currentTimeMillis() - currentTime;
- if(diff > 5000) {
- break;
- }
- }
- }
- }
这个例子改造自JDK的API提供的示例,其中ReadWriteLockSampleSupport辅助类负责维护一个Map,当然前提是这个Map大部分的多线程下都是读取,只有很少的比例是多线程竞争修改Map的值。其中的initCache()简单的说明了特性(a),(b).在这个方法中如果把注释(1)和(2)处的代码调换位置,就会发现轻而易举的死锁了,当然是因为特性(1)的作用了。而注释(3),(4)处的代码位置则再次证明了特性(a),并且有力的反映了特性(b)--WriteLock在cache初始化完毕之后,降级为ReadLock。另外get(),put()方法在线程获取锁之后会在方法中呆上近5s的时间。
ReentrantReadWriteLockSample中的两个静态测试方法则分别测试了ReadLock和WriteLock的排斥性。testReadLock()中,开启三个线程,前两者试图获取ReadLock而后者去获取WriteLock。执行结果可以看到:ReadWriteLockSampleSupport的get()方法中的打印结果在前两个线程中几乎同时显示,而put()中的打印结果则要等上近5s。这就说明了,ReadLock可以多线程持有并且排斥WriteLock的持有线程。testWriteLock()中,也开启三个线程。前两个是去获取WriteLock,最后一个获取ReadLock。执行的结果是三个打印结果都有近5s的间隔时间,这说明了WriteLock是独占的,比较独!
来源:http://yanxuxin.iteye.com/blog/571480
使用ReentrantReadWriteLock可以推广到大部分读,少量写的场景,因为读线程之间没有竞争,所以比起sychronzied,性能好很多。
相关推荐
读写锁ReentrantReadWriteLock&StampLock详解_e读写锁ReentrantReadWriteLock&StampLock详解_e读写锁ReentrantReadWriteLock&StampLock详解_e读写锁ReentrantReadWriteLock&StampLock详解_e读写锁...
【深入浅出ReentrantReadWriteLock源码解析】 ReentrantReadWriteLock是Java并发包中的一个核心类,它提供了读写锁的实现,使得多个线程可以并发读取共享资源,但写操作是互斥的,即同一时间只能有一个线程进行写...
根据提供的文件信息,本文将详细解析读写锁`ReentrantReadWriteLock`以及`StampLock`在Java并发编程中的应用场景及其实现原理。 ### 一、读写锁介绍 #### 1.1 读写锁的基本概念 读写锁是一种特殊的锁机制,它可以...
在Java多线程并发编程中,ReentrantReadWriteLock(可重入读写锁)是一个重要的同步工具,它属于Java并发包(java.util.concurrent.locks)中的一个类。这个锁提供了比标准的synchronized关键字更细粒度的控制,允许...
ReadWriteLock 接口及其实现 ReentrantReadWriteLock 方法 在 Java 并发编程中,锁机制是非常重要的一种同步机制,用于解决多线程之间的资源竞争问题。在 Java 中,有多种锁机制,如 ReentrantLock、...
在学习Java过程中,自己收集了很多的Java的学习资料,分享给大家,有需要的欢迎下载,希望对大家有用,一起学习,一起进步。
Java的ReentrantReadWriteLock是Java并发包`java.util.concurrent.locks`中的一个重要工具,它提供了一种更细粒度的锁定机制,相比普通的独占锁(如ReentrantLock)在某些场景下能显著提高程序的并发性能。...
6.5 深入理解 AQS之 ReentrantReadWritelock 实战副本.mp4
6.5 深入理解 AQS之 ReentrantReadWritelock 实战副本副本.mp4
ReentrantReadWriteLock 读写锁除了保证写操作对读操作可见性以及并发行提升外,简化了读写交互场景开发
ReadWriteLock的使用,实际上由于ReadWriteLock是一个接口,所以实际使用的是ReentrantReadWriteLock子类。同时ReadWriteLock的使用其实也是比较简单的,就是读写的锁的使用以及注意事项而已。
`ReentrantLock`和`ReentrantReadWriteLock`是Java并发包`java.util.concurrent.locks`中的两个重要工具,它们提供了比标准`synchronized`关键字更高级别的锁控制机制。本文将深入探讨这两个类,以及如何通过配置...
本篇文章主要介绍了Java concurrency之共享锁和ReentrantReadWriteLock,非常具有实用价值,需要的朋友可以参考下
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(); ``` 然后通过以下方式获取读锁和写锁: ```java ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock...
本文将深入探讨Java中的两种读写锁:ReentrantReadWriteLock和StampedLock,并分析它们的工作原理、特点以及如何在实际开发中进行应用。 一、ReentrantReadWriteLock(可重入读写锁) 1. **简介**: ...
内容包括 01-并发编程之深入理解JMM&并发三大特性(一)-fox 02-并发编程之深入理解JMM&并发三...11-深入理解AQS之CyclicBarrier&ReentrantReadWriteLock详解-fox 12-深入理解AQS之ReentrantReadWriteLock详解-fox ...
ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); ReadLock readLock = lock.readLock(); WriteLock writeLock = lock.writeLock(); ``` 2. **读者部分**:当一个线程想要读取数据时,它需要获取读锁...
ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); // 公平锁 ``` 然后,我们可以定义读操作和写操作的方法: 对于读者: ```java public void read() { lock.readLock().lock(); try { // 读取...
- `ReentrantReadWriteLock`是Java提供的可重入的读写锁实现,它继承自`AbstractQueuedSynchronizer`(AQS)。可重入意味着一个线程可以多次获取同一锁,这在递归调用或者锁嵌套时很有用。 - **公平性**:`...