一、科普定义

这篇博文的两个主角“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. 11月16日《奥威Power-BI基于SQL的存储过程及自定义SQL脚本制作报表》腾讯课堂开课啦

           上周的课程<奥威Power-BI vs微软Power BI>带同学们全面认识了两个Power-BI的使用情况,同学们已经迫不及待想知道这周的学习内容了吧!这周的课程关键词—— ...

  2. 改变对update的做法

    以前都是 先根据id或者其他条件查出来  再根据查出来的结果 进行修改  再update提交 这里可以改所有的字段 现在是做法 是直接new 一个 Do或者Vo  把要改变的值 先填充进去  然后再去 ...

  3. Java基础之一组有用的类——使用Scanner对象(TryScanner)

    控制台程序. java.util.Scanner类定义的对象使用正则表达式来扫描来自各种源的字符输入,并把输入显示为各种基本类型的一系列标记或者显示为字符串. 默认情况下,Scanner对象读取标记时 ...

  4. HTML调用servlet(一)

    1.页面的数据表单 在使用Servlet处理用户请求之前,先准备一个页面,该页面用来提供数据表单.数据表单就是HTML中的<form>...</form>部分,当用户单击Sub ...

  5. 转:python webdriver API 之下拉框处理

    下拉框也是 web 页面上非常常见的功能,webdriver 对于一般的下拉框处理起来也相当简单,要想定位下拉框中的内容,首先需要定位到下拉框:这样的二次定位,我们在前面的例子中已经有过使用,下面通过 ...

  6. codeforces 520 Two Buttons

    http://codeforces.com/problemset/problem/520/B B. Two Buttons time limit per test 2 seconds memory l ...

  7. C#: 获取当前应用程序所在路径

    ref: http://www.cnblogs.com/netlyf/archive/2011/06/22/2086718.html 一.获取当前文件的路径 string str1=Process.G ...

  8. kafka0.8.2以下版本删除topic

    一些说明 kafka0.8.2及以上版本已经支持delete命令删除topic,可是之前的版本要是想删除topic还是要费一番手脚,绝对是个体力活... 该方法最好仅在线下开发环境中使用,毕竟要重启z ...

  9. Android DatePickerDialog TimepickerDialog

    package com.example.myact5; import java.util.Calendar; import android.app.DatePickerDialog; import a ...

  10. Tostring(); 括号中的参数 格式化字符串

    最近在逛 msdn 发现    查到  getTypeCode 方法  看到里边居然有 tostring("D")这样的写法      运行了一遍 感觉可以显示值      然后就 ...