ReentrantLock中的公平锁可能并不是真正意义上的公平

一、情景

假设当前有三个线程A、B、C,分别取调用公平锁的

lock.lock();

假设线程A一马当先,先获取到锁,此时state == 1
然后线程B,也来到了tryAcquire方法

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()) {
     	//重入锁的代码
         ...
     }
     return false;
}

公平锁与非公平锁的区别就是在tryAcquire中会判断是否有先驱节点,也就是方法hasQueuedPredecessors

public final boolean hasQueuedPredecessors() {
	//其实这个赋值顺序也是很有讲究的,倒过来有可能会导致空指针
    Node t = tail; 
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

此时tailheadnull,所以肯定这个方法返回false
线程B回到tryAcquire中执行cas_state方法,由于A还没有释放锁,所以肯定获取不到,最终返回false,需要加入同步队列。
addWaiter中,由于tail == null 直接进入enq方法。

 private Node enq(final Node node) {
     for (;;) {
         Node t = tail;
         if (t == null) { 
         	//初始化
             if (compareAndSetHead(new Node()))......①
                 tail = head;........................} else {
             node.prev = t;
             if (compareAndSetTail(t, node)) {
                 t.next = node;
                 return t;
             }
         }
     }
}

①和②便是重点。

Scenario 1

当线程B执行到①,此时head有值,但是tail还是为null
此时线程C也执行到hasQueuedPredecessors

Node t = null;
Node h = new Node();
此时 h != t && ((s = h.next) == null)true

因此线程C不能插队,也要加入等待队列。

Scenario 2

当线程B执行到②,此时head有值,且head == tail
此时线程C也执行到hasQueuedPredecessors

Node t ==h
此时 h != t 为false 短路直接返回

因此线程C可以插队,去执行cas_state方法
假设在执行cas方法之前,线程A已经释放了锁,那么线程C就可以插队,先于B抢到锁。

tail 和 head 赋值小tips

如果head先于tail赋值

public final boolean hasQueuedPredecessors() {
    Node h = head; //如果此时head还没有初始化,获得的是null,赋值完后失去时间片
    Node t = tail; //此时head完成初始化,且tail != null
    Node s;
    return h != t && // h != t 成立 没有短路
    	//h == null 因此h.next会产生NPE
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

总结

ReentrantLock中的公平锁只有在等待队列中存在等待节点(不包括虚节点)的时候,才是真正意义上的公平锁。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值