java学习-reentrantlock-fair和unfair
关于reentrantLock 中 fair 和 unfair体现
对于 reentrantLock 其核心实际是利用AbstractQueueSynchronizer (AQS);
对于reentrant就是通过判断当前线程是否相等作为可重入条件
AQS 实际就是一个双向链表组成的队列,对于队列中存在的节点实际就是等待获取锁的线程;
对于AQS一般是设置在有效时间内获取到锁,当获取锁失败的情况下才会被添加到队列中,对于队列中的等待者实际就是通过循环一直尝试获取锁,当在尝试获取锁失败的情况下且未达到获取锁等待时间时,会尝试将当前线程(这里主要是针对的当前执行循环的线程)挂起,当在释放锁的情况下,会通知当前被挂起的线程,重新执行获取锁的过程; 直到获取锁成功或等待获取锁超时或竞争锁的线程消亡,节点才会被移除掉;
对于AQS实际队列的形式满足先进先出(FIFO)的特点;
对于reentrantLock 中的 fair 和 unfair 其主要体现在 当同时存在 未保存到队列中的竞争者 和 队列中排队等候的竞争者 在上一个线程释放锁之后,其获取锁的顺序优先级
- 关于unfair,其核心就是在 当同时存在 未保存到队列中的竞争者 和 队列中排队等候的竞争者时,其并没有优先级的关系,属于公平竞争加锁过程,当使用cas加锁成功后,如果是不属于AQS中的加锁成功,则AQS中的节点会继续等待;如果是AQS中的head 加锁成功,则AQS中的head移除队列;而对于不存在与队列中的竞争者会被追加到链表的尾部作为新的tail节点
- 关于fair,其核心就是 在执行加锁操作时,首先判断队列中是否存在等待的节点,如果存在等待的节点则会终止尝试加锁过程,而是直接追加到队列中;反之,则直接尝试加锁;
public final boolean hasQueuedPredecessors() { // The correctness of this depends on head being initialized // before tail and on head.next being accurate if the current // thread is first in queue. Node t = tail; // Read fields in reverse initialization order Node h = head; Node s; return h != t && // 首先判断队列中是否存在数据,当 h!=t时说明队列中存在数据 // 当队列中存在数据时,判断当前head节点是否已出队,当s==null说明已出队 // 当s!=null时,需要判断s节点的线程是否是当前线程, //当其等于当前线程时,说明队列中下一个等待者就是当前线程 ((s = h.next) == null || s.thread != Thread.currentThread()); }
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; }
对于fair 中 hasQueuedPredecessors中出现的判断情况
- h !=t ,当结果为false时后续的判断都不需要执行,对于为false说明 h==t,代表队列为空,整体结果返回false;反之为true则表示队列中存在数据;
- (s=h.next) == null,当结果为true时表示当前head节点可能已出队,则后续的流程无需执行,整体结果返回true;当判断s节点不为空时,则继续执行后续的判断
- s.thread != Thread.currentThread(),判断当前head节点的后续节点是否为当前线程,如果为false说明是当前线程,则整体返回结果为false;如果为true,说明不是当前线程,则整体结果返回true;
因此对于fair 锁释放(state 为 0)情况下,非队列中的竞争者能直接进行尝试加锁的情况就只有以下两种情况
- 队列为空
- 当前请求(线程)存在于队列中,且就是下一个等待者节点
除以上两种情况外其余情况都会直接追加到AQS等待队列中
简单总结一下,对于fair主要是针对AQS队列中元素的公平