首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

简单总结阻塞队列源码

前面Java并发这块的锁、并发工具、原子类、线程池都已经学习完了,接下来是队列的学习。

ArrayBlockingQueue总结

先直接总结ArrayBlockingQueue相关的特性再根据源码来进行说明,它的主要特性如下:

1、他是一个由数组实现的FIFO有界阻塞队列,数组由final修饰

2、ArrayBlockingQueue有界且固定,在构造函数时必须指定大小,确认后不支持改变(确定数组且不可变)

3、在多线程环境下不保证“公平性”;

4、通过ReentrantLock与Condition实现线程安全;

重要属性介绍

主要属性如下图:

ArrayBlockingQueue的属性还是比较简单,首先是存放数据的Object数组,它是final修饰的,所以队列的长度不可变。

然后三个int属性分别表示下次获取数据时应该从数组的哪里获取,下次保存数据时应该保存到数组的哪里,count记录着还有多少个可以拿。

所有方法首先都必须获取到lock的锁,lock有公平与非公平锁,默认实现的是非公平锁,也可以在初始化ArrayBlockingQueue指定,所以默认ArrayBlockingQueue并不保证公平性。

notEmpty与notFull都是通过lock创建,都是在初始化ArrayBlockingQueue是初始化出来。

这里简单介绍了属性的作用,接下来会通过源码再来理解它的作用。

最关键的两个私有方法

首先要说两个私有方法,应该队列主要的方法最后都依赖这两个私有方法,直接看源码如下图:

enqueue方法用来把数据保存到数组items中,会递增putIndex,也就是下次应该保存的位置,如果putIndex等于了数组的长度,则下次为0。

最后会唤醒那些调用notEmpty.await()阻塞的线程,实际上只有take方法调用了。

dequeue方法是获取数据,获取的是takeIndex处的数据,在获取过后会把items[takeIndex]处设置为null,同样递增takeIndex后如果等于了数组的长度则会被置为0。

最后会唤醒那些调用notFull.await()阻塞的线程,只有put方法调用了。

所以主要的变化在于putIndex和takeIndex,这里总结了他们有如下4种关系:

如果不加保护他们不止这些关系,比如takeIndex比putIndex跑的快,那么就会获取到null值,获取putIndex比takeIndex多走一个轮回还多那么就会出现数据被覆盖,造成数据丢失。

只有保证它们是这几种关系才能保证数据的安全性,避免数据被覆盖或者获取到null值,并且实现了FIFO先进先出,而队列提供的公共方法都必须要保证这种安全

添加数据方法介绍

ArrayBlockingQueue提供了4个添加方法add、offer、offer(指定阻塞时间)、put;

add方法调用的是offer方法,而offer方法先获取到锁,再判断队列是否已满,已满直接返回false,没有满则调用enqueue保存数据。所以add与offer没有阻塞。

offer还有一个可以在队列已满情况下阻塞指定时间(timeout)在尝试保存的方法,在判断队列已满的情况会调用”nanos = notFull.awaitNanos(nanos);”阻塞线程,一定时间后如果还是满的则会返回false。

put获取到锁,如果数组已满则调用notFull.await一直阻塞等待唤醒,唤醒后再次验证是否已满,没满则调用enqueue,否则再次阻塞。所以向队列中加数据只有put方法才支持真正的阻塞,保证添加成功,其他方法会可能保存失败

获取数据方法介绍

获取数据提供了poll、poll(指定阻塞时间)、take、peek

poll方法先获取到锁,如果count==0则返回null,否则调用dequeue方法获取结果。

poll(指定阻塞时间)在判断count==0时会先阻塞执行时间,然后再次判断,如果还是等于0则返回null,如果不等于0则调用dequeue方法获取结果。

take方法先获取到锁然后在循环判断count是否等于0如果等于0则调用notEmpty.await()阻塞,否则调用dequeue获取结果,同样take方法也会保证一定能拿到数据,否则会一直阻塞

peek是偷看的意思,peek方法在获取到锁后直接获取item[takeIndex]的元素返回,然后不做任何事情,就好像偷偷看下下一次会获取哪一个元素,但是不影响队列。

总结

ArrayBlockingQueue还有一些其他次要的方法,就不再一一说明了,ArrayBlockingQueue通过两个索引来保存和拉取数据,并且实现了先进先出的特性。虽然数组的长度是固定有限的,但是通过循环使用数组,也能通过他同步无限的数据。

分析方法发现只有take方法和put方法才能保证真正获取或者保存数据成功,如果没有成功则会一直阻塞,其他方法则不一定。

Java程序员日常学习笔记,如理解有误欢迎各位交流讨论!

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200910A0OPNR00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券