Java并发编程之ReentrantLock源码解析

一、关于ReetrantLock

在上篇文章Java并发编程之AQS中,比较详细的说了一下关于AQS的设计和AQS的代码相关原理。在上篇文章中也说了,AQS是J.U.C的核心,是用来构建锁或其他同步组件的基础框架,这其中就包括了ReentrantLock。由于前面已经详细的说了AQS的原理,在本篇文章中,对于涉及到AQS相关的东西,便会一带而过。

ReentrantLock和synchronized一样都是具有重入的功能,但相比synchronized而言,ReentrantLock的功能显得更丰富。从上面图片中,可以知道ReentrantLock实现了Lock接口和Serializable,则说明ReentrantLock具有Lock接口的所有实现和序列化的功能。从下面图片中,同样可以看出关于Lock接口的所有功能。

上面说了关于ReentrantLock的相关设计,那么下面就开始ReentrantLock的主要实现吧!

二、ReetrantLock的实现

在说ReentrantLock具体实现之前,先大体说下关于ReentrantLock的一个简单的工作流程:

  • 在ReentrantLock中同样是维护着一个state变量(因为ReentrantLock类中有一个Sync抽象类,而Sync正是继承着了AbstractQueuedSynchronizer,也就是我们上篇文章所说的AQS,具体的后面会详说),这个state的初始化值为0,表示未锁定状态。
  • A线程lock()时,会调用tryAcquire()(公平与非公平都是如此)独占锁,并将state+1
  • 此后,其他线程再调用tryAcquire()的时候就会失败,直到A线程unlock()到state=0(即释放锁)为止,其他线程便可以获取该锁了
  • 当然,在释放之前,A线程自己可以重复获取此锁的(state变量的累加),这就是重入的概念
  • 但要注意,获取多少次就必须要释放多少次,这样才能保证state恢复到0的状态

简单介绍完ReentrantLock的工作流程,现在开始正式进入到ReentrantLock类中的代码解析了。

 private final Sync sync;

在ReentrantLock类中,有一个核心的成员变量(如上代码所示),这是一个同步的变量,前面亦曾提及。这个类使用abstract修饰着,这证明着它的下面肯定会有这具体的实现,这个类的具体实现由两种方式:FairSync和NonfairSync(如下图所示)。这说明在使用ReentrantLock的时候,可以添加一个参数,来说明你是使用公平锁的方式还是使用非公平锁的方式。

从上图中,可以看到Sync类继承了AQS,既然继承了AQS,也就说明Sync 具有AQS的所有的特性。接下来看下Sync类的具体代码:

    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         * 抽象类,具体实现有fair和nonfair 
         */
        abstract void lock();

        /**
         * 非公平锁尝试获取许可的方法
         */
        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;
        }

        // 尝试释放许可
        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;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

        // 判断当前的线程是否是独占的
        protected final boolean isHeldExclusively() {
            // 判断当前线程是否与getExclusiveOwnerThread()相等,如果相等则是,反之不是。
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        // 
        final ConditionObject newCondition() {
            return new ConditionObject();
        }

        // 获取线程本身,首先getState() == 0,就返回一个null,不等0就放回当前这个独占的线程
        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }

        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }

        // 判断是否上锁
        final boolean isLocked() {
            return getState() != 0;
        }

        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }

在说完ReentrantLock顶层的一些实现后,现在开始说一下它的具体的一些实现了。

扫描二维码关注公众号,回复: 8609080 查看本文章

公平锁

公平锁的UML结构图如下:

    ReentrantLock reentrantLock = new ReentrantLock(true);

    // 因为传入的是true,所以会new FairSync()
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        // 公平锁,默认传值为1
        final void lock() {
            acquire(1);
        }

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            // 把当前线程赋值给current
            final Thread current = Thread.currentThread();
            // 获取state
            int c = getState();
            // 如果等于0,说明当前没有任何线程去获取资源,那么就可以去获取锁了
            if (c == 0) {
                /**
                 * 1.hasQueuedPredecessors():判断这个队列里前面有没有排队等待的线程,如果     
                 *   有等待则返回true,反之为false
                 * 2.如果前面没有等待的线程,那么就可以继续调用CAS操作,这个操作就是对state进
                 *   行+1,CAS操作是保证多线程操作不会产生线程安全问题的,如果CAS操作成功,将
                 *   会进行下一步
                 * 3.调用setExclusiveOwnerThread方法,设置当前线程为独占的
                 */
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // 如果当前线程引用和当前的线程相匹配,说明是同一个线程,就可以再次获取这把锁(重入)
            else if (current == getExclusiveOwnerThread()) {
                // 将c和新传入的中进行相加
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                // 将nextc赋值到state变量中
                setState(nextc);
                return true;
            }
            return false;
        }
    }

