一 引言

在多线程的环境下,对同一份数据进行读写,会涉及到线程安全的问题。比如在一个线程读取数据的时候,另外一个线程在写数据,而导致前后数据的不一致性;一个线程在写数据的时候,另一个线程也在写,同样也会导致线程前后看到的数据的不一致性。这时候可以在读写方法中加入互斥锁,任何时候只能允许一个线程的一个读或写操作,而不允许其他线程的读或写操作,这样是可以解决这样以上的问题,但是效率却大打折扣了。因为在真实的业务场景中,一份数据,读取数据的操作次数通常高于写入数据的操作,而线程与线程间的读读操作是不涉及到线程安全的问题,没有必要加入互斥锁,只要在读-写,写-写期间上锁就行了

读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的。

ReentrantReadWriteLock是可重入的读写锁,允许多个读线程获得ReadLock,但只允许一个写线程获得WriteLock

二 准备

读写锁的机制:

   "读-读" 不互斥
   "读-写" 互斥
   "写-写" 互斥
 
ReentrantReadWriteLock不支持锁升级(从读锁变成写锁)
ReadWriteLock rtLock = new ReentrantReadWriteLock();
rtLock.readLock().lock();
System.out.println("get readLock.");
rtLock.writeLock().lock();
System.out.println("blocking");

锁升级

这个代码会死锁,没释放读锁就去申请写锁

ReentrantReadWriteLock支持锁降级(从写锁变成读锁)
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的更多相关文章

  1. 「java.util.concurrent并发包」之 ThreadPoolExecutor

    一 异步用new Thread? 大写的"low"!! new Thread(new Runnable() { @Override public void run() { // T ...

  2. 「java.util.concurrent并发包」之 CountDownLatch

    一 CountDownLatch是什么 CountDownLatch是在java1.5被引入的,跟它一起被引入的并发工具类还有CyclicBarrier.Semaphore.ConcurrentHas ...

  3. 「java.util.concurrent并发包」之 CopyOnWrite

    一 CopyOnWrite容器概述 Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容C ...

  4. 「java.util.concurrent并发包」之 CAS

    一  引言 在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁 锁机制存在以下问题: (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能 ...

  5. 「java.util.concurrent并发包」之 CyclicBarrier

    一 描述 CyclicBarrier初始化时规定一个数目,然后计算调用了CyclicBarrier.await()进入等待的线程数.当线程数达到了这个数目时,所有进入等待状态的线程被唤醒并继续.Cyc ...

  6. Java并发:多线程和java.util.concurrent并发包总结

    多线程和java.util.concurrent并发包 转载:

  7. Java并发—java.util.concurrent并发包概括(转载)

    一.描述线程的类:Runable和Thread都属于java.lang包 二.内置锁synchronized属于jvm关键字,内置条件队列操作接口Object.wait()/notify()/noti ...

  8. java.util.concurrent并发包诸类概览

    java.util.concurrent包的类都来自于JSR-166:Concurrent Utilities,官方的描述叫做“The JSR proposes a set of medium-lev ...

  9. java多线程---------java.util.concurrent并发包----------等待多线程完成

    一.等待多线程完成的join的使用.CoundownLantch.CyclicBarrier .

随机推荐

  1. 【线性代数】1-1:线性组合(Linear Combinations)

    title: [线性代数]1-1:线性组合(Linear Combinations) toc: true categories: Mathematic Linear Algebra date: 201 ...

  2. 集合家族——Vector

    一.vector简介 Vector 可以实现可增长的对象数组.与数组一样,它包含可以使用整数索引进行访问的组件.不过,Vector 的大小是可以增加或者减小的,以便适应创建 Vector 后进行添加或 ...

  3. python 装饰器,传递类以及参数

    #!/usr/bin/env python # coding=utf- import time #import redis class RedisLock(object): def __init__( ...

  4. mybatis sql不等于

    转载地址:    https://blog.csdn.net/weixin_40797576/article/details/78796028 select * from test where id& ...

  5. sentinel控制台监控数据持久化【InfluxDB】

    根据官方wiki文档,sentinel控制台的实时监控数据,默认仅存储 5 分钟以内的数据.如需持久化,需要定制实现相关接口. https://github.com/alibaba/Sentinel/ ...

  6. 数据库 | SQL语法优化方法及实例详解

    使用复合索引 如果经常执行如上查询,那么建立三个单独索引不如建立一个复合索引,因为三个单独索引通常数据库每次执行只能使用其中一个,虽然这样比不使用索引而进行全表扫描提高了很多效率,但使用复合索引因为索 ...

  7. MySQL:行锁、表锁、乐观锁、悲观锁、读锁、写锁

    1.锁的分类 1.1从对数据操作的类型来分 读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响. 结论1: --如果某一个会话 对A表加了read锁,则 该会话 可以对A表进行读操作 ...

  8. LC 934. Shortest Bridge

    In a given 2D binary array A, there are two islands.  (An island is a 4-directionally connected grou ...

  9. 深度学习之强化学习Q-Learning

    1.知识点 """ 1.强化学习:学习系统没有像很多其他形式的机器学习方法一样被告知应该做什么行为, 必须在尝试之后才能发现哪些行为会导致奖励的最大化,当前的行为可能不仅 ...

  10. LVS系列一、LVS集群-NAT模式

    一. 集群概述 1. 什么是集群? 一组各自相互独立且又相互依赖的,通过高速网络互联的计算机组成的一个计算机组, 以单一的系统模式加以管理, 为用户提供服务, 对用户来说, 用户只会认为对方是一个服务 ...