java concurrent lock_java并发编程之深入学习Concurrent包(四,可重入ReentrantLock锁)

引言:

上一章,我们学习了AQS同步队列的使用,了解了同步队列如何进行线程同步及线程唤醒,及同步队列的运行。这章,让我们一起来看下同步队列的时间应用--ReentrantLock(可重入锁)类

本章基于上一章AQS的内容实现,如果对AQS有不理解,请先阅读上一章内容

简介:

可重入锁,表示同一线程如果已经抢占到锁,那么可以直接重复进入被锁的代码而不需要做额外的操作。

ReentrantLock不仅是可重入锁,且支持公平锁与非公平锁两种方式,公平锁表示新加入的线程无视同步队列的存在而抢占锁,非公平锁则需要先进入同步队列等待,让同步队列中的线程先抢占锁。

实现分析:

1,同步状态值

ReentrantLock锁的实现,首先基于AQS中的同步状态值state,该值表示锁被重入的次数,如果锁未被抢占,state=0,重入一次则state+1。

/**

* The synchronization state.

*/

private volatile int state;

2,ReentrantLock的初始化实现

如下代码所示,默认ReentrantLock是实现非公平锁,只有传入true构造时,才能创建公平锁。

/**

*/

public ReentrantLock() {

sync = new NonfairSync();

}

/**

*/

public ReentrantLock(boolean fair) {

sync = fair ? new FairSync() : new NonfairSync();

}

3,AQS的虚拟子类的实现

如下图所示,ReentrantLock中先定义了一个虚拟子类,定义了非公平锁加锁及释放锁的方法。

3.1 非公平锁nonfairTryAcquire方法

步骤1,获取当前同步器同步状态c

步骤2(如果锁未被抢占,c=0),则直接抢占锁

步骤3(如果锁已被抢占但是当前线程所占),将c+抢占次数,并更新同步器同步状态。

步骤4,非以上两种情况,则直接返回失败。

3.2 释放锁tryRelease公用方法

步骤1,获取同步器同步状态c=当前状态值-释放锁次数

步骤2,如果c=0,表示锁完全释放,则返回true,否则返回false。

abstract static class Sync extends AbstractQueuedSynchronizer {

private static final long serialVersionUID = -5179523762034025860L;

abstract void lock();

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;

}

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;

}

}

如上代码所示,ReentrantLock类使用AQS类中的同步状态判断可重入锁的状态。

4,非公平锁的特殊实现

可以看出,非公平锁锁定时,直接使用CAS方法试图抢占锁,成功则更新为锁所有线程,失败调用 acquire 进入同步器处理,在同步器处理中再次调用tryAcquire尝试获取锁。后续详细步骤见上一章。

/**

* Sync object for non-fair locks

*/

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))

setExclusiveOwnerThread(Thread.currentThread());

else

acquire(1);

}

protected final boolean tryAcquire(int acquires) {

return nonfairTryAcquire(acquires);

}

}

5,公平锁的特殊实现

如下图所示,公平锁实现了自己的tryAcquire方法,与非公平锁的最主要区别在于公平锁当前线程只有同步队列为空,同步队列中当前线程在头节点时,才可以直接抢占锁,否则需要进入同步队列进行排队。

static final class FairSync extends Sync {

private static final long serialVersionUID = -3000897897090466540L;

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;

}

}

从上面代码可以了解,公平非公平锁,在于抢占锁时当前线程是否具有优待。

以上就是ReentrantLock类的主要内容,其实了解了AQS之后,这块内容就比较轻松了。

整个Concurrent包中的Lock已经差不多了,明天是最后一篇读写重入锁,后续开始学习Concurrent包中其他内容

Concurrent包

感兴趣的同学们请关注本人公众号:暖爸的java家园

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值