美文网首页
Java锁之ReentrantLock(一)

Java锁之ReentrantLock(一)

作者: 木木匠 | 来源:发表于2018-08-22 16:52 被阅读0次

    一、ReenTrantLock结构

    image

    图1-1

    根据上图可以知道,ReenTrantLock继承了Lock接口,Lock接口声明方法如下:

    方法名 说明 抛出异常
    lock() 一直阻塞获取锁,直到获取成功
    lockInterruptibly() 尝试获取锁,直到获取锁或者线程被中断 InterruptedException
    tryLock() 尝试获取空闲的锁,获取成功返回true,获取失败返回false,不会阻塞,立即返回
    tryLock(long time, TimeUnit unit) 尝试在time时间内获取空闲的锁,在等待时间内可以被中断 InterruptedException
    unlock() 释放锁
    newCondition() 返回当前锁的一个condition实例,用于条件性等待

    二、Lock的实现类ReentrantLock

    1.ReentrantLock的部分方法

    image

    图2-1

    • 根据图2-1可以知道 Sync对象提供了所有的实现机制,而Sync继承了AbstractQueuedSynchronizer
    • NonfairSync 不公平锁,继承了Sync
    • FairSync 公平同步,继承了Sync

    1. Sync


    Sync是NonfairSync 和FairSync 的父类,声明方法如下:

          /**
           * 抽象方法,获取锁
           */
          abstract void lock();
    
          /**
           * 实现非公平锁获取逻辑
           */
          final boolean  nonfairTryAcquire(int acquires) {
              final Thread current = Thread.currentThread();
              int c = getState(); //父类同步器方法,获取当前同步状态,后续文章会分析
              if (c == 0) {//状态等于0表示没有获取到锁
                  if (compareAndSetState(0, acquires)) { //CAS方式修改状态
                      setExclusiveOwnerThread(current); //修改成功后设置当前线程为锁的所有者
                      return true;
                  }
              }
              else if (current == getExclusiveOwnerThread()) {//当前锁已被占用,判断是不是自己获取到了锁,锁重入
                  int nextc = c + acquires; //获取锁的计数器
                  if (nextc < 0) // overflow //因为是int类型,如果超过int最大值会溢出为负
                      throw new Error("Maximum lock count exceeded");
                  setState(nextc);//设置计数器为状态值
                  return true;
              }
              return false;
          }
    
          protected final boolean tryRelease(int releases) {
              int c = getState() - releases;//释放锁,同步状态减int值
              if (Thread.currentThread() != getExclusiveOwnerThread())
                  throw new IllegalMonitorStateException(); //如果当前相差不是锁的拥有者,抛出异常
              boolean free = false;
              if (c == 0) { //如果同步状态值为0,表示锁已经释放成功
                  free = true; 
                  setExclusiveOwnerThread(null); // 设置锁的拥有线程为null
              }
              setState(c);//重新赋值同步状态
              return free;
          }
          //判断当前线程是不是锁独占
          protected final boolean isHeldExclusively() {
            
              return getExclusiveOwnerThread() == Thread.currentThread();
          }
          //返回锁的ConditionObject实例
          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; //可以知道判断获取锁的关键就是是否不等于0
          }
    

    2. NonfairSync 非公平锁


     static final 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() {
               if (compareAndSetState(0, 1))//CAS获取锁
                   setExclusiveOwnerThread(Thread.currentThread());
               else
                   acquire(1);
           }
    
           protected final boolean tryAcquire(int acquires) {
               return nonfairTryAcquire(acquires);
           }
       }
    
    • 根据源码发现 非公平锁继承了Sync父类,由于锁的释放不存在公平与不公平,所以公平锁和非公平锁只实现各自获取锁的逻辑。根据非公平锁的源码发现,其内部只实现了lock()tryAcquire(int acquires)方法,其中和tryAcquire(int acquires)方法直接调用了父类的nonfairTryAcquire(acquires),介绍父类的时候已经解析过,不清楚可以看上文Sync解析部分。根据lock源码发现,开始判断是否是第一次获取锁,如果获取锁成功,就把当前线程设置为锁的占有者,否则调用父类的acquire(1)方法(下一篇介绍同步器会介绍)。

    3. FairSync 公平锁


     static final class FairSync extends Sync {
            private static final long serialVersionUID = -3000897897090466540L;
            /**调用父类的acquire()方法*/
            final void lock() {
                acquire(1);
            }
    
            /**
             * 尝试获取锁
             */
            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()判断,其实该方法就是判断是否有比当前线程等待最长时间的线程,如果没有,那么就尝试获取锁,获取成功后设置当前线程为锁的占有者,所以,公平与不公平就是是否按照时间等待来获取锁的,比如食堂吃饭,排队一个个来,这就是公平,如果有人插队,这就是不公平。

    3. ReentrantLock 其他方法


     public ReentrantLock() {
            sync = new NonfairSync();
        }
    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);
        }
        //指定超时时间内获取锁,阻塞时间为timeout
     public boolean tryLock(long timeout, TimeUnit unit)
                throws InterruptedException {
            return sync.tryAcquireNanos(1, unit.toNanos(timeout));
        }
     public Condition newCondition() {
            return sync.newCondition();
        }
    public int getHoldCount() {
            return sync.getHoldCount();
        }
      public boolean isHeldByCurrentThread() {
            return sync.isHeldExclusively();
        }
     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);
        }
    
    
    • 其实根据上面源码发现,不管是实现LOCK接口的方法,还是后续新增的方法,其实现功能都依托于一个对象,那就是sync,在介绍sync时候已经说过,它就是继承了AbstractQueuedSynchronizer同步器,很多方法都是直接调用父类同步器的方法,下一篇《java锁之ReentrantLock(二)》会重点解析AbstractQueuedSynchronizer同步器源码,分析同步器是如何依托于FIFO队列完成锁的机制。

    三、 总结

    • ReentrantLock实现了LOCK接口
    • ReentrantLock可以实现公平锁和非公平锁获取
    • ReentrantLock可以进行超时时间内获取锁
    • ReentrantLock可以进行条件等待和唤醒
    • ReentrantLock可以获取锁响应中断

    相关文章

      网友评论

          本文标题:Java锁之ReentrantLock(一)

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