一、科普定义

这篇博文的两个主角“synchronized”和“读写锁”

1)synchronized

这个同步关键字相信大家都用得比较多,在上一篇“多个线程之间共享数据的方式”中也详细列举他的应用,在这就不多说只做几点归纳:

  • Java提供这个关键字,为防止资源冲突提供的内置支持。当任务执行到被synchronized保护的代码片段的时候,它检查锁是否可用,然后获取锁,执行代码,释放锁。
  • 常用这个关键字可以修饰成员方法和代码块

2)读写锁

我们对数据的操作无非两种:“读”和“写”,试想一个这样的情景,当十个线程同时读取某个数据时,这个操作应不应该加同步。答案是没必要的。只有以下两种情况需要加同步:

  • 这十个线程对这个公共数据既有读又有写
  • 这十个线程对公共数据进行写操作
  • 以上两点归结起来就一点就是有对数据进行改变的操作就需要同步

所以

java5提供了读写锁

这种锁支持多线程读操作不互斥,多线程读写互斥,多线程写写互斥。
读操作不互斥这样有助于性能的提高,这点在java5以前没有
二.用一道面试题来具体比较这两点
题目:“白板编程,实现一个缓存系统”
题目分析:

对这个缓存系统的理解:
间于用户和数据库中间的一个环节,我们知道用户直接访问数据库的时间是远大于直接访问内存,所以有了缓存区后用户访问数据时 这样,用户先访问缓存区当缓存区有用户需要的数据时直接拿走,当缓存区没有这样的数据,访问数据库并把访问所得的数据放在缓存区,这样当下一个需要这个数据的用户就直接访问内存即可得到。
核心代码实现:
首先用synchronized实现
  1. public synchronized Object getData(String key){
  2. Object result = map.get(key);
  3. if(result ==null){
  4. result = "new";//用这步代替访问数据库得数据
  5. }
  6. return result;
  7. }
  1. public synchronized Object getData(String key){
  2. Object result = map.get(key);
  3. if(result ==null){
  4. result = "new";//用这步代替访问数据库得数据
  5. }
  6. return result;
  7. }

用读写锁实现

  1. public Object getData(String key){
  2. rw.readLock().lock();//在读前先上读锁
  3. Object result = null;
  4. try{
  5. result = map.get(key);
  6. //这个if比较关键,它避免了多余的几次对数据哭的读取
  7. if(result==null){
  8. //如果内存中没有所要数据
  9. rw.readLock().unlock();
  10. rw.writeLock().lock();
  11. if(result==null){
  12. try{
  13. //我们用这个代替对数据库访问得到数据的步骤
  14. result = "new";
  15. }finally{
  16. rw.writeLock().unlock();
  17. }
  18. rw.readLock().lock();
  19. }
  20. }
  21. }finally{
  22. rw.readLock().unlock();
  23. }
  24. return result;
  25. }
  1. public Object getData(String key){
  2. rw.readLock().lock();//在读前先上读锁
  3. Object result = null;
  4. try{
  5. result = map.get(key);
  6. //这个if比较关键,它避免了多余的几次对数据哭的读取
  7. if(result==null){
  8. //如果内存中没有所要数据
  9. rw.readLock().unlock();
  10. rw.writeLock().lock();
  11. if(result==null){
  12. try{
  13. //我们用这个代替对数据库访问得到数据的步骤
  14. result = "new";
  15. }finally{
  16. rw.writeLock().unlock();
  17. }
  18. rw.readLock().lock();
  19. }
  20. }
  21. }finally{
  22. rw.readLock().unlock();
  23. }
  24. return result;
  25. }

 代码分析

  1. 用第一种方法处理,整个过程比较粗线条,代码比较简单单执行效率很低。这种方法的中心思想是不管你是什么操作,但凡涉及到公共资源就都给你同步。这么做可以是可以但是并不好。
  2. 第二种用读写锁处理显然是对前者的一个优化,对第二种方法做如下几点说明:
  • 关于unlock操作,我们知道只要是上了锁就必须要解锁,但是有这么一种情况就是当你上完锁后在执行解锁操作前程序出现异常,那这个所可能就一直存在。所以针对这个问题我们一般将unlock操作放在finally代码块中,就可以保证上了的锁一定会被解。
  • 上面的两次if判断,第一个if相信大家很好理解。但为什么要用第二个if呢?再假设一个场景,现在有十个线程来读这个数据,而这个数据又不存在与缓存区,那么这十个线程中最先到的线程将执行“rw.writeLock().lock();”而另外九个线程将被阻塞,当第一个线程读完以后缓存区实际上已经就有了这个数据,但另外九个阻塞在“rw.writeLock().lock();”如果不加这层if他们会继续访问数据库,由此可见加了这层if对整个过程影响很大。这是比较细节的一点,就这一点Java的API文档也考虑到了,它的样例代码如下:
  1. class CachedData {
  2. Object data;
  3. volatile boolean cacheValid;
  4. ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
  5. void processCachedData() {
  6. rwl.readLock().lock();
  7. <span style="color: rgb(255, 0, 0);">if (!cacheValid)</span> {
  8. // Must release read lock before acquiring write lock
  9. rwl.readLock().unlock();
  10. rwl.writeLock().lock();
  11. // Recheck state because another thread might have acquired
  12. //   write lock and changed state before we did.
  13. <span style="color: rgb(255, 0, 0);"> if (!cacheValid)</span> {
  14. data = ...
  15. cacheValid = true;
  16. }
  17. // Downgrade by acquiring read lock before releasing write lock
  18. rwl.readLock().lock();
  19. rwl.writeLock().unlock(); // Unlock write, still hold read
  20. }
  21. use(data);
  22. rwl.readLock().unlock();
  23. }
  24. }
  1. class CachedData {
  2. Object data;
  3. volatile boolean cacheValid;
  4. ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
  5. void processCachedData() {
  6. rwl.readLock().lock();
  7. <span style="color:#ff00;">if (!cacheValid)</span> {
  8. // Must release read lock before acquiring write lock
  9. rwl.readLock().unlock();
  10. rwl.writeLock().lock();
  11. // Recheck state because another thread might have acquired
  12. //   write lock and changed state before we did.
  13. <span style="color:#ff00;"> if (!cacheValid)</span> {
  14. data = ...
  15. cacheValid = true;
  16. }
  17. // Downgrade by acquiring read lock before releasing write lock
  18. rwl.readLock().lock();
  19. rwl.writeLock().unlock(); // Unlock write, still hold read
  20. }
  21. use(data);
  22. rwl.readLock().unlock();
  23. }
  24. }

