ReentrantLock源码解析

2018-09-17  本文已影响0人  hcy0411

简介

ReentrantLock是一个可重入的独享锁,是平时常用的一个锁,用法和实现都比较简单,如下:

    private static ReentrantLock lock = new ReentrantLock();
    private static void testLock() {

        for (int i=0;i<100000;i++) {
            lock.lock();
            try {
                test++;
            } catch (Exception e) {
                System.out.println(e.getMessage());
            } finally {
                lock.unlock();
            }
        }
    }
    private static void testLock2() {

        for (int i=0;i<100000;i++) {
            if(lock.tryLock()) {
                try {
                    test++;
                } catch (Exception e) {
                    System.out.println(e.getMessage());
                } finally {
                    lock.unlock();
                }
            }
        }
    }
    private static void testLock3() throws Exception {

        for (int i=0;i<100000;i++) {
            if(lock.tryLock(1, TimeUnit.MINUTES)) {
                try {
                    test++;
                } catch (Exception e) {
                    System.out.println(e.getMessage());
                } finally {
                    lock.unlock();
                }
            }
        }
    }

还有ReentrantLock支持可重入。

ReentrantLock是基于aqs独占模式实现的,
本文重点介绍下公平和非公平模式,和ReentrantLock实现原理。

同步器-Sync

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


        abstract void lock();

        //ReentrantLock 默认是非公平模式,这里给出了非公平模式下的尝试获取锁
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            // AbstractQueuedSynchronizer 的state常用于保存资源被占用状态,这里保存ReentrantLock锁状态,=0表示现在锁现在没被占用
            int c = getState();
            if (c == 0) {
                // 设置成占用状态,返回true,并设置lock拥有线程为当前线程,支持可重入
                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;
        }

        // 实现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;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
        ```
    }

了解AbstractQueuedSynchronizer 都知道,AbstractQueuedSynchronizer暴露tryAcquire,tryRelease让实现者自己去实现,ReentrantLock的Sync实现了这两个方法,用于判断是否可以获取锁和释放锁,接下来介绍非公平模式和公平模式

非公平模式-NonfairSync

ReentrantLock支持公平、非公平两种策略,并通过继承AQS实现了对应两种策略的同步器NonfairSync与FairSync,默认是非公平策略

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


        // 这里实现 lock方法,即ReentrantLock.lock方法
        final void lock() {
            // 这里判断是否有其他资源占用,cas设置成功,说明当前线程获取成功,这里
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                // 否则申请一个资源,就是这里会调用tryAcquire方法
                acquire(1);
        }
        
        //尝试获取锁资源,调用的是非公平获取锁
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

公平模式-FairSync

    /**
     * Sync object for fair locks
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        //获取锁
        final void lock() {
            acquire(1);
        }

        //公平模式和非公平模式区别就在这里,多了hasQueuedPredecessors判断,就是看是否已有等待获取锁资源线程并且不是当前线程,如果有不去竞争
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            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;
        }
    }

公平模式和非公平模式,区别就在于一个hasQueuedPredecessors函数,该函数用于判断是否已有节点在等待资源了并且不是该线程等待的,附上代码

    public final boolean hasQueuedPredecessors() {
        // The correctness of this depends on head being initialized
        // before tail and on head.next being accurate if the current
        // thread is first in queue.
        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());
    }

以上就是公平模式和非公平模式实现,都是实现了AbstractQueuedSynchronizer 的tryAcquire和tryRelease方法,用于判断是否能够获取资源和释放资源,实现简单。

ReentrantLock的方法就不介绍了,都是调sync实现的方法,ReentrantLock最重要实现就是实现AbstractQueuedSynchronizer 的tryAcquire和tryRelease方法,支持可重入,还有支持Condition等待通知实现。

上一篇下一篇

猜你喜欢

热点阅读