博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java 读写锁的实现
阅读量:5823 次
发布时间:2019-06-18

本文共 5205 字,大约阅读时间需要 17 分钟。

一、

   synchronized和ReentrantLock的对比

到现在,看到多线程中,锁定的方式有2种:synchronized和ReentrantLock。两种锁定方式各有优劣,下面简单对比一下:

1、synchronized是关键字,就和if...else...一样,是语法层面的实现,因此synchronized获取锁以及释放锁都是Java虚拟机帮助用户完成的;ReentrantLock是类层面的实现,因此锁的获取以及锁的释放都需要用户自己去操作。特别再次提醒,ReentrantLock在lock()完了,一定要手动unlock()

2、synchronized简单,简单意味着不灵活,而ReentrantLock的锁机制给用户的使用提供了极大的灵活性。这点在Hashtable和ConcurrentHashMap中体现得淋漓尽致。synchronized一锁就锁整个Hash表,而ConcurrentHashMap则利用ReentrantLock实现了锁分离,锁的知识segment而不是整个Hash表

3、synchronized是不公平锁,而ReentrantLock可以指定锁是公平的还是非公平的

4、synchronized实现等待/通知机制通知的线程是随机的,ReentrantLock实现等待/通知机制可以有选择性地通知

5、和synchronized相比,ReentrantLock提供给用户多种方法用于锁信息的获取,比如可以知道lock是否被当前线程获取、lock被同一个线程调用了几次、lock是否被任意线程获取等等

总结起来,我认为如果只需要锁定简单的方法、简单的代码块,那么考虑使用synchronized,复杂的多线程处理场景下可以考虑使用ReentrantLock。当然这只是建议性地,还是要具体场景具体分析的。

最后,查看了很多资料,JDK1.5版本只有由于对synchronized做了诸多优化,效率上synchronized和ReentrantLock应该是差不多。

  二、读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可。如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上读锁,写的时候上写锁!

  ReentrantReadWriteLock会使用两把锁来解决问题,一个读锁,一个写锁

线程进入读锁的前提条件:
    没有其他线程的写锁,
    没有写请求或者有写请求,但调用线程和持有锁的线程是同一个
线程进入写锁的前提条件:
    没有其他线程的读锁
    没有其他线程的写锁

 

到ReentrantReadWriteLock,首先要做的是与ReentrantLock划清界限。它和后者都是单独的实现,彼此之间没有继承或实现的关系。然后就是总结这个锁机制的特性了: 

     (a).重入方面其内部的WriteLock可以获取ReadLock,但是反过来ReadLock想要获得WriteLock则永远都不要想。 
     (b).WriteLock可以降级为ReadLock,顺序是:先获得WriteLock再获得ReadLock,然后释放WriteLock,这时候线程将保持Readlock的持有。反过来ReadLock想要升级为WriteLock则不可能,为什么?参看(a),呵呵. 
     (c).ReadLock可以被多个线程持有并且在作用时排斥任何的WriteLock,而WriteLock则是完全的互斥。这一特性最为重要,因为对于高读取频率而相对较低写入的数据结构,使用此类锁同步机制则可以提高并发量。 
     (d).不管是ReadLock还是WriteLock都支持Interrupt,语义与ReentrantLock一致。 
     (e).WriteLock支持Condition并且与ReentrantLock语义一致,而ReadLock则不能使用Condition,否则抛出UnsupportedOperationException异常。

 

     示例:读锁,写锁及读写锁的缓存机制:

       

