java可重入锁ReentrantLock原理

来源:互联网 发布:淘宝如何代运营 编辑:程序博客网 时间:2024/06/01 07:48

ReentrantLock

JUC中ReentrantLock实现了独占模式重入锁,对于可重入,此类的注释是这样的:

当一个线程锁住ReentrantLock,但是没有解锁,这个线程再执行加锁方法会返回成功,并获得这把锁

ReentrantLock类图

这里写图片描述
由上图可知,ReentrantLock包含两个静态属性,一个是java 序列化Serializable使用的属性serialVersionUID,一个是sync,sync是内部类Sync的实例,ReentrantLock中还包含两个另外两个子类FairSync,NonfairSync。它们都继承自Sync类。FairSync是公平锁的实现方式,NonfairSync是非公平锁的实现方式。他们两都实现了可重入。

ReentrantLock初始化

ReentrantLock有两个不同的初始化方法

public ReentrantLock() {       //默认方式:设置sync属性为非公平模式锁NonfairSync实例       sync = new NonfairSync();}public ReentrantLock(boolean fair) {   //根据用户传入是否公平参数,设置sync属性为非公平模式锁NonfairSync或者公平模式锁FairSync实例   sync = fair ? new FairSync() : new NonfairSync();}

ReentrantLock加锁解锁

ReentrantLock解锁解锁执行的都是sync的方法

//加锁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);}

Sync加锁解锁

Sync的加锁方法是一个abstract 方法,交由子类实现

abstract void lock();

公平重入锁

其中公平重入锁方式直接调用aqs的acquire,然后自身实现了aqs acquire方法会调用的tryAcquire方法(aqs的流程详见上一篇aqs的介绍)。其中tryAcquire获取锁首先会判断当前state是否为0。
如果不为0则代表锁已被锁住,如果被锁住,则查看占用的线程是否为当前线程。如果是当前线程占用了这个锁,则可重入,进行state增加。state可以表示持有者重入次数。
如果为0,则判断当前线程代表的节点在队列中有没有在排队的前任,如果没有在排队的前任则尝试获取锁。因为新加入获取锁队列的节点都是加在队列的尾部。而只有队列中没有前任在排队获取锁的节点才能尝试获取锁,维持了一个先进先出的规则。所有线程都是根据进入顺序去持有锁的,是公平的方式。

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;         }     }     //如果持有锁的为自己,则重入state+=acquires     else if (current == getExclusiveOwnerThread()) {         int nextc = c + acquires;         if (nextc < 0)             throw new Error("Maximum lock count exceeded");         setState(nextc);         return true;     }     return false; }

非公平重入锁

非公平重入锁NonfairSync 在调用lock方法时会快速cas尝试修改state状态0->1,如果成功则设置锁持有者为自身,不去管是否有其他线程在等待。是非公平的方式。当cas不成功时,则调用acquire,并且NonfairSync 自身也和FairSync类一样,实现了acquire会调用的tryAcquire方法。tryAcquire方法使用的是父类Sync的非公平尝试获取锁方法nonfairTryAcquire

protected final boolean tryAcquire(int acquires) {    return nonfairTryAcquire(acquires);}

nonfairTryAcquire方法与FairSync类的tryAcquire不同点在于这个方法实现的不公平方式不会判断是否有前任在排队。直接进行尝试获取锁。会造成插队情况,是不公平获取锁的形式,但是比公平方式少了一些判断,性能更优。
nonfairTryAcquire的重入方式与公平锁FairSync的一样。

final boolean nonfairTryAcquire(int acquires) {  final Thread current = Thread.currentThread();  int c = getState();  if (c == 0) {      //如果状态为0,当前锁未锁住,尝试cas修改状态      if (compareAndSetState(0, acquires)) {          //设置当前线程为锁的独占线程          setExclusiveOwnerThread(current);          return true;      }  }  //如果占据锁的线程是当前线程,state+=acquires  else if (current == getExclusiveOwnerThread()) {      int nextc = c + acquires;      if (nextc < 0) // overflow          throw new Error("Maximum lock count exceeded");      setState(nextc);      return true;  }

ReentrantLock unlock

ReentrantLock unlock调用的Sync的父类AQS的release方法释放锁。而Sync实现了release方法会调用的tryRelease。tryRelease会使锁状态state减少,调用一次则state减少1,代表重入次数减少一次。当减少到为0时,表示当前线程没有持有该锁,将锁的持有者置为空。

 protected final boolean tryRelease(int releases) {            //state-=releases            int c = getState() - releases;            if (Thread.currentThread() != getExclusiveOwnerThread())                throw new IllegalMonitorStateException();            boolean free = false;            if (c == 0) {                free = true;                //如果state为0,设置锁的所有者为空                setExclusiveOwnerThread(null);            }            setState(c);            return free;        }

总结

ReentrantLock的重入为当持有锁的为当前线程时,重入,将锁状态state增加。
ReentrantLock的公平非公平模式,交由Sync的两个子类控制,区别在于公平模式只有没有前任在排队的情况下才可以获取锁。公平模式不进行这个判断。直接尝试获取锁,可插队。

1 0
原创粉丝点击