zoukankan      html  css  js  c++  java
  • 「java.util.concurrent并发包」之 ReentrantReadWriteLock

    一 引言

    在多线程的环境下,对同一份数据进行读写,会涉及到线程安全的问题。比如在一个线程读取数据的时候,另外一个线程在写数据,而导致前后数据的不一致性;一个线程在写数据的时候,另一个线程也在写,同样也会导致线程前后看到的数据的不一致性。这时候可以在读写方法中加入互斥锁,任何时候只能允许一个线程的一个读或写操作,而不允许其他线程的读或写操作,这样是可以解决这样以上的问题,但是效率却大打折扣了。因为在真实的业务场景中,一份数据,读取数据的操作次数通常高于写入数据的操作,而线程与线程间的读读操作是不涉及到线程安全的问题,没有必要加入互斥锁,只要在读-写,写-写期间上锁就行了

    读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的。

    ReentrantReadWriteLock是可重入的读写锁,允许多个读线程获得ReadLock,但只允许一个写线程获得WriteLock

    二 准备

    读写锁的机制:

       "读-读" 不互斥
       "读-写" 互斥
       "写-写" 互斥
     
    ReentrantReadWriteLock不支持锁升级(从读锁变成写锁)
    ReadWriteLock rtLock = new ReentrantReadWriteLock();
     rtLock.readLock().lock();
     System.out.println("get readLock.");
     rtLock.writeLock().lock();
     System.out.println("blocking");
    锁升级

    这个代码会死锁,没释放读锁就去申请写锁

    ReentrantReadWriteLock支持锁降级(从写锁变成读锁)
    ReadWriteLock rtLock = new ReentrantReadWriteLock();
    rtLock.writeLock().lock();
    System.out.println("writeLock");
    
    rtLock.readLock().lock();
    System.out.println("get read lock");
    锁降级

      以上这段代码虽然不会导致死锁,但没有正确的释放锁。从写锁降级成读锁,并不会自动释放当前线程获取的写锁,仍然需要显示的释放,否则别的线程永远也获取不到写锁。

    三 javadoc的例子

     1 class CachedData {
     2   Object data;
     3   volatile boolean cacheValid;
     4   final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
     5 
     6   public void processCachedData() {
     7     rwl.readLock().lock();
     8     if (!cacheValid) {
     9       // Must release read lock before acquiring write lock
    10       rwl.readLock().unlock();
    11       rwl.writeLock().lock();
    12       try {
    13         // Recheck state because another thread might have,acquired write lock and changed state before we did.
    14         if (!cacheValid) {
    15           data = ...
    16           cacheValid = true;
    17         }
    18         // 在释放写锁之前通过获取读锁降级写锁(注意此时还没有释放写锁)
    19         rwl.readLock().lock();
    20       } finally {
    21         rwl.writeLock().unlock(); // 释放写锁而此时已经持有读锁
    22       }
    23     }
    24 
    25     try {
    26       use(data);
    27     } finally {
    28       rwl.readLock().unlock();
    29     }
    30   }
    31 }
    ReadWriteLock实例

    注意最后的释放写锁「line21」,在之前是要加读锁「line19」的,因为在get过程中,可能有其他线程竞争到锁或是更新数据,会产生脏读。

     

    四 缓存例子
     private static Map<Integer, Integer> cache = Maps.newHashMap();
        private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    
        public Integer get(Integer key) {
            Integer value;
            readWriteLock.readLock().lock();
            try {
                value = cache.get(key);
                if (value == null) {
                    readWriteLock.readLock().unlock();
                    readWriteLock.writeLock().lock();
                    try {
                        if (value == null) {
                            value = 1; // 从数据库读取等
                        }
                        readWriteLock.readLock().lock();
                    } finally {
                        readWriteLock.writeLock().unlock();
                    }
                }
            } finally {
                readWriteLock.readLock().unlock();
            }
            return value;
        }
    
        public void put(Integer key, Integer value) {
            readWriteLock.writeLock().lock();
            cache.put(key, value);
            readWriteLock.writeLock().unlock();
        }
    缓存
     
     
  • 相关阅读:
    wireshark筛选器汇总
    .net中的"异步"-手把手带你体验
    Javascript手记-垃圾收集
    Sqlserver作业-手把手带你体验
    oracle11g重置system密码,外二
    return Acad::ErrorStatus::eOk引发error C2220: warning treated as error
    RegOpenKeyEx和RegSetValueEx返回ERROR_SUCCESS,但注册表未发生变化。
    windows7 阻止copyfile到windows目录的解决办法
    如何让AutoCAD自动加载Arx,比如ArxDbg.arx
    入口点函数的19种消息,AcRxArxApp只处理16种。
  • 原文地址:https://www.cnblogs.com/balfish/p/7805363.html
Copyright © 2011-2022 走看看