上面代码中,如果要加锁的话会调用acquire(1)方法,这里是直接传入1的,因为这个FairSync 是继承自Sync的,而Sync又是继承自AQS的,所以这里的acquire方法时调用AQS中的方法的,看下面的代码一中的代码,这里首先会调用!tryAcquire(arg)方法尝试获取锁,如果获取成功直接调用selfInterrupt()方法(这个方法的作用是打断自己本身),如过获取成功,那将应该打断自己唤醒自己,继续去做一些 后续的事情。如果没有获取成功,那肯定是处于排队等待状态。当然没有获取成功它将会首先去调用addWaiter(Node.EXCLUSIVE),把当前这个线程加入到FIFO双线链表队列中,并且设置这个Node为EXCLUSIVE(独占的),然后调用acquireQueued方法使线程在等待队列中获取资源(自旋),一直获取到资源后才返回。这段代码具体可以去看上篇文章。

对于上面说到的!tryAcquire(arg)方法,如果点进去,你会发现如代码二中所示,这里会说不做具体实现,这里肯定是交由子类去实现的,因为这里说的是公平锁,那么tryAcquire方法的实现,就是上面那段代码中的tryAcquire方法了。

代码一:

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

代码二:

    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

非公平锁

非公平锁的UML结构图如下:

说完了公平锁,下面来说下非公平锁,其实在ReentrantLock中默认是为非要公平锁的,只有在新建ReentrantLock时传入true时才为公平锁。下面首先看下代码:

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

        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

相比较公平锁而言,公平锁的实现,是简单的多了,在非公平锁的代码中,如果当前是非公平锁,就会直接进行CAS操作,就相当于当前线程进来后,不需要排队了,直接进行CAS操作,哪个线程CPU优先分配给它了,就直接对它进行原子操作。也就是谁成功了,谁就去获得这把锁,然后就把当前线程设置为独占。

获取锁

一般获取锁的方式如下:

        ReentrantLock reentrantLock = new ReentrantLock();
        reentrantLock.lock();

lock()方法:

    public void lock() {
        sync.lock();
    }

前面说了Sync是ReentrantLock中的一个抽象内部类,而且他继承了AQS,且有两个子类FairSync和NonfairSync。则说明ReentrantLock中的大部分功能是由Sync去实现的。在Sync中定义了内部方法lock(),如图:

对于公平锁和非公平锁具体的获取方式前面已经进行说明了,这里就不再进行说明了。

锁释放

获取锁之后,自然是要进行释放的,释放方法如下:

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

这里释放锁的还是调用Sync中的release,release(1)方法的具体代码如下:

    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

在上面的代码中,与获取锁的方法相似,也会首先调用tryRelease(int arg):

        protected final boolean tryRelease(int releases) {
            // 首先用getState()到的值减去传入的releases
            int c = getState() - releases;
            // 然后判断,如果当前线程不是被独占的,就抛异常
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            // 
            boolean free = false;
            if (c == 0) {
                free = true;
                // 将独占置为空
                setExclusiveOwnerThread(null);
            }
            // 把减完后的值赋给state
            setState(c);
            return free;
        }

ReentrantLock与synchronized的区别

区别如下:

  • synchronized是关键字,ReentrantLock是类
  • ReentrantLock可以获取锁的等待时间进行设置,避免死锁
  • ReentrantLock可以获取各种锁的信息
  • ReentrantLock可以灵活的实现多路通知
  • 机制:sync操作Mark Word,lock调用Unsafe类的park()方法

版权声明:尊重博主原创文章,转载请注明出处:https://blog.csdn.net/zfy163520

发布了41 篇原创文章 · 获赞 8 · 访问量 4265

猜你喜欢

转载自blog.csdn.net/zfy163520/article/details/89291224