读-写锁

                                           读-写锁

      ReentrantLock实现了标准的互斥锁: 一 次最多只有一个线程能够持有相同 ReentrantLocko但是互斥通常作为保护数据一致性的很强的加锁约束, 因此过分地限制了并发性。 直斥是保守的加锁策略, 避免了 “写/写 ” 和 “写/读 ” 的重叠, 但是同样避开了“读/读”的重叠。在 很多情况下, 数据结构是 “频繁被读取” 的一一它们是可变的,有 的时候会被改变,但多数访问只进行读操作。此时, 如果能够放宽, 允许多个读者同时访问数据结构就非常好了。只要每个线程保证能够读到最新的数据,并且在读者读取数据的 时候没有其他线程修改数据,就不会发生问题。这就是读,写锁允许的情况: 一个资源能够 被多个读者访问, 或者被一个写者访问, 两者不能同时进行。
      ReadWriteLock,暴露了两个Lock对象, 一个用来读, 另一个用来写。 读取ReadWriteLock 锁守护的数据, 你必须首先获得读取的锁, 当需要修改 ReadWriteLock守护的数据时, 你必须首先获得写入的锁。 尽管看起来是两个分离的锁,读取的锁和写入的锁只不过是统一的读-写锁对象的两个视角。
ReadWritelock接口

public interface ReadWriteLock { 
  Lock readLock();
  Lock writeLock();
}


      读.写锁实现的加锁策略允许多个同时存在的读者,但是只允许一个写者。 与Lock 一 样,ReadWriteLock允许多种实现, 造成了性能、 调度保证、获取优先、公平性、以及加 锁语义等方面的不尽相同。读.写锁的设计是用来进行性能改进的, 使得特定情况下能够有更好的并发性。 在实 践中, 当多处理器系统中, 频繁的访问主要为读取数据结构的时候, 读-写锁能够改进性能:在其他情况下运行的情况比独占的锁要稍差一些, 这归国于它更大的复杂性。
使用它究竟能否带来改进,最好通过对系统进行剖析来判断:
好在ReadWriteLock使用Lock作为读、 写部分的锁, 所以如果剖析的结果发现读-写锁没有能提高性能, 把读,写锁置换为独占锁是比较容易的。
      读取和写入锁之间的互动可以有很多种实现。 ReadWriteLock的-些实现选择如下:
释放优先.当写者释放写入锁,并且读者和写者都排在队列中, 应该选择哪个一一读 者, 写者, 还是先请求的那个呢?
读者闯入.如果锁由读者获得,但是有写者正在等待, 那么新到达的写者应该被授予 读取的权力么?还是应该等待?允许读者闯入到写者之前提高了并发性,但是却带来了写 者饥饿的风险。
重进入.读取锁和写入锁允许重入吗?
降级.如果线程持有写入的锁,它能够在不释放该锁的情况下获得读取锁么?这可能 会造成写着 “ 降级” 为一个读取锁, 同时不允许其他写者修改这个被守护的资源。
升级.读取锁能够优先于其他的读者和写者升级为一个写入锁么?大多数读”写锁的 实现并不支持升级, 因为在没有显式的升级操作的情况下, 很容易造成死锁。 (如果两个 读者同时试图升级到同一个写入锁, 并都不释放读取锁。 )
      ReentrantReadWriteLock为两个锁提供了可重进入的加锁语义。与ReentrantLock 相同,ReentrantReadWriteLock能够被构造为非公平(默认)或者是公平的。在公平的 锁中, 选择权交给等待时间最长的钱程:如果锁由读者获得, 而→个线程请求写入锁, 那 么不再允许读者获得读取锁,直到写者被受理,并且已经释放了写入锁。在非公平的锁中, 线程允许访问的顺序是不定的。 由写者降级为读者是允许的:从读者升级为写者是不允许 的(尝试这样的行为会导致死锁)。
      与ReentrantLock相同,ReentrantReadWriteLock的写入锁有一个唯一的所有者,
并只能被获得了该锁的线程释放。Java 5.0中, 读取锁的行为更类似于一个Semaphore, 只维护活跃的读者数量, 而不考虑它们的身份。 这个行为在Java 6中获得了修改, 现在也可以保持追踪哪些线程已经获得了读者锁  。
     当锁被持有的时间相对较长,并且大部分操作都不会改变锁守护的资源,那么读-写 锁能够改进并发性。ReadWriteMap使用了ReentrantReadWriteLock来包装Map,使得它能够在多线程间被安全地共享,并仍然能够避免“读-写”或者“写-写” 冲突。现实中,ConcurrentHashMap的性能已经足够好了,所以你可以使用它,而不必使用这个新的解决方案,如果你需要并发的部分只有哈希Map,但是如果你需要为 LinkedHashMap这种可替换元素的Map提供更多的并发访问,那么这项技术是非常有用 的。

public class ReadWriteMap<K,V> { 
private final Map<K,V> map; 
private final ReadWriteLock lock= new ReentrantReadWriteLock(); 
private final Lock r = lock.readLock(); 
private final Lock w = lock.writeLock(); 
public ReadWriteMap(Map<K,V> map) { 
    this.map.= map;
}
public V put(K key, V value) {
    w.lock();
    try {
    return map.put(key,value);
   }finally {
      w.unlock();
   }
}

public V get(Object key) {
    r.lock();
    try {
      return map.get(key);
    }  finally{
     r.unlock();
    }
}

猜你喜欢

转载自blog.csdn.net/xiao__jia__jia/article/details/81667884