ReentrantLockのロックと他の実装の原則

序文

まず、リエントラントロックは何ですか?

ReentrantLockのがリエントラントロックされ、再入可能ロックは、それは何ですか?リエントラントロックは現在、待たずにロックを複数回取得できるロック・スレッドを保持しています。リエントラントロックはそれを達成する方法ですか?この内部クラス同期親話すからReentrantLockの、同期親はAbstractQueuedSynchronizer(後AQSをいう)です。そして達成リエントラントJVMによって同期。

二、FIFOキューの同期

  • 同期キューは、AQSの非常に重要な部分である、それはFIFO原則に従って、両端キューで、主な役割は、スレッドがロックを取得しようとしたときに既に占有場合、現在のスレッド、スレッドをブロックされたロックさに保持していますノードノードは、同期キューの末尾に追加されるように構築され、キューのヘッドノードが正常にヘッドノードは、ノードが戻って目を覚ますと、現在のヘッドノードへの参照を解放すると、スレッドがロックを解除し、ロックノードを取得し、
    ここに画像を挿入説明
    ここに画像を挿入説明

第三に、AQSは何ですか?

  • AQS JDK1.5が上に設けられているFIFOキューフィルタの実装ベースフレーム同期。
  • AQS主な役割は、例えば、ReentrantLockのは、Javaの並行同期コンポーネントの統一基礎となるサポートを提供することであり、CountdowLatch AQSの認識に基づいている、使用量はそのテンプレート法、内部クラス同期成分として、サブクラスを達成継承AQSを介するものです。
  • AQSの核となるアイデアは、ロックの現在の状態を変更する達成するために、その動作アトミック安全でないツールと組み合わせて、そのような揮発性INT状態の性質に基づいています。状態がゼロである場合には、ロゴの変更ロックは、どのスレッドによって占有されていません。
  1. 競争を判断します。
    ここに画像を挿入説明
  • 3つのスレッドが入って来、彼らが最初にそれはtryAcquireはfalseを返している、CAS成功し、他の二つのスレッドが失敗今回これだけ3つのスレッド、競争の後、成功、変更が成功した場合、CASによって状態の状態を変更するために行くだろう。次に、addWaiter EXCLUSIVEタイプは、キューに現在のノードのスレッドに関連付けられます
  1. キューは
    ここに画像を挿入説明
    テール・ノードがnullでない場合は、キューをまっすぐ尾に、スレッドを待っていました。この例では、ここでのロジックはENQを行くべきである、また、尾の始まりがnullされ、実際には、キュー全体のこの時点ではnullです
  2. キューに頭や尾の後に追加の分析
    ここに画像を挿入説明
  • スレッド2とThread3がENQを入力しながら、場合、T == nullの一方で、CAS操作は、今回唯一のスレッドが成功するために初期化されているキューで実行され、その後、彼らはサイクルに引き続き、二度目は、この時間を他のブロックに入っていますだけでなく、彼の尾部にチームをCAS動作を行うので、この時間は、1つのスレッドしか成功している、我々はスレッド2成功した、ハハ、スレッド2幸せなリターンを想定し、Thread3はキューに最終的には、次のサイクルを失っします自分に戻り、成功、

四、ReentrantLockのロックアーキテクチャ

  • ReentrantLockのアーキテクチャは、主要な内部同期同期抽象クラスを含み、かつ抽象クラスの2つのクラスを実装し、比較的簡単です。これは、すでに次のようにその構造図であり、同期がAQSから継承されたと述べています:
    ここに画像を挿入説明
  • 上图除了 AQS 之外,我把 AQS 的父类 AbstractOwnableSynchronizer(后面简称AOS)也画了进来,可以稍微提一下,AOS 主要提供一个 exclusiveOwnerThread 属性,用于关联当前持有该所的线程。另外、Sync 的两个实现类分别是 NonfairSync 和 FairSync,由名字大概可以猜到,一个是用于实现公平锁、一个是用于实现非公平锁。那么 Sync 为什么要被设计成内部类呢?我们可以看看 AQS 主要提供了哪些 protect 的方法用于修改 state 的状态,我们发现 Sync 被设计成为安全的外部不可访问的内部类。ReentrantLock 中所有涉及对 AQS 的访问都要经过 Sync,其实,Sync 被设计成为内部类主要是为了安全性考虑
  • ReentrantLock还提供公平锁和非公平锁两种模式。

非公平锁

  • 是指当锁状态为可用时,不管在当前锁上是否有其他线程在等待,新近线程都有机会抢占锁。

  • 下述代码即为非公平锁和核心实现,可以看到只要同步状态为0,任何调用lock的线程都有可能获取到锁,而不是按照锁请求的FIFO原则来进行的。ここに画像を挿入説明

