显式锁ReadWriteLock 和 ReentrantReadWriteLock
ReadWriteLock 也是一个接口,提供了readLock 和 writeLock 两种锁的操作。
package路径:java.util.locks.ReadWriteLock。
也就是:
一个资源能够被多个读线程访问,或者被一个写线程访问。但不能同时存在读写线程。
读写锁使用的场合:
一个共享资源被大量读取操作,而只有少量的写操作。
1 public interface ReadWriteLock { 2 3 Lock readLock(); 4 Lock writeLock(); 5 }
ReadWriteLock 并不是 Lock 的子接口,只不过是借助Lock来实现读写两个锁并存、互斥的操作机制。
在ReadWriteLock中每次读取共享数据就需要读取锁,当需要修改共享数据时就需要写入锁。
ReentrantReadWriteLock 是 ReadWriteLock 在java.util里面唯一的实现类。读的时候并发的,写的时候是有顺序的带阻塞机制的。
主要应用场景是:
当很多线程都从某个数据结构中读取数据,而很少有线程对其修改时,在这种情况下,允许读取器线程共享访问是合适的。写入器线程必须是互斥的。
读写锁机制:
(1)读-读不互斥,10个线程去读(没有线程去写),这10个线程可以并发读,而不会堵塞。
(2)读-写互斥,当前写线程在使用的时候,读线程就会堵塞,反过来,有读线程在使用的时候,写的线程也会堵塞,看谁先拿到锁。
(3)写写互斥,写线程都是互斥的,如果2个线程去写,A线程先拿到先写,B就堵塞,直到A线程释放锁。
ReentrantReadWriteLock 是对 ReentrantLock的复杂扩展,能适合更加复杂的业务场景,ReentrantReadWriteLock可以实现一个方法中读写分离的锁的机制。
而ReentrantLock加锁解锁只有一种机制。
ReentrantReadWriteLock对Lock又进行了扩展,引入了read和write阻塞和并发机制,相对于ReentrantLock,它可以实现更复杂的锁机制,且并发性也更高。
读写数据的场景:
1 private final Map<String, Object> map = new HashMap<String, Objects>(); 2 // map 缓存了数据 3 private final ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock(); 4 5 public Object readWrite(String id) { 6 7 Object value = null; 8 9 rwlock.readLock().lock(); // 开读锁,从缓存中去获取 10 11 try { 12 13 value = map.get(id); 14 15 if (value == null) { 16 17 rwlock.readLock().unlock(); // 缓存中没有数据, 释放读锁, 上写锁。 18 19 rwlock.writeLock().lock(); 20 21 try { 22 23 if (value == null) { 24 value = "file"; // 数据库读取, 文件读取 25 } 26 27 } finally { 28 rwlock.writeLock().unlock(); // 释放写锁 29 } 30 31 rwlock.readLock().lock(); // 然后再上读锁 32 } 33 34 } finally { 35 rwlock.readLock().unlock(); // 最后释放读锁 36 } 37 38 return value; 39 }