美文网首页Java 并发编程&JUC源码解析
Java Concurrent ReentrantLock(Ja

Java Concurrent ReentrantLock(Ja

作者: 邹志全 | 来源:发表于2019-07-17 21:50 被阅读0次

    前言

    Reentrant是一种可重入锁,是一种递归无阻塞的同步机制。实现了和synchronized类似的同步策略。与synchronized配合使用的wait、notify、notifyall等函数,由Codition负责提供,这个后续另外会说。

    使用

    1、在使用上与synchronized差异的是 实现lock接口的reentrant需要手动的去lock和release,因为synchronized是JVM也就是Java语法层面实现的,而Lock是JDK里面实现的。相对于sychronized来说,我们使用时要比synchronized更加严谨,因为忘记释放锁非常容易导致死锁。建议选择在finally中进行锁的释放
    2、lock实现的锁粒度可以控制更加小
    3、Lock 实现能更支持更多高级的特性,比如说锁超时等。
    4、因为是JDK实现,所以具有了更多特性的高级锁比如说:read lock、write lock,并且支持我们自定义特殊的锁,这个虽然通常用不太到,但必要时是非常有用的。
    demo:首先是使用synchronize,感受一下

    class Test1 {
        private static volatile int condition = 0;
        private static final Object lock = new Object();
        public static void main(String[] args) throws InterruptedException {
            Thread A = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (lock) {
                        while (!(condition == 1)) {
                            try {
                                lock.wait();
                            } catch (InterruptedException e) {
                                Thread.currentThread().interrupt();
                            }
                        }
                        System.out.println("a executed by notify");
                    }
                }
            });
            A.start();
            Thread.sleep(2000);
            condition = 1;
            synchronized (lock) {
                lock.notify();
            }
        }
    }
    

    然后是使用Lock

    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    class Test2 {
            private static volatile int condition = 0;
            private static Lock lock = new ReentrantLock();
            private static Condition lockCondition = lock.newCondition();
            public static void main(String[] args) throws InterruptedException {
                Thread A = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        lock.lock();
                        try {
                            while (!(condition == 1)) {
                                lockCondition.await();
                            }
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        } finally {
                            lock.unlock();
                        }
                        System.out.println("a executed by condition");
                    }
                });
                A.start();
                Thread.sleep(2000);
                condition = 1;
                lock.lock();
                try {
                    lockCondition.signal();
                } finally {
                    lock.unlock();
                }
            }
    }
    

    原理

    开始看源码:
    首先可以看到ReentrantLock实现了LockSerializable 接口,序列化不多说,Lock定义了是现在JDK中锁的规范,然后类中持有一个Sync对象,其中Sync是ReentrantLock的一个静态内部类,这是整个ReentrantLock的基础,Sync继承自AQS,换句话说AQS也就是ReentrantLock的实现基础。

    public class ReentrantLock implements Lock, java.io.Serializable {
        private static final long serialVersionUID = 7373984872572414699L;
        /** Synchronizer providing all implementation mechanics */
        private final Sync sync;
        /**
         * Base of synchronization control for this lock. Subclassed
         * into fair and nonfair versions below. Uses AQS state to
         * represent the number of holds on the lock.
         */
        abstract static class Sync extends AbstractQueuedSynchronizer {
            private static final long serialVersionUID = -5179523762034025860L;
    

    然后往后看会得到另外几个静态内部类:FairSyncNonfairSync,他们均继承自Sync
    这两个区别最大的操作是:
    如果当前线程不是锁的占有者,则NonfairSync并不判断是否有等待队列,直接使用CAS去进行锁的占用;
    如果当前线程不是锁的占有者,则FairSync则会判断当前是否有等待队列,如果有则将自己加到等待队列尾;
    这其实就是公平锁&非公平锁的实现,默认非公平。
    下面的代码中,我加了几行注释,大家注重关注下可重入及公平非公平是如何实现的。

    /**
     * Sync object for non-fair locks
     */
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
    

    它这里使用的是Sync里面的实现:

    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;
        /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         */
        @ReservedStackAccess
        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;
    }
    

    然后是FairSync的实现

    /**
     * Sync object for fair locks
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        @ReservedStackAccess
        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;
        }
    }
    

    看到这里,大体已经能猜到具体实现了。ReentrantLock中维护着一个AQS,然后竞争锁的线程是在这里排队的,然后通过对应的CAS操作进行锁的争用。具体的实现参考AQS及CAS。
    默认非公平:

    /**
     * Creates an instance of {@code ReentrantLock}.
     * This is equivalent to using {@code ReentrantLock(false)}.
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }
    

    可以很明显的看到底层实现几乎完全依赖于AQS,其实就是AQS包了一层罢了。

    public void lock() {
        sync.acquire(1);
    }
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }
    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }
    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }
    public void unlock() {
        sync.release(1);
    }
    public int getHoldCount() {
        return sync.getHoldCount();
    }
    

    相关文章

      网友评论

        本文标题:Java Concurrent ReentrantLock(Ja

        本文链接:https://www.haomeiwen.com/subject/cuxkkctx.html