ReadWriteLock的使用
2016-05-15 20:36
323 查看
jdk1.5提供的api,位于java.util.concurrent.locks 包下.ReadWriteLock是接口,其实现类是ReentrantReadWriteLock .
ReadWriteLock 维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有 writer,读取锁可以由多个 reader 线程同时保持。写入锁是独占的。
与互斥锁相比,读-写锁允许对共享数据进行更高级别的并发访问。虽然一次只有一个线程(writer 线程)可以修改共享数据,但在许多情况下,任何数量的线程可以同时读取共享数据(reader 线程),读-写锁利用了这一点。从理论上讲,与互斥锁相比,使用读-写锁所允许的并发性增强将带来更大的性能提高。
读写锁分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,写锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可,如果你的代码只读数据,可以很线程同时读,当不能同时写,那就上读锁,如果你的代码修改数据,只能由其中一个线程在写,且不能同时读取,那就上写锁.总之,读的时候上读锁,写的时候上写锁.
运行结果:
Thread-0 be ready to read data!
Thread-2 be ready to read data!
Thread-4 be ready to read data!
Thread-0have read data :null
Thread-4have read data :null
Thread-2have read data :null
Thread-5 be ready to write data!
Thread-5 have write data: 1992
Thread-1 be ready to write data!
Thread-1 have write data: 9789
Thread-0 be ready to read data!
Thread-4 be ready to read data!
Thread-2 be ready to read data!
Thread-4have read data :9789
Thread-2have read data :9789
Thread-0have read data :9789
从运行结果可以看到,读操作可以多个线程同时进行,但是写操作始终只能有一个线程在操作.
展示了如何利用重入来执行升级缓存后的锁降级(为简单起见,省略了异常处理):
在使用某些种类的 Collection 时,可以使用 ReentrantReadWriteLock 来提高并发性。通常,在预期 collection 很大,读取者线程访问它的次数多于写入者线程,并且 entail 操作的开销高于同步开销时,这很值得一试。例如,以下是一个使用 TreeMap 的类,预期它很大,并且能被同时访问。
ReadWriteLock 维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有 writer,读取锁可以由多个 reader 线程同时保持。写入锁是独占的。
与互斥锁相比,读-写锁允许对共享数据进行更高级别的并发访问。虽然一次只有一个线程(writer 线程)可以修改共享数据,但在许多情况下,任何数量的线程可以同时读取共享数据(reader 线程),读-写锁利用了这一点。从理论上讲,与互斥锁相比,使用读-写锁所允许的并发性增强将带来更大的性能提高。
读写锁分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,写锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可,如果你的代码只读数据,可以很线程同时读,当不能同时写,那就上读锁,如果你的代码修改数据,只能由其中一个线程在写,且不能同时读取,那就上写锁.总之,读的时候上读锁,写的时候上写锁.
示例1
package com.example; import java.util.Random; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Created by mChenys on 2016/5/15. */ public class ReadWriteLockTest { public static void main(String[] args) { final Queue3 q3 = new Queue3(); for (int i = 0; i < 3; i++) { new Thread() { public void run() { while (true) { q3.get();//操作读方法 } } }.start(); new Thread() { public void run() { while (true) { q3.put(new Random().nextInt(10000));//操作写方法 } } }.start(); } } } class Queue3 { private Object data = null;//共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。 ReadWriteLock rwl = new ReentrantReadWriteLock(); /** * 读操作 */ public void get() { rwl.readLock().lock(); //上读锁 try { System.out.println(Thread.currentThread().getName() + " be ready to read data!"); Thread.sleep((long) (Math.random() * 1000)); System.out.println(Thread.currentThread().getName() + "have read data :" + data); } catch (InterruptedException e) { e.printStackTrace(); } finally { rwl.readLock().unlock();//释放读锁 } } /** * 写操作 * @param data */ public void put(Object data) { rwl.writeLock().lock();//上写锁 try { System.out.println(Thread.currentThread().getName() + " be ready to write data!"); Thread.sleep((long) (Math.random() * 1000)); this.data = data; System.out.println(Thread.currentThread().getName() + " have write data: " + data); } catch (InterruptedException e) { e.printStackTrace(); } finally { rwl.writeLock().unlock();//释放写锁 } } }
运行结果:
Thread-0 be ready to read data!
Thread-2 be ready to read data!
Thread-4 be ready to read data!
Thread-0have read data :null
Thread-4have read data :null
Thread-2have read data :null
Thread-5 be ready to write data!
Thread-5 have write data: 1992
Thread-1 be ready to write data!
Thread-1 have write data: 9789
Thread-0 be ready to read data!
Thread-4 be ready to read data!
Thread-2 be ready to read data!
Thread-4have read data :9789
Thread-2have read data :9789
Thread-0have read data :9789
从运行结果可以看到,读操作可以多个线程同时进行,但是写操作始终只能有一个线程在操作.
实例2
一个操作缓存的读写锁应用package com.example; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class CacheDemo { //缓存Map private Map<String, Object> cache = new HashMap<String, Object>(); public static void main(String[] args) { // TODO Auto-generated method stub } //定义读写锁 private ReadWriteLock rwl = new ReentrantReadWriteLock(); /** * 获取数据的方法,先从缓存获取,缓存有则直接返回,否则查询数据库并保存到缓存中再返回结果. */ public Object getData(String key){ rwl.readLock().lock();//上读锁 Object value = null; try{ value = cache.get(key);//先从缓存读取数据 if(value == null){ rwl.readLock().unlock();//先进来的那个线程先释放读锁 rwl.writeLock().lock();//然后再上写锁,后面进来的线程就只能等待 try{ if(value==null){ //这里继续判空,是避免当拿到写锁的线程释放了写锁后,后面等待的线程拿到写锁后继续操作value value = "query db....";//如果数据为空,再执行查询数据库操作 cache.put(key,value);//查询到后写入缓存 } }finally{ rwl.writeLock().unlock();//最后释放写锁 } rwl.readLock().lock();//重新恢复读锁 } }finally{ rwl.readLock().unlock();//最后释放读锁 } return value;//返回结果 } }
官方示例
该例子摘至jdk api文档.展示了如何利用重入来执行升级缓存后的锁降级(为简单起见,省略了异常处理):
class CachedData { Object data; volatile boolean cacheValid; ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); void processCachedData() { rwl.readLock().lock(); if (!cacheValid) { // Must release read lock before acquiring write lock rwl.readLock().unlock(); rwl.writeLock().lock(); // Recheck state because another thread might have acquired // write lock and changed state before we did. if (!cacheValid) { data = ... cacheValid = true; } // Downgrade by acquiring read lock before releasing write lock rwl.readLock().lock(); rwl.writeLock().unlock(); // Unlock write, still hold read } use(data); rwl.readLock().unlock(); } }
在使用某些种类的 Collection 时,可以使用 ReentrantReadWriteLock 来提高并发性。通常,在预期 collection 很大,读取者线程访问它的次数多于写入者线程,并且 entail 操作的开销高于同步开销时,这很值得一试。例如,以下是一个使用 TreeMap 的类,预期它很大,并且能被同时访问。
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 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(); } } }
相关文章推荐
- 我所了解的WEB开发(2) - PS切片
- 欢迎使用CSDN-markdown编辑器
- mysql性能优化-慢查询分析、优化索引和配置 分析
- 已知中序和后序建树
- codis功能性和可用性测试
- 【37】String,StringBuffer,StringBuilder区别和概念
- 【opencv+python】图像处理之二、几何变换(仿射与投影)的应用
- 【37】String,StringBuffer,StringBuilder区别和概念
- 【37】String,StringBuffer,StringBuilder区别和概念
- 《Java 程序设计》团队博客第十一周(第一次)
- [Flask Security]当不能通过认证的时候制定跳转
- swift 2.2 语法 (上)
- PAT (Advanced Level) 1007. Maximum Subsequence Sum (25)
- CharSequence类型
- Binary Tree Preorder Traversal
- SIFT算法详解
- 闽江学院2015-2016学年下学期《软件测试》课程-第四次博客作业
- 闽江学院2015-2016学年下学期《软件测试》课程-第五次博客作业
- 【CodeVS2822】爱在心中
- 欢迎使用CSDN-markdown编辑器