Java的可重入锁(ReentrantLock)

可重入锁(ReentrantLock)

介绍ReentrantLock类就需要讲到AQS(AbstractQueuedSynchronizer,抽象队列同步器)了,因为ReentrantLock内部加锁操作的实现是调用的内部类Sync及其子类NonfairSync和FairSync的lock()方法,而Sync是直接继承的AbstractQueuedSynchronizer类。

以下是ReentrantLock的lock()方法的源码和内部类Sync的继承关系:

public void lock() {
  sync.lock();
}

//ReentrantLock的属性
private final Sync sync;
//内部类Sync的继承关系
abstract static class Sync extends AbstractQueuedSynchronizer 

AQS(AbstractQueuedSynchronizer)

AQS全称AbstractQueuedSynchronizer,抽象队列同步器。java并发包下很多API都是基于AQS来实现的加锁和释放锁等功能的,如ReentrantLock和ReentrantReadWriteLock,AQS是java并发包的基础类。

ReentrantLock和AQS之间的关系:

在这里插入图片描述

ReentrantLock内部包含了一个继承自AQS的内部类对象,这个对象就是sync,是ReentrantLock可以实现加锁和释放锁的关键性的核心组件。

AQS类内部的属性:

/**
  * Head of the wait queue, lazily initialized.  Except for
  * initialization, it is modified only via method setHead.  Note:
  * If head exists, its waitStatus is guaranteed not to be
  * CANCELLED.
*/ 
private transient volatile Node head;

/**
  * Tail of the wait queue, lazily initialized.  Modified only via
  * method enq to add new wait node.
*/
private transient volatile Node tail;
 /**
   * The synchronization state.
   *如果为0代表没有加锁,大于0则代表对当前占有锁的线程加锁的次数
 */
private volatile int state;

此外还有一个很重要的属性,是继承自AbstractOwnableSynchronizer类的,是Thread类的exclusiveOwnerThread,这个是记录当前占着锁的线程。这个AbstractOwnableSynchronizer类主要就是exclusiveOwnerThread属性和它的set方法和get方法。

AQS类继承关系:

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable
  
  /**
  * The current owner of exclusive mode synchronization.
  * 记录当前占着锁的线程
  */
  private transient Thread exclusiveOwnerThread;

AQS类里面有一个Node内部类,主要是为了存储等待加锁的线程,这个是结点的实现。此外在AQS类里面保存了这个双向链表的头结点和尾结点。

ReentrantLock加锁和释放锁的底层原理

默认是非公平锁,非公平锁的吞吐量大,但是可能会导致线程饿死,也就是存在如果多个线程同时抢锁,可能会出现其中一个线程一直占着锁,但是其他线程一直抢不到锁的情况。

非公平锁

ReentrantLock中非公平锁是通过内部类NonfairSync实现的。

ReentrantLock的无参构造:

public ReentrantLock() {
  sync = new NonfairSync();
}

说明默认的是非公平锁。

非公平锁的加锁过程

现在如果有一个线程过来尝试用ReentrantLock的lock()方法进行加锁,会调用sync的lock()方法

1.ReentrantLock的lock()方法:

public void lock() {
  sync.lock();
}

2.执行sync的lock()方法:

final void lock() {
    if (compareAndSetState(0, 1))
    		setExclusiveOwnerThread(Thread.currentThread());
    else
    		acquire(1);
}

3.首先方法执行CAS操作将state的值从0变为1,如果执行成功,则设置占有锁的线程为当前线程,加锁成功。

CAS方法:

