当前位置:   article > 正文

AQS之await和signal源码解析_aqs wait signal源码

aqs wait signal源码

上篇的文章中我们介绍了AQS源码中lock方法和unlock方法,这两个方法主要是用来解决并发中互斥的问题,这篇文章我们主要介绍AQS中用来解决线程同步问题的await方法、signal方法和signalAll方法,这几个方法主要对应的是synchronized中的wait方法、notify方法和notifAll方法。在介绍着这几个方法之前,我们先来看看这个几个方法是怎么使用的。

  • 我们实现一个阻塞的队列。

    1. import java.util.ArrayList;
    2. import java.util.Arrays;
    3. import java.util.List;
    4. import java.util.concurrent.locks.Condition;
    5. import java.util.concurrent.locks.Lock;
    6. import java.util.concurrent.locks.ReentrantLock;
    7. public class MyBlockedQueue<T> {
    8. final Lock lock = new ReentrantLock();
    9. // 条件变量:队列不满
    10. final Condition notFull = lock.newCondition();
    11. // 条件变量:队列不空
    12. final Condition notEmpty = lock.newCondition();
    13. private volatile List<T> list = new ArrayList<>();
    14. // 入队
    15. void enq(T x) {
    16. lock.lock();
    17. try {
    18. while (list.size() == 10) {
    19. // 等待队列不满
    20. try {
    21. notFull.await();
    22. } catch (InterruptedException e) {
    23. e.printStackTrace();
    24. }
    25. }
    26. // 省略入队操作
    27. list.add(x);
    28. // 入队后, 通知可出队
    29. notEmpty.signal();
    30. } finally {
    31. lock.unlock();
    32. }
    33. }
    34. // 出队
    35. void deq() {
    36. lock.lock();
    37. try {
    38. while (list.isEmpty()) {
    39. // 等待队列不空
    40. try {
    41. notEmpty.await();
    42. } catch (InterruptedException e) {
    43. e.printStackTrace();
    44. }
    45. }
    46. list.remove(0);
    47. // 出队后,通知可入队
    48. notFull.signal();
    49. } finally {
    50. lock.unlock();
    51. }
    52. }
    53. public List<T> getList() {
    54. return list;
    55. }
    56. public static void main(String[] args) throws InterruptedException {
    57. MyBlockedQueue<Integer> myBlockedQueue = new MyBlockedQueue<>();
    58. Thread thread1 = new Thread(() -> {
    59. for (int i = 0; i < 20; i++) {
    60. myBlockedQueue.enq(i);
    61. }
    62. });
    63. Thread thread2 = new Thread(() -> {
    64. for (int i = 0; i < 10; i++) {
    65. myBlockedQueue.deq();
    66. }
    67. });
    68. thread1.start();
    69. thread2.start();
    70. try {
    71. Thread.sleep(3000);
    72. } catch (InterruptedException e) {
    73. e.printStackTrace();
    74. }
    75. System.out.println(Arrays.toString(myBlockedQueue.getList().toArray()));
    76. }
    77. }
  • 运行的结果如下:
    在这里插入图片描述

    我们可以看到condition在多线程的中使用,类似于实现了线程之前的通信,当一个条件满足的时候,执行某个线程中操作,当某个条件不满足的时候,将当前的线程挂起,等待这个条件满足的时候,其他的线程唤醒当前线程。

  • 我们再来看看await的源码,具体如下图:

在这里插入图片描述

大致的流程就是:如果某个线程的调用了await的方法,走来会将这个线程通过CAS和尾插法的方式将这个等待的线程添加到AQS的等待的队列中去。然后将当前的线程进行解锁,避免这个线程没有释放的锁的时候,然后就被挂起。导致其他的线程获取不到锁,亦或者导致死锁的情况。然后将当前的线程进行park,最后等待其他的线程调用signal方法将当前的线程unpark。

  • 我们再来看看signal方法和signalAll方法的源码
    在这里插入图片描述

在这里插入图片描述
大致的流程就是:某个线程调用signal方法或者signalAll方法,signal方法会将当前的等待队列中第一个等待的线程的节点加入到原来的AQS队列中去,而signalAll方法是将等待队列中的所有的等待线程的节点全部加入到原来的AQS的队列中去,然后让他们重新获取锁,进行unpark。然后线程被唤醒,执行对应的线程中代码。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/149097
推荐阅读
  

闽ICP备14008679号