概述

​ ReentrantReadWriteLock是Lock的另一种实现方式,我们已经知道了ReentrantLock是一个排他锁,同一时间只允许一个线程访问,而ReentrantReadWriteLock允许多个读线程同时访问,但不允许写线程和读线程、写线程和写线程同时访问。相对于排他锁,提高了并发性。在实际应用中,大部分情况下对共享数据(如缓存)的访问都是读操作远多于写操作,这时ReentrantReadWriteLock能够提供比排他锁更好的并发性和吞吐量。

​ 读写锁内部维护了两个锁,一个用于读操作,一个用于写操作。所有 ReadWriteLock实现都必须保证 writeLock操作的内存同步效果也要保持与相关 readLock的联系。也就是说,成功获取读锁的线程会看到写入锁之前版本所做的所有更新。

ReentrantReadWriteLock支持以下功能:

  1. 支持公平与非公平的获取锁方式。
  2. 支持可重入,读线程获取读锁后还可以获取读锁,但是不能获取写锁;写线程获取写锁后既可以再次获取写锁还可以获取读锁。
  3. 允许从写锁降级为读锁,其实现方式是:先获取写锁,然后获取读锁,最后释放写锁。但是,从读锁升级到写锁是不可以的;
  4. 读取锁和写入锁都支持锁获取期间的中断;
  5. Condition支持。仅写入锁提供了一个 Conditon 实现;读取锁不支持 Conditon ,readLock().newCondition() 会抛出 UnsupportedOperationException。

使用场景

示例一:利用重入执行升级缓存后的锁降级

在缓存有效的情况下,支持并发读。缓存失效,只允许独占写。

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;
}
}

示例二:高并发读写共享数据

当一份共享数据只能一个西安测绘给你写数据,可以多个线程读数据。可以选择读写锁,支持并发读,独占写,提高并发。

代码如下:

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();
}
}
}

单元测试

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获取最新文章。

ReentrantReadWriteLock读写锁的更多相关文章

  1. ReentrantReadWriteLock读写锁的使用

    Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象.两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象. 读写锁:分为读 ...

  2. ReentrantReadWriteLock读写锁的使用2

    本文可作为传智播客<张孝祥-Java多线程与并发库高级应用>的学习笔记. 这一节我们做一个缓存系统. 在读本节前 请先阅读 ReentrantReadWriteLock读写锁的使用1 第一 ...

  3. 锁对象-Lock: 同步问题更完美的处理方式 (ReentrantReadWriteLock读写锁的使用/源码分析)

    Lock是java.util.concurrent.locks包下的接口,Lock 实现提供了比使用synchronized 方法和语句可获得的更广泛的锁定操作,它能以更优雅的方式处理线程同步问题,我 ...

  4. Java并发包源码学习系列:ReentrantReadWriteLock读写锁解析

    目录 ReadWriteLock读写锁概述 读写锁案例 ReentrantReadWriteLock架构总览 Sync重要字段及内部类表示 写锁的获取 void lock() boolean writ ...

  5. ReentrantReadWriteLock读写锁简单原理案例证明

    ReentrantReadWriteLock存在原因? 我们知道List的实现类ArrayList,LinkedList都是非线程安全的,Vector类通过用synchronized修饰方法保证了Li ...

  6. ReentrantReadWriteLock读写锁详解

    一.读写锁简介 现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁.在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源:但是如果一个线 ...

  7. java多线程:并发包中ReentrantReadWriteLock读写锁的锁降级模板

    写锁降级为读锁,但读锁不可升级或降级为写锁. 锁降级是为了让当前线程感知到数据的变化. //读写锁 private ReentrantReadWriteLock lock=new ReentrantR ...

  8. java中ReentrantReadWriteLock读写锁的使用

    Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象.两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象. 读写锁:分为读 ...

  9. java多线程:ReentrantReadWriteLock读写锁使用

    Lock比传统的线程模型synchronized更多的面向对象的方式.锁和生活似,应该是一个对象.两个线程运行的代码片段要实现同步相互排斥的效果.它们必须用同一个Lock对象. 读写锁:分为读锁和写锁 ...

  10. ReentrantReadWriteLock读写锁的使用1

    本文可作为传智播客<张孝祥-Java多线程与并发库高级应用>的学习笔记. 一个简单的例子 两个线程,一个不断打印a,一个不断打印b public class LockTest { publ ...

随机推荐

  1. 【转】Oracle AWR 报告 每天自动生成并发送邮箱 Python脚本(一)

    Oracle 的AWR 报告能很好的提供有关DB性能的信息. 所以DBA 需要定期的查看AWR的报告. 有关AWR报告的说明参考: Oracle AWR 介绍 http://blog.csdn.net ...

  2. Java多线程并发技术

    Java多线程并发技术 参考文献: http://blog.csdn.net/aboy123/article/details/38307539 http://blog.csdn.net/ghsau/a ...

  3. leetcode 【 Find Minimum in Rotated Sorted Array 】python 实现

    题目: Suppose a sorted array is rotated at some pivot unknown to you beforehand. (i.e., 0 1 2 4 5 6 7  ...

  4. linux环境搭建系列之tomcat安装步骤

    前提: Linux centOS 64位 JDK 1.7 安装包从官网上下载 安装Tomcat之前要先安装JDK. 我的JDK是1.7版本的,所以Tomcat版本也选了7的 1.新建目录tomcat ...

  5. python - 接口自动化测试 - basic_data - 基础数据参数化方法封装

    # -*- coding:utf-8 -*- ''' @project: ApiAutoTest @author: Jimmy @file: basic_data.py @ide: PyCharm C ...

  6. Java开发微信公众号(二)---开启开发者模式,接入微信公众平台开发

    接入微信公众平台开发,开发者需要按照如下步骤完成: 1.填写服务器配置 2.验证服务器地址的有效性 3.依据接口文档实现业务逻辑 资料准备: 1.一个可以访问的外网,即80的访问端口,因为微信公众号接 ...

  7. AsyncSocket

    如果需要在项目中像QQ微信一样做到即时通讯,必须使用socket通讯,本人也是刚学习,分享一下,有什么不对的地方希望大家指正 ios原生的socket用起来不是很直观,所以我用的是AsyncSocke ...

  8. POJ 2376:Cleaning Shifts(贪心)

    题目大意:有n个奶牛,他们负责在长为t个时间点的时间内值班,每个时间点至少有一个在值班,每个奶牛有一段空闲时间可以值班,求满足要求所需最少奶牛数量,无法满足则输出-1. 分析: 将奶牛空闲时间段看成线 ...

  9. JMeter 性能测试进阶实战

    课程简介 本课程制作的主要目的是为了让大家快速上手 JMeter,期间穿插了大量主流项目中用到的技术,以及结合当今主流微服务技术提供了测试 Dubbo 接口.Java 工程技术具体实施方案,注重实践. ...

  10. [AGC003F] Fraction of Fractal(矩阵乘法)

    Description Snuke从他的母亲那里得到了生日礼物--一个网格.网格有H行W列.每个单元格都是黑色或白色.所有黑色单元格都是四联通的,也就是说,只做水平或垂直移动且只经过黑色单元格即可从任 ...