博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ReentrantLock(重入锁)以及公平性
阅读量:5329 次
发布时间:2019-06-14

本文共 2626 字,大约阅读时间需要 8 分钟。

ReentrantLock(重入锁)以及公平性

标签(空格分隔): java NIO


如果在绝对时间上,先对锁进行获取的请求一定被先满足,那么这个锁是公平的,反之,是不公平的,也就是说等待时间最长的线程最有机会获取锁,也可以说锁的获取是有序的。ReentrantLock这个锁提供了一个构造函数,能够控制这个锁是否是公平的。

而锁的名字也是说明了这个锁具备了重复进入的可能,也就是说能够让当前线程多次的进行对锁的获取操作,这样的最大次数限制是Integer.MAX_VALUE,约21亿次左右。

具体实现

非公平:

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;    }    //状态不为0(锁被占用中),当前线程不是持有线程,返回失败    //即 锁正被别的线程占用,则请求失败    return false;}

公平:

protected final boolean tryAcquire(int acquires) {    final Thread current = Thread.currentThread();    int c = getState();    if (c == 0) {        //区别在于这里多了一项判断:是否是等待队列里的第一个        //效果是:如果锁被自己的线程持有着,仍然可以重入(即'else-if')。        //但如果锁时空闲着的,则先来先得(排在队首)        if (isFirst(current) && 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;}

进行lock操作时

/*ReentrantLock->NonfairSync */final void lock() {    //如果锁空闲,直接使用,无需排队;否则进行请求    if (compareAndSetState(0, 1))        setExclusiveOwnerThread(Thread.currentThread());    else        acquire(1);}/*AbstractQueuedSynchronizer */public final void acquire(int arg) {    //注意,A&B运算,如果A==False,则不会执行B    //因此,如果tryAcquire成功,则不执行后面的“在sync队列中排队”操作    if (!tryAcquire(arg) &&        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))        selfInterrupt();}

上述代码,即非公平的acquire,如下图所示

非公平的acquire

  • Fast:tryAcquire,非公平的获取
  • normal: 进入请求队列
    这意味着,某条线程释放完锁,也可以不经过等待队列,而通过Fast通道再次获得锁。

这种模式,可以保证进入和退出锁的吞吐量,但是sync队列中过早排队的线程会一直处于阻塞状态,造成“饥饿”场景。在非公平锁中,CPU的处理时间中,只有很少的时间花在线程调度上,大多都用在实际工作上。

***

非公平锁性能高于公平锁性能的原因

在恢复一个被挂起的线程与该线程真正运行之间存在着严重的延迟。

考虑如下场景:线程A释放了锁,队列中第一个线程B将从[挂起]状态向[唤醒]状态,并再次请求锁。在B未完成唤醒前,线程C也开始请求锁。

  • 公平锁:虽然锁现在是空闲的,但因为队列中还有线程B排在前面,因此C需进队列等待,状态变为[挂起]。从‘C到来’至‘B完全唤醒’之间的时间T,锁是空闲的,被浪费了。
  • 非公平锁:由于此时锁是空闲的,C直接获得锁。如果C工作执行的快,可能当释放锁之后,B尚未完全唤醒,则原本会被浪费的时间T被利用了。但如果B唤醒后C还未执行完,则B又要挂起继续等待。

因此,当持有锁的时间相对较长或者请求锁的平均时间间隔较长,应该使用公平锁。在这些情况下,插队带来的吞吐量提升(当锁处于可用状态时,线程却还处于被唤醒的过程中)可能不会出现。

转载于:https://www.cnblogs.com/EagleOfEble/p/4648990.html

你可能感兴趣的文章
web.xml 中加载顺序
查看>>
mysql学习之安装(一)
查看>>
[数据库]关于主键与外键
查看>>
pycharm激活地址
查看>>
hdu 1207 四柱汉诺塔
查看>>
Vue 2.x + Webpack 3.x + Nodejs 多页面项目框架(上篇——纯前端多页面)
查看>>
display:none与visible:hidden的区别
查看>>
我的PHP学习之路
查看>>
【题解】luogu p2340 奶牛会展
查看>>
wnmp安装配置的坑
查看>>
神奇的Scala Macro之旅(二)- 一个实例
查看>>
sicily 1128. DICE
查看>>
e.Row.Attributes.Add
查看>>
SCOPE_IDENTITY()和 SELECT @@IDENTITY 的用法
查看>>
PLoP(Pattern Languages of Programs,程序设计的模式语言)
查看>>
对PostgreSQL的 SPI_prepare 的理解。
查看>>
android"百码"2——基础小知识积累(逐步完善)2015-06-15
查看>>
解决响应式布局下兼容性的问题
查看>>
京东静态网页练习记录
查看>>
Filebeat Config 参数详解:
查看>>