Java面试之锁-读写锁
独占锁(写锁) / 共享锁(读锁) / 互斥锁
概念
独占锁:指该锁一次只能被一个线程所持有。对ReentrantLock和Synchronized而言都是独占锁
共享锁:指该锁可以被多个线程锁持有
对ReentrantReadWriteLock其读锁是共享,其写锁是独占
写的时候只能一个人写,但是读的时候,可以多个人同时读
为什么会有写锁和读锁
原来我们使用ReentrantLock创建锁的时候,是独占锁,也就是说一次只能一个线程访问,但是有一个读写分离场景,读的时候想同时进行,因此原来独占锁的并发性就没这么好了,因为读锁并不会造成数据不一致的问题,因此可以多个人共享读
多个线程 同时读一个资源类没有任何问题,所以为了满足并发量,读取共享资源应该可以同时进行,
但是如果一个线程想去写共享资源,就不应该再有其它线程可以对该资源进行读或写
读-读:能共存
读-写:不能共存
写-写:不能共存
代码实现
实现一个读写缓存的操作,假设开始没有加锁的时候,会出现什么情况
/*** 读写锁* 多个线程 同时读一个资源类没有任何问题,所以为了满足并发量,读取共享资源应该可以同时进行* 但是,如果一个线程想去写共享资源,就不应该再有其它线程可以对该资源进行读或写**/import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;/*** 资源类*/
class MyCache {private volatile Map<String, Object> map = new HashMap<>();// private Lock lock = null;/*** 定义写操作* 满足:原子 + 独占* @param key* @param value*/public void put(String key, Object value) {System.out.println(Thread.currentThread().getName() + "\t 正在写入:" + key);try {// 模拟网络拥堵,延迟0.3秒TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}map.put(key, value);System.out.println(Thread.currentThread().getName() + "\t 写入完成");}public void get(String key) {System.out.println(Thread.currentThread().getName() + "\t 正在读取:");try {// 模拟网络拥堵,延迟0.3秒TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}Object value = map.get(key);System.out.println(Thread.currentThread().getName() + "\t 读取完成:" + value);}}
public class ReadWriteLockDemo {public static void main(String[] args) {MyCache myCache = new MyCache();// 线程操作资源类,5个线程写for (int i = 0; i < 5; i++) {// lambda表达式内部必须是finalfinal int tempInt = i;new Thread(() -> {myCache.put(tempInt + "", tempInt + "");}, String.valueOf(i)).start();}// 线程操作资源类, 5个线程读for (int i = 0; i < 5; i++) {// lambda表达式内部必须是finalfinal int tempInt = i;new Thread(() -> {myCache.get(tempInt + "");}, String.valueOf(i)).start();}}
}
我们分别创建5个线程写入缓存
// 线程操作资源类,5个线程写for (int i = 0; i < 5; i++) {// lambda表达式内部必须是finalfinal int tempInt = i;new Thread(() -> {myCache.put(tempInt + "", tempInt + "");}, String.valueOf(i)).start();}
5个线程读取缓存,
// 线程操作资源类, 5个线程读for (int i = 0; i < 5; i++) {// lambda表达式内部必须是finalfinal int tempInt = i;new Thread(() -> {myCache.get(tempInt + "");}, String.valueOf(i)).start();}
最后运行结果:
0 正在写入:0
4 正在写入:4
3 正在写入:3
1 正在写入:1
2 正在写入:2
0 正在读取:
1 正在读取:
2 正在读取:
3 正在读取:
4 正在读取:
2 写入完成
4 写入完成
4 读取完成:null
0 写入完成
3 读取完成:null
0 读取完成:null
1 写入完成
3 写入完成
1 读取完成:null
2 读取完成:null
我们可以看到,在写入的时候,写操作都没其它线程打断了,这就造成了,还没写完,其它线程又开始写,这样就造成数据不一致
解决方法
上面的代码是没有加锁的,这样就会造成线程在进行写入操作的时候,被其它线程频繁打断,从而不具备原子性,这个时候,我们就需要用到读写锁来解决了
/**
* 创建一个读写锁
* 它是一个读写融为一体的锁,在使用的时候,需要转换
*/
private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
当我们在进行写操作的时候,就需要转换成写锁
// 创建一个写锁
rwLock.writeLock().lock();// 写锁 释放
rwLock.writeLock().unlock();
当们在进行读操作的时候,在转换成读锁
// 创建一个读锁
rwLock.readLock().lock();// 读锁 释放
rwLock.readLock().unlock();
这里的读锁和写锁的区别在于,写锁一次只能一个线程进入,执行写操作,而读锁是多个线程能够同时进入,进行读取的操作
完整代码:
/*** 读写锁* 多个线程 同时读一个资源类没有任何问题,所以为了满足并发量,读取共享资源应该可以同时进行* 但是,如果一个线程想去写共享资源,就不应该再有其它线程可以对该资源进行读或写**/import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;/*** 资源类*/
class MyCache {/*** 缓存中的东西,必须保持可见性,因此使用volatile修饰*/private volatile Map<String, Object> map = new HashMap<>();/*** 创建一个读写锁* 它是一个读写融为一体的锁,在使用的时候,需要转换*/private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();/*** 定义写操作* 满足:原子 + 独占* @param key* @param value*/public void put(String key, Object value) {// 创建一个写锁rwLock.writeLock().lock();try {System.out.println(Thread.currentThread().getName() + "\t 正在写入:" + key);try {// 模拟网络拥堵,延迟0.3秒TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}map.put(key, value);System.out.println(Thread.currentThread().getName() + "\t 写入完成");} catch (Exception e) {e.printStackTrace();} finally {// 写锁 释放rwLock.writeLock().unlock();}}/*** 获取* @param key*/public void get(String key) {// 读锁rwLock.readLock().lock();try {System.out.println(Thread.currentThread().getName() + "\t 正在读取:");try {// 模拟网络拥堵,延迟0.3秒TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}Object value = map.get(key);System.out.println(Thread.currentThread().getName() + "\t 读取完成:" + value);} catch (Exception e) {e.printStackTrace();} finally {// 读锁释放rwLock.readLock().unlock();}}/*** 清空缓存*/public void clean() {map.clear();}}
public class ReadWriteLockDemo {public static void main(String[] args) {MyCache myCache = new MyCache();// 线程操作资源类,5个线程写for (int i = 1; i <= 5; i++) {// lambda表达式内部必须是finalfinal int tempInt = i;new Thread(() -> {myCache.put(tempInt + "", tempInt + "");}, String.valueOf(i)).start();}// 线程操作资源类, 5个线程读for (int i = 1; i <= 5; i++) {// lambda表达式内部必须是finalfinal int tempInt = i;new Thread(() -> {myCache.get(tempInt + "");}, String.valueOf(i)).start();}}
}
运行结果:
1 正在写入:1
1 写入完成
2 正在写入:2
2 写入完成
3 正在写入:3
3 写入完成
4 正在写入:4
4 写入完成
5 正在写入:5
5 写入完成
2 正在读取:
3 正在读取:
1 正在读取:
4 正在读取:
5 正在读取:
2 读取完成:2
1 读取完成:1
4 读取完成:4
3 读取完成:3
5 读取完成:5
从运行结果我们可以看出,写入操作是一个一个线程进行执行的,并且中间不会被打断,而读操作的时候,是同时5个线程进入,然后并发读取操作
Java面试之锁-读写锁相关推荐
- 自旋锁/互斥锁/读写锁/递归锁的区别与联系
自旋锁 互斥锁 读写锁 递归锁 互斥锁(mutexlock): 最常使用于线程同步的锁:标记用来保证在任一时刻,只能有一个线程访问该对象,同一线程多次加锁操作会造成死锁:临界区和互斥量都可用来实现此锁 ...
- 专科 java转go 翱翔之路(四)协议通信 锁,互斥锁 读写锁 条件变量锁 连接mysql 查询
2.7.5 2.7.5.1 协议通信过程 应用层 hello 传输层(udp tcp) 源port 目的端口 hello 网络层(ip) 源ip 目的ip 源port 目的端口 hello 链路层(m ...
- java 可重入读写锁 ReentrantReadWriteLock 详解
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt206 读写锁 ReadWriteLock读写锁维护了一对相关的锁,一个用于只 ...
- Java并发编程-ReadWriteLock读写锁
1.ReadWriteLock介绍 为什么我们有了Lock,还要用ReadWriteLock呢.我们对共享资源加锁之后,所有的线程都将会等待.Lock读操作也锁,写操作也会锁,而对共享资源读的时候,其 ...
- Java多线程编程之读写锁【ReentrantReadWriteLock】
有时候我们需要有这样的需求: 对于同一个文件进行读和写操作,普通的锁是互斥的,这样读的时候会加锁,只能单线程的读,我们希望多线程的进行读操作,并且读的时候不能进行写操作,写的时候不能进 ...
- JUC-9.“锁”事(显式锁与隐式锁/悲观锁与乐观锁/公平锁与非公平锁/可重入锁/读写锁(独占/共享/降级)/邮戳锁/死锁)、锁升级
目录 一.悲观锁与乐观锁 1.1 悲观锁 1.2 乐观锁 二.公平锁与非公平锁 2.1 为什么会有公平锁/非公平锁的设计为什么默认非公平? 2.2 如何选择使用哪种锁? 三.可重入锁(又名递归锁) 3 ...
- Java面试之锁-可重入锁和递归锁
可重入锁和递归锁ReentrantLock 概念 可重入锁就是递归锁 指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取到该锁的代码. 在同一线程在外层方法获取锁的时候,在进入内层方法会自动获取 ...
- Java 并发编程之读写锁 ReentrantReadWriteLock
ReentrantReadWriteLock 允许多个读操作同时进行,但是只允许一个写操作同时进行,在某些情况下,可以实现更高的并发性. 举个栗子: public class ReadAndWrite ...
- Java面试之锁-自旋锁
Java锁之自旋锁 自旋锁:spinlock,是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁 这样的优点:是减少线程上下文切换的消耗, 缺点是循环会消耗CPU 原来提到的比较并交换, ...
最新文章
- 端午安康 | 经久熬煮,方能出“粽”
- 文章收录查询 php,php实现检查文章是否被百度收录,_PHP教程
- make: *** [ext/fileinfo/libmagic/apprentice.lo] Er
- javascript学习系列(20):数组中的bind,apply,call
- 前端学习(2737):重读vue电商网站47之生成打包报告
- python 导入数据对不齐_[Python] 大文件数据读取分析
- 剑指offer--3
- c/c++中一个 类似 a[2] 的数组引发的一些思考
- 蚂蚁课堂视频笔记思维导图-4期 四、微服务安全
- 电量统计(1)-原理
- neo4j中心度算法(Centrality algorithm)-3.Closeness Centrality algorithm
- 说唱天王 Eminem 自传《The Way I am》3
- python查看mac的usb信息_Python实现的读取电脑硬件信息功能示例
- nextpolish安装_使用nextpolish对三代组装进行polish
- 懒人起名神器,百度翻译内容改为驼峰格式
- 英语单词常见词根总结
- 性能测试——CPU占用率的计算原理
- Exceptions In Java
- python爬虫开发与项目实战pdf_Python爬虫开发与项目实战PDF高清文档下载
- 高并发技巧-redis和本地缓存使用技巧
热门文章
- java基础之-I/O流和File类解析
- 【NOI2004】【洛谷P1486】郁闷的出纳员(Splay写法)
- chattr 改变文件的扩展属性
- 优达学城数据分析笔记3--------数据分析过程(python篇)
- java集合框架(hashSet自定义元素是否相同,重写hashCode和equals方法)
- sharepoint webpart
- Installation error: INSTALL_FAILED_UID_CHANGED
- c 语言比较三个字符串,C语言字符篇(三)字符串比较函数
- 全国计算机二级c语言答案,全国计算机二级C语言试题及答案
- Linux下 sshd服务不能启动