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

Java
277
0
0
2022-12-05
标签   Java并发

先来回顾下公平锁的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;
  }
}

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

img

公平锁公不公平关键就在与hasQueuedPredecessors是否会出现误判。