Java并发编程之ReentrantLock重入锁

ReentrantLock:

  1. 源码层面分析:

    public class ReentrantLock implements Lock, java.io.Serializable {
        private static final long serialVersionUID = 7373984872572414699L;
        //实现同步逻辑的同步器,提供了所有锁相关的操作
        private final Sync sync;
        
        //默认构造函数,提供了非公平锁
        public ReentrantLock() {
            sync = new NonfairSync();
        }
    
        /**
         * 根据策略(公平,非公平)来构建重入锁
         * @param fair true表示采用公平锁,false表示采用非公平锁
         */
        public ReentrantLock(boolean fair) {
            sync = fair ? new FairSync() : new NonfairSync();
        }
        
        //获取锁,获取成功则返回,否则阻塞获取线程直到获取成功
        public void lock() {
            sync.lock();
        }
        
         //获取锁,获取成功则返回,否则阻塞获取线程直到获取成功;并响应中断,也就是说获取线程被Thread.interrupt()中断后,该方法会抛出InterruptedException并返回。
        public void lockInterruptibly() throws InterruptedException {
            sync.acquireInterruptibly(1);
        }
        
        /** 
         * 尝试获取锁,不管成功,失败都立即返回,不会阻塞
         * @return true表示获取成功,false表示失败
         */       
        public boolean tryLock() {
            return sync.nonfairTryAcquire(1);
        }
        
        /** 
         * 尝试获取锁,获取成功则返回,否则等待timeout时间返回
         * @param timeout 等待获取锁的超时时间
         * @param unit 时间单位
         * @return true表示获取成功,false表示超时时间到达仍然获取失败 
         */
        public boolean tryLock(long timeout, TimeUnit unit)
                throws InterruptedException {
            return sync.tryAcquireNanos(1, unit.toNanos(timeout));
        }
        
        //释放锁,该方法不会阻塞
        public void unlock() {
            sync.release(1);
        }
        
        /** 
         * 获取一个条件对象
         * @return 条件对象
         */   
        public Condition newCondition() {
            return sync.newCondition();
        }
    
    
  2. Sync内部类:

    //同步抽象类,基于AQS实现
    abstract static class Sync extends AbstractQueuedSynchronizer {
    
        private static final long serialVersionUID = -5179523762034025860L;
    
        //具体由子类NoFairSync,FairSync实现
        abstract void lock();
    
        /**
             * 非公平的方式尝试获取资源
             * @param acquires 资源个数
             * @return true表示获取成功,false获取失败
             */
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //获取资源状态,volatile读
            int c = getState();
            //0表示初始状态或者锁释放状态,可以直接获取资源
            if (c == 0) {
                //CAS判断资源获取是否成功
                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;
        }
    
        /**
             * 尝试释放资源
             * @param releases 资源个数
             * @return true表示释放成功,false释放失败
             */
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            //如果当前线程不为持有资源的线程,则抛出异常
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            //如果释放后,资源的个数为0表示释放成功
            if (c == 0) {
                free = true;
                //设置资源的独占线程为null,此时没有线程占用资源
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
    
        /**
             * 判断当前线程是否持有资源
             * @return true表示是,false表示否
             */
        protected final boolean isHeldExclusively() {
            return getExclusiveOwnerThread() == Thread.currentThread();
        }
    
        /**
             * 获取条件对象
             * @return 新的条件对象
             */
        final ConditionObject newCondition() {
            return new ConditionObject();
        }
    
        /**
             * 获取持有资源的线程
             * @return 持有资源的线程
             */
        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }
    
        /**
             * 获取持有资源的个数
             * @return 持有资源的个数
             */
        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }
    
        /**
             * 获取资源是否被线程占用
             * @return true表示是,false否
             */
        final boolean isLocked() {
            return getState() != 0;
        }
    }
    

    ReentrantLock内部基于Sync提供了非公平锁和公平锁的实现。

  3. 非公平锁

    //非公平锁继承自Sync
     static final class NonfairSync extends Sync {
         private static final long serialVersionUID = 7316153563782823691L;
    
         /**
             * 非公平锁提供的锁定方法
             */
         final void lock() {
             //先尝试CAS获取操作,获取成功则设置当前线程为资源持有线程并返回
             if (compareAndSetState(0, 1))
                 setExclusiveOwnerThread(Thread.currentThread());
             else
                 //获取失败则进入AQS的acquire逻辑,排队获取资源
                 acquire(1);
         }   
    
         /**
             * 非公平的方法尝试获取资源,AQS对外提供的需要实现方法
             * @param acquires资源个数
             * @return true表示是,false否
             */
         protected final boolean tryAcquire(int acquires) {
             //用Sync类的非公平方式尝试获取资源
             return nonfairTryAcquire(acquires);
         }
     }
    
  4. 公平锁:

    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
    
        /**
             * 公平锁提供的锁定方法
             */
        final void lock() {
            //排队获取资源,体现公平性
            acquire(1);
        }
    
        /**
             * 公平的方式尝试获取资源
             * @param acquires资源个数
             * @return true表示是,false否
             */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            //0表示当前可以直接获取资源
            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;
        }
    }
    
  5. **总结:**非公平主要体现在资源获取时,如果获取线程发现当前资源可以获取成功(其他线程释放了资源)那就直接获取返回,而此时可能有其他线程先于当前线程在排队获取,那么对于这部分线程来说是“不公平”的,没有体现先到先得的原则。反之,就是公平锁,先排队的线程先获取锁,不存在插队的情况,体现了公平性,所以叫公平锁。

  6. ReentrantLock默认使用非公平方式提供锁的获取和释放,可以看到相比公平模式,非公平模式获取锁的效率更高,因为在非公平模式下如果能够获取锁成功则线程不需要再排队,而在公平模式下,所有线程都必须排队获取锁,所以从当前获取锁的线程的角度非公平模式效率更高。还有一个地方需要说明的是:ReentrantLock是可重入锁,也就是说同一个线程获取锁后可再次重复获取锁无需阻塞。

  7. 典型应用场景:

    class X {
       private final ReentrantLock lock = new ReentrantLock();
       private Condition condition = lock.newCondition(); 
       public void m() {
         lock.lock();  // 获取锁
         try {
               try {
                    //必须先获取锁才能执行条件等待,其他线程需要执行condition.signal()方法,来唤醒等待线程
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            // 执行具体业务方法
          } finally {
            lock.unlock() //释放锁,必须在finally块中,防止发生异常时未执行释放锁操作,导致其他线程无法获取锁
          }
        }
     }}
    

你可能感兴趣的:(java进阶部分笔记,java,juc,锁)