「java.util.concurrent并发包」之 ReentrantReadWriteLock
一 引言
在多线程的环境下,对同一份数据进行读写,会涉及到线程安全的问题。比如在一个线程读取数据的时候,另外一个线程在写数据,而导致前后数据的不一致性;一个线程在写数据的时候,另一个线程也在写,同样也会导致线程前后看到的数据的不一致性。这时候可以在读写方法中加入互斥锁,任何时候只能允许一个线程的一个读或写操作,而不允许其他线程的读或写操作,这样是可以解决这样以上的问题,但是效率却大打折扣了。因为在真实的业务场景中,一份数据,读取数据的操作次数通常高于写入数据的操作,而线程与线程间的读读操作是不涉及到线程安全的问题,没有必要加入互斥锁,只要在读-写,写-写期间上锁就行了
读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的。
ReentrantReadWriteLock是可重入的读写锁,允许多个读线程获得ReadLock,但只允许一个写线程获得WriteLock
二 准备
读写锁的机制:
ReadWriteLock rtLock = new ReentrantReadWriteLock();
rtLock.readLock().lock();
System.out.println("get readLock.");
rtLock.writeLock().lock();
System.out.println("blocking");
锁升级
这个代码会死锁,没释放读锁就去申请写锁
ReadWriteLock rtLock = new ReentrantReadWriteLock();
rtLock.writeLock().lock();
System.out.println("writeLock"); rtLock.readLock().lock();
System.out.println("get read lock");
锁降级
以上这段代码虽然不会导致死锁,但没有正确的释放锁。从写锁降级成读锁,并不会自动释放当前线程获取的写锁,仍然需要显示的释放,否则别的线程永远也获取不到写锁。
三 javadoc的例子
class CachedData {
Object data;
volatile boolean cacheValid;
final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
public void processCachedData() {
rwl.readLock().lock();
if (!cacheValid) {
// Must release read lock before acquiring write lock
rwl.readLock().unlock();
rwl.writeLock().lock();
try {
// Recheck state because another thread might have,acquired write lock and changed state before we did.
if (!cacheValid) {
data = ...
cacheValid = true;
}
// 在释放写锁之前通过获取读锁降级写锁(注意此时还没有释放写锁)
rwl.readLock().lock();
} finally {
rwl.writeLock().unlock(); // 释放写锁而此时已经持有读锁
}
}
try {
use(data);
} finally {
rwl.readLock().unlock();
}
}
}
ReadWriteLock实例
注意最后的释放写锁「line21」,在之前是要加读锁「line19」的,因为在get过程中,可能有其他线程竞争到锁或是更新数据,会产生脏读。
private static Map<Integer, Integer> cache = Maps.newHashMap();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); public Integer get(Integer key) {
Integer value;
readWriteLock.readLock().lock();
try {
value = cache.get(key);
if (value == null) {
readWriteLock.readLock().unlock();
readWriteLock.writeLock().lock();
try {
if (value == null) {
value = 1; // 从数据库读取等
}
readWriteLock.readLock().lock();
} finally {
readWriteLock.writeLock().unlock();
}
}
} finally {
readWriteLock.readLock().unlock();
}
return value;
} public void put(Integer key, Integer value) {
readWriteLock.writeLock().lock();
cache.put(key, value);
readWriteLock.writeLock().unlock();
}
缓存
「java.util.concurrent并发包」之 ReentrantReadWriteLock的更多相关文章
- 「java.util.concurrent并发包」之 ThreadPoolExecutor
一 异步用new Thread? 大写的"low"!! new Thread(new Runnable() { @Override public void run() { // T ...
- 「java.util.concurrent并发包」之 CountDownLatch
一 CountDownLatch是什么 CountDownLatch是在java1.5被引入的,跟它一起被引入的并发工具类还有CyclicBarrier.Semaphore.ConcurrentHas ...
- 「java.util.concurrent并发包」之 CopyOnWrite
一 CopyOnWrite容器概述 Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容C ...
- 「java.util.concurrent并发包」之 CAS
一 引言 在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁 锁机制存在以下问题: (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能 ...
- 「java.util.concurrent并发包」之 CyclicBarrier
一 描述 CyclicBarrier初始化时规定一个数目,然后计算调用了CyclicBarrier.await()进入等待的线程数.当线程数达到了这个数目时,所有进入等待状态的线程被唤醒并继续.Cyc ...
- Java并发:多线程和java.util.concurrent并发包总结
多线程和java.util.concurrent并发包 转载:
- Java并发—java.util.concurrent并发包概括(转载)
一.描述线程的类:Runable和Thread都属于java.lang包 二.内置锁synchronized属于jvm关键字,内置条件队列操作接口Object.wait()/notify()/noti ...
- java.util.concurrent并发包诸类概览
java.util.concurrent包的类都来自于JSR-166:Concurrent Utilities,官方的描述叫做“The JSR proposes a set of medium-lev ...
- java多线程---------java.util.concurrent并发包----------等待多线程完成
一.等待多线程完成的join的使用.CoundownLantch.CyclicBarrier .
随机推荐
- NotFoundError (see above for traceback): Key local3/weights not found in checkpoint
解决办法 原文 https://www.jianshu.com/p/2de8e01af88d with tf.Session() as sess: tf.get_variable_scope().re ...
- 下载 OllyDbg
http://www.ollydbg.de/
- 论一种基于JS技术的WEB前端动态生成框图的方法
前言 HTML是一种标记语言,由HTML的标签元素和文本编写的文档可被浏览器描述为一幅网页.通常情况下网页的实现是由HTML.CSS和Javascript三者结合完成的,HTML负责网页的结构,CSS ...
- moveLeft()
这里大致都和上面一样,就是在记录左边坐标时,应该应该是lx = x - 1. void moveLeft(){ //定义变量存放人物左边的坐标 int lx, ly; //当左边没有元素时,直接ret ...
- FFmpeg之Linux下编译与调试
注:下面的一切都是在 root 模式下进行的,可以不再 root 模式下进行 1. 安装linux的基础环境 基础环境就是编译代码的基础库,Ubuntu联网安装软件很简单,一个语句即可搞定,这里列出语 ...
- legend3---8、烦请节约时间
legend3---8.烦请节约时间 一.总结 一句话总结: 时间不要浪费在垃圾情绪和无效社交上面. 1.商标不能以个人的名义注册? 可以先注册个体工商户,然后再可以注册商标 2.注册一个商标大概花多 ...
- EBS 查看输出HTML报表问题总结
问题一: 请求输出格式为HTML(如下图,默认浏览器输出),希望 查看输出 的时候能够实现excel输出. 解决方法: 路径:系统管理员/安装/浏览器选项 注:维护如下记录 文件格式:HTML ...
- 解决“mysql不是内部/外部命令,也不是可执行程序,也不是批处理文件”
解决方案: 1.切换到mysql.exe文件所在目录: 2.将mysql.exe文件所在目录添加到操作系统内的环境变量中: 如何添加环境变量: 1.右击“我的电脑”——>属性——>高级—— ...
- 封装带SSH跳板机的REDIS
一.封装ssh的redis 二.setting的配置 三.应用示例 import redis from sshtunnel import SSHTunnelForwarder from conf.se ...
- [go]redis基本使用
redis 数据结构操作 import ( "github.com/garyburd/redigo/redis" ) // set c, err := redis.Dial(&qu ...