zoukankan      html  css  js  c++  java
  • 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。

     

     

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    java socket知识点
    java io流缓冲理解
    如何理解java回电话
    它们的定义Adapterg在etView( )正在使用View.setTag()与不同的是不使用。
    打造你自己ajax上传图片
    POJ 1166 The Clocks
    spring-framework-3.2.4.RELEASE 综合hibernate-release-4.3.5.Final一个错误Caused by: java.lang.NoClassDefFound
    [Android]Volley源代码分析(店)应用
    金融脱媒砸进?
    音乐家演奏乐器
  • 原文地址:https://www.cnblogs.com/zumengjie/p/14697589.html
Copyright © 2011-2022 走看看