盡管對多線程使用難度有一些抱怨,但多線程仍然再被使用,自然是因為它有一些好處。其中一些好處是:
- 資源利率更好(Better resource utilization);
- 某些情況下,程序設(shè)計很簡單(Simpler program design in some situations);
- 響應(yīng)式的程序很適合(More responsive programs)。
Better resource utilization
設(shè)想一個從本地文件系統(tǒng)讀取和處理文件的應(yīng)用。假設(shè)從磁盤讀取af文件需要5秒,處理需要2秒。處理兩個文件需要:
5 秒讀文件A
2 秒處理文件A
5 秒讀文件B
2 秒處理文件B
-----------------------
14 秒一共
當(dāng)從文件系統(tǒng)讀取文件的時候,CPU的大部分時間都花在了等待磁盤讀取數(shù)據(jù)。CPU在此期間可以說是非??臻e。它可以干點其他什么事。通過改變操作的順序,CPU可以得到更好的利用。看看這個順序:
5 秒讀文件A
5 秒讀取文件B + 2 秒處理文件A
2 秒處理文件B
-----------------------
12 秒一共
CPU等待讀取第一個文件,然后開始讀取第二個文件。在讀取第二個文件的同時,CPU處理第一個文件。記住,在等待磁盤讀取文件的時候,CPU大部分時候都是空閑的。
通常,在等待IO的時候,CPU都可以干點其他事。不一定非要是磁盤IO。也可以是網(wǎng)絡(luò)IO,或者等待用戶輸入。網(wǎng)絡(luò)和磁盤IO通常比CPU和內(nèi)存IO慢得多。
Simpler Program Design
如果你要在單線程應(yīng)用程序手工編寫上述讀取和處理文件的程序,你必須跟蹤每個文件的讀取和處理狀態(tài)。相反,你可以啟動兩個線程,每個線程只負(fù)責(zé)讀取和處理單個文件。每個線程在讀取其對應(yīng)的文件的時候都會被阻塞。在等待的時候,其他線程就可以利用CPU處理已經(jīng)讀取了的文件。其結(jié)果是,磁盤一直忙碌,將各種文件讀入內(nèi)存。這使得磁盤和CPU都得到了更有效的利用。它也更容易編程,因為每個線程只需要跟蹤一個文件。
More responsive programs
將單線程應(yīng)用改為多線程應(yīng)用的另一個常見目標(biāo)是實現(xiàn)響應(yīng)更快的應(yīng)用。設(shè)想一個在某個端口監(jiān)聽接入請求的服務(wù)器應(yīng)用。當(dāng)一個請求到達(dá)后,它先處理這個請求,然后返回繼續(xù)監(jiān)聽。服務(wù)器的循環(huán)算法如下:
while (server is active) {
listen for request
process request
}
如果這個請求需要花很長世間處理,那么在此期間沒有新的請求可以到達(dá)服務(wù)器。只有在服務(wù)器監(jiān)聽時才能接收到請求。
另一種設(shè)計是監(jiān)聽線程將請求傳給工作線程處理,然后立即返回監(jiān)聽。工作現(xiàn)場會處理請求并給客戶端發(fā)送回復(fù)。本設(shè)計算法如下:
while (server is active) {
listen for request
hand request to worker thread
}
這樣的話服務(wù)器線程會很快恢復(fù)監(jiān)聽。因此,更多的客戶端可以向服務(wù)器發(fā)送請求。服務(wù)器的響應(yīng)速度提高了。
對桌面應(yīng)用也是一樣的。如果你點擊了一個按鈕,啟動了一個長時任務(wù),并且執(zhí)行這個任務(wù)的線程是更新窗口、按鈕等的線程,那么這個應(yīng)用在該任務(wù)執(zhí)行期間會變得無法響應(yīng)。相反,可以將任務(wù)交給工作線程。在工作現(xiàn)場忙于執(zhí)行該任務(wù)的時候,窗口線程可以自由的響應(yīng)用戶的其他請求。當(dāng)工作線程完成了任務(wù)后,它給窗口線程發(fā)個信號。然后,窗口線程可以將任務(wù)的執(zhí)行結(jié)果更新到應(yīng)用程序窗口上。帶有工作線程設(shè)計的程序?qū)τ脩舻捻憫?yīng)更靈敏。
有必要說明下,這個Java Concurrency系列來源于jenkov.com,本文只是翻譯,希望大家千萬不要誤會,本文不是原創(chuàng)。原文地址:Java Concurrency。