场景:多个线程,顺序打印出1-30的数据;

    1. public class Test {
    2. public static void main(String[] args) {
    3. Data data = new Data();
    4. new Thread(() -> {
    5. for (int i = 0; i < 10; i++) {
    6. data.printA();
    7. }
    8. }, "A").start();
    9. new Thread(() -> {
    10. for (int i = 0; i < 10; i++) {
    11. data.printB();
    12. }
    13. }, "B").start();
    14. new Thread(() -> {
    15. for (int i = 0; i < 10; i++) {
    16. data.printC();
    17. }
    18. }, "C").start();
    19. }
    20. }
    21. class Data{
    22. Lock lock = new ReentrantLock();
    23. Condition condition1 = lock.newCondition();
    24. Condition condition2 = lock.newCondition();
    25. Condition condition3 = lock.newCondition();
    26. private int num = 1;
    27. private int size = 0;
    28. public void printA() {
    29. lock.lock();
    30. try {
    31. while (num != 1) {
    32. condition1.await();
    33. }
    34. System.out.println(Thread.currentThread().getName() + ":" + size ++);
    35. num = 2;
    36. condition2.signal();
    37. } catch (InterruptedException e) {
    38. e.printStackTrace();
    39. } finally {
    40. lock.unlock();
    41. }
    42. }
    43. public void printB() {
    44. lock.lock();
    45. try {
    46. while (num != 2) {
    47. condition2.await();
    48. }
    49. System.out.println(Thread.currentThread().getName() + ":" + size ++);
    50. num = 3;
    51. condition3.signal();
    52. } catch (InterruptedException e) {
    53. e.printStackTrace();
    54. } finally {
    55. lock.unlock();
    56. }
    57. }
    58. public void printC() {
    59. lock.lock();
    60. try {
    61. while (num != 3) {
    62. condition3.await();
    63. }
    64. System.out.println(Thread.currentThread().getName() + ":" + size ++);
    65. num = 1;
    66. condition1.signal();
    67. } catch (InterruptedException e) {
    68. e.printStackTrace();
    69. } finally {
    70. lock.unlock();
    71. }
    72. }
    73. }

    执行结果:

    1. A:1
    2. B:2
    3. C:3
    4. A:4
    5. B:5
    6. C:6
    7. A:7
    8. B:8
    9. C:9
    10. A:10
    11. B:11
    12. C:12
    13. A:13
    14. B:14
    15. C:15
    16. A:16
    17. B:17
    18. C:18
    19. A:19
    20. B:20
    21. C:21
    22. A:22
    23. B:23
    24. C:24
    25. A:25
    26. B:26
    27. C:27
    28. A:28
    29. B:29
    30. C:30

    image.png

    1. // 唤醒等待了最久的线程
    2. // 其实就是,将这个线程对应的 node 从条件队列转移到阻塞队列
    3. public final void signal() {
    4. // 调用 signal 方法的线程必须持有当前的独占锁
    5. if (!isHeldExclusively())
    6. throw new IllegalMonitorStateException();
    7. Node first = firstWaiter;
    8. if (first != null)
    9. doSignal(first);
    10. }
    11. // 从条件队列队头往后遍历,找出第一个需要转移的 node
    12. // 因为前面我们说过,有些线程会取消排队,但是可能还在队列中
    13. private void doSignal(Node first) {
    14. do {
    15. // 将 firstWaiter 指向 first 节点后面的第一个,因为 first 节点马上要离开了
    16. // 如果将 first 移除后,后面没有节点在等待了,那么需要将 lastWaiter 置为 null
    17. if ( (firstWaiter = first.nextWaiter) == null)
    18. lastWaiter = null;
    19. // 因为 first 马上要被移到阻塞队列了,和条件队列的链接关系在这里断掉
    20. first.nextWaiter = null;
    21. } while (!transferForSignal(first) &&
    22. (first = firstWaiter) != null);
    23. // 这里 while 循环,如果 first 转移不成功,那么选择 first 后面的第一个节点进行转移,依此类推
    24. }
    25. // 将节点从条件队列转移到阻塞队列
    26. // true 代表成功转移
    27. // false 代表在 signal 之前,节点已经取消了
    28. final boolean transferForSignal(Node node) {
    29. // CAS 如果失败,说明此 node 的 waitStatus 已不是 Node.CONDITION,说明节点已经取消,
    30. // 既然已经取消,也就不需要转移了,方法返回,转移后面一个节点
    31. // 否则,将 waitStatus 置为 0
    32. if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
    33. return false;
    34. // enq(node): 自旋进入阻塞队列的队尾
    35. // 注意,这里的返回值 p 是 node 在阻塞队列的前驱节点
    36. Node p = enq(node);
    37. int ws = p.waitStatus;
    38. // ws > 0 说明 node 在阻塞队列中的前驱节点取消了等待锁,直接唤醒 node 对应的线程。唤醒之后会怎么样,后面再解释
    39. // 如果 ws <= 0, 那么 compareAndSetWaitStatus 将会被调用,上篇介绍的时候说过,节点入队后,需要把前驱节点的状态设为 Node.SIGNAL(-1)
    40. if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
    41. // 如果前驱节点取消或者 CAS 失败,会进到这里唤醒线程,之后的操作看下一节
    42. LockSupport.unpark(node.thread);
    43. return true;
    44. }

    https://javadoop.com/post/AbstractQueuedSynchronizer-2