学习在继续!!!Go!Go!Go!!!

一道面试题比较synchronized和读写锁的更多相关文章

  1. <转>一道面试题比较synchronized和读写锁

    一.科普定义(原文:http://903497571.iteye.com/blog/1874752) 这篇博文的两个主角“synchronized”和“读写锁” 1)synchronized 这个同步 ...

  2. 比较synchronized和读写锁

    一.科普定义 这篇博文的两个主角“synchronized”和“读写锁” 1)synchronized 这个同步关键字相信大家都用得比较多,在上一篇“多个线程之间共享数据的方式”中也详细列举他的应用, ...

  3. Java 线程锁机制 -Synchronized Lock 互斥锁 读写锁

    (1)synchronized 是互斥锁: (2)ReentrantLock 顾名思义 :可重入锁 (3)ReadWriteLock :读写锁 读写锁特点: a)多个读者可以同时进行读b)写者必须互斥 ...

  4. 浅谈Java中的锁:Synchronized、重入锁、读写锁

    Java开发必须要掌握的知识点就包括如何使用锁在多线程的环境下控制对资源的访问限制 ◆ Synchronized ◆ 首先我们来看一段简单的代码: 12345678910111213141516171 ...

  5. 201709020工作日记--synchronized、ReentrantLock、读写锁

    1.reentrantLock java.util.concurrent.lock 中的Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现.这就为Lock ...

  6. 多线程面试题系列(14):读者写者问题继 读写锁SRWLock

    在第十一篇文章中我们使用事件和一个记录读者个数的变量来解决读者写者问题.问题虽然得到了解决,但代码有点复杂.本篇将介绍一种新方法--读写锁SRWLock来解决这一问题.读写锁在对资源进行保护的同时,还 ...

  7. 【分布式锁】07-Zookeeper实现分布式锁:Semaphore、读写锁实现原理

    前言 前面已经讲解了Zookeeper可重入锁的实现原理,自己对分布式锁也有了更深的认知. 我在公众号中发了一个疑问,相比于Redis来说,Zookeeper的实现方式要更好一些,即便Redis作者实 ...

  8. java多线程-读写锁

    Java5 在 java.util.concurrent 包中已经包含了读写锁.尽管如此,我们还是应该了解其实现背后的原理. 读/写锁的 Java 实现(Read / Write Lock Java ...

  9. 可重入锁 公平锁 读写锁、CLH队列、CLH队列锁、自旋锁、排队自旋锁、MCS锁、CLH锁

    1.可重入锁 如果锁具备可重入性,则称作为可重入锁. ========================================== (转)可重入和不可重入 2011-10-04 21:38 这 ...

随机推荐

  1. Android基本控件属性设置

    EditText: EditText去边框:<EditText  android:background="@null"  /> EditText的hint的字体大小的设 ...

  2. ajax get和post请求 后端接收并返回数据

    get请求$(function(){ //alert("23"); var x = "#page"; var y = "${ctx!}/static/ ...

  3. Java基础之线程——派生自Thread类的子类(TryThread)

    控制台程序. 程序总是至少有一个线程,程序开始执行时就会创建这个线程.在普通的Java应用程序中,这个线程从mian()方法的开头启动. 要开始执行线程,可以调用Thread对象的start()方法. ...

  4. Caused by: org.springframework.core.NestedIOException: ASM ClassReader failed to parse class file

    springframework.version  3.2.6.RELEASE jdk 1.8

  5. Leetcode: Create Maximum Number

    Given two arrays of length m and n with digits 0-9 representing two numbers. Create the maximum numb ...

  6. Mac下无法安装Dragon Bones的解决方案

    在Mac下安装dragon bones 然后很郁闷的发现 没有 Exchange Manger 然后就去官网下载了一个 不过下载以后发现 怎么都认不出我的Flash CC来 一安装zxp扩展就提示我没 ...

  7. windows 计算机 管理 命令

    compmgmt.msc

  8. WOW: 宏

    1.常用的宏命令 1.1常用的宏命令 1.释放技能命令 /cast 释放一个或多个技能,可以加入一些条件判断,是最常用的命令 /castsequence 依次释放释放数个技能,同样可以加入一些条件判断 ...

  9. ant的那些闹挺事

    今日发现了一个问题,用ant+hudson去运行脚本代码时,突然ant报错: 即使我在ant的build文件中指定了basedir=“C:/Users/145064/.hudson/jobs/haix ...

  10. 为什么接口要规定成员变量必须是public static final的呢?(转)

    在interface里面的变量默认都是public static final 的.所以可以直接省略修饰符: String param="ssm"://变量需要初始化 为什么接口要规 ...