写锁之基于AQS加锁

首先,这个lock的流程都是一样的。
image.png
唯有不同的就是tryAcquire的实现是不同的。
image.png

  1. protected final boolean tryAcquire(int acquires) {
  2. /*
  3. * Walkthrough:
  4. * 1. If read count nonzero or write count nonzero
  5. * and owner is a different thread, fail.
  6. * 2. If count would saturate, fail. (This can only
  7. * happen if count is already nonzero.)
  8. * 3. Otherwise, this thread is eligible for lock if
  9. * it is either a reentrant acquire or
  10. * queue policy allows it. If so, update state
  11. * and set owner.
  12. */
  13. Thread current = Thread.currentThread();
  14. int c = getState();
  15. int w = exclusiveCount(c);
  16. if (c != 0) {
  17. // (Note: if c != 0 and w == 0 then shared count != 0)
  18. if (w == 0 || current != getExclusiveOwnerThread())
  19. return false;
  20. if (w + exclusiveCount(acquires) > MAX_COUNT)
  21. throw new Error("Maximum lock count exceeded");
  22. // Reentrant acquire
  23. setState(c + acquires);
  24. return true;
  25. }
  26. if (writerShouldBlock() ||
  27. !compareAndSetState(c, c + acquires))
  28. return false;
  29. setExclusiveOwnerThread(current);
  30. return true;
  31. }
  1. int c = getState();获取state,目前是0
  2. int w = exclusiveCount(c);,state二进制里面的高低16位分别代表了读锁和写锁,高十六位代表读锁,第十六位代表写锁。该w就获取了state的低16位,代表了写锁的加锁次数
    1. image.png
  3. if (c != 0) ;由于此时state=0,这个时候这个判断是false不进入这个代码块中
  4. writerShouldBlock() ||!compareAndSetState(c, c + acquires),非公平锁现在一定尝试去加锁(因为是返回false)。如果是公平锁就会判断下队列有没有等待线程,有等待线程第一个方法就返回true随后直接return false了,如果没有等待线程是会返回false的,因此就要去执行cas操作,只有当cas设置不成功了才会直接return false。
    1. writerShouldBlock():非公平锁,此时返回fasle,公平锁判断如果队列里面有等待的线程那么就返回true
      1. image.png
  5. setExclusiveOwnerThread(current);当第4不cas成功了,那么就可以设置thread标记位了。

    写锁之可重入加锁

    1. protected final boolean tryAcquire(int acquires) {
    2. /*
    3. * Walkthrough:
    4. * 1. If read count nonzero or write count nonzero
    5. * and owner is a different thread, fail.
    6. * 2. If count would saturate, fail. (This can only
    7. * happen if count is already nonzero.)
    8. * 3. Otherwise, this thread is eligible for lock if
    9. * it is either a reentrant acquire or
    10. * queue policy allows it. If so, update state
    11. * and set owner.
    12. */
    13. Thread current = Thread.currentThread();
    14. int c = getState();
    15. int w = exclusiveCount(c);
    16. if (c != 0) {
    17. // (Note: if c != 0 and w == 0 then shared count != 0)
    18. //1
    19. if (w == 0 || current != getExclusiveOwnerThread())
    20. return false;
    21. //2
    22. if (w + exclusiveCount(acquires) > MAX_COUNT)
    23. throw new Error("Maximum lock count exceeded");
    24. // Reentrant acquire
    25. //3
    26. setState(c + acquires);
    27. return true;
    28. }
    29. if (writerShouldBlock() ||
    30. !compareAndSetState(c, c + acquires))
    31. return false;
    32. setExclusiveOwnerThread(current);
    33. return true;
    34. }

    此时由于是可重入加锁,就会进入if (c != 0) 的判断

  6. w == 0 || current != getExclusiveOwnerThread();因为c!=0,意味着有可能加了写锁或者读锁。

    1. 如果w是0,有人加了读锁,此时不能加写锁就要返回false。
    2. 如果w不是0,证明有人加了写锁,随后要判断一下当前线程是不是之前加锁的线程,如果不是就返回false,是的话继续往下走开始重入锁操作。
  7. if (w + exclusiveCount(acquires) > MAX_COUNT) ,判断加锁是否大于重入最大加锁数,大于的话就要抛异常了
  8. setState(c + acquires);, return true; 加锁成功设置state值并返回true

    写锁加锁失败如何入队阻塞

    现在有一个线程2来加锁,由于线程1加锁失败因此线程2要入队列。
    在代码层面上那个tryAcquire返回false,然后走acquireQueued(addWaiter(Node.EXCLUSIVE), arg),入队操作都是一样的。
    image.png