1. 程式人生 > >Java多執行緒1:為什麼要使用多執行緒——學習方騰飛Java併發程式設計的藝術

Java多執行緒1:為什麼要使用多執行緒——學習方騰飛Java併發程式設計的藝術

為什麼要使用多執行緒

簡單來講,就是為了程式執行的更快

現在,多處理器系統正日益盛行,並且價格不斷降低,即時在低端伺服器和中斷桌面系統中,通常也會採用多個處理器,這種趨勢還在進一步加快,因為通過提高時鐘頻率來提升效能已變得越來越困難,處理器生產廠商都開始轉而在單個晶片上放置多個處理器核。試想,如果只有單個執行緒,雙核處理器系統上程式只能使用一半的CPU資源,擁有100個處理器的系統上將有99%的資源無法使用。多執行緒程式則可以同時在多個處理器上執行,如果設計正確,多執行緒程式可以通過提高處理器資源的利用率來提升系統吞吐率。

2、在單處理器系統上獲得更高的吞吐率

如果程式是單執行緒的,那麼當程式等待某個同步I/O操作完成時,處理器將處於空閒狀態。而在多執行緒程式中,如果一個執行緒在等待I/O操作完成,另一個執行緒可以繼續執行,使得程式能在I/O阻塞期間繼續執行。

3、建模的簡單性

通過使用執行緒,可以將複雜並且非同步的工作流進一步分解為一組簡單並且同步的工作流,每個工作流在一個單獨的執行緒中執行,並在特定的同步位置進行互動。我們可以通過一些現有框架來實現上述目標,例如Servlet和RMI,框架負責解決一些細節問題,例如請求管理、執行緒建立、負載平衡,並在正確的時候將請求分發給正確的應用程式元件。編寫Servlet的開發人員不需要了解多少請求在同一時刻要被處理,也不需要了解套接字的輸入流或輸出流是否被阻塞,當呼叫Servlet的service方法來響應Web請求時,可以以同步的方式來處理這個請求,就好像它是一個單執行緒程式。

4、非同步事件的簡化處理

伺服器應用程式在接受多個來自遠端客戶端的套接字連線請求時,如果為每個連線都分配其各自的執行緒並且使用同步I/O,那麼就會降低這類程式的開發難度。如果某個應用程式對套接字執行讀操作而此時還沒有資料到來,那麼這個讀操作將一直阻塞,直到有資料到達。在單執行緒應用程式中,這不僅意味著在處理請求的過程中將停頓,而且還意味著在這個執行緒被阻塞期間,對所有請求的處理都將停頓。為了避免這個問題,單執行緒伺服器應用程式必須使用非阻塞I/O,但是這種I/O的複雜性要遠遠高於同步I/O,並且很容易出錯。然而,如果每個請求都擁有自己的處理執行緒,那麼在處理某個請求時發生的阻塞將不會影響其他請求的處理。

多執行緒的挑戰

1.上下文切換

2.死鎖

3.資源限制

那麼多執行緒一定快嗎(上下文切換)?

不一定

當併發數不超過百萬次時,並行的速度會比序列速度慢,原因就是執行緒有建立和上下文切換的開銷,如何減少上下文切換呢

1.無鎖併發程式設計

例如ConcurrentHashMap,可以將資料的ID按hash分成不同的段(segment),然後分段加鎖,不同段的操作無需加鎖,而不是每次操作都鎖住全部

2.CAS演算法

使用JAVA的Atomic包的CAS操作,無需加鎖

3.使用最小執行緒

避免建立不需要的執行緒,根據需要建立

4.使用協程

在單執行緒中實現多工排程,並在但執行緒中維持多個任務的切換

死鎖

死鎖就是,當一個執行緒永遠地持有一個鎖,並且其他執行緒都嘗試去獲得這個鎖時,那麼它們將永遠被阻塞,這個我們都知道。如果執行緒A持有鎖L並且想獲得鎖M,執行緒B持有鎖M並且想獲得鎖L,那麼這兩個執行緒將永遠等待下去,這種情況就是最簡單的死鎖形式。

如何避免死鎖呢?

1.避免一個執行緒同時獲得多個鎖

2.儘量保證每個鎖只佔用一個資源

3.嘗試使用定時鎖

4.對於資料庫鎖,加鎖解鎖必須在一個數據庫連線中,否則會出現解鎖失敗的情況

資源限制

併發程式設計中,程式的執行受限於計算機硬體軟體資源的限制,如伺服器頻寬2M/s,某個資源下載速度1M/s,系統啟動10個執行緒,速度並不會10M/s,所以併發程式設計時,要考慮這種情況。

怎麼解決呢?硬體上當然可以讓程式在多機上執行,如使用OPDS,hadoop搭建自己的叢集,不同機器處理不同的資料,軟體上,考慮使用資源池將資源複用,比如資料庫連線池。

總之,多執行緒技術有優點也有挑戰,但是值得我們去深入研究並應用多執行緒。