如何提高Java並行程序性能

  在Java程序中,多線程幾乎已經無處不在。與單線程相比,多線程程序的設計和實現略微困難,但通過多線程,我們卻可以獲得多核CPU帶來的性能飛躍,從這個角度說,多線程是一種值得嘗試的技術。那麼如何寫出高效的多線程程序呢?

  1. 1、有關多線程的誤區:線程越多,性能越好

不少初學者可能認爲,線程數量越多,那麼性能應該越好。因爲程序給我們的直觀感受總是這樣。一個兩個線程可能跑的很難,線程一多可能就快了。但事實並非如此。因爲一個物理CPU一次只能執行一個線程,多個線程則意味着必須進行線程的上下文切換,而這個代價是很高的。因此,線程數量必須適量,最好的情況應該是N個CPU使用N個線程,並且讓每個CPU的佔有率都達到100%,這種情況下,系統的吞吐量才發揮到極致。但現實中,不太可能讓單線程獨佔CPU達到100%,一個普遍的願意是因爲IO操作,無論是磁盤IO還是網絡IO都是很慢的。線程在執行中會等待,因此效率就下來了。這也就是爲什麼在一個物理核上執行多個線程會感覺效率高了,對於程序調度來說,一個線程等待時,也正是其它線程執行的大好機會,因此,CPU資源得到了充分的利用。 

  1. 2、儘可能不要掛起線程

多線程程序免不了要同步,最直接的方法就是使用鎖。每次只允許一個線程進入臨界區,讓其它相關線程等待。等待有2種,一種是直接使用操作系統指令掛起線程,另外一種是自旋等待。在操作系統直接掛起,是一種簡單粗暴的實現,性能較差,不太適用於高併發的場景,因爲隨之而來的問題就是大量的線程上下文切換。如果可以,嘗試一下進行有限的自旋等待,等待不成功再去掛起線程也不遲。這樣很有可能可以避免一些無謂的開銷。JDK中ConcurrentHashMap的實現裏就有一些自旋等待的實現。此外Java虛擬機層面,對synchronized關鍵字也有自旋等待的優化。 

  1. 3、善用“無鎖”

阻塞線程會帶來性能開銷,因此,一種提供性能的方案就是使用無鎖的CAS操作。JDK中的原子類,如AtomicInteger正是使用了這種方案。在高併發環境中,衝突較多的情況下,它們的性能遠遠好於傳統的鎖操作(《實戰Java高併發程序設計》 P158)。 

  1. 4、處理好“僞共享”問題

大家知道,CPU有一個高速緩存Cache。在Cache中,讀寫數據的最小單位是緩存行,如果2個變量存在一個緩存行中,那麼在多線程訪問中,可能會相互影響彼此的性能。因此將變量存放於獨立的緩存行中,也有助於變量在多線程訪問是的性能提升(《實戰Java高併發程序設計》 P200),大量的高併發庫都會採用這種技術。

參考:


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章