7.Java锁之读写锁

为什么算法这么难 2021-02-23 18:30:00
java SegmentFault 读写 7.java


概念

独占锁:该锁一次只能被一个线程锁持有(ReetrantLockSynchronized都是独占锁)

共享锁:该锁可以被多个线程持有

对ReentrantReadWriteLock而言,它的读锁是共享,写锁是独占。写的时候一个人写,但是可以多个人同时读。

为什么会有写锁和读锁

原来我们使用ReentrantLock创建锁的时候,是独占锁,也就是说一次只能一个线程访问。但是有一个读写分离场景,读的时候想同时进行,因为读锁并不会造成数据不一致的问题,因此可以多个人共享读。

多个线程同时去读一个资源类没有任何问题,如果一个线程想去写共享内存,就不应该有其他线程对该资源进行读或写。

代码实现

  • 实现一个读写缓存的操作,假设开始没有加锁的时候,会出现什么情况

先模拟出HashMap的putget方法:

 public class MyCache {
 ​
 private volatile Map<String,Object> map = new HashMap<>();
 
 //写入的方法
 public void put(String key,Object value) throws InterruptedException {
  System.out.println("线程"+ Thread.currentThread().getName() +"正在写入:" + key);
 
  TimeUnit.MILLISECONDS.sleep(300); //模拟网络拥堵,延迟0.3s
  map.put(key,value); //写入元素
 
  System.out.println("线程"+ Thread.currentThread().getName() +"写入成功");
 }
 
 //读取的方法
 public void get(String key) throws InterruptedException {
  System.out.println("线程"+ Thread.currentThread().getName() +"正在读取:");
 
  TimeUnit.MILLISECONDS.sleep(300); //模拟网络拥堵,延迟0.3s
  Object value = map.get(key); //读取元素
 
  System.out.println("线程"+ Thread.currentThread().getName() +"读取完成:" + value );
 }
 }

测试:

 public static void main(String[] args) {
 ​
  MyCache myCache = new MyCache();
 ​
  //创建5个线程写入缓存
  for (int i = 0; i < 5; i++) {
  final int tempInt = i; //lambda表达式内部变量必须是final
 ​
  new Thread(() -> {
  myCache.put(tempInt + "", tempInt + "");
  }, "线程" + i).start();
  }
 
  //创建5个线程读取缓存
  for (int i = 0; i < 5; i++) {
  final int tempInt = i; //lambda表达式内部变量必须是final
 ​
  new Thread(() -> {
  myCache.get(tempInt + "");
  }, "线程" + i).start();
  }
 ​
 }

运行结果显示:

解决方法

上面的代码是没有加锁的,这样就会造成线程在进行写入操作的时候,被其他线程频繁打断,从而不具备原子性,这时候,我们就需要用到读写锁来解决了。

创建一个读写锁:

 private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
 ​
 //它是一个读写融为一体的锁,在使用的时候,需要转换

当我们在进行写操作的时候,就需要转换成写锁:

 // 创建一个写锁
 rwLock.writeLock().lock();
 ​
 // 写锁 释放
 rwLock.writeLock().unlock();

当们在进行读操作的时候,在转换成读锁

 // 创建一个读锁
 rwLock.readLock().lock();
 ​
 // 读锁 释放
 rwLock.readLock().unlock();

这里的读锁和写锁的区别在于,写锁一次只能一个线程进入,执行写操作,而读锁是多个线程能够同时进入,进行读取的操作。

我们就把MyCache类的put方法的最前面加上一个写锁,最后释放锁。get方法的最前面加一个读锁,最后释放锁。

最后我们看看添加读写锁后的运行结果:

从运行结果我们可以看出,写入操作是一个一个线程进行执行的,并且中间不会被打断,而读操作的时候,是同时5个线程进入,然后并发读取操作。

版权声明
本文为[为什么算法这么难]所创,转载请带上原文链接,感谢
https://segmentfault.com/a/1190000039264654

  1. JAVA的函数式接口
  2. JAVA里的元祖类
  3. JQuery Gantt package to create a new ASP.NET project
  4. Binary conversion of Unicode code (Java)
  5. The functional interface of Java
  6. Yuanzu class in Java
  7. Java中的CPU占用高和内存占用高的问题排查
  8. HashMap连环问你能答出几道?
  9. IntelliJ IDEA 还能画思维导图,果然最强 IDE!
  10. java中的反射和代理
  11. Troubleshooting of high CPU and memory usage in Java
  12. How many questions can you answer?
  13. IntelliJ idea can also draw mind maps. It's really the strongest ide!
  14. Reflection and proxy in Java
  15. Java中的CPU占用高和内存占用高的问题排查
  16. Linux OOM(out of memory)
  17. mysql 自定义函数因参数名称报错
  18. Troubleshooting of high CPU and memory usage in Java
  19. Linux OOM(out of memory)
  20. MySQL user defined function error due to parameter name
  21. echarts-gl 3D 地图柱状图可视化GDP
  22. Visualization of histogram of ecarts GL 3D map
  23. 金三银四如何应对Redis面试,一文深入Redis实战实践!
  24. 阿里资深架构师定制金三银四面试整理出来的一份Java核心知识点.pdf
  25. 为什么Java开发工程师工资高,却很多人想转行?
  26. How to deal with the interview of redis!
  27. Ali senior architect customizes a Java core knowledge point sorted out in the interview of golden, silver and four.pdf
  28. Why do java development engineers have high salaries, but many people want to change careers?
  29. 用注解开发SpringMVC
  30. Developing spring MVC with annotations
  31. 编译redis报错/deps/hiredis/libhiredis.a解决
  32. Compile redis report error / DEPs / hirredis / libhirredis. A solution
  33. 用注解开发SpringMVC
  34. Developing spring MVC with annotations
  35. Spring学习笔记-01
  36. Centos安装和卸载docker
  37. Spring learning notes-01
  38. Installing and uninstalling docker in CentOS
  39. Java基础-异常
  40. Java基础-反射
  41. Java基础-继承
  42. k8s部署 (进行中)
  43. Hive-常见调优方式 && 两个面试sql
  44. 死磕Spring之IoC篇 - BeanDefinition 的加载阶段(XML 文件)
  45. Java basics exception
  46. Java Basics - Reflection
  47. Java Basics - inheritance
  48. K8s deployment (in progress)
  49. Hive common tuning methods & two interview SQL
  50. The loading phase of beandefinition (XML file)
  51. 死磕Spring之IoC篇 - BeanDefinition 的加载阶段(XML 文件)
  52. Hive-常见调优方式 && 两个面试sql
  53. The loading phase of beandefinition (XML file)
  54. Hive common tuning methods & two interview SQL
  55. iconv文件编码转换由windows文件放到linux下
  56. The code conversion of iconv file is put into Linux by windows file
  57. SpringBoot2+intellij IDEA开发前环境准备
  58. Preparation of pre development environment for springboot2 + IntelliJ idea
  59. Docker私有仓库部署
  60. Docker private warehouse deployment