Java源码解析阻塞队列ArrayBlockingQueue介绍

所属分类: 软件编程 / java 阅读数: 29
收藏 0 赞 0 分享

Java的阻塞队列,在实现时,使用到了lock和condition,下面是对其主要方法的介绍。

首先看一下,阻塞队列中使用到的锁。

/** Main lock guarding all access **/
  final ReentrantLock lock;​
  /** Condition for waiting takes **/
  private final Condition notEmpty;​
  /** Condition for waiting puts **/
  private final Condition notFull;

主要的锁是一个可重入锁,根据注释,它是用来保证所有访问的同步。此外,还有2个condition,notEmpty用于take等待,notFull用于put等待。

两个condition的初始化方法如下:

public ArrayBlockingQueue(int capacity, boolean fair) {
    if (capacity <= 0)
      throw new IllegalArgumentException();
    this.items = new Object[capacity];
    lock = new ReentrantLock(fair);
    notEmpty = lock.newCondition();
    notFull = lock.newCondition();
  }

下面介绍一下put方法。代码如下。

public void put(E e) throws InterruptedException {
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
      while (count == items.length)
        notFull.await();
      enqueue(e);
    } finally {
      lock.unlock();
    }
  }

进行put时,首先对待插入的元素进行了非null判断。然后获取锁。之后用一个循环进行判断,如果元素已满,那么,就调用notFull的await方法,进行阻塞。当有别的线程(其实是take元素的线程)调用notFull的siginal方法后,put线程会被唤醒。唤醒后再确认一下count是否小于items.length,如果是,则进行加入队列的操作。

下面介绍一下take方法,代码如下:

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
      while (count == 0)
        notEmpty.await();
      return dequeue();
    } finally {
      lock.unlock();
    }
  }

进行take时,同样先要获取锁,然后判断元素个数是否为0,为0时需要等待在notEmpty条件上,等待被唤醒。唤醒之后,会再进行一次元素个数判断,然后进行出队列操作。

分析代码到这里的时候,我产生了一个疑问,如果当前队列慢了,执行put的线程在获取到锁之后,等待notFull条件上。那么,当执行take操作的线程想获取锁时,阻塞队列的锁已经被前面put的线程获取了,那么take将永远得不到机会执行。怎么回事呢?

后来,我查了condition的await方法,它的注释如下:

  • Causes the current thread to wait until it is signalled or interrupted.
  • The lock associated with this Condition is atomically released and the current thread becomes disabled for thread scheduling purposes and lies dormant until one of four things happens......

原因在await方法的作用上。因为condition是通过lock创建的,而调用condition的await方法时,会自动释放和condition关联的锁。所以说,当put线程被阻塞后,它实际已经释放了锁了。所以,当有take线程想执行时,它是可以获取到锁的。

另一个问题:当等待在condition上的线程被唤醒时,因为之前调用await前,已经获取了锁,那么被唤醒时,它是自动就拥有了锁,还是需要重新获取呢?

在await方法的注释中,有如下的一段话:

  • In all cases, before this method can return the current thread must re-acquire the lock associated with this condition. When the thread returns it is guaranteed to hold this lock.

说明当等待在condition上的线程被唤醒时,它需要重新获取condition关联的锁,获取到之后,await方法才会返回。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对脚本之家的支持。如果你想了解更多相关内容请查看下面相关链接

更多精彩内容其他人还在看

Spring Boot 配置 IDEA和DevTools 热部署的方法

这篇文章主要介绍了Spring Boot 配置 IDEA和DevTools 热部署的方法,需要的朋友可以参考下
收藏 0 赞 0 分享

SpringBoot使用Redis缓存的实现方法

这篇文章主要介绍了SpringBoot使用Redis缓存的实现方法,需要的朋友可以参考下
收藏 0 赞 0 分享

SpringBoot中自定义参数绑定步骤详解

这篇文章主要介绍了SpringBoot中自定义参数绑定步骤详解,非常不错,具有参考借鉴价值 ,需要的朋友可以参考下
收藏 0 赞 0 分享

Java实现abc字符串排列组合

这篇文章主要为大家详细介绍了JAVA实现abc字符串的排列组合,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

Java中后台线程实例解析

这篇文章主要介绍了Java中后台线程实例解析,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

ehcache模糊批量移除缓存的方法

本篇文章主要介绍了ehcache模糊批量移除缓存的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

Java多线程join方法实例代码

这篇文章主要介绍了Java多线程join方法实例代码,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

java实现字符串排列组合问题

这篇文章主要为大家详细介绍了java实现字符串排列组合问题,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

Java排列组合字符串的方法

这篇文章主要介绍了Java排列组合字符串的方法
收藏 0 赞 0 分享

Java语言中的自定义类加载器实例解析

这篇文章主要介绍了Java语言中的自定义类加载器实例解析,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享
查看更多