Java并发学习笔记:ReentrantLock

接着查看Sync这个类,发现是ReetrantLock的内部类,继承了大名鼎鼎的AbstractQueuedSynchronizer(即AQS)类。跟锁有关的主要操作都是在这个类中实现。下面看一些具体的操作。

锁对象的创建


使用ReentrantLock,首先得用ReentrantLock lock = new ReentrantLock();语句来创建一个锁的对象。看一下源码里ReentrantLock的构造函数。

public ReentrantLock() {

sync = new NonfairSync();

}

// other code

public ReentrantLock(boolean fair) {

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

}

复制代码

可见,构造函数创建了Sync的对象sync(NonfairSync和FairSync都是Sync的子类,分别代表非公平锁和公平锁)。可以看出,默认的无参情况下,创建的是非公平锁;带参情况下,fair传true时,会创建公平锁。

获取锁


public void lock() {

sync.lock();

}

public boolean tryLock() {

return sync.tryLock();

}

复制代码

锁的获取主要是这两个函数,当然还有 lockInterruptibly( ) 和 tryLock(long timeout, TimeUnit unit) 这种响应中断和带时间限制的函数,不过和普通的lock( )和tryLock( )机理大致相同,就不介绍了。先以非公平锁的lock流程为例:

// 位于Sync中

final void lock() {

if (!initialTryLock())

acquire(1);

}

// 位于NonfairSync中

final boolean initialTryLock() {

Thread current = Thread.currentThread();

if (compareAndSetState(0, 1)) { // first attempt is unguarded

setExclusiveOwnerThread(current);

return true;

} else if (getExclusiveOwnerThread() == current) {

int c = getState() + 1;

if (c < 0) // overflow

throw new Error(“Maximum lock count exceeded”);

setState©;

return true;

} else

return false;

}

// 位于AQS中

public final void acquire(int arg) {

if (!tryAcquire(arg))

acquire(null, arg, false, false, false, 0L);

}

// 位于NonfairSync中的

protected final boolean tryAcquire(int acquires) {

if (getState() == 0 && compareAndSetState(0, acquires)) {

setExclusiveOwnerThread(Thread.currentThread());

return true;

}

return false;

}

复制代码

可见,一个完整的最长的调用关系为:sync.lock( )->sync.initialTryLock( )->acquire(1)->tryAcquire(1)->acquire(many args)。

详细流程为:

先调用initialTryLock( ),方法首先尝试用CAS方式将state从0设置为1(state是AQS类的一个变量,用来说明锁是否被获取,自然被Sync类继承了),成功了就将此锁的拥有线程设置为此线程;否则,查看此线程是否已经拥有此锁,若是,则设置state的重入次数,由此可见,ReentrantLock是可重入锁,一个线程可多次获取。否则,返回false。

接着调用AQS类的acquire( ),首先会调用tryAcquire( )函数,这个函数是由NonfairSync重写的。在这里会再检查一下此锁是否被释放,若是,直接获取它,否则,返回false。

如果以上尝试都返回false了,说明这个锁一时半会确实获取不到,就调用AQS类的带许多参数的acquire( )函数,这个函数的作用是把这个线程放入这个锁的阻塞队列里。是AQS的内容,这里就不介绍了。

上面介绍了非公平锁的获取锁的流程。公平锁和非公平锁各自实现了initiTryLock( )和tryAcquire( )方法。对于公平锁来说,相较于非公平锁,获取锁的其它代码全都一样,只是在设置一个线程获取锁时,会多一个!hasQueuedThreads()判断,这是AQS里的方法,作用是判断有没有线程在此线程前面被阻塞。这就体现了所谓的公平和非公平:非公平锁不检查有没有线程在它前面,只要发现锁可以获取就直接获取,所以不公平(你先等的,结果它时机凑巧,来的晚还把锁抢走了),公平锁就比较‘公平’了。

再看tryLock():

final boolean tryLock() {

Thread current = Thread.currentThread();

int c = getState();

if (c == 0) {

if (compareAndSetState(0, 1)) {

setExclusiveOwnerThread(current);

return true;

}

} else if (getExclusiveOwnerThread() == current) {

if (++c < 0) // overflow

throw new Error(“Maximum lock count exceeded”);

setState©;

return true;

}

return false;

}

复制代码

这个方法在Sync类里实现,所以不管是不是公平锁,可以获取就直接获取锁。代码和initialTyeLock( )类似,就不解释了。同时也可以看出,tryLock( )并不会把线程加入到阻塞队列里,获取失败就直接返回false了。

锁的释放


位于Sync中

public void unlock() {

sync.release(1);

}

// 位于AQS中

public final boolean release(int arg) {

if (tryRelease(arg)) {

signalNext(head);

return true;

}

return false;

}

// 位于Sync中

protected final boolean tryRelease(int releases) {

int c = getState() - releases;

if (getExclusiveOwnerThread() != Thread.currentThread())

throw new IllegalMonitorStateException();

boolean free = (c == 0);

if (free)

setExclusiveOwnerThread(null);

setState©;

return free;

}

复制代码

释放锁时,首先调用AQS中的release(1)函数,接着调用Sync重写的tryRelease( )函数,在这里将锁的重入数减一,如果state变为了0,说明此锁被释放了,进行释放锁的操作setExclusiveOwnerThread(null);。如果锁被释放了,会接着调用AQS的signalNext( )函数功能。通知其它线程此锁可获得。

值得一提的是,这里的tryRelease( )函数和上述的tryAcquire( )函数不一样,后者由公平锁和非公平锁各自实现,而tryRelease( )则在Sync类里实现,公平锁和非公平锁共用。这是因为释放锁不用考虑是否公平,直接释放了就可以了。

创建条件对象


ReentrantLock另一个常用方法就是创建条件对象,在源码里也很简单。

public Condition newCondition() {

return sync.newCondition();

}

// 位于AQS

final ConditionObject newCondition() {

return new ConditionObject();

}

复制代码

newCondition( )方法和ConditionObject的主要操作都定义在AQS类里,这里不做介绍了。

image.png

一个ReentrantLock对象对应的阻塞和条件队列示意图如上。相关的AQS操作之后有空我会写一下。

锁的状态state


在上面获取和释放锁的代码里,经常会出现getState( )这个方法的身影。这个方法实际上是返回了state变量。state是AQS类的一个变量,在ReentrantLock里,用来标识锁的重入次数以及是否被持有。下面是关于它的一些方法:

private volatile int state;

protected final int getState() {

return state;

}

protected final void setState(int newState) {

state = newState;

}

protected final boolean compareAndSetState(int expect, int update) {

return U.compareAndSetInt(this, STATE, expect, update);

}

复制代码

这些方法均位于AQS类里。首先,state是一个volatile类型的变量,保证每个线程读到的state值都是最近一个线程更新的值。get和set方法比较简单,不再解释。最后有一个compareAndSetState( )方法:顾名思义,用CAS方式更新state的值。这里使用Unsafe类(类似于一个指针,直接操作对应地址的数据)进行更新:

p

【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】

浏览器打开:qq.cn.hn/FTf 免费领取

rivate static final Unsafe U = Unsafe.getUnsafe();

// 获取属性偏移量

private static final long STATE

= U.objectFieldOffset(AbstractQueuedSynchronizer.class, “state”);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值