java的Lock实例化_【认真学java—锁】可重入锁ReentrantLock

学习记录,有不对的或更好见解,还请多多交流

参考

需了解的知识

synchronized

不明白,可以看以下文章,慢慢看,沉下心

看完以上文章,就不用看本文了😀

ReenentrantLock概述

ReentrantLock 是对synchronized的扩展。突出的特点就是可重入,更灵活。可重入是指什么?那反过来问不可重入是什么?

不可重入顾名思义就是不可以重复进入,具体指的是在没有释放锁的情况下,不可以再获取锁,否则就会造成死锁dead lock。(很多名词都是英文翻译过来的,所以英文很重要,不然获取的知识就是二手的)

错误例子如下:public void test(Object object){

synchronized(object){ // first lock

synchronized(object){ // second lock

//do something

}

}

}

上面的second lock 永远也获取不到锁,所以造成了死锁,程序就卡死在第二次加锁。

主要原理:ReentrantLock类的内部有个Sync类继承了AbstractQueuedSynchronizer(AQS封装了多线程下队列的同步操作)对锁操作进行了封装。NonfairSync和FairSync继承了Sync类,并重载了各自的tryAcquire方法。通过不同的构造器ReentrantLock(),ReentrantLock(boolean fair)来实例化不同的Sync对象,以创建公平可重入锁 / 不公平可重入锁对象,后续的所有操作都是基于这个lock对象进行操作。

而对于ReentrantLock来说,它提供了在锁没释放时,可以再次获取锁的方法 tryLock。先来看看ReentrantLock中重要的方法及含义:lock 和synchronized类似,仅当没有其他线程持有该对象的锁时,才能加锁成功。在其他线程持有锁期间,该线程会进去睡眠状态,(dormant),直到获取到锁。区别在于:若自己对自己再次加锁,是可以获得锁的。(可重入)(注:synchronized对自己加锁两次是死锁)

构造器中的参数fairness,设为true时,锁会优先让等待最长时间的线程持有;若为false(默认为false),则不保证线程持有锁的先后顺序。

注意:fairness为true,会严重影响整体性能。而且fairness并不保证线程的调度的公平(不保证每个线程平均获取锁,只是让排队最长时间先的获取而已)

tryLock方法不受fairness设置影响。即使有其他线程获取了锁,锁还是可以被继续获取的,最大支持递归 2147483647次(int的最大整数)。(可重入)

注tryLock()在没获得锁时,不会像lock()一样进入睡眠等待;tryLock(long timeout, TimeUnit unit)会睡眠等待timeout时长,同时会fairness规则unlock释放自己获得的锁,使锁的次数-1。

源码:

1、Sync内部类nonfairTryAcquire方法final boolean nonfairTryAcquire(int acquires) {

final Thread current = Thread.currentThread();

int c = getState();

if (c == 0) { //初始态,没有线程持有锁时

if (compareAndSetState(0, acquires)) { //见下文2

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;

}

2、AQS中的compareAndSetState(int expect, int update)方法protected final boolean compareAndSetState(int expect, int update) {

return STATE.compareAndSet(this, expect, update); //见下文3

}

3、本地方法(C++编写的)compareAndSet位于java.lang.invoke.VarHandle下(常说的CAS)

官网api链接:CAS如果witness value == exceptedValue,则赋新值newVaule。其中witness value 为main memory中该变量的值(getVolatile获取);赋新值通过setVolatile赋值。

return true,赋值成功。

return false,变量被其他线程改变了,赋值失败。

4、NonfairSync类的tryAcquire就是调用Sync中nonfairTryAcquire(acquires);

5、FairSync中的tryAcquire/**

* Fair version of tryAcquire. Don't grant access unless

* recursive call or no waiters or is first.

* 翻译:公平锁,只有在重复获取锁 / 没有其他等得更久的线程在等待获取锁 / 第一个获取锁时,

* 才能获取到锁。

*/

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;

}

6、hasQueuedPredecessors(AQS中)/*

* Queries whether any threads have been waiting to acquire longer than the current thread.

* 翻译:查看是否有其他想获取锁的线程比当前线程等的长(公平锁的原则:先服务等得久的)

*/

源码略,因为需要先看懂AQS这个类的实现机制

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值