ReentrantLock(重入锁)以及公平性

转自 并发编程网http://ifeve.com/reentrantlock-and-fairness/


简介

ReentrantLock的实现不仅可以替代隐式的synchronized关键字,而且能够提供超过关键字本身的多种功能。
这里提到一个锁获取的公平性问题,如果在绝对时间上,先对锁进行获取的请求一定被先满足,那么这个锁是公平的,反之,是不公平的,也就是说等待时间最长的线程最有机会获取锁,也可以说锁的获取是有序的。ReentrantLock这个锁提供了一个构造函数,能够控制这个锁是否是公平的。
而锁的名字也是说明了这个锁具备了重复进入的可能,也就是说能够让当前线程多次的进行对锁的获取操作,这样的最大次数限制是Integer.MAX_VALUE,约21亿次左右。
事实上公平的锁机制往往没有非公平的效率高,因为公平的获取锁没有考虑到操作系统对线程的调度因素,这样造成JVM对于等待中的线程调度次序和操作系统对线程的调度之间的不匹配。对于锁的快速且重复的获取过程中,连续获取的概率是非常高的,而公平锁会压制这种情况,虽然公平性得以保障,但是响应比却下降了,但是并不是任何场景都是以TPS作为唯一指标的,因为公平锁能够减少“饥饿”发生的概率,等待越久的请求越是能够得到优先满足。

实现分析

在ReentrantLock中,对于公平和非公平的定义是通过对同步器AbstractQueuedSynchronizer的扩展加以实现的,也就是在tryAcquire的实现上做了语义的控制。

非公平的获取语义:

01 final boolean nonfairTryAcquire(int acquires) {
02     final Thread current = Thread.currentThread();
03     int c = getState();
04     if (c == 0) {
05         if (compareAndSetState(0, acquires)) {
06             setExclusiveOwnerThread(current);
07             return true;
08         }
09     else if (current == getExclusiveOwnerThread()) {
10         int nextc = c + acquires;
11                 if (nextc < 0// overflow
12             throw new Error("Maximum lock count exceeded");
13         setState(nextc);
14         return true;
15     }
16     return false;
17 }

上述逻辑主要包括:

  • 如果当前状态为初始状态,那么尝试设置状态;
  • 如果状态设置成功后就返回;
  • 如果状态被设置,且获取锁的线程又是当前线程的时候,进行状态的自增;
  • 如果未设置成功状态且当前线程不是获取锁的线程,那么返回失败。

公平的获取语义:

01 protected final boolean tryAcquire(int acquires) {
02     final Thread current = Thread.currentThread();
03     int c = getState();
04     if (c == 0) {
05         if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
06             setExclusiveOwnerThread(current);
07             return true;
08         }
09     else if (current == getExclusiveOwnerThread()) {
10         int nextc = c + acquires;
11         if (nextc < 0)
12             throw new Error("Maximum lock count exceeded");
13         setState(nextc);
14         return true;
15     }
16     return false;
17 }

上述逻辑相比较非公平的获取,仅加入了当前线程(Node)之前是否有前置节点在等待的判断。hasQueuedPredecessors()方法命名有些歧义,其实应该是currentThreadHasQueuedPredecessors()更为妥帖一些,也就是说当前面没有人排在该节点(Node)前面时候队且能够设置成功状态,才能够获取锁。

释放语义:

01 protected final boolean tryRelease(int releases) {
02     int c = getState() - releases;
03     if (Thread.currentThread() != getExclusiveOwnerThread())
04         throw new IllegalMonitorStateException();
05     boolean free = false;
06     if (c == 0) {
07         free = true;
08         setExclusiveOwnerThread(null);
09     }
10     setState(c);
11     return free;
12 }

上述逻辑主要主要计算了释放状态后的值,如果为0则完全释放,返回true,反之仅是设置状态,返回false。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值