Lock的实现之ReentrantLock详解

Lock在硬件层面依赖CPU指令,完全由Java代码完成,底层利用LockSupport类和Unsafe类进行操作;

虽然锁有很多实现,但是都依赖AbstractQueuedSynchronizer类,我们用ReentrantLock进行讲解;

ReentrantLock调用过程

ReentrantLock类的API调用都委托给一个内部类 Sync ,而该类继承了 AbstractQueuedSynchronizer类;

public class ReentrantLock implements Lock, java.io.Serializable { ...... abstract static class Sync extends AbstractQueuedSynchronizer { ......

而Sync又分为两个子类:公平锁和非公平锁,默认为非公平锁

/** * Sync object for non-fair locks */ static final class NonfairSync extends Sync { 

/** * Sync object for fair locks */ static final class FairSync extends Sync { 

Lock的调用过程如下图(其中涉及到 ReentrantLock类、Sync(抽象类)、AbstractQueuedSynchronizer类,NofairSync类,这些类将 Template方法用的淋漓尽致,相当赞):

先来一张类依赖图:

Lock的实现之ReentrantLock详解

再来一张lock调用图:

Lock的实现之ReentrantLock详解

Lock API详解

自底而上来看,由被调用一步步向上分析

nofairTryAcquire

/** * Performs non-fair tryLock. tryAcquire is implemented in * subclasses, but both need nonfair try for trylock method. */ final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }

来看这段代码,首先获取当前状态(初始化为0),当它等于0的时候,代表还没有任何线程获得该锁,然后通过CAS(底层是通过CompareAndSwapInt实现)改变state,并且设置当前线程为持有锁的线程;其他线程会直接返回false;当该线程重入的时候,state已经不等于0,这个时候并不需要CAS,因为该线程已经持有锁,然后会重新通过setState设置state的值,这里就实现了一个偏向锁的功能,即锁偏向该线程;

addWaiter

只有当锁被一个线程持有,另外一个线程请求获得该锁的时候才会进入这个方法

/** * Creates and enqueues node for current thread and given mode. * * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared * @return the new node */ private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); // Try the fast path of enq; backup to full enq on failure Node pred = tail; if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } enq(node); return node; }

首先持有该锁之外的线程进入到该方法,这里涉及到一个CLH(三个人的名字首字母:Craig, Landin, and Hagersten)队列,其实就是一个链表,

简单说下CLH队列:

CLH队列由node节点组成,mode代表每个Node有两种模式:共享模式和排他模式,并且维护了一个状态:waitStatus,可取值如下:

CANCELLED = 1   由于超时或者被打断,该线程被取消,将不会被block;

SIGNAL = -1    当前线程的后继节点线程通过park正处于或即将处于block状态;

CONDITION = -2    当前线程正处于条件队列,正式因为调用了condition.await造成阻塞;

PROPAGATE = -3    共享锁应该被传播出去

首先,new一个节点,这个时候模式为:mode为 Node.EXCLUSIVE,默认为null即排它锁;

然后:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/d43128d59cd0ab46213f0d10b1c35f86.html