ReentrantLock加锁及解锁过程之源码分析

Posted 八阿哥克星

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ReentrantLock加锁及解锁过程之源码分析相关的知识,希望对你有一定的参考价值。

一、ReentrantLock锁lock() 流程

首先不论公平锁还是非公平锁,都会调用下面的(非公平锁显示用cas尝试获取锁,获取不到才进入下面的acquire())

公平锁和非公平锁(在没有指定的情况下,默认是非公平)有不同的tryAcquire方法实现,公平锁是在tryAcquire方法中先对锁状态做判断,是否为0,为0则表示当前没有线程持有锁,可获取,这里,公平锁会进行hasQueuedPredecessors,判断在阻塞队列里是否有线程,并且判断是不是当前线程,若有且并不是当前线程直接返回false;如果锁状态不为0的情况下,判断当前线程是不是exclusiveOwnerThread,是则代表当前要获取锁的线程和正在持有锁的线程为同一线程,基于可重入的原则,获取锁成功,锁状态加一,若不是,则直接返回false;

//公平锁的tryAcquire
protected final boolean tryAcquire(int acquires) 
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) 
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires))  //用cas方式获取锁
                    setExclusiveOwnerThread(current);   //获取锁成功,将exclusiveOwnerThread                                                                            
                                                        //设为当前线程
                    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;
        
public final boolean hasQueuedPredecessors() 
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    

若是非公平锁,则没有此步骤,判断当前锁状态是否为0,是0直接cas尝试获取锁,其他过程与公平锁并无二致;

如果经过上述过程没有获取到锁,就会将当前线程放到阻塞队列中并阻塞该线程,即执行acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法;
(addWaiter是创建node节点对象,并将其放到阻塞队列(双向列表)的尾部,acquireQueued是有一个死循环,在此循环中
经过判断shouldParkAfterFailedAcquire(p, node)是否需要阻塞,是,则阻塞,否,继续进行循环,阻塞后的线程等待唤醒,唤醒后继续执行循环,
尝试获取锁,如此往复:

final boolean acquireQueued(final Node node, int arg) 
        boolean failed = true;
        try 
            boolean interrupted = false;
            for (;;) 
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg))  //判断当前节点是否为头节点的next节点,是 
                                                    //尝试获取锁;
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            
         finally 
            if (failed)
                cancelAcquire(node);
        
    

二、ReentrantLock锁unlock() 流程

释放锁时,首先调用tryRelease,对锁状态减一,为0时,则返回true,并将exclusiveOwnerThread置为null,表示当前所可以获取,没被线程持有

protected final boolean tryRelease(int releases)  //releases值为1
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) 
                free = true;
                setExclusiveOwnerThread(null);
            
            setState(c);
            return free;
        

tryRelease返回true后 ,调用unparkSuccessor,唤醒头部节点的next节点对应线程,该线程继续执行上述acquireQueued中代码;

三、condition的await()与signal()方法

condition.await()方法,是指对持有锁的线程进去等待状态,加入到等待队列(单向链表),并且释放锁
 

public final void await() throws InterruptedException 
            if (Thread.interrupted())
                throw new InterruptedException();
            Node node = addConditionWaiter(); //将当前线程封装成Node节点,放到等待队列中
            long savedState = fullyRelease(node); //释放锁
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) 
                LockSupport.park(this); //将当前线程置为阻塞状态
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        

fullyRelease方法中调用了上面的releas方法,释放锁,并且唤醒阻塞队列的头节点的next节点对应线程;
 

final long fullyRelease(Node node) 
        boolean failed = true;
        try 
            long savedState = getState();
            if (release(savedState)) 
                failed = false;
                return savedState;
             else 
                throw new IllegalMonitorStateException();
            
         finally 
            if (failed)
                node.waitStatus = Node.CANCELLED;
        
    

condition.signal()方法,调用了doSignal方法中的transferForSignal,将等待队列的最早进入的线程对应的Node加入到阻塞队列,并判断Node中的waitStatus状态,满足(waitStatus> 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL),会用LockSupport.unpark(node.thread)将此线程唤醒。

 private void doSignal(Node first) 
            do 
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                first.nextWaiter = null;
             while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);
        
final boolean transferForSignal(Node node) 
       
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;

       
        Node p = enq(node);
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    

以上是关于ReentrantLock加锁及解锁过程之源码分析的主要内容,如果未能解决你的问题,请参考以下文章

从ReentrantLock加锁解锁角度分析AQS

并发系列五:基于两种案例来认识ReentrantLock源码解锁过程(公平锁)

ReentrantLock 与 AQS 源码分析

ReentrantLock源码简析

Java并发编程之ReentrantLock源码分析

二探ReentrantLock非公平锁的加解锁过程