独占锁(写锁) / 共享锁(读锁) / 互斥锁

概念

独占锁:指该锁一次只能被一个线程所持有。对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面试之锁-读写锁相关推荐

  1. 自旋锁/互斥锁/读写锁/递归锁的区别与联系

    自旋锁 互斥锁 读写锁 递归锁 互斥锁(mutexlock): 最常使用于线程同步的锁:标记用来保证在任一时刻,只能有一个线程访问该对象,同一线程多次加锁操作会造成死锁:临界区和互斥量都可用来实现此锁 ...

  2. 专科 java转go 翱翔之路(四)协议通信 锁,互斥锁 读写锁 条件变量锁 连接mysql 查询

    2.7.5 2.7.5.1 协议通信过程 应用层 hello 传输层(udp tcp) 源port 目的端口 hello 网络层(ip) 源ip 目的ip 源port 目的端口 hello 链路层(m ...

  3. java 可重入读写锁 ReentrantReadWriteLock 详解

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt206 读写锁 ReadWriteLock读写锁维护了一对相关的锁,一个用于只 ...

  4. Java并发编程-ReadWriteLock读写锁

    1.ReadWriteLock介绍 为什么我们有了Lock,还要用ReadWriteLock呢.我们对共享资源加锁之后,所有的线程都将会等待.Lock读操作也锁,写操作也会锁,而对共享资源读的时候,其 ...

  5. Java多线程编程之读写锁【ReentrantReadWriteLock】

    有时候我们需要有这样的需求:         对于同一个文件进行读和写操作,普通的锁是互斥的,这样读的时候会加锁,只能单线程的读,我们希望多线程的进行读操作,并且读的时候不能进行写操作,写的时候不能进 ...

  6. JUC-9.“锁”事(显式锁与隐式锁/悲观锁与乐观锁/公平锁与非公平锁/可重入锁/读写锁(独占/共享/降级)/邮戳锁/死锁)、锁升级

    目录 一.悲观锁与乐观锁 1.1 悲观锁 1.2 乐观锁 二.公平锁与非公平锁 2.1 为什么会有公平锁/非公平锁的设计为什么默认非公平? 2.2 如何选择使用哪种锁? 三.可重入锁(又名递归锁) 3 ...

  7. Java面试之锁-可重入锁和递归锁

    可重入锁和递归锁ReentrantLock 概念 可重入锁就是递归锁 指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取到该锁的代码. 在同一线程在外层方法获取锁的时候,在进入内层方法会自动获取 ...

  8. Java 并发编程之读写锁 ReentrantReadWriteLock

    ReentrantReadWriteLock 允许多个读操作同时进行,但是只允许一个写操作同时进行,在某些情况下,可以实现更高的并发性. 举个栗子: public class ReadAndWrite ...

  9. Java面试之锁-自旋锁

    Java锁之自旋锁 自旋锁:spinlock,是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁 这样的优点:是减少线程上下文切换的消耗, 缺点是循环会消耗CPU 原来提到的比较并交换, ...

最新文章

  1. 端午安康 | 经久熬煮,方能出“粽”
  2. 文章收录查询 php,php实现检查文章是否被百度收录,_PHP教程
  3. make: *** [ext/fileinfo/libmagic/apprentice.lo] Er
  4. javascript学习系列(20):数组中的bind,apply,call
  5. 前端学习(2737):重读vue电商网站47之生成打包报告
  6. python 导入数据对不齐_[Python] 大文件数据读取分析
  7. 剑指offer--3
  8. c/c++中一个 类似 a[2] 的数组引发的一些思考
  9. 蚂蚁课堂视频笔记思维导图-4期 四、微服务安全
  10. 电量统计(1)-原理
  11. neo4j中心度算法(Centrality algorithm)-3.Closeness Centrality algorithm
  12. 说唱天王 Eminem 自传《The Way I am》3
  13. python查看mac的usb信息_Python实现的读取电脑硬件信息功能示例
  14. nextpolish安装_使用nextpolish对三代组装进行polish
  15. 懒人起名神器,百度翻译内容改为驼峰格式
  16. 英语单词常见词根总结
  17. 性能测试——CPU占用率的计算原理
  18. Exceptions In Java
  19. python爬虫开发与项目实战pdf_Python爬虫开发与项目实战PDF高清文档下载
  20. 高并发技巧-redis和本地缓存使用技巧

热门文章

  1. java基础之-I/O流和File类解析
  2. 【NOI2004】【洛谷P1486】郁闷的出纳员(Splay写法)
  3. chattr 改变文件的扩展属性
  4. 优达学城数据分析笔记3--------数据分析过程(python篇)
  5. java集合框架(hashSet自定义元素是否相同,重写hashCode和equals方法)
  6. sharepoint webpart
  7. Installation error: INSTALL_FAILED_UID_CHANGED
  8. c 语言比较三个字符串,C语言字符篇(三)字符串比较函数
  9. 全国计算机二级c语言答案,全国计算机二级C语言试题及答案
  10. Linux下 sshd服务不能启动