ThreadPoolExecutor BlockingQueue講解
- 2022 年 10 月 31 日
- 筆記
- JAVA, ThreadPoolExecutor
有四種常用阻塞隊列策略:
1.直接拒絕:(Direct Handoffs)
一個好的工作隊列應該是不快取任務,而是直接交給執行緒處理,就如SynchronousQueue一樣。一個任務將會入隊失敗,如果沒有執行緒執行它,也就是說每次都會創建一個新執行緒。這樣做有什麼好處呢?
當有一批內部有相互依賴的任務需要要執行時,不會因為需要長時間等待其它任務而被鎖住。一般都會將maximumPoolSizes設置為沒有限制,避免新創建的任務被拒絕。但有一個缺點是:當新任務提交的速度比被執行緒消費的速度快時,會造成無限制的執行緒增長,導致系統load過高,甚至OOM。
2.無界隊列(Unbounded Queue):
如果使用沒有界限的隊列(如LinkedBlockingQueue),則當新任務到來時,發現執行緒池中的執行緒數達到corePoolSize大小時,很不幸,他就會被加入隊列,等待執行緒池中有執行緒執行完任務來讀取。也就意味著,執行緒池中的執行緒數不會超過corePoolSize。當任務之間相互獨立時,適合使用無界隊列,例如,一個web伺服器,使用無界隊列可以緩和瞬間激增的請求對伺服器的壓力。但是當任務提交的速度比處理速度快時,會導致無界對列不斷增漲。
3. 有界隊列(Bounded Queue)
如果使用有界隊列,例如: ArrayBlockingQueue, 則當新任務到來時,發現執行緒池中的執行緒數達到corePoolSize大小時,也會被加入隊列,但當隊列滿時,會創建新的執行緒去執行任務,直到達到maxPoolSize。如果達到maxPoolSize,仍有任務到來,則會調用拒絕策略進行拒絕操作。當任務沒有很高的及時性要求,也不想佔用伺服器過多CPU資源時, 可以考慮快取一部分任務,並設置執行緒數的最高值。
4. 優先順序隊列(Priority Queue)
顧名思意,優先順序隊列,適合於具有優先順序的任務。優先順序隊列也是一種有界隊列,但與有界隊列不同的時,有界隊列在一開始就界定了大小,而優先順序隊列可以設置一個初始大小,當空間不夠時,會自動擴容,直到(Integer.MAX_VALUE – 8)。例如: 轉賬任務,優先給VIP客戶轉賬;
為什麼最大是Integer.MAX_VALUE – 8?
我們看下JDK中的描述:
Some VMs reserve some header words in an array.
Attempts to allocate larger arrays may result in
OutOfMemoryError: Requested array size exceeds VM limit
意思是有一些JVM虛擬機會在數組中保留Header, 如果分配更大的長度,會超成OOM。這段注釋只說明了為什麼要減去8,因為Header資訊佔8個位元組,那為什麼是Integer.MAX_VALUE,因為數組的長度類型是非負的int類型, 這也是JVM規範規定的。比如String類型,它的底層是使用字元數組存儲,所以String佔用的最大記憶體空間是(Integer.MAX_VALUE – 8)*一個字元占的空間。看一些文章說一個字元佔2個位元組,其實是不準確的,因為不同的編碼格式,字元對應的編碼結果是不一樣的,佔用的記憶體空間當然也不一樣的。比如我們常用的UTF-8編碼,一個漢字可能佔用2,3,4個位元組,長度並不是固定的。