ReentranLock锁解析

Posted JF Coder

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ReentranLock锁解析相关的知识,希望对你有一定的参考价值。

ReentranLock锁解析

悲观锁

总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java 中synchronized 和
ReentrantLock 等独占锁就是悲观锁思想的实现

乐观锁

总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS
算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition
机制,其实都是提供的乐观锁。在Java 中java.util.concurrent.atomic
包下面的原子变量类就是使用了乐观锁的一种实现方式CAS 实现的。

基本使用-可重入锁

class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...建议的做法是始终在调用lock立即使用try块,最常见的是在 before/after 构造中
   public void m() {
     lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
 }

AbstractQueuedSynchronizer类中AQS算法的体现

AQS = AbstractQueuedSynchronizer

用于实现依赖先进先出 (FIFO) 等待队列的阻塞锁和相关同步器(信号量、事件等)

AQS维护了一个volatile语义(支持多线程下的可见性)的共享资源变量state和一个FIFO线程等待队列(多线程竞争state被阻塞时会进入此队列)

走进AbstractQueuedSynchronizer类

此类支持默认独占模式和共享模式中的一种或两种。 当以独占模式获取时,其他线程尝试获取不会成功。 多个线程获取的共享模式可能(但不一定)成功。 这个类不“理解”这些差异,除了机械意义上的区别,当共享模式获取成功时,下一个等待线程(如果存在)也必须确定它是否也可以获取。 在不同模式下等待的线程共享同一个 FIFO 队列。 通常,实现子类仅支持这些模式中的一种,但两种模式都可以发挥作用,例如在ReadWriteLock 。 仅支持独占或仅共享模式的子类不需要定义支持未使用模式的方法。

CAS内部实现

等待队列是“CLH”实现

等待队列是“CLH”(Craig、Landin 和 Hagersten)锁定队列的变体。 CLH 锁通常用于自旋锁。 我们改为将它们用于阻塞同步器,但使用相同的基本策略,即在其节点的前驱中保存有关线程的一些控制信息。 每个节点中的“状态”字段跟踪线程是否应该阻塞。 节点在其前任发布时收到信号。 队列的每个节点都充当一个特定的通知式监视器,持有一个等待线程。 尽管状态字段不控制线程是否被授予锁定等。 一个线程可能会尝试获取它是否在队列中的第一个。 但成为第一并不能保证成功; 它只给予抗争的权利。 所以当前发布的竞争者线程可能需要重新等待。
要加入 CLH 锁,您可以原子地将其拼接为新的尾部。 要出列,您只需设置 head 字段。
±-----+ prev ±----+ ±----+
head | | <---- | | <---- | | tail
±-----+ ±----+ ±----+

公平锁和不公平锁

独占模式,实现公平锁

ReentrantLock继承AQS类实现

共享模式,不公平锁(可能会造成线程排队,速度慢,所以ReentrantLock默认是不公平锁)

注意公平锁使用Semaphore类的实现

注意Semaphore类

此类的构造函数可以选择接受公平参数。 当设置为 false 时,此类不保证线程获取许可的顺序。 特别是, barging是允许的,也就是说,可以在一个一直在等待的线程之前为调用acquire的线程分配一个 permit——逻辑上,新线程将自己置于等待线程队列的头部。 当公平性设置为真时,信号量保证调用任何acquire方法的线程被选择以按照它们对这些方法的调用的处理顺序(先进先出;FIFO)获得许可。 请注意,FIFO 排序必然适用于这些方法中的特定内部执行点。 因此,一个线程可能在另一个线程之前调用acquire ,但在另一个线程之后到达排序点,从方法返回时也是如此。 另请注意,未计时的tryAcquire方法不遵守公平设置,但会采用任何可用的许可。

ReentrantLock锁常用方法

tryLock和unLock方法


以上是关于ReentranLock锁解析的主要内容,如果未能解决你的问题,请参考以下文章

ReentranLock锁解析

ReentranLock锁解析

Java多线程学习之Lock与ReentranLock详解

java ReentranLock锁

显示锁(ReentranLock)

AbstractQueuedSynchronizer和ReentranLock基本原理