JAVA多线程之——读写锁 ReentrantReadWriteLock
2017-04-02 23:17
861 查看
ReentrantReadWriteLock
锁的类型有很多,前面学习了阻塞锁、互斥锁、自旋锁等。今天学习读写锁。所谓读写锁就是维护了一个读锁和写锁。但是读锁和写锁互斥、写锁和写锁互斥。读锁和读锁不互斥。既允许多个读锁同时读。但是同时间只有一个写锁写。读写锁也是可重入锁。根据JDK文档描述,所以读写锁以下几个特点:
1. 读写锁对于读锁与写锁的获取顺序不会干涉。
2. 非公平模式下可能会导致饥饿状态。也就是说可能会无限推迟一个读锁或者写锁线程获取锁。
3. 公平模式下当锁被释放时候,会为等待最长时间的写锁分配锁,如果有线程的等待时间大于所有写线程的等待时间,那就分配这些线程读锁。
4. 读写锁是可以重入的。但是要切记其中的锁互斥问题。也就是读锁要重入,就必须所有的写锁都已经释放。写锁可以重入读锁,但是读锁不能重入写锁。
5. 重入还允许从写入锁降级为读取锁,实现方式是:先获取写入锁,然后获取读取锁,最后释放写入锁。但是,从读取锁升级到写入锁是不可能的。
6. 写入锁提供了一个 Condition 实现,对于写入锁来说,该实现的行为与 ReentrantLock.newCondition() 提供的Condition 实现对 ReentrantLock 所做的行为相同。当然,此 Condition 只能用于写入锁。
读取锁不支持 Condition,readLock().newCondition() 会抛出 UnsupportedOperationException。
7. 此类支持一些确定是读取锁还是写入锁的方法。这些方法设计用于监视系统状态,而不是同步控制。
先来看一下部分源码:
/** Inner class providing readlock */ private final ReentrantReadWriteLock.ReadLock readerLock; /** Inner class providing writelock */ private final ReentrantReadWriteLock.WriteLock writerLock; /** Performs all synchronization mechanics */ final Sync sync;
代码中可以看到,读写锁中包含了一个读锁和一个写锁。
public static class WriteLock implements Lock, java.io.Serializable { private static final long serialVersionUID = -4992448646407690164L; private final Sync sync; /** * Constructor for use by subclasses * * @param lock the outer lock object * @throws NullPointerException if the lock is null */ protected WriteLock(ReentrantReadWriteLock lock) { sync = lock.sync; } }
这是写锁的一部分源码,可以看出写锁实现的就是Lock。通过前面学习了解了,Lock的一个实现原理是基于一个内部类Sync 实现AQS的方式。而AQS底层维护的是一个修改过的CLH队列。
AQS如何用一个状态同时表示读锁和写锁?
/* * Read vs write count extraction constants and functions. * Lock state is logically divided into two unsigned shorts: * The lower one representing the exclusive (writer) lock hold count, * and the upper the shared (reader) hold count. */ static final int SHARED_SHIFT = 16; static final int SHARED_UNIT = (1 << SHARED_SHIFT); static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1; static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; /** Returns the number of shared holds represented in count */ static int sharedCount(int c) { return c >>> SHARED_SHIFT; } /** Returns the number of exclusive holds represented in count */ static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
看注释可以知道,一个整型32位,然后将它分为两部分,高16位表示读锁的线程数量,低16位表示写锁的线程数量。
如何统计每个线程的重入数?
abstract static class Sync extends AbstractQueuedSynchronizer { /** * 每个线程特定的 read 持有计数。存放在ThreadLocal,不需要是线程安全的。 */ static final class HoldCounter { int count = 0; // 使用id而不是引用是为了避免保留垃圾。 final long tid = Thread.currentThread().getId(); } /** * 如果ThreadLocal没有当前线程的计数,则new一个,再放进ThreadLocal里。 * 可以直接调用 get。 * */ static final class ThreadLocalHoldCounter extends ThreadLocal<HoldCounter> { public HoldCounter initialValue() { return new HoldCounter(); } } /** * 保存当前线程重入读锁的次数的容器。在读锁重入次数为 0 时移除。 */ private transient ThreadLocalHoldCounter readHolds; }
分析源码可以看出,每个读线程都维护自己一个重入数的一个ThreadLocal变量readHolds。这样就能各自统计自己的重入次数。
写一段小代码来实战一下读写锁的用法。JDK文档中也有一个经典的例子。
public class ReadWriteLockTest { p 4000 rivate static Map<String, Object> map = new HashMap<String,Object>(); private static Object obj = null; private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private static Lock readLock = lock.readLock(); private static Lock writeLock = lock.writeLock(); public static Object get(String key) { try{ readLock.lock(); obj = map.get(key); if(null == obj) { try{ readLock.unlock(); writeLock.lock(); if(null == obj) { obj = "abc"; map.put(key,obj); } }finally{ readLock.lock(); writeLock.unlock(); } } }finally{ readLock.unlock(); } if( null == obj) { readLock.unlock(); writeLock.lock(); obj = map.get(key); } return obj; } }
基本就是实现一个缓存的机制。多个线程可以去读取。每次只有一个线程可以写入。
相关文章推荐
- JAVA多线程之——读写锁 ReentrantReadWriteLock
- JAVA多线程之——读写锁 ReentrantReadWriteLock
- java多线程:并发包中ReentrantReadWriteLock读写锁的锁降级模板 写锁降级为读锁
- JAVA多线程之——读写锁 ReentrantReadWriteLock
- JAVA多线程之——读写锁 ReentrantReadWriteLock
- java多线程:并发包中ReentrantReadWriteLock读写锁的原理
- JAVA多线程之——读写锁 ReentrantReadWriteLock
- JAVA多线程之——读写锁 ReentrantReadWriteLock
- JAVA多线程之——读写锁 ReentrantReadWriteLock
- JAVA多线程之——读写锁 ReentrantReadWriteLock
- JAVA多线程之——读写锁 ReentrantReadWriteLock
- JAVA多线程之——读写锁 ReentrantReadWriteLock
- JAVA多线程之——读写锁 ReentrantReadWriteLock
- Java:多线程,线程同步,同步锁(Lock)的使用(ReentrantLock、ReentrantReadWriteLock)
- 多线程读写文件利器-ReentrantReadWriteLock
- java多线程基础---synchronized与ReentrantReadWriteLock的介绍和比较
- Java多线程 ReentrantReadWriteLock深入分析
- 《深入浅出 Java Concurrency》—锁机制(八) 读写锁 (ReentrantReadWriteLock) (1)
- Java多线程(十)之ReentrantReadWriteLock深入分析
- java 读写锁 , 官方自带示例读解,ReentrantReadWriteLock