DelayQueue 是 Java 并发包 java.util.concurrent 下的一个 Class,其官方定义如下所示。
/**
* An unbounded {@linkplain BlockingQueue blocking queue} of
* {@code Delayed} elements, in which an element can only be taken
* when its delay has expired. The <em>head</em> of the queue is that
* {@code Delayed} element whose delay expired furthest in the
* past. If no delay has expired there is no head and {@code poll}
* will return {@code null}. Expiration occurs when an element&#39;s
* {@code getDelay(TimeUnit.NANOSECONDS)} method returns a value less
* than or equal to zero. Even though unexpired elements cannot be
* removed using {@code take} or {@code poll}, they are otherwise
* treated as normal elements. For example, the {@code size} method
* returns the count of both expired and unexpired elements.
* This queue does not permit null elements.
* /
由定义可知,DelayQueue 是一个无界阻塞队列,队列中的元素只有在延迟期满后才能被取出。队列的头部存储的是最先到期的元素。添加进该队列的元素必须实现 Delayed 接口,指定延迟时间,元素过期的判断是根据 getDelay(TimeUnit unit) 方法的返回值,返回值小于等于 0,则认为元素过期。队列不允许存储空元素。
二、DelayQueue 的使用场景
饭店的员工一般都是分角色的,比如接待员、服务员、厨师等等。
假如有一个叫做 A 的人,固定他作为饭店接待员,来客人了就分给客人一个座位号,然后交给其他服务员,比如 B 进行后续处理。B 会根据座位号为客人引路,为客人点菜等等。如果把 A、B 比作两个线程,客人比作任务,任务由 A 处理,到交接给 B 处理,有一次线程上下文切换。
4.2 Leader-Follower 模式
这次饭店不分角色了,每个人都是接待员和服务员,统称为员工。
每次只能有一个员工在门口等待,比如 A 先在门口等待,其他员工在屋里歇着。来客人了的话,A 会叫一个其他员工,比如 B 来门口接替自己。然后 A 开始为客人服务,比如分配座位号,引路,点菜等全流程服务。拿线程来说的话,就是接受任务,处理任务都是由线程 A 负责,没有线程上下文切换。
4.3 DelayQueue 的 Leader-Follower 模式
这次饭店也不分角色,都是员工,但是改变了经营策略,每个客人必须预约吃饭时间,预约采用 APP 预约。因为加入了延时,逻辑变得复杂了一些。
每次还是只能有一个员工在门口等待,比如 A 先在门口等待,A 看了眼预约登记表,发现离预约最早到店的时间还有 30 分钟,A 就什么都不干了,先休息 30 分钟。
其他员工还是先在屋里歇着,但是因为采用 APP 预约,客人约几点都有可能,如果此时有客人约的是 10 分钟后到店,因为 A 要 30 分钟后才能醒来干活,所以如果这位客人来了,门口就没有人接待了。
对于这个问题,饭店的软件系统在监听到最早到店时间变了的话,会再叫一个员工来门口等待,此员工可能是新员工 B,也可能是叫醒了之前在门口休息的员工 A。我们叫这位新员工 X。 如果新员工 X 发现最早到店时间是现在,或者客人已经来了,就会叫一个员工 C 来门口接替自己,并立即开始为客人提供全流程服务。如果新员工 X 发现最早到店时间是 10 分钟后,新员工 X 就像 A 之前一样,什么都不干了,先休息 10 分钟。
如果最早到店时间没有变化,还是 30 分钟后,软件系统不会叫人,其他员工看到 A 在门口等待,自己可以安心的在屋里歇着,等着 A 叫人替换他。
员工 A 在 30 分钟后醒来,客人也到了,A 会叫一个同事(比如 B)接替自己,而 A 为客人提供全流程服务。
五、图解 DelayQueue 的生产/消费过程