`
uule
  • 浏览: 6337877 次
  • 性别: Icon_minigender_1
  • 来自: 一片神奇的土地
社区版块
存档分类
最新评论

ReentrantReadWriteLock

 
阅读更多

 

/*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的基础了。下面还是写个小例子说明部分内容: 

Java代码   收藏代码
  1. import java.util.HashMap;  
  2. import java.util.Map;  
  3. import java.util.concurrent.locks.Lock;  
  4. import java.util.concurrent.locks.ReentrantReadWriteLock;  
  5.   
  6. /** 
  7.  * @author: yanxuxin 
  8.  * @date: 2010-1-7 
  9.  */  
  10. public class ReentrantReadWriteLockSample {  
  11.   
  12.     public static void main(String[] args) {  
  13.         testReadLock();  
  14. //      testWriteLock();  
  15.     }  
  16.       
  17.     public static void testReadLock() {  
  18.        final ReadWriteLockSampleSupport support = new ReadWriteLockSampleSupport();  
  19.         support.initCache();  
  20.           
  21.         Runnable runnable = new Runnable() {  
  22.             public void run() {  
  23.                 support.get("test");  
  24.             }  
  25.         };  
  26.           
  27.         new Thread(runnable).start();  
  28.         new Thread(runnable).start();  
  29.           
  30.         new Thread(new Runnable() {  
  31.             public void run() {  
  32.                 support.put("test""test");  
  33.             }  
  34.         }).start();  
  35.     }  
  36.       
  37.     public static void testWriteLock() {  
  38.        final ReadWriteLockSampleSupport support = new ReadWriteLockSampleSupport();  
  39.         support.initCache();  
  40.           
  41.         new Thread(new Runnable() {  
  42.             public void run() {  
  43.                 support.put("key1""value1");  
  44.             }  
  45.         }).start();  
  46.           
  47.         new Thread(new Runnable() {  
  48.             public void run() {  
  49.                 support.put("key2""value2");  
  50.             }  
  51.         }).start();  
  52.           
  53.         new Thread(new Runnable() {  
  54.             public void run() {  
  55.                 support.get("key1");  
  56.             }  
  57.         }).start();  
  58.     }  
  59. }  
  60.   
  61. class ReadWriteLockSampleSupport {  
  62.     private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();  
  63.     private final Lock readLock = lock.readLock();  
  64.     private final Lock writeLock = lock.writeLock();  
  65.       
  66.     private volatile  boolean completed;  
  67.     private Map<String,String> cache;  
  68.       
  69.     public void initCache() {  
  70.         readLock.lock();  
  71.         if(!completed) {  
  72.             // Must release read lock before acquiring write lock  
  73.             readLock.unlock(); // (1)  
  74.             writeLock.lock();  // (2)  
  75.             if(!completed) {  
  76.                 cache = new HashMap<String,String>(32);  
  77.                 completed = true;  
  78.             }  
  79.             // Downgrade by acquiring read lock before releasing write lock  
  80.             readLock.lock();    // (3)  
  81.             writeLock.unlock(); // (4) Unlock write, still hold read  
  82.         }  
  83.           
  84.         System.out.println("empty? " + cache.isEmpty());  
  85.         readLock.unlock();  
  86.     }  
  87.       
  88.     public String get(String key) {  
  89.         readLock.lock();  
  90.         System.out.println(Thread.currentThread().getName() + " read.");  
  91.         startTheCountdown();  
  92.         try{  
  93.             return cache.get(key);  
  94.         }  
  95.         finally{  
  96.             readLock.unlock();  
  97.         }  
  98.     }  
  99.       
  100.     public String put(String key, String value) {  
  101.         writeLock.lock();  
  102.         System.out.println(Thread.currentThread().getName() + " write.");  
  103.         startTheCountdown();  
  104.         try{  
  105.             return cache.put(key, value);  
  106.         }  
  107.         finally {  
  108.             writeLock.unlock();  
  109.         }  
  110.     }  
  111.       
  112.     /** 
  113.      * A simple countdown,it will stop after about 5s.  
  114.      */  
  115.     public void startTheCountdown() {  
  116.         long currentTime = System.currentTimeMillis();  
  117.         for(;;) {  
  118.             long diff = System.currentTimeMillis() - currentTime;  
  119.             if(diff > 5000) {  
  120.                 break;  
  121.             }  
  122.         }  
  123.     }  
  124. }  


    这个例子改造自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,性能好很多。

 

 

 

分享到:
评论
1 楼 巴尾的兔兔帅 2014-06-26  
请问:
线程进入读锁的前提条件:

    没有写请求或者有写请求,但调用线程和持有锁的线程是同一个
没有写请求是什么意思,怎么体现?

相关推荐

Global site tag (gtag.js) - Google Analytics