ReentrantReadWriteLock读写锁
概述
 ReentrantReadWriteLock是Lock的另一种实现方式,我们已经知道了ReentrantLock是一个排他锁,同一时间只允许一个线程访问,而ReentrantReadWriteLock允许多个读线程同时访问,但不允许写线程和读线程、写线程和写线程同时访问。相对于排他锁,提高了并发性。在实际应用中,大部分情况下对共享数据(如缓存)的访问都是读操作远多于写操作,这时ReentrantReadWriteLock能够提供比排他锁更好的并发性和吞吐量。
 读写锁内部维护了两个锁,一个用于读操作,一个用于写操作。所有 ReadWriteLock实现都必须保证 writeLock操作的内存同步效果也要保持与相关 readLock的联系。也就是说,成功获取读锁的线程会看到写入锁之前版本所做的所有更新。
ReentrantReadWriteLock支持以下功能:
- 支持公平与非公平的获取锁方式。
 - 支持可重入,读线程获取读锁后还可以获取读锁,但是不能获取写锁;写线程获取写锁后既可以再次获取写锁还可以获取读锁。
 - 允许从写锁降级为读锁,其实现方式是:先获取写锁,然后获取读锁,最后释放写锁。但是,从读锁升级到写锁是不可以的;
 - 读取锁和写入锁都支持锁获取期间的中断;
 - 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读写锁的更多相关文章
- ReentrantReadWriteLock读写锁的使用
		
Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象.两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象. 读写锁:分为读 ...
 - ReentrantReadWriteLock读写锁的使用2
		
本文可作为传智播客<张孝祥-Java多线程与并发库高级应用>的学习笔记. 这一节我们做一个缓存系统. 在读本节前 请先阅读 ReentrantReadWriteLock读写锁的使用1 第一 ...
 - 锁对象-Lock: 同步问题更完美的处理方式 (ReentrantReadWriteLock读写锁的使用/源码分析)
		
Lock是java.util.concurrent.locks包下的接口,Lock 实现提供了比使用synchronized 方法和语句可获得的更广泛的锁定操作,它能以更优雅的方式处理线程同步问题,我 ...
 - Java并发包源码学习系列:ReentrantReadWriteLock读写锁解析
		
目录 ReadWriteLock读写锁概述 读写锁案例 ReentrantReadWriteLock架构总览 Sync重要字段及内部类表示 写锁的获取 void lock() boolean writ ...
 - ReentrantReadWriteLock读写锁简单原理案例证明
		
ReentrantReadWriteLock存在原因? 我们知道List的实现类ArrayList,LinkedList都是非线程安全的,Vector类通过用synchronized修饰方法保证了Li ...
 - ReentrantReadWriteLock读写锁详解
		
一.读写锁简介 现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁.在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源:但是如果一个线 ...
 - java多线程:并发包中ReentrantReadWriteLock读写锁的锁降级模板
		
写锁降级为读锁,但读锁不可升级或降级为写锁. 锁降级是为了让当前线程感知到数据的变化. //读写锁 private ReentrantReadWriteLock lock=new ReentrantR ...
 - java中ReentrantReadWriteLock读写锁的使用
		
Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象.两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象. 读写锁:分为读 ...
 - java多线程:ReentrantReadWriteLock读写锁使用
		
Lock比传统的线程模型synchronized更多的面向对象的方式.锁和生活似,应该是一个对象.两个线程运行的代码片段要实现同步相互排斥的效果.它们必须用同一个Lock对象. 读写锁:分为读锁和写锁 ...
 - ReentrantReadWriteLock读写锁的使用1
		
本文可作为传智播客<张孝祥-Java多线程与并发库高级应用>的学习笔记. 一个简单的例子 两个线程,一个不断打印a,一个不断打印b public class LockTest { publ ...
 
随机推荐
- Django templates(模板)
			
为什么用templates? views.py视图函数是用来写Python代码的,HTML可以被直接硬编码在views.py之中.如下: import datetime def current_tim ...
 - setTimeout相关整理
			
setTimeout里面函数有无双引号的区别 双引号中的作用域不捕捉局部变量,不用双引号包着的是捕捉局部作用域 var a = function(){ alert(1111) } function a ...
 - mysql之面试问题总结
			
问题1.char 与varchar的区别? varchar是变长而char的长度是固定的.如果你的内容是固定的大小,char性能更好. char[4] 与varchar[4] 存储字母a a占一个 ...
 - MySQL基础4-SQL简单查询(单表)
			
1.SELECT语句 2.运算符的优先级 利用Navicat中的查询方法: 栗子1:查询所有货品信息 栗子2:查询所有货品的id,productName,salePrice 当查询错误的时候出现的界面 ...
 - Java开发配置
			
http://www.runoob.com/java/java-environment-setup.html
 - MongoDB用PCRE正则表达式
			
介绍 下面说明 PCRE 所支持的正则表达式的语法和语义.Perl 文档和很多其它书中也解说了正则表达式,有的书中有很多例子.Jeffrey Friedl 写的“Mastering Regular E ...
 - nyoj 325
			
zb的生日 时间限制:3000 ms | 内存限制:65535 KB 难度:2 描述 今天是阴历七月初五,acm队员zb的生日.zb正在和C小加.never在武汉集训.他想给这两位兄弟买点什么 ...
 - hdu6212[区间dp] 2017青岛ACM-ICPC网络赛
			
原题: BZOJ1032 (原题数据有问题) /*hdu6212[区间dp] 2017青岛ACM-ICPC网络赛*/ #include <bits/stdc++.h> using name ...
 - P4555 最长双回文串
			
题目描述 顺序和逆序读起来完全一样的串叫做回文串.比如acbca是回文串,而abc不是(abc的顺序为abc,逆序为cba,不相同). 输入长度为 n的串 S ,求 S的最长双回文子串 T ,即可将 ...
 - BZOJ1487  [HNOI2009]无归岛  【仙人掌dp】
			
题目链接 BZOJ1487 题解 就是一个简单的仙人掌最大权独立集 还是不会圆方树 老老实实地树形Dp + 环处理 #include<iostream> #include<cstdi ...
 
			
		