ReentrantReadWriteLock是Lock的另外一种实现方式,咱们已经知道了ReentrantLock是一个排他锁,同一时间只容许一个线程访问,而ReentrantReadWriteLock容许多个读线程同时访问,但不容许写线程和读线程、写线程和写线程同时访问。相对于排他锁,提升了并发性。在实际应用中,大部分状况下对共享数据(如缓存)的访问都是读操做远多于写操做,这时ReentrantReadWriteLock可以提供比排他锁更好的并发性和吞吐量。java
读写锁内部维护了两个锁,一个用于读操做,一个用于写操做。全部 ReadWriteLock实现都必须保证 writeLock操做的内存同步效果也要保持与相关 readLock的联系。也就是说,成功获取读锁的线程会看到写入锁以前版本所作的全部更新。数据库
ReentrantReadWriteLock支持如下功能:缓存
在缓存有效的状况下,支持并发读。缓存失效,只容许独占写。微信
import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class HibernateCache { /* 定义一个Map来模拟缓存 */ private Map<String, Object> cache = new HashMap<String, Object>(); /* 建立一个读写锁 */ private ReadWriteLock rwLock = new ReentrantReadWriteLock(); /** * 模拟Hibernate缓存,优先缓存,若缓存不存在写锁更新 * * @param key * @return */ public Object getData(String key) { /* 上读锁 */ rwLock.readLock().lock(); /* 定义从缓存中读取的对象 */ Object value = null; try { /* 从缓存中读取数据 */ value = cache.get(key); if (value == null) { /* 若是缓存中没有数据,咱们就把读锁关闭,直接上写锁【让一个线程去数据库中取数据】 */ rwLock.readLock().unlock(); /* 上写锁 */ rwLock.writeLock().lock(); try { /* 上了写锁以后再判断一次【咱们只让一个线程去数据库中取值便可,当第二个线程过来的时候,发现value不为空了就去缓存中取值】 */ if (value == null) { /* 模拟去数据库中取值 */ value = "hello"; System.out.println("修改换缓存"); cache.put(key, value); } } finally { /* 写完以后把写锁关闭 */ rwLock.writeLock().unlock(); } /* 缓存中已经有了数据,咱们再把已经 关闭的读锁打开 */ rwLock.readLock().lock(); } return value; } finally { /* 最后把读锁也关闭 */ rwLock.readLock().unlock(); } } public Map<String, Object> getCache() { return cache; } public void setCache(Map<String, Object> cache) { this.cache = cache; } }
当一份共享数据只能一个西安测绘给你写数据,能够多个线程读数据。能够选择读写锁,支持并发读,独占写,提升并发。并发
代码以下:dom
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadWrite { private ReadWrite() { } private static class singleFactory { private static final ReadWrite INSTANCE = new ReadWrite(); } public static ReadWrite getInstance() { return singleFactory.INSTANCE; } /* 共享数据,只能一个线程写数据,能够多个线程读数据 */ private Object data = null; /* 建立一个读写锁 */ ReadWriteLock rwlock = new ReentrantReadWriteLock(); /** * 读数据,能够多个线程同时读, 因此上读锁便可 */ public void get() { /* 上读锁 */ rwlock.readLock().lock(); try { System.out.println(Thread.currentThread().getName() + " 准备读数据!"); /* 休眠 */ Thread.sleep((long) (Math.random() * 1000)); System.out.println(Thread.currentThread().getName() + "读出的数据为 :" + data); } catch (InterruptedException e) { e.printStackTrace(); } finally { rwlock.readLock().unlock(); } } /** * 写数据,多个线程不能同时 写 因此必须上写锁 * * @param data */ public void put(Object data) { /* 上写锁 */ rwlock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName() + " 准备写数据!"); /* 休眠 */ Thread.sleep((long) (Math.random() * 1000)); this.data = data; System.out.println(Thread.currentThread().getName() + " 写入的数据: " + data); } catch (Exception e) { e.printStackTrace(); } finally { rwlock.writeLock().unlock(); } } }
单元测试socket
public class LockTest { public static void main(String[] args) { ReadWrite readWrite = ReadWrite.getInstance(); for (int i = 0; i < 8; i++) { /* 建立并启动8个读线程 */ new Thread(() -> readWrite.get()).start(); /*建立8个写线程*/ new Thread(() -> readWrite.put(new Random().nextInt(8))).start(); } } }
运行结果:高并发
Thread-0读出的数据为 :null Thread-1 准备写数据! Thread-1 写入的数据: 6 Thread-3 准备写数据! Thread-3 写入的数据: 4 Thread-4 准备读数据! Thread-2 准备读数据! Thread-2读出的数据为 :4 Thread-4读出的数据为 :4 Thread-5 准备写数据! Thread-5 写入的数据: 1 Thread-6 准备读数据! Thread-6读出的数据为 :1 Thread-7 准备写数据! Thread-7 写入的数据: 6 Thread-8 准备读数据! Thread-8读出的数据为 :6 Thread-9 准备写数据! Thread-9 写入的数据: 4 Thread-10 准备读数据! Thread-10读出的数据为 :4 Thread-11 准备写数据! Thread-11 写入的数据: 4 Thread-12 准备读数据! Thread-12读出的数据为 :4 Thread-13 准备写数据! Thread-13 写入的数据: 6 Thread-14 准备读数据! Thread-14读出的数据为 :6 Thread-15 准备写数据! Disconnected from the target VM, address: '127.0.0.1:55431', transport: 'socket' Thread-15 写入的数据: 0
这里会有一个规律:获取了写锁后数据必须从准备写数据到写入数据一鼓作气,也就是原子操做,线程独占。单元测试
而读锁的状况下可有多个线程准备读,多个线程同时读出数据。测试
关注微信公众号JavaStorm获取最新文章。