Java 1.7 ReentrantLock源码解析

Posted Mr-yuenkin

tags:

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

 

1.   简介

    可重入锁是基于AQS实现的,和synchronized有相同的语义,同时有更多的扩展功能,比如可以tryLock和在指定时间内获取锁、响应中断获取锁等。典型用法如JDK 1.7中的示例:

lock.lock();  // block until condition holds
 *     try 
 *       // ... method body
 *      finally 
 *       lock.unlock()
 *     

  有两种模式:公平模式和非公平模式。公平和非公平的的含义见Java1.7 ReentrantReadWriteLock源码解析,类结构图如下所示:


主类ReentrantLock就一个sync成员变量,借助其实现lock/unlock功能。

2.   ReentrantLock

2.1 lock

public void lock() 
        sync.lock();
    

lock函数实现synchronized语义:1、如果当前没有线程占有锁,立即返回,设置独占标识且state值设置为1;2、如果当前线程已经占有锁了,立即返回,设置state+=1;3、如果其他线程已经占有锁了,当前线程会一直等待,直到获取到锁。

2.2    unlock

public void unlock() 
        sync.release(1);
    

tryLock函数释放锁,如果当前线程占有锁,则设置state-=1,减1后如果state为0了,则清空独占线程标识(setExclusiveOwnerThread(null));如果当前线程没有占有锁就调用unlock方法,则抛出异常IllegalMonitorStateException。

2.3    lockInterruptibly

public void lockInterruptibly() throws InterruptedException 
        sync.acquireInterruptibly(1);
    

lockInterruptibly函数实现了synchronized语义,但是支持中断(当其他线程调用了interrupt方法)。

2.4    tryLock

public boolean tryLock() 
        return sync.nonfairTryAcquire(1);
    

tryLock函数尝试获取锁(不管公平模式还是非公平模式都不排队)。如果锁已经被其他线程获取了直接返回false,否则,返回true。

2.5    带超时的tryLock

public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException 
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    

和2.4上面的不同,这个支持中断且在公平模式下是要排队的。如果当前没有线程获取到锁,直接返回true,否则,等待直至超时或被中断。如果不想排队又想要支持超时,你可以这么写:if(lock.tryLock() || lock.tryLock(timeout, unit) ) ...

2.6   getHoldCount

public int getHoldCount() 
        return sync.getHoldCount();
    

 getHoldCount函数返回当前线程重入锁了几次(也有可能是0)。

1.   Sync

3.1 nonfairTryAcquire

final boolean nonfairTryAcquire(int acquires) 
            final Thread current = Thread.currentThread();
            int c = getState();
// 如果没有线程占有锁且设置state成功,则设置线程独占标识并返回true
            if (c == 0) 
                if (compareAndSetState(0, acquires)) 
                    setExclusiveOwnerThread(current);
                    return true;
                
            
// 如果是重入的,设置state+=acquires并返回true(如果int没越界)
            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;
        

  nonfairTryAcquire函数尝试获取锁,不排队方式,如果当前没有线程占有锁或者是重入的,返回true;否则,返回false。

3.2 tryRelease

protected final boolean tryRelease(int releases) 
            int c = getState() - releases;
// 如果当前线程没有持有锁,直接抛异常
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) 
                free = true;
// 如果state==0了,清空线程独占标识
                setExclusiveOwnerThread(null);
            
// 肯定是占有锁了,不用循环CAS了,直接设置
            setState(c);
            return free;
        

 tryRelease函数释放锁,如果state为0了,则要清空线程独占标识。

4.   NonfairSync

static final class NonfairSync extends Sync 
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * 先插个队尝试获取锁(大多数情况会成功的),如果失败了再调用tryAcquire
         * 里面会考虑重入的情况。
         */
        final void lock() 
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        
// 可以直接插队获取锁的且会考虑重入情况
        protected final boolean tryAcquire(int acquires) 
            return nonfairTryAcquire(acquires);
        
    

5.   FairSync

static final class FairSync extends Sync 
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() 
            acquire(1);
        

        /**
         * 公平模式下获取锁的方式,如果state==0且前面没有线程排队才能获取锁;
         * 或者是重入的情况。
         */
        protected final boolean tryAcquire(int acquires) 
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) 
// c==0且前面没有线程排队且CAS设置成功,则设置线程独占标识,返回true
                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;
        
    


以上是关于Java 1.7 ReentrantLock源码解析的主要内容,如果未能解决你的问题,请参考以下文章

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

源码分析 CurrentHashMap 1.7

ReentrantLock 与 AQS 源码分析

Java并发——ReentrantLock类源码阅读

Java并发编程之ReentrantLock源码分析

JAVA并发之ReentrantLock源码