我的jdk源码(二十三):ReentrantLock类

凉白开 2020-06-17

一、概述

    ReentrantLock类是在内部利用自己的内部类Sync继承了AbstractQueuedSynchronizer,实现了tryAcquire()方法,在这个方法中增强自己的功能,比如添加了重入和公平锁、非公平锁。ReentrantLock再将Sync作进一步的封装,开放出去Lock的接口。

    ReentrantLock是通过在请求锁的时候判断CLH列表有没有比当前线程等待时间更久的线程来实现公平性的。当一个线程请求公平锁的时候,如果state为0,还需要在CHL中没有等待节点,或CHL首位就是当前线程,那么就可以使用CAS请求锁。否则请求直接失败。非公平锁没有这个限制,只要state为0,就可以使用CAS操作请求锁。非公平锁的吞吐量要比公平锁要高,因为公平锁就是完全要入队然后依次执行,在入队后线程会被park,到当前线程后需要unpark。线程之前的切换,资源准备等都是比较耗时的。非公平锁在在任务请求获取锁的时候如果正好锁被释放,则可以不用入队省去了线程阻塞唤醒。

二、源码分析

    1. 类的声明

public class ReentrantLock implements Lock, java.io.Serializable

    ReentrantLock类实现了Lock和Serializable接口,表示其实例是可序列化的琐。

    2. 构造函数

// 构造一个 ReentrantLock 实例(非公平锁)	
public ReentrantLock() {	
    sync = new NonfairSync();	
}	
	
// 构造一个 ReentrantLock 实例(指定是否公平)	
public ReentrantLock(boolean fair) {	
    sync = fair ? new FairSync() : new NonfairSync();	
}

    3. 成员变量

//序列化标识id
    private static final long serialVersionUID = 7373984872572414699L;

    //同步器实例,可以是公平琐,也可以是非公平琐
    private final Sync sync;

    4. 内部类-Sync类

//Sync也是一个抽象类,因为锁有非公平和公平的区别。
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;
 
        //非公平和公平锁的lock()方法有不同的实现。
        abstract void lock();
        //tryLock()在子类中实现,该方法是非公平的独占式获取同步状态。       
        //该方法首先判断同步状态是否被获取,如果没有,CAS获取同步状态并将锁的拥有者设为当前线程,如果有,判断获取锁的线程是否是当前线程,如果是,将同步值进行累加。
        //成功获取锁的线程,再次获取锁,只是增加了同步值。
        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;
        }
        //获取了多少次锁,同样也要释放多少次锁。
        //当同步值不为0时,还是当前线程占有锁。
        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;
        }
       //是否被当前线程所占有
        protected final boolean isHeldExclusively() {
            return getExclusiveOwnerThread() == Thread.currentThread();
        }
 
        final ConditionObject newCondition() {
            return new ConditionObject();
        }
 
        //得到锁的占有者
        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }
        //获取锁的次数
        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }
     //锁是否被获取
        final boolean isLocked() {
            return getState() != 0;
        }
 
        //反序列化操作
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }

    ReentrantLock通过将继承AQS的子类sync作为类成员变量来实现锁,sync实现AQS的抽象方法来管理同步状态。锁的获取和释放是通过修改 AQS 的 state 变量来实现的。lock 方法可以看做对 state 执行“加法”操作,而 unlock 可以看做对 state 执行“减法”操作,当 state 为 0 时,表示当前没有线程占用资源。

    5. 内部类-NonfairSync类

//非公平锁的实现
static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;
 
    //非公平锁加锁,acquire调用tryAcquire,tryAcquire调用nonfairTryAcquire
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }
 
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

    非公平锁的lock()方法步骤为:先尝试以 CAS 方式修改 state 的值,若修改成功,则表示成功获取到锁,将 owner 设为当前线程;否则就执行 AQS 中的 acquire 方法。

    6. 内部类-FairSync类

//公平锁的实现
static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;
 
    final void lock() {
        acquire(1);
    }
 
    //lock()调用acquire(),acquire()调用tryAcquire(),公平锁和非公平锁的tryAcquire()的实现唯一不同      
    //就是加入了hasQueuesPredecessors(),通过判断当前节点是否有前驱节点,如果有则当前节点不是等待时间最长的线程
    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,即首先判断主队列中是否有其他线程在等待,当没有其他线程在排队时再去获取,否则获取失败。

    公平锁和非公平锁的主要在于两个地方:

    (1) 获取锁lock()方法的不同:非公平锁会直接进行一次CAS,如果CAS成功,就直接获取锁了。

    (2) tryAcquire()方法实现的不同:当同步状态此时被释放的时候,state等于0,并没有任何线程占有锁,如果是公平锁,会判断队列中是否有线程等待获取锁,如果有的话,直接将自己构造成一个节点加入同步队列,但是非公平锁会直接CAS设置状态,不考虑同步队列中是否有其他线程等待获取锁。如果获取同步状态失败,会将自己构造成一个节点加入同步队列中,加入同步队列之后,等待前驱节点释放同步状态,唤醒自己,这后面的步骤公平锁和非公平锁是一样的。

    7. 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);
  }
 
  //尝试非阻塞的获取锁,调用方法之后立马返回,能获取返回true,不能获取返回false
  public boolean tryLock() {
      return sync.nonfairTryAcquire(1);
  }
 
  //释放锁
  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();
  }

三、总结

    ReentrantLock类总的来说,就是利用内部类sync类的子类NonfairSync类和FairSync类来实现非公平琐与公平琐,sync类则是继承自AbstractQueuedSynchronizer类。

    ReentrantLock类有以下几点特点:

    1. 是一个可重入锁,能保证同一个线程多次加锁。

    2. 可以实现公平琐和非公平琐,初始化的时候可以指定类型。

    3. 默认是非公平琐,线程每次再加入同步队列之前,都会尝试获取琐。

    4. ReentrantLock类实现的琐可以看作是synchronized的升级版,可以手动获取琐和释放锁,并且可以绑定多个条件(基于AQS的Condition)。

    更多精彩内容,敬请扫描下方二维码,关注我的微信公众号【Java觉浅】,获取第一时间更新哦!

我的jdk源码(二十三):ReentrantLock类

推荐:http://www.1994july.club/?cat=518&paged=2

相关推荐

软件设计 / 0评论 2017-06-16