ReentrantLock源码解析

ReentrantLock的继承与组合

公平锁-加锁成功

公平锁-加锁失败

公平锁-释放锁 

公平锁小结

非公平锁-加锁成功

非公平锁-加锁两次插队

 

ReentrantLock的继承与组合

 ReentrantLock继承,组合。ReentrantLock实现了Lock接口,持有Sync实例,Sync的抽象父类是AbstractQueuedSynchronizer(以下称为AQS)AQS继承自AbstractOwnableSynchronizer(以下称之为AOS)AOS中只有一个成员变量Thread,当前持有锁的线程。

Sync有两个实现分别为NonfairSync非公平锁,FairSync公平锁。无参构造器默认为非公平锁,可以通过有参构造使用公平锁。

class ReentrantLock implements Lock, java.io.Serializable{
    private final Sync sync;
    abstract static class Sync extends AbstractQueuedSynchronizer{}
    static final class NonfairSync extends Sync{}
    static final class FairSync extends Sync{}      
}

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable{}

public abstract class AbstractOwnableSynchronizer implements java.io.Serializable{
    private transient Thread exclusiveOwnerThread;
}
  public ReentrantLock() {
        sync = new NonfairSync();
    }
 public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

公平锁-加锁成功

加锁代码,先看公平锁,假设现在只有一条线程获取锁,还原获取锁成功的步骤。

 public void lock() {
        sync.lock();
    }
final void lock() {
    acquire(1);//AQS中
}
//AQS中
public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
        protected final boolean tryAcquire(int acquires) {//入参1,尝试获取锁操作。
            final Thread current = Thread.currentThread();//当前线程。
            int c = getState();//AQS中的变量,默认为0,表示锁未被占用。
            if (c == 0) {//能进来说明当前锁未被占用。
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);//把当前线程设置到AOS中,占有锁成功。
                    return true;//返回成功。
                }
            }
            else if (current == getExclusiveOwnerThread()) {//getExclusiveOwnerThread从AOS中获取当前持有锁的线程是不是当前线程。
                int nextc = c + acquires;//重入锁+1。
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);//设置锁占用标记。
                return true;//返回成功。
            }
            return false;//返回失败。
        }
//AQS
public final boolean hasQueuedPredecessors() {
        Node t = tail; //将获取锁的线程封装到Node中,在AQS中有尾部,头部两个链表指针。
          Node h = head;
        Node s;
     //h!=t表示链表有元素,h.next==null表示链表中只有一个元素,链表中唯一的元素不是当前线程。
return h != t &&((s = h.next) == null || s.thread != Thread.currentThread()); } //AQS protected final boolean compareAndSetState(int expect, int update) { //CAS操作。成员变量state为1,不了解Unsafe的可以看上一篇博客。 return unsafe.compareAndSwapInt(this, stateOffset, expect, update); } //AOS protected final void setExclusiveOwnerThread(Thread thread) { exclusiveOwnerThread = thread;//设置当前线程为独占线程
}

公平锁-加锁失败

还原锁获取失败的步骤。