公平锁

  • 公平锁是指当多个线程尝试获取锁时,成功获取锁的顺序与请求获取锁的顺序相同,下面看一个ReentrantLock的实现
    ここに画像を挿入説明
  • 从上面的代码中可以看出,公平锁与非公平锁的区别仅在于是否判断当前节点是否存在前驱节点!hasQueuedPredecessors() &&,由AQS可知,如果当前线程获取锁失败就会被加入到AQS同步队列中,那么,如果同步队列中的节点存在前驱节点,也就表明存在线程比当前节点线程更早的获取锁,故只有等待前面的线程释放锁后才能获取锁

与synchronize的区别

  • ReentrantLock是API级别的,synchronized是JVM级别的
  • ReentrantLock可以实现公平锁
  • ReentrantLock通过 Condition可以绑定多个条件
  • synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生; 而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象, 因此使用Lock时需要在finally块中释放锁。
  • Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时, 等待的线程会一直等待下去,不能够响应中断。
  • 通过Lock可以知道有没有成功获取锁,而synchronized却无法办到

五、其他锁实现

乐观锁

  • 乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为 别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数 据,采取在写时先读出当前版本号,进行加锁操作(比较跟上一次的版本号,如果一样则更新), 如果失败则要重复读-比较-写的操作。
  • java 中的乐观锁基本都是通过 CAS 操作实现的,CAS 是一种更新的原子操作,比较当前值跟传入 值是否一样,一样则更新,否则失败。

悲观锁

  • 悲观锁是就是悲观思想,即认为写多,遇到并发写的可能性高,每次去拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上锁,这样别人想读写这个数据就会block直到拿到锁。
  • java中的悲观锁就是Synchronized,AQS框架下的锁则是先尝试cas乐观锁去获取锁,获取不到, 才会转换为悲观锁,如RetreenLock

独占锁的获取和释放流程

  • 获取

调用入口方法acquire(arg)

调用模版方法tryAcquire(arg)尝试获取锁,若成功则返回,若失败则走下一步

将当前线程构造成一个Node节点,并利用CAS将其加入到同步队列到尾部,然后该节点对应到线程进入自旋状态

自旋时,首先判断其前驱节点释放为头节点&是否成功获取同步状态,两个条件都成立,则将当前线程的节点设置为头节点,如果不是,则利用LockSupport.park(this)将当前线程挂起 ,等待被前驱节点唤醒

  • 释放

调用入口方法release(arg)

调用模版方法tryRelease(arg)释放同步状态

获取当前节点的下一个节点

利用LockSupport.unpark(currentNode.next.thread)唤醒后继节点(接获取的第四步)

共享锁的获取和释放流程

  • 获取锁

调用acquireShared(arg)入口方法

进入tryAcquireShared(arg)模版方法获取同步状态,如果返返回值>=0,则说明同步状态(state)有剩余,获取锁成功直接返回

如果tryAcquireShared(arg)返回值<0,说明获取同步状态失败,向队列尾部添加一个共享类型的Node节点,随即该节点进入自旋状态

自旋时,首先检查前驱节点释放为头节点&tryAcquireShared()是否>=0(即成功获取同步状态)

如果是,则说明当前节点可执行,同时把当前节点设置为头节点,并且唤醒所有后继节点

如果否,则利用LockSupport.unpark(this)挂起当前线程,等待被前驱节点唤醒

  • 释放锁

调用releaseShared(arg)模版方法释放同步状态

如果释放成,则遍历整个队列,利用LockSupport.unpark(nextNode.thread)唤醒所有后继节点

独占锁和共享锁在实现上的区别

独占锁的同步状态值为1,即同一时刻只能有一个线程成功获取同步状态

共享锁的同步状态>1,取值由上层同步组件确定

独占锁队列中头节点运行完成后释放它的直接后继节点

共享锁队列中头节点运行完成后释放它后面的所有节点

共享锁中会出现多个线程(即同步队列中的节点)同时成功获取同步状态的情况

读写锁

  • AQSはReentrantReadWriteLockを達成するために、Javaベースの読み書きロックを提供し、原理を達成するために読み書きロックである:高い16とロー16に状態の分割の変数は、16高は、読み取りロックを示し、下位16ビットは、書き込みを表しますロック。

ここに画像を挿入説明

  • 書き込みロックは排他ロックであります
  • 読み取りロックが共有ロックであります

锁粗化

  • 一般的に、複数のスレッド間で効果的な同時性を確保するために、各スレッドは、パブリックリソースの使用が終了した後に、それはすぐにロックを解除する必要があり、あること、できるだけ短く、時間のロックを保持するように求められます。しかし、すべてが、学位を持っているにも最適なパフォーマンスにつながる貴重なシステムリソースを消費ではなく、同じロック要求、同期およびリリースに繰り返し実行した場合

ロックの粒度を小さくします

  • 非常に大きな物体(オブジェクトが多数のスレッドのアクセスであってもよい)、小さなオブジェクトに分割、ロック競合を低減する、並列度を増加させます。競争のロックの削減、ロックする傾向があり、軽量ロックの成功率が向上します。典型的なケースは、ロックの粒度を減少させることにあるのConcurrentHashMap
公開された20元の記事 ウォンの賞賛0 ビュー258

おすすめ

転載: blog.csdn.net/white_zzZ/article/details/103349903