• Jdk1.6 JUC源码解析(7)-locks-ReentrantLock


    功能简介:
    • Java代码层面提供的锁机制,可做为Synchronized(jvm内置)的替代物,和Synchronized一样都是可重入的。
    • 与Synchronized相比较而言,ReentrantLock有以下优 势:支持公平/非公平锁、支持可中断的锁、支持非阻塞的tryLock(可超时)、支持锁条件、可跨代码块使用(一个地方加锁,另一个地方解锁),总之比 Synchronized更加灵活。但也有缺点,比如锁需要显示解锁、无法充分享用JVM内部性能提升带来的好处等等。
    源码分析:
    • ReentrantLock实现了Lock接口,先来看下这个接口:
    public interface Lock {
        /**
         * 获取锁,如果锁无法获取,当前线程被阻塞,直到锁可以获取并获取成功为止。
         */
        void lock();
        /**
         * 在当前线程没有被中断的情况下获取锁。
         * 如果获取成功,方法结束。
         * 如果锁无法获取,当前线程被阻塞,直到下面情况发生:
         * 1.当前线程(被唤醒后)成功获取锁。
         * 2.当前线程被其他线程中断。
         */
        void lockInterruptibly() throws InterruptedException;
        /**
         * 如果当前锁是可用的,获取锁。
         * 获取成功后,返回true。
         * 如果当前锁不可用,返回false。
         */
        boolean tryLock();
        /**
         * 如果锁在给定超时时间内可用,并且当前线程没有被中断,那么获取锁。
         * 如果锁可用,获取锁成功并返回true。
         * 如果锁无法获取,当前线程被阻塞,直到下面情况发生:
         * 1.当前线程(被唤醒后)成功获取锁。
         * 2.当前线程被其他线程中断。
         * 3.指定的等待时间超时。
         */
        boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
        /**
         * 释放锁。
         */
        void unlock();
        /**
         * 返回一个和当前锁实例相关联的条件。
         * 当前线程必须首先获取锁后才能在锁条件上等待。
         * 一个Condition的await()方法调用会在等待之前自动释放锁,在等待结束
         * 前重新获取锁。
         */
        Condition newCondition();
    }
    • 之前分析AQS的时候提到过,基于AQS构建的同步机制都会使用内部帮助类继承AQS的方式构建,看下ReentrantLock中的同步机制:
        //内部同步机制的引用。
        private final Sync sync;
        /**
         * 这个锁实现的基本同步控制机制,下面会提供公平和非公平版本的子类。
         * 利用AQS的state来表示锁持有(重入)的次数。.
         */
        static abstract class Sync extends AbstractQueuedSynchronizer {
            private static final long serialVersionUID = -5179523762034025860L;
            /**
             * Performs {@link Lock#lock}. The main reason for subclassing
             * is to allow fast path for nonfair version.
             */
            abstract void lock();
            /**
             * 方法用来支持非公平的tryLock 
             */
            final boolean nonfairTryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                int c = getState();
                if (c == 0) {
                    //如果当前没有任何线程获取锁(锁可用),尝试设置state。
                    if (compareAndSetState(0, acquires)) {
                        //如果设置成功,将当前线程信息设置到AQS中(所有权关联)。 
                        setExclusiveOwnerThread(current);
                        return true;
                    }
                }
                //如果锁已经被持有,那么判断一下持有锁的线程是否为当前线程。
                else if (current == getExclusiveOwnerThread()) {
                    //如果是当前线程在持有锁,那么这里累计一下重入次数。
                    int nextc = c + acquires;
                    if (nextc < 0) // overflow  重入次数最大不能超过int的最大值
                        throw new Error("Maximum lock count exceeded");
                    //设置到AQS的state中
                    setState(nextc);
                    return true;
                }
                //如果锁已经被持有,且持有线程不是当前线程,返回false。
                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) {
                    //如果当前线程完全释放了锁(重入次数为0)
                    free = true;
                    //解除所有权关系。
                    setExclusiveOwnerThread(null);
                }
                //设置重入次数。
                setState(c);
                //返回是否释放成功(或者说是否完全释放)。
                return free;
            }
            protected final boolean isHeldExclusively() {
                // While we must in general read state before owner,
                // we don't need to do so to check if current thread is owner
                return getExclusiveOwnerThread() == Thread.currentThread();
            }
            final ConditionObject newCondition() {
                return new ConditionObject();
            }
            // Methods relayed from outer class
            final Thread getOwner() {
                return getState() == 0 ? null : getExclusiveOwnerThread();
            }
            final int getHoldCount() {
                return isHeldExclusively() ? getState() : 0;
            }
            final boolean isLocked() {
                return getState() != 0;
            }
            /**
             * Reconstitutes this lock instance from a stream.
             * @param s the stream
             */
            private void readObject(java.io.ObjectInputStream s)
                throws java.io.IOException, ClassNotFoundException {
                s.defaultReadObject();
                setState(0); // reset to unlocked state
            }
        }

           接下来先看一下非公平版本的子类:

        /**
         * Sync object for non-fair locks
         */
        final static class NonfairSync extends Sync {
            private static final long serialVersionUID = 7316153563782823691L;
            /**
             * Performs lock.  Try immediate barge, backing up to normal
             * acquire on failure.
             */
            final void lock() {
                //这里首先尝试一个短代码路径,直接CAS设置state,尝试获取锁。
                //相当于一个插队的动作(可能出现AQS等待队列里有线程在等待,但当前线程竞争成功)。
                if (compareAndSetState(0, 1))
                    setExclusiveOwnerThread(Thread.currentThread());
                else 
                    acquire(1);//如果CAS失败,调用AQS的独占请求方法。
            }
            protected final boolean tryAcquire(int acquires) {
                //调用上面父类的nonfairTryAcquire方法。
                return nonfairTryAcquire(acquires);
            }
        }

           再来先看一下公平版本的子类:

        /**
         * Sync object for fair locks
         */
        final static class FairSync extends Sync {
            private static final long serialVersionUID = -3000897897090466540L;
            final void lock() {
                acquire(1);
            }
            /**
             * 公平版本的tryAcquire。 
             * 只有在递归(重入)或者同步队列中没有其他线程
             * 或者当前线程是等待队列中的第一个线程时才准许访问。
             */
            protected final boolean tryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                int c = getState();
                if (c == 0) {
                    if (!hasQueuedPredecessors() &&
                        compareAndSetState(0, acquires)) { //如果当前锁可用,且同步等待队列中没有其他线程,那么尝试设置state
                        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;
            }
        }
           小总结一下:
           非公平版的锁-加锁操作
    1. 当前线程首先会无条件的执行一个CAS操作来获取锁,如果CAS操作成功,获取锁成功。
    2. 如果第1步没成功,当前会检查锁是否被其他线程持有,也就是锁是否可用。
    3. 如果没有其他线程持有锁,会以CAS的方式尝试获取锁,如果CAS操作成功,获取锁成功。
    4. 如果有其他线程持有锁,会判断一下持有锁的线程是否为当前线程,如果是当前线程,重入次数+1,获取锁成功。
    5. 根据AQS的分析,上述2、3、4步会执行多次,如果最终获取锁失败,当前线程会被阻塞,等待其他线程执行解锁操作将其唤醒。
           公平版的锁-加锁操作
    1. 当前线程首先会检查锁是否被其他线程持有,并且当前同步等待队列里有没有其他线程在等待。
    2. 如果没有其他线程持有锁,且同步等待队列里没有其他线程,会以CAS的方式尝试获取锁,如果CAS操作成功,获取锁成功。
    3. 如果有其他线程持有锁,会判断一下持有锁的线程是否为当前线程,如果是当前线程,重入次数+1,获取锁成功。
    4. 根据AQS的分析,上述1、2、3步会执行多次,如果最终获取锁失败,当前线程会被阻塞,等待其他线程执行解锁操作将其唤醒。
           非公平版和公平版锁的解锁操作一样
    1. 当前线程首先将锁重入次数减1(AQS的state),如果减1后结果为0,将当前同步器的线程信息置空,并唤醒同步等待队列中队头的等待线程。
    2. 如果第1步中,重入次数减1后结果不为0(说明当前线程还持有当前锁),方法结束。
    • 有了内部的基础同步机制,ReentrantLock的实现就很简单了,直接看代码:
        /**
         * 默认情况下构建非公平锁。
         */
        public ReentrantLock() {
            sync = new NonfairSync();
        }
        /**
         * 根据给定的公平策略生成相应的实例。
         *
         * @param fair {@code true} if this lock should use a fair ordering policy
         */
        public ReentrantLock(boolean fair) {
            sync = (fair)? new FairSync() : new NonfairSync();
        }
    
        public void lock() {
            sync.lock();
        }
        
        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 Condition newCondition() {
            return sync.newCondition();
        }

           最后看一下一些支持监测的方法:

        /**
         * 获取当前线程的对当前锁的持有(重入)次数。
         */
        public int getHoldCount() {
            return sync.getHoldCount();
        }
        /**
         * 判断当前锁是否被当前线程持有。
         */
        public boolean isHeldByCurrentThread() {
            return sync.isHeldExclusively();
        }
        /**
         * 判断当前锁是否被(某个线程)持有。
         */
        public boolean isLocked() {
            return sync.isLocked();
        }
        /**
         * 当前锁是否为公平锁。
         */
        public final boolean isFair() {
            return sync instanceof FairSync;
        }
        /**
         * 获取持有当前锁的线程。
         */
        protected Thread getOwner() {
            return sync.getOwner();
        }
        /**
         * 判断是否有线程在当前锁的同步等待队列中等待。 
         */
        public final boolean hasQueuedThreads() {
            return sync.hasQueuedThreads();
        }
        /**
         * 判断给定的线程是否在当前锁的同步等待队列中等待。
         */
        public final boolean hasQueuedThread(Thread thread) {
            return sync.isQueued(thread);
        }
        /**
         * 获取当前锁的同步等待队列中的等待线程(估计)数量。
         */
        public final int getQueueLength() {
            return sync.getQueueLength();
        }
        /**
         * 获取当前锁的同步等待队列中等待的线程。
         */
        protected Collection<Thread> getQueuedThreads() {
            return sync.getQueuedThreads();
        }
        /**
         * 判断是否有线程在给定条件的条件等待队列上等待。
         */
        public boolean hasWaiters(Condition condition) {
            if (condition == null)
                throw new NullPointerException();
            if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
                throw new IllegalArgumentException("not owner");
            return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
        }
        /**
         * 获取给定条件的条件等待队列中等待线程的(估计)数量。
         */
        public int getWaitQueueLength(Condition condition) {
            if (condition == null)
                throw new NullPointerException();
            if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
                throw new IllegalArgumentException("not owner");
            return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
        }
        /**
         * 获取给定条件的条件等待队列中等待线程。
         */
        protected Collection<Thread> getWaitingThreads(Condition condition) {
            if (condition == null)
                throw new NullPointerException();
            if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
                throw new IllegalArgumentException("not owner");
            return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
        }
  • 相关阅读:
    基础设施公募REITs介绍
    《苔》袁枚
    南京大学 静态软件分析(static program analyzes) Pointer Analysis 学习笔记
    南京大学 静态软件分析(static program analyzes) Data Flow Analysis:Applications 学习笔记
    南京大学 静态软件分析(static program analyzes) Static Analysis for Security 学习笔记
    tp5,把耗时操作转为队列,queue + redis + supervisor消息推送(队列的执行异步不异步不知道,workman,swoole可以异步)
    ThinkPHP 使用 thinkqueue 实现 redis 消息队列
    JAVA中“LIST泛型MAP根据某一个KEY去重
    nodejs搭建一个webscoket服务器【转】
    windows下安装node.js及环境配置、部署项目【转】
  • 原文地址:https://www.cnblogs.com/wxgblogs/p/5806504.html
Copyright © 2020-2023  润新知