/**     *    读写锁实现     *        读写锁的缓存机制     */    // 缓存的map    private Map
map = new HashMap
(); // 读写锁对象 private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private String data="1"; /** * 进行读操作 * 可以多个读线程同时进入,写线程不能执行 */ public String getReadWriteLock(String tt) { //获取读锁,并加锁 Lock readLock = readWriteLock.readLock(); readLock.lock(); try { System.out.println("线程名称:"+Thread.currentThread().getName() + " be ready to read data!");// Thread.sleep((long) (Math.random() * 3000)); Thread.sleep(3000);// this.data =tt; this.data = mainDao.getData(tt); System.out.println(Thread.currentThread().getName() + "------->>>>have read data :"+data ); return this.data; } catch (InterruptedException e) { e.printStackTrace(); } finally { //!!!!!!注意:锁的释放一定要在trycatch的finally中,因为如果前面程序出现异常,锁就不能释放了 //释放读锁 readLock.unlock(); } return null; } /** * 进行写操作 * 只能一个写线程进入,读线程不能执行 */ public void putReadWriteLock(String data){ //获取写锁,并加锁 Lock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { System.out.println("线程名称:"+Thread.currentThread().getName() + " be ready to write data!"); Thread.sleep(3000); this.data = data; System.out.println(Thread.currentThread().getName() + " have write data: " + data); } catch (InterruptedException e) {// e.printStackTrace(); System.out.println("error!!!"); }finally { //释放写锁 writeLock.unlock(); } } /** * 设计一个缓存系统 * 读写锁的应用。 * JDK1.5自带的读写锁特性,读与读不互斥,读与写互斥,写与写互斥。 * 为什么要使用读写锁?一句话概括那就是提高系统性能,如何提高呢? * 试想,对于所有对读的操作是不需要线程互斥的,而如果方法内 * 使用了synchronized关键字同步以达到线程安全,对于所有的线程不管是读还是写的操作都要同步。 * 这时如果有大量的读操作时就会又性能瓶颈。 * * 所以,当一个方法内有多个线程访问,并且方法内有读和写读操作时, * 提升性能最好的线程安全办法时采用读写锁的机制对读写互斥、写写互斥。这样对于读读就没有性能问题了 * @author zhurudong * */ public void readWriteMathod(String key){ readWriteLock.readLock().lock();//读锁,只对写的线程互斥// String key = "tt"; Object value = null; try { // 尝试从缓存中获取数据 value = map.get(key); if (value == null) { readWriteLock.readLock().unlock();//发现目标值为null,释放掉读锁 readWriteLock.writeLock().lock();//发现目标值为null,需要取值操作,上写锁 try { value = map.get(key);// 很严谨这一步。再次取目标值 if (value == null) {
//很严谨这一步。再次判断目标值,防止写锁释放后,后面获得写锁的线程再次进行取值操作 // 模拟DB操作 value = new Random().nextInt(10000) + "test"; map.put(key, value); System.out.println("db completed!"); } readWriteLock.readLock().lock();//再次对读进行锁住,以防止写的操作,造成数据错乱 } finally { /* * 先加读锁再释放写锁读作用: * 防止在100行出多个线程获得写锁进行写的操作,所以在写锁还没有释放前要上读锁 */ readWriteLock.writeLock().unlock(); } } } finally { readWriteLock.readLock().unlock(); } }

 

 

   读写锁与多线程验证代码: https://gitee.com/xdymemory00/AsyncWithLock.git

 

         

 

转载于:https://www.cnblogs.com/memoryXudy/p/readWriteMathod.html

你可能感兴趣的文章
关于一个app中数据库的问题
查看>>
CSS浮动讲解好文章推荐
查看>>
宜信开源微服务任务调度平台(SIA-TASK)
查看>>
第6章核心代码《跟老男孩学习Linux运维:Shell编程实战》
查看>>
常用软件(长期更新中)。。。
查看>>
如何将别人的知识转化成自己的知识?
查看>>
Heartbeat3.x应用全攻略之:概念组成及工作原理
查看>>
windows server 2008 AD站点建立与子网的管理
查看>>
asp.net core策略授权
查看>>
程序与生活:浮躁的日子
查看>>
详解Linux运维工程师高级篇(大数据安全方向)
查看>>
自动化测试更适合缺陷预防,而不是提高测试效率
查看>>
数据库操作是磁盘IO密集型的论证
查看>>
pycharm初体验
查看>>
MongoDB误操作后的point in time recovery
查看>>
关于性能测试中“并发”的解释
查看>>
在BT5里Metasploit内网***(exploit/windows/smb/psexec)
查看>>
构建Windows 2012可扩展文件服务器
查看>>
Provisioning Services 7.8 入门系列教程之十一 通过版本控制自动更新虚拟磁盘
查看>>
AgileEAS.NET平台开发实例-药店系统-数据库设计
查看>>