一睬愤、前言
本文來講解遇到的一個有意思的與定時器相關(guān)的生產(chǎn)消費模型,模型如下圖:
生產(chǎn)者是一個定時器線程界阁,使用ScheduledThreadPoolExecutor的scheduleAtFixedRate控制每間隔3s投遞一個元素到隊列1沦泌,2,3(使用offer方法)拌夏。比如第1s放入一個元素到隊列1,2盆顾,3诬像,這時候每個隊列里面有一個元素屋群。然后第4s在放入一個元素到隊列1,2坏挠,3芍躏,如果沒有消費線程的時候這時候每個隊列里面有2個元素.
隊列為有界阻塞隊列(ArrayBlockingQueue),隊列元素的大小設(shè)置為6個元素降狠。
消費者是一個定時器線程对竣,使用ScheduledThreadPoolExecutor的scheduleAtFixedRate控制每間隔1s輪詢?nèi)〕雒總€隊列里面全部元素(使用poll方法)。比如第1s取出隊列1里面的全部元素榜配,第2s取出隊列2里面的元素否纬,第3s取出隊列3里面的元素,第4s取出隊列1里面的元素....
并且如果發(fā)現(xiàn)當(dāng)前隊列為空則會結(jié)束當(dāng)前s的任務(wù)蛋褥,然后等下1s到了的時候從下一個隊列開始取临燃。消費線程啟動后,生產(chǎn)線程才啟動烙心,生產(chǎn)者和消費者線程優(yōu)先級相同
消費線程和生產(chǎn)線程里面沒有耗時操作膜廊,或者耗時都不超過1s。這個保證定時器任務(wù)不會被延遲執(zhí)行淫茵。
問題:每個隊列里面最多時候會有幾個元素爪瓜?
二、分析
- (1)假設(shè)第1s時候消費線程去獲取第一個隊列元素痘昌,這時候第一個隊列為空钥勋,則當(dāng)前任務(wù)結(jié)束,消費線程等到第2s時候會去第二個隊列取元素辆苔。
- (2)假設(shè)第1.000000000001s時候生產(chǎn)者線程放入元素到每個隊列算灸,這時候每個隊列有一個元素
- (3)第2s時候消費者線程去獲取第二個隊列元素,里面有一個元素驻啤,取出后菲驴,隊列為空
- (4)第3s時候消費者線程去獲取第三個隊列元素,里面有一個元素骑冗,取出后赊瞬,隊列為空
- (5)第4s時候消費者線程去獲取第一個隊列元素,里面有一個元素贼涩,取出后巧涧,隊列為空。
- (6)第4.000000000001s時候生成者線程放入元素到每個隊列遥倦,這時候每個隊列元素為1個谤绳。
按照上面的邏輯看的話,每個隊列里面最多有一個元素。其實不然缩筛,因為在多線程模型中每個線程占用cpu執(zhí)行的時間是按照時間片來劃分的消略,每個線程執(zhí)行完自己的時間片后會被掛起,然后下一個獲取到時間片的線程會占用CPU執(zhí)行自己的任務(wù)瞎抛,當(dāng)下一輪被掛起的線程獲取到自己的時間片后艺演,會恢復(fù)執(zhí)行上下文從之前被掛起的地方執(zhí)行。
所以這里步驟(6)并不能保證比步驟(5)先執(zhí)行桐臊,有可能消費線程在執(zhí)行步驟(5)前時間片用完了胎撤,則這時候消費線程會被掛起,而如果現(xiàn)在生產(chǎn)者線程獲取到了cpu并且到達了定時執(zhí)行任務(wù)的時間點,則步驟(6)會執(zhí)行豪硅,那么這時候隊列一哩照,里面會有2個元素挺物,那么等消費線程獲取CPU時間片執(zhí)行時候會從隊列1里面拿到2個元素懒浮。
注:這里使用1.000000000001s是為了說明和1s比較接近,其實由于影響調(diào)度因素很多识藤,有可能有比這更接近1s的時間
三砚著、總結(jié)
多線程下會遇到很多微妙的情況,有時候遇到的問題要結(jié)合OS的知識才能解釋清楚痴昧,本節(jié)從其中一個角度分析了每個隊列里面有可能會有兩個元素的原因稽穆,歡迎大家補充其他原因,并考慮會不會存在一個隊列里面最多時候有3個元素的情況