比较synchronized和读写锁

一、科普定义

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

1)synchronized

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

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

2)读写锁

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

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

所以

java5提供了读写锁
这种锁支持多线程读操作不互斥,多线程读写互斥,多线程写写互斥。
读操作不互斥这样有助于性能的提高,这点在java5以前没有
二.用一道面试题来具体比较这两点
题目: “白板编程,实现一个缓存系统”
题目分析:
对这个缓存系统的理解:
间于用户和数据库中间的一个环节,我们知道用户直接访问数据库的时间是远大于直接访问内存,所以有了缓存后用户访问数据时  这样,用户先访问缓存区当缓存区有用户需要的数据时直接拿走,当缓存区没有这样的数据,访问数据库并把访问所得的数据放在 缓存区,这样当下一个需要这个数据的用户就直接访问内存即可得到。
核心代码实现:
首先用synchronized实现
Java代码   收藏代码
  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.           
  8. }  
 用读写锁实现
Java代码   收藏代码
  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.           
  26.     }  
 代码分析
  1. 用第一种方法处理,整个过程比较粗线条,代码比较简单单执行效率很低。这种方法的中心思想是不管你是什么操作,但凡涉及到公共资源就都给你同步。这么做可以是可以但是并不好。
  2. 第二种用读写锁处理显然是对前者的一个优化,对第二种方法做如下几点说明:
  • 关于unlock操作,我们知道只要是上了锁就必须要解锁,但是有这么一种情况就是当你上完锁后在执行解锁操作前程序出现异常,那这个所可能就一直存在。所以针对这个问题我们一般将unlock操作放在finally代码块中,就可以保证上了的锁一定会被解。
  • 上面的两次if判断,第一个if相信大家很好理解。但为什么要用第二个if呢?再假设一个场景,现在有十个线程来读这个数据,而这个数据又不存在与缓存区,那么这十个线程中最先到的线程将执行“rw.writeLock().lock();”而另外九个线程将被阻塞,当第一个线程读完以后缓存区实际上已经就有了这个数据,但另外九个阻塞在“rw.writeLock().lock();”如果不加这层if他们会继续访问数据库,由此可见加了这层if对整个过程影响很大。这是比较细节的一点,就这一点Java的API文档也考虑到了,它的样例代码如下:

Java代码   收藏代码
  1. class CachedData {  
  2.   Object data;  
  3.   volatile boolean cacheValid;  
  4.   ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();  
  5.   
  6.   void processCachedData() {  
  7.     rwl.readLock().lock();  
  8.     <span style="color: #ff0000;">if (!cacheValid)</span> {  
  9.        // Must release read lock before acquiring write lock  
  10.        rwl.readLock().unlock();  
  11.        rwl.writeLock().lock();  
  12.        // Recheck state because another thread might have acquired  
  13.        //   write lock and changed state before we did.  
  14.       <span style="color: #ff0000;"if (!cacheValid)</span> {  
  15.          data = ...  
  16.          cacheValid = true;  
  17.        }  
  18.        // Downgrade by acquiring read lock before releasing write lock  
  19.        rwl.readLock().lock();  
  20.        rwl.writeLock().unlock(); // Unlock write, still hold read  
  21.     }  
  22.   
  23.     use(data);  
  24.     rwl.readLock().unlock();  
  25.   }  
  26. }  

来自:http://903497571.iteye.com/blog/1874752
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
synchronized和lock锁是Java中用于实现线程同步的两种不同机制。 1. synchronized关键字是Java语言提供的内置机制,用于保证代码块或方法在同一时刻只能由一个线程执行。synchronized关键字可以用于修饰代码块或方法,当一个线程进入synchronized代码块或方法时,会自动获取锁,并在执行完毕后释放锁。synchronized关键字的锁是隐式的,由Java虚拟机自动管理。 2. Lock接口是Java.util.concurrent包提供的显式锁机制,也是一种更灵活、可控制性更强的锁机制。Lock接口的实现类可以通过调用lock()方法获取锁,并通过调用unlock()方法释放锁。与synchronized不同,Lock接口可以实现更细粒度的锁定,并提供了更多高级功能,如可重入锁、读写锁等。 下面是synchronized和Lock锁之间的一些区别: - 可重入性:synchronized是可重入锁,即一个线程可以多次获取同一个锁;而Lock接口可以通过实现ReentrantLock类来实现可重入锁。 - 锁的获取方式:synchronized关键字是隐式的,在进入synchronized代码块或方法时自动获取锁,并在退出时释放锁;而Lock接口需要显式地调用lock()方法获取锁,并在finally块中调用unlock()方法释放锁。 - 等待可中断:通过Lock接口提供的lockInterruptibly()方法,可以在等待获取锁的过程中响应中断请求,而synchronized关键字在等待获取锁时无法响应中断。 - 公平性:Lock接口可以实现公平锁,即按照线程请求锁的顺序来获取锁,而synchronized关键字是非公平锁。 - 性能:在低竞争情况下,synchronized关键字的性能表现更好;而在高竞争情况下,Lock接口的性能更好。 总的来说,synchronized是一种简单易用的线程同步机制,适用于大部分场景;而Lock接口提供了更多灵活、可控制的锁定方式,适用于一些特殊需求的场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值