Java的ReentrantLock数据结构图解

 ReentrantLock数据结构图解

ReentrantLock总的来说是维护了一个等待队列(或者叫做同步队列)和若干个条件队列(取决于程序中创建了几个条件对象),队列里放的是一个个Node。下面通过ReentrantLock的类图来详细说明一下:

Java的ReentrantLock数据结构图解_第1张图片

从类图中可以看到ReentrantLock里面有一个Sync类型的sync对象,Sync是ReentrantLock的一个内部类,该内部类继承了AbstractQueuedSynchronizer(简称AQS)。AQS就是Lock维护的那个等待队列。

  • AQS的headtail属性分别指向等待队列的头结点和尾结点。这两个属性都是Node类型。

  • AQS的state属性是各个线程竞争的对象,线程想要获取锁的执行权力必须通过cas操作从state的旧值更新为新的值。若修改成功则获取到锁的执行权力,否则该线程被包装成一个Node节点进入到等待队列。

  • stateOffset,headOffset,tailOffset分别是上面几个属性在内存中相对锁的偏移量。当对其属性进行cas操作时会用到。

Node是AQS的一个内部类,等待队列的每个元素都是一个Node类型的对象。

  • prev指向的是等待队列的Node的前置节点,当自己入队时,该属性会指向tail指向的节点p,然后tail会指向自己,p.next也会指向自己,来完成瑞对操作

  • next指向的是等待队列Node的后继节点。

  • nextWaiter指向的条件队列的后继节点。注意Node里只有nextWaiter,没有所谓的prevWaiter说明条件队列是一个单向链表。同时条件队列与等待队列是两个不同的队列。当一个线程在调用condition.await()的时候,会将线程包装成一个node,放入到条件队列。此时条件队列里的队尾node的nextWaiter指向这个node,node的prev和next都是空的(因为这两个属性只有进入等待队列才有可能被赋值)

  • thread指向node包装的线程。

  • waitStatus代表当前的node里的线程是在什么状态,其有五个值:

  1. CANCELLED(1) : 代表该线程已经被取消,多数情况下是由于被其他线程中断导致。
  2. SIGNAL(-1) : 该值只会出现在等待队列里的节点,代表该节点的后继节点已经或者将要park。
  3. CONDITION(-2) : 代表该节点是一个条件队列的节点,当节点被移动到等待队列时,waitStatus置为0
  4. 0 : 没有具体的代表含义,新创建的等待队列的节点和从条件队列移动到等待队列的节点的waitStatus值为0
  5. PROPAGATE(-3) : 待完善

ConditonObject也是AQS维护的一个内部类,该类维护了条件队列。

  • firstWaiter和lastWaiter分别指向条件队列的头节点和尾结点
  • REINTERRUPT 记录线程需要重新自中断
  • THROW_IE 线程需要直接抛出异常

从上面可以总结出等待队列是AQS通过head和tail属性维护的一个双向链表,这个双向链表是ReentrantLock的一个内部属性sync。所以ReentrantLock里面有一个双向链表作为等待队列。ReentrantLock实现了Lock接口,该接口有一个newConditon()的方法,每次调用该方法都会创建一个ConditonObject,而一个ConditonObject会维护一个单向链表所以会得出:ReentrantLock维护了一个等待队列(或者叫做同步队列)和若干个条件队列(取决于程序中创建了几个条件对象)。如图所示:

Java的ReentrantLock数据结构图解_第2张图片

条件队列里的node是不能竞争锁资源的,只有被移动到等待队列里面才能竞争锁资源,这点在condition.await()方法中可以体现出来。           

你可能感兴趣的:(ReentrantLock,java)