一,前言
本文来讲解遇到的一个有意思的与定时器相关的生产消费模型,模型如下图:
image.png
生产者是一个定时器线程,使用 ScheduledThreadPoolExecutor 的 scheduleAtFixedRate 控制每间隔 3s 投递一个元素到队列 1,2,3(使用 offer 方法).比如第 1s 放入一个元素到队列 1,2,3,这时候每个队列里面有一个元素.然后第 4s 在放入一个元素到队列 1,2,3,如果没有消费线程的时候这时候每个队列里面有 2 个元素.
队列为有界阻塞队列(ArrayBlockingQueue),队列元素的大小设置为 6 个元素.
消费者是一个定时器线程,使用 ScheduledThreadPoolExecutor 的 scheduleAtFixedRate 控制每间隔 1s 轮询取出每个队列里面全部元素(使用 poll 方法).比如第 1s 取出队列 1 里面的全部元素,第 2s 取出队列 2 里面的元素,第 3s 取出队列 3 里面的元素,第 4s 取出队列 1 里面的元素....
并且如果发现当前队列为空则会结束当前 s 的任务,然后等下 1s 到了的时候从下一个队列开始取.
消费线程启动后,生产线程才启动,生产者和消费者线程优先级相同
消费线程和生产线程里面没有耗时操作,或者耗时都不超过 1s.这个保证定时器任务不会被延迟执行.
问题:每个队列里面最多时候会有几个元素?
二,分析
(1)假设第 1s 时候消费线程去获取第一个队列元素,这时候第一个队列为空,则当前任务结束,消费线程等到第 2s 时候会去第二个队列取元素.
(2)假设第 1.000000000001s 时候生产者线程放入元素到每个队列,这时候每个队列有一个元素
(3)第 2s 时候消费者线程去获取第二个队列元素,里面有一个元素,取出后,队列为空
(4)第 3s 时候消费者线程去获取第三个队列元素,里面有一个元素,取出后,队列为空
(5)第 4s 时候消费者线程去获取第一个队列元素,里面有一个元素,取出后,队列为空.
(6)第 4.000000000001s 时候生成者线程放入元素到每个队列,这时候每个队列元素为 1 个.
按照上面的逻辑看的话,每个队列里面最多有一个元素.其实不然,因为在多线程模型中每个线程占用 cpu 执行的时间是按照时间片来划分的,每个线程执行完自己的时间片后会被挂起,然后下一个获取到时间片的线程会占用 CPU 执行自己的任务,当下一轮被挂起的线程获取到自己的时间片后,会恢复执行上下文从之前被挂起的地方执行.
所以这里步骤(6)并不能保证比步骤(5)先执行,有可能消费线程在执行步骤(5)前时间片用完了,则这时候消费线程会被挂起, 而如果现在生产者线程获取到了 cpu 并且到达了定时执行任务的时间点,则步骤(6)会执行,那么这时候队列一,里面会有 2 个元素,那么等消费线程获取 CPU 时间片执行时候会从队列 1 里面拿到 2 个元素.
注:这里使用 1.000000000001s 是为了说明和 1s 比较接近,其实由于影响调度因素很多,有可能有比这更接近 1s 的时间
三,总结
多线程下会遇到很多微妙的情况,有时候遇到的问题要结合 OS 的知识才能解释清楚,本节从其中一个角度分析了每个队列里面有可能会有两个元素的原因,欢迎大家补充其他原因,并考虑会不会存在一个队列里面最多时候有 3 个元素的情况
来源: http://www.jianshu.com/p/6402676abc86