final void lock() {
     acquire(1);
}
public final void acquire(int arg) {
      //tryAcquire返回false取反为true。执行&&后操作,Node EXCLUSIVE = null。这是Node内部类中的成员变量。
      //addWaiter将当前阻塞线程设置到Node链表中
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
 protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();//有线程持有锁当前state!=0
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {//持有锁的线程不是当前线程
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
}
    private Node addWaiter(Node mode) {
//Node(Thread thread, Node mode) {this.nextWaiter = mode;this.thread = thread;}
     //构造一个Node对象,入参为当前线程和一个空的Node,空的Node为下一个阻塞线程。
Node node = new Node(Thread.currentThread(), mode); Node pred = tail;//获取阻塞链表中最后一个节点。 if (pred != null) {//如果尾部节点存在 node.prev = pred;//将尾部节点设置为新节点的prev。
   //private final boolean compareAndSetTail(Node expect, Node update) {return unsafe.compareAndSwapObject(this, tailOffset, expect, update);}
       // CAS操作,将新节点设置为tail.这个操作是可能失败的。例如有其他线程创建了newNode替换了tail。 if (compareAndSetTail(pred, node)) { pred.next = node;//如果CAS操作成功,将新节点设置为上一个尾部节点的next return node;//返回新的tail } } enq(node);//添加失败或尾部节点为null后会走这里。 return node; }
    private Node enq(final Node node) {
        for (;;) {//自旋操作,两个操作 1 如果阻塞链表中没有线程阻塞,则创建一个空的Node当作head和tail,此时阻塞队列中已经有了head不过是哑节点。再次自旋将node设置为tail。或者直接走else
            Node t = tail;//获取尾部节点
            if (t == null) { //如果尾部节点==null表示当前阻塞链表中已经没有阻塞的线程了。
         //private final boolean compareAndSetHead(Node update) {return unsafe.compareAndSwapObject(this, headOffset, null, update);}
         //CAS操作,new Node()然后设置该Node为head if (compareAndSetHead(new Node())) tail = head;//head赋值到tail } else {//如果阻塞链表中有线程。 node.prev = t;将当前tail赋值给Node的prev if (compareAndSetTail(t, node)) {//CAS设置当前线程为tail t.next = node;//设置node为原tail的next return t;//返回当前tail。 } } } }
    final boolean acquireQueued(final Node node, int arg) {//传入当前线程的阻塞节点和1
        boolean failed = true;//失败标记
        try {
            boolean interrupted = false;//中断信号
            for (;;) {//自旋操作
         //final Node predecessor() throws NullPointerException {Node p = prev;if (p == null){throw new NullPointerException();}else {return p;}}
         //获取当前阻塞节点的上一个Node。无论走不走enq方法当前阻塞节点的prev都不可能为null。
          final Node p = node.predecessor();
          if (p == head && tryAcquire(arg)) {//prev是head表示当前Node的上一个就是哑节点,则可以再次执行tryAcquire(1)尝试获取锁。
                    //private void setHead(Node node) {head = node;node.thread = null;node.prev = null;}
          setHead(node);//如果获取锁成功则将当前节点设置为head,这个操作其实也是把当前已经获取到锁的节点设置为哑节点。 p.next = null; //设置原来的哑节点的next为null此操作为了GC释放内存。 failed = false; return interrupted;//返回false }
          //如果当前节点的上一个不是哑节点或者当前节点抢占锁失败。shouldParkAfterFailedAcquire(p,n)起码要走两次。
          //parkAndCheckInterrupt()将线程阻塞。
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()){ interrupted = true;
          } } }
finally { if (failed)//如果失败则执行这里,目前还看不出什么情况下会走这里。 cancelAcquire(node); } }
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
     //获取上一个Node的状态。此处的状态有
     //CANCELLED= 1 表示线程已经取消
     //SIGNAL= -1 线程的后续线程需要阻塞
     //CONDITION= -2 表示线程正在处于等待
     //PROPAGATE= -3 表示该线程以及后续线程进行无条件传播
    
int ws = pred.waitStatus;//这里是判断上一个节点的status.默认为0 if (ws == Node.SIGNAL)//一个节点创建后默认是0也就是说要想走到if起码要走一次compareAndSetWatiStatus(p,w,s); return true;//如果已经是-1则返回true if (ws > 0) { do { node.prev = pred = pred.prev;//如果pred的状态大于0则表示非正常状态,则需要将当前Node.prev指向上一个的上一个,这个操作是循环的。直到排除所有非正常状态的节点。 } while (pred.waitStatus > 0); pred.next = node; } else {//第一次为0需要执行CAS操作将状态更改为-1,这里也是设置上一个节点的status。 compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; }
 private final boolean parkAndCheckInterrupt() {//目前先不考虑线程被中断的情况。
        LockSupport.park(this);//阻塞当前线程
        return Thread.interrupted();//阻塞结束后,返回线程中断状态。
    }

公平锁-释放锁 

释放锁操作

 public void unlock() {
        sync.release(1);
    }
 public final boolean release(int arg) {//AQS
        if (tryRelease(arg)) {//释放锁的操作是由Sync做的。
            Node h = head;
            if (h != null && h.waitStatus != 0)//如果阻塞链表中的head!=null并且head这个哑节点的waitStatus!=0才会执行。
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
protected final boolean tryRelease(int releases) {//ReentrantLock.Sync
            int c = getState() - releases;//获取state冲入次数然后-1
            if (Thread.currentThread() != getExclusiveOwnerThread())//如果持有锁的线程不是当前线程则报错。从AOS中获取
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {//如果冲入次数==0则表示释放锁成功。
                free = true;
                setExclusiveOwnerThread(null);//将AOS中的线程置为null
            }
            setState(c);//设置重入数量
            return free;//只有重入次数为0,才会返回true
}
private void unparkSuccessor(Node node) {//接收head节点
        //拿到head的status正常情况是-1
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);//设置head的status为0
     //拿到哑节点的下一个节点
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {//如果哑节点的下一个节点是无效节点。
            s = null;//将无效节点设置为null
       //从链表的尾部开始找status是-1的节点。直到找到null,然后取最后一个有效节点为s.
for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } if (s != null)//在这里释放next节点。也就是从parkAndCheckInterrupt()方法中继续执行。
      LockSupport.unpark(s.thread); }

公平锁小结 

公平锁小结。
以下线程执行前提为顺序执行。

线程1 t1 获取锁
1 获取ReentrantLock中的state是0,表示锁无占用
    1 队列内head和tail都是null,CAS设置state成功。设置当前线程到AOS,获取锁成功
2 state不是0,判断持有锁的线程如果是当前线程则表示同一个线程多次重入,修改state。

线程2 t2 获取锁
1 获取ReentrantLock中的state不是0.AOS中持有锁的线程也不是当前线程,获取锁失败。
2 获取 Node mode = Node.EXCLUSIVE其实该节点是null,用于作为线程Node的next。
3 addWaiter()接收mode.
    1 创建新的阻塞节点,封装了t2,Node t2Node = new Node(ct,mode);
    2 获取tail节点。tail节点为null。
        (进入enq自旋,接收node)
        1 获取tail为null,创建一个Node new Node();为哑节点。
        2 CAS将哑节点设置为head,然后赋值给tail.
        1 获取tail!=null,设置node.prev为哑节点。
        2 CAS将t2Node设置为tail,将哑节点.next设置为t2Node然后返回。
    3 返回t2Node。这个node.prev是一个哑节点。
4 acquireQueued()接收封装了t2Node和1
5 自旋操作
    1 获取t2Node.prev==head,head就是哑节点,然后尝试获取锁失败。
    2 进入shouldParkAfterFailedAcquire传入哑节点和t2Node
    3 判断哑节点的status不是-1也不大于0当前哑节点的status为0
    4 CAS设置哑节点的status为-1,返回false.
    重复1,2步骤
    3 判断哑节点的status是-1返回true。
    4 进入parkAndCheckInterrupt方法阻塞t2线程。
    
线程3 t3 获取锁
1 获取ReentrantLock中的state不是0.AOS中持有锁的线程也不是当前线程,获取锁失败。
2 获取 Node mode = Node.EXCLUSIVE其实该节点是null,用于作为线程Node的next。 
3 addWaiter()接收mode.
    1 创建新的阻塞节点,封装了t3,Node t3Node = new Node(ct,mode);
    2 获取tail节点。tail节点为t2Node
    3 设置t3Node.prev=t2Node
    4 CAS设置t3Node为tail。设置t2Node.next为t3Node。返回t3的node
4 acquireQueued()接收封装了t3Node和1
5 自旋操作
    1 获取t3Node.prev是t2Node并不是哑节点head。
    2 进入shouldParkAfterFailedAcquire传入t2Node和t3Node
    3 判断t2的status不是-1也不大于0,当前t2的status是0
    4 CAS设置t2Node中status为-1,返回false.
    重复1,2
    3 判断t2的status是-1返回true
    4 进入parkAndCheckInterrupt方法阻塞t3线程。

线程1 t1 释放锁
1 执行tryRelease(1),重入次数state-1.
2 判断释放锁的线程是否是AOS中持有锁的线程,如果不是则抛出异常。
3 判断state==0,true,AOS持有锁的线程设置为null.
4 设置state为0,返回true。
5 获取head哑节点,!=null,并且status==-1。哑节点的status是t2线程设置为-1的
6 进入unparkSuccessor接收哑节点。
7 CAS将哑节点的status设置为0.
8 获取哑节点.next。此处可能会循环获取,因为哑节点.next指向的Node.status可能不是-1.
  这种情况将从队列的尾部开始寻找距离哑节点最近的一个-1节点。按照上边的流程当前哑节点.next是t2线程
9 unpark释放t2线程。

线程2 被唤醒
1 线程2从parkAndCheckInterrupt方法内唤醒。返回中断状态为false
2 继续执行acquireQueued方法,自旋中
    1 获取t2Node.prev,当前t2Node.prev为head也就是哑节点
    2 tryAcquire(1)尝试获取锁
    3 获取当前线程,获取state重入次数为0
    4 执行hasQueuedPredecessors(),tail为t3,head为哑节点。h!=t h.next.thread==当前线程
    5 CAS设置state为1,设置AOS中的当前持有锁线程为t2.返回true
    6 设置t2Node为head,t2Node.prev为null,t2Node.thread=null
    7 设置哑节点.next=null,释放GC
    8 线程2继续执行

线程2 释放锁
1 执行tryRelease(1),重入次数state-1.
2 判断释放锁的线程是否是AOS中持有锁的线程,如果不是则抛出异常。
3 判断state==0,true,AOS持有锁的线程设置为null.
4 设置state为0,返回true。
5 获取head节点,当前hean为t2Node,status是-1,t2Node的status是t3Node设置的。
6 进入unparkSuccessor接收t2Node
7 CAS将t2Node.status设置为0
8 获取t2Node.next为t3Node。当前t3Node.status是0
9 unpark释放t3线程。

线程3 被唤醒
1 线程3从parkAndCheckInterrupt方法内唤醒。返回中断状态为false
2 继续执行acquireQueued方法,自旋中
    1 获取t3Node.prev,当前t2Node.prev为t2Node,也就是head
    2 tryAcquire(1)尝试获取锁
    3 获取当前线程,获取state重入次数为0
    4 执行hasQueuedPredecessors(),tail为t2Node,head为t2Node.h!=t, h.next.thread==当前线程
    5 CAS设置state为1,设置AOS中的当前持有锁线程为t3.返回true
    6 设置t3Node为head。t3Node.prev=null,t3Node.thread=null
    7 t2Node.next=null,释放GC
    8 线程3继续执行

线程3 释放锁
1 执行tryRelease(1),重入次数state-1.
2 判断释放锁的线程是否是AOS中持有锁的线程,如果不是则抛出异常。
3 判断state==0,true,AOS持有锁的线程设置为null.
4 设置state为0,返回true。
5 获取head节点,当前head为t3Node
6 head节点status状态是0无需进入unparkSuccessor,head节点status为0表示不需要唤醒其他线程,也没有其他线程。

非公平锁-加锁成功

final void lock() {
            if (compareAndSetState(0, 1))//插队操作,不管队列中有没有阻塞线程,直接CAS抢夺锁。
                setExclusiveOwnerThread(Thread.currentThread());//抢夺成功,将当前线程设置到AOS中
            else
                acquire(1);//抢占失败
}
 public final void acquire(int arg) {//AQS中
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
}
 protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);//父类Sync的方法
}
        final boolean nonfairTryAcquire(int acquires) {//入参1
            final Thread current = Thread.currentThread();//当前线程
            int c = getState();//获取重入锁次数
            if (c == 0) {//如果重入锁次数==0表示当前锁没有线程持有
                if (compareAndSetState(0, acquires)) {//插队,CAS修改state
                    setExclusiveOwnerThread(current);//AOS设置持有锁的线程
                    return true;//返回成功
                }
            }
            else if (current == getExclusiveOwnerThread()) {//锁已经被持有并且是当前线程
                int nextc = c + acquires;//重入次数+1
                if (nextc < 0) //小于0报错
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);//设置重入次数
                return true;//返回失败
            }
            return false;//返回失败
        }

非公平锁-加锁两次插队

 

第一次插队再lock()方法内,公平锁会执行acquire(1)尝试获取锁,而非公平锁则直接CAS尝试修改状态,如果修改成功直接拿到锁。

公平锁尝试获取锁

非公平锁尝试获取锁

 

公平锁再tryAcquire时多做了一步hasQueuedPredecessors判断,这个方法期望返回false因为只有返回了false取反才可以继续执行CAS操作。

返回false的情况

1 head==tail直接返回false,这种情况表示等待队列中没有线程等待。

2 head!=tail说明等待队列中有Node,如果有Node,这个Node.next==null也可以,表示Node是哑节点。

3 如果head!=tail,Node.next!=null,并且Node.next.thread还不是当前线程那就必须返回true了,表示等待队列的下一个Node不是当前线程。

综合以上三条,非公平锁第二次插队在于不管阻塞队列中有没有线程再等待,它都会直接CAS。

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2021-04-24 17:53  顶风少年  阅读(76)  评论(0编辑  收藏  举报
返回顶部