Juc并发编程07——公平锁真的公平吗(源码剖析)

先来回顾下公平锁的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()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

重点关注注释代码处,如果hasQueuedPredecessors出现误判会怎么样呢?公平锁是不是就不公平了呀。那我们来研究下hasQueuedPredecessors,是不是真有百密一疏的情况。

假如线程1已经持有锁了。这个该时候线程2来获取锁,走到hasQueuedPredecessors()返回false,接着往后面执行CAS,肯定执行失败,因为现在锁被线程1占有,返回aquire方法。

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

接着走就是走到addWaiterenq方法。线程2进入等待队列。

    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // 此时等head、tail为空,满足`t==null`的条件。
                if (compareAndSetHead(new Node())) //这里没有其它线程争抢,成功
                    tail = head; // 设置成功
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

假设线程2走到注释截至处,现在有一个线程3来抢锁。当它判断等待队列时hasQueuedPredecessors会返回false,因为h==t

    public final boolean hasQueuedPredecessors() {
        Node t = tail; 
        Node h = head;
        Node s;
        return h != t && //不满足条件
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

那么问题来了,实际上等待队列中应该有线程2,现在我们却得出了等待队列为空的判断呀,线程3不的直接走CAS吗,万一线程1刚好释放锁了,线程3就插队了阿。由此可见,公平锁并不公平。

     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;
        }
    }

用一张图来总结下上面的过程。

在这里插入图片描述
公平锁公不公平关键就在与hasQueuedPredecessors是否会出现误判。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半旧518

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值