IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java并发学习笔记:ReentrantLock -> 正文阅读

[Java知识库]Java并发学习笔记:ReentrantLock

锁的获取主要是这两个函数,当然还有 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类里实现,所以不管是不是公平锁,可以获取就直接获取锁。代码

【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】

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

和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类(类似于一个指针,直接操作对应地址的数据)进行更新:

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

// 获取属性偏移量

private static final long STATE

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

复制代码

锁的持有线程

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-10-30 12:25:55  更:2021-10-30 12:27:26 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 0:06:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码