protected final boolean compareAndSetState(int expect, int update) {
  // See below for intrinsics setup to support this
  return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

在这里插入图片描述

4.如果CAS没有成功,代表当前的state不为0,则有两种可能,第一种是已经被其他线程占有了,第二种是被当前线程占有,执行acquire()方法,acquire()方法在Sync和NonfairSync中都没有重写,因此执行的还是AQS中的方法过程。

public final void acquire(int arg) {
  if (!tryAcquire(arg) &&
      acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
    selfInterrupt();
}

5.首先执行tryAcquire()方法,这个方法是尝试加锁,如果加锁成功则短路不执行后续方法,如果加锁失败则将调用acquireQueued()方法用CAS将当前线程加入等待队列也就是AQS中的双向链表中,不断尝试加锁。

NonfairSync类中的tryAcquire()方法:

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

Sync类中的nonfairTryAcquire()方法:

    /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         */
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;
}

首先获取当前的线程,然后获取state的值,如果这时state的值为0(可能在从第一次CAS到此处的判断时间里,其他线程释放锁了),则再用CAS将state的值从0改为1,然后设置占有锁的线程为当前线程然后返回true,加锁成功。如果不是0则判断占有锁的线程是否为当前线程,如果是则将state的值加一,不过在这之前会判断是否超出了int类型的最大值,然后加锁成功,返回true。如果当前锁被占用且不是当前线程占用的则返回false,然后执行acquireQueued()方法,参数为addWaiter(Node.EXCLUSIVE)和1。

addWaiter()方法:将当前线程加入等待队列中

 private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
 }

acquireQueued()方法:等待队列中的线程不断的尝试加锁,加锁成功则退出死循环

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
 }

在这里插入图片描述

非公平锁的释放锁的过程

调用ReentrantLock的unlock()方法

1.ReentrantLock的unlock()方法:

public void unlock() {
  sync.release(1);
}

2.执行sync的release()方法:

public final boolean release(int arg) {
    if (tryRelease(arg)) {
      Node h = head;
      if (h != null && h.waitStatus != 0)
        unparkSuccessor(h);
      return true;
    }
    return false;
}

3.首先执行tryRelease()方法,参数为1,如果释放成功则返回true

Sync中的tryRelease()方法:

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;
}

首先定义一个局部变量存一下当前的state释放一次锁即减1后的值,然后判断当前的线程是否是占有锁的线程,如果不是则抛出异常,如果是且释放一次锁后的值是0则更改当前占有锁的线程为null,并且更改state的值然后返回true,释放成功。如果不为0则代表锁依然由当前线程持有,只是释放了一次而已。

总结:释放锁的过程就是不断的将AQS内的state变量的值递减1,如果state值为0,则彻底释放锁,会将exclusiveOwnerThread变量也设置为null。

公平锁

ReentrantLock中公平锁是通过内部类FairSync实现的。

ReentrantLock的有参构造:如果为true则为公平锁,false为非公平锁

public ReentrantLock(boolean fair) {
  sync = fair ? new FairSync() : new NonfairSync();
}
公平锁的加锁过程

1.ReentrantLock的lock()方法:

public void lock() {
  	sync.lock();
}

2.FairSync中的lock()方法

final void lock() {
  	acquire(1);
}

3.AQS中的acquire()方法

public final void acquire(int arg) {
  if (!tryAcquire(arg) &&
      acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
    selfInterrupt();
}

4.FairSync中的tryAcquire()方法

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;
}

首先获取当前线程,然后获取当前的state的值,如果state为0的话判断是否轮到当前线程获取锁,如果队列中有线程在当前线程之前则hasQueuedPredecessors()方法返回为true,如果队列是空的或者当前线程在队列的头部则返回为false,如果返回为false则用CAS将state的值由0改为1,并且设置占有锁的线程为当前线程,加锁成功,返回true。如果为true则代表还未轮到当前线程。如果不为0且占有锁的线程为当前线程的话,则state加一,如果超出int类型的最大值则抛出错误,加锁成功,返回true。否则加锁失败,并将当前线程加入等待队列中,然后不断的去请求加锁。

公平锁的释放锁的过程

与非公平锁一致。

线程间通信

private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();

public static void main(){
  	//线程1启动
  	lock.lock();
    try {
        //改变条件的语句
      
      	
        condition.signalAll();
    }catch (InterruptedException e) {
      	e.printStackTrace();
    } finally {
        lock.unlock();
    }
  	
  	//线程2启动
    lock.lock();
    try{
        while(//条件不满足时等待){
            condition.await();
        }
        //等改变条件后满足当前的条件可执行以下业务
          
    }catch (InterruptedException e) {
      	e.printStackTrace();
    } finally {
        lock.unlock();
    }

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值