什么是公平锁?

加锁的时候会去判断等待队列中是否有线程在等待,有则进入排队(不一定睡眠),无则去获取锁

源码解析

  • FairSync 公平锁源码

    1. static final class FairSync extends Sync {
    2. private static final long serialVersionUID = -3000897897090466540L;
    3. //加锁
    4. final void lock() {
    5. acquire(1);
    6. }
    7. //尝试加锁
    8. protected final boolean tryAcquire(int acquires) {
    9. //获取当前线程
    10. final Thread current = Thread.currentThread();
    11. //获取当前锁状态
    12. int c = getState();
    13. //如果锁未被持有
    14. if (c == 0) {
    15. //hasQueuedPredecessors() 查询队列中是否有等待的线程
    16. //compareAndSetState() 尝试修改锁状态
    17. if (!hasQueuedPredecessors() &&
    18. compareAndSetState(0, acquires)) {
    19. //如果没有队列且修改锁状态成功,则将占用锁的线程设置为当前线程
    20. setExclusiveOwnerThread(current);
    21. return true;
    22. }
    23. }
    24. //判断是否重入锁(是否是同一个线程加锁两次)
    25. else if (current == getExclusiveOwnerThread()) {
    26. //锁状态值+1
    27. int nextc = c + acquires;
    28. //锁状态值小于0则抛出异常
    29. if (nextc < 0)
    30. throw new Error("Maximum lock count exceeded");
    31. //重新设置锁状态值
    32. setState(nextc);
    33. return true;
    34. }
    35. return false;
    36. }
    37. }

    lock()方法调用了acquire ()方法

  • acquire 获取锁方法

    1. public final void acquire(int arg) {
    2. //tryAcquire再次尝试加锁
    3. //addWaiter,将线程加入到队列中
    4. //acquireQueued,判断队列(链表)的上一个元素是否是头部,如果是自旋去获取锁,如果不是则进入park状态
    5. if (!tryAcquire(arg) &&
    6. acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
    7. selfInterrupt();
    8. }

    tryAcquire 在上面的FairSync 中,如果为tryAcquire 方法中加锁成功,返回true,那么! true = false,就不会走acquireQueued()方法。如果没加锁成功则先运行addWaiter()方法,再运行acquireQueued()方法得到整个if表达式的最终结果

  • addWaiter 将当前线程添加到队列中,返回node
  1. private Node addWaiter(Node mode) {
  2. //根据当前线程创建node节点,这里的node节点其实是双向队列
  3. Node node = new Node(Thread.currentThread(), mode);
  4. // 获取队尾
  5. Node pred = tail;
  6. if (pred != null) {
  7. //队尾不为空,快速尝试在队尾添加node
  8. //先将当前节点的上一个指向获取到的队尾元素
  9. node.prev = pred;
  10. //尝试将队尾设置为node
  11. if (compareAndSetTail(pred, node)) {
  12. //如果CAS成功,就说明没有别的线程设置队尾成功
  13. //只需要将之前的队尾的next指向当前node
  14. pred.next = node;
  15. return node;
  16. }
  17. }
  18. enq(node);
  19. return node;
  20. }