zoukankan      html  css  js  c++  java
  • ReentrantLock和synchronized的区别

    Java 5为了加强内置锁的功能,引入了可重入锁(ReentrantLock)。在此之前“synchronized”和“volatile”是实现并发的方式。

    Synchronized关键字使用内置锁(intrinsic lock)或者称作监视锁(monitor lock)。每一个Java对象都有一个内置锁与之相关联。无论什么时候,当一个线程尝试去访问一个synchronized代码块或者synchronized方法的时候,线程都需要首先获取到对象关联的内置锁。对于static方法,线程获取的是类对象的内置锁。

    内置锁机制使得代码的书写非常整洁,并且大部分场合下功能也够用。所以为什么我们需要额外的去显式的创建锁?让我们讨论一下。

    内置锁机制在功能上有一些限制:

    1. 不能中断(interrupt)一个正在等待获取锁的线程
    2. 不能测试锁是否空闲从而不用一直等待锁
    3. 不能实现非代码块结构的加锁与释放锁。因为内置锁必须在和获取锁相同的代码块释放锁。

    除此之外,ReentrantLock还支持对锁的测试,支持既可以被中断又可以设置超时。ReentrantLock还可以设置公平原则,允许更灵活的线程调度。

    让我们看一下实现了Lock类的ReentrantLock类中的部分方法:

     

    让我们理解一下如何使用上面的分析结果,看看我们会得到什么好处。

    轮询和设置了超时的对锁的获取

    让我们看一段代码的例子:

     

    在上面的方法中,当两个线程A和B几乎在同时转账(transfer money)的时候有可能会发生死锁。

     

    有可能线程A已经获取了acc1对象的锁并且正在等待获取acc2对象的锁,与此同时,线程B已经获取了acc2对象的锁并且正在等待获取acc1的锁。这将会导致死锁,程序不得不需要重启!!

    然而有一个方法可以避免这种情况:就是所谓的按相同的顺序获取锁。我个人觉得这个实现起来比较困难。

    一个更简便的实现方式是用ReentrantLock的tryLock()方法。这种方法称为“轮询式可超时的锁获取”。即便你不能够获取所有必须的锁,它也可以使你重新获得对程序的控制,释放部分已经获取的锁,然后重新尝试。

    因此,我们将会使用trylock()来获取两个锁,如果我们不能获取到两个锁,可以释放已经获取到的一个,然后重试。

     

    这里我们实现了一个支持超时的加锁机制,所以,如果锁在指定的时间段内不能被获取到,方法将会返回失败(false),优雅的退出。

    获取锁可以被中断

    获取锁可以被中断使得锁可以使用在可以取消的操作上。

    lockInterruptibly()方法使得我们可以尝试去获取锁但是保留线程可以被中断的能力。基本的意思是这个方法使得线程可以立即响应从其它线程发过来的中断信号。这在当我们想要发送中断信号到所有等待锁的线程时会很有用。

    让我们看一个例子,假设我们有一个共享的方法来发送消息,希望如果有其他的线程请求中断,那么正在发送的线程应当释放锁并且退出或者停止正在进行的操作以取消当前的任务。

     

    带超时的tryLock(long time, TimeUnit unit)方法也是可以响应中断的。

    非代码块加锁

    内置锁的获取和释放是以代码块的结构出现的,即锁总是在被获取的同一个代码块被释放,不管程序逻辑如何。

    外置锁提供了更加显式的控制。在一些哈希容器和链表中使用到了外置锁。

    公平性

    ReentrantLock的构造方法可以设置是否使用公平原则:创建一个公平锁或者非公平锁。使用公平锁的线程们将会以他们请求获取锁的顺序得到锁,而非公平锁允许线程不按请求顺序获取锁,这称作“闯入”(当锁空闲时,打破队列顺序去获取锁)。

    公平锁因为会涉及到线程的挂起和恢复所以有很大的性能方面的代价。可能会有以下的情况:从一个挂起的线程被恢复到它开始实际执行会有严重的延时。让我们看一个情形:

    A -> 持有锁

    B -> 请求锁,等待A释放锁,然后进入了挂起状态

    C -> 请求锁,同时A释放了锁,此时C并没有进入挂起状态

    由于C并没有处于挂起状态,所以它是有机会先去获取A释放的锁,完成工作,然后甚至在线程B被唤醒之前就释放锁的。因此,这种情形下,非公平锁具有非常大的性能上的优势。

    结篇

    内置锁和外置锁的内部实现机制是一样的,所以性能的提升是主观上的。它依赖于我们上面讨论的具体情况。外置锁提供了对死锁,线程饥饿等问题的显示的处理方式。

    作者公众号(码年)扫码关注:

    英文原文:

    https://www.javacodegeeks.com/2013/11/what-are-reentrant-locks.html

    转载于:https://www.cnblogs.com/yufengzhang/p/9443492.html

  • 相关阅读:
    MySQL根据逗号将一行数据拆成多行数据
    pyhon实现excel的转置
    python批量加入数据库
    jieba词频统计
    python 求顾客相邻两次购买周期
    mysql求取最后两次价格上涨趋势
    mysql求每一个顾客购买商品的相邻时间间隔
    利用Python从一个数据库中查询结果插入到另一个数据库中
    电商项目数据分析
    线程的两种调用方式
  • 原文地址:https://www.cnblogs.com/twodog/p/12136259.html
Copyright © 2011-2022 走看看