Java并发编程札记-(四)JUC锁-05ReentrantReadWriteLock简介

前面在Java并发编程札记-(四)JUC锁-02Lock与ReentrantLock一文中已经学习了ReentrantLock,其中提到了ReentrantLock是互斥锁。与互斥锁相对应的是共享锁。ReadWriteLock就是一种共享锁。ReentrantReadWriteLock是支持与ReentrantLock 类似语义的ReadWriteLock实现。

ReadWriteLock维护了两个锁,读锁和写锁,所以一般称其为读写锁。写锁是独占的。读锁是共享的,如果没有写锁,读锁可以由多个线程共享。与互斥锁相比,虽然一次只能有一个写线程可以修改共享数据,但大量读线程可以同时读取共享数据,所以,在共享数据很大,且读操作远多于写操作的情况下,读写锁值得一试。
ReadWriteLock源码如下:

public interface ReadWriteLock {
    //返回用于读取操作的锁。
    Lock readLock();
    //返回用于写入操作的锁。
    Lock writeLock();
}

从源码中可以看到,ReadWriteLock并不是Lock的子接口。所以ReadWriteLock并没有Lock的那些特性。

在使用某些种类的Collection时,可以使用ReentrantReadWriteLock来提高并发性。通常,在预期collection很大,读取者线程访问它的次数多于写入者线程,并且entail操作的开销高于同步开销时,这很值得一试。例如,以下是一个使用 TreeMap的类,预期它很大,并且能被同时访问。

import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import javax.xml.crypto.Data;

public 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 (String[])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();
        }
    }
}

特性
ReentrantReadWriteLock具有以下特性:(有待详细介绍)

  • 公平性
  • 重入性
  • 锁降级
  • 锁获取中断
  • 支持Condition
  • 检测系统状态

优点
与互斥锁相比,虽然一次只能有一个写线程可以修改共享数据,但大量读线程可以同时读取共享数据。在共享数据很大,且读操作远多于写操作的情况下,ReentrantReadWriteLock值得一试。

缺点
只有当前没有线程持有读锁或者写锁时才能获取到写锁,这可能会导致写线程发生饥饿现象,即读线程太多导致写线程迟迟竞争不到锁而一直处于等待状态。StampedLock可以解决这个问题,解决方法是如果在读的过程中发生了写操作,应该重新读而不是直接阻塞写线程。

本文就讲到这里,想了解Java并发编程更多内容请参考:

  • Java并发编程札记-目录
    原文作者:java并发
    原文地址: http://blog.csdn.net/panweiwei1994/article/details/78821763
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