高性能編程小結

高性能編程

多線程併發編程

CPU性能優化手段

爲了提高程序運行的性能,現代CPU在很多方面對程序進行了優化。
例如:CPU高速緩存。儘可能地避免處理器訪問主內存的時間開銷,處理器大多會利用緩存以提高性能。
例如:運行時指令重排。當CPU寫緩存時發現緩存區正被其他CPU佔用,爲了提高CPU處理性能,可能將後面的讀緩存命令優先執行。

線程通信

想要實現多個線程之間的協同,涉及到線程之間的相互通信。JDK提供了線程協同的API,細分爲suspend/resume、wait/notify、park/unpark。suspend/resume由於容易寫出死鎖的代碼,已經被棄用。

線程池

線程是不是越多越好?
1、 線程在java中是一個對象,更是操作系統的資源,線程創建、銷燬需要時間。如果創建時間+銷燬時間>執行任務時間 就很不合算。
2、 java對象佔用堆內存,操作系統線程佔用系統內存,根據jvm規範,一個線程默認最大棧大學1M,這個棧空間是需要從系統內存中分配的。線程過多,會消耗很多的內存。
3、 操作系統需要頻繁切換線程上下文,影響性能。
線程池的推出,就是爲了方便控制線程數量。
如何確定合適數量的線程?
計算型任務:CPU數量的1-2倍;
IO型任務:相對比計算型任務,需要多一些線程,要根據具體的IO阻塞時長進行考量決定。

java內存模型

java內存模型描述了運行多線程程序的合法行爲。
可見性問題:讓一個線程對共享變量的修改,能夠及時被其他線程看到。
volatile關鍵字有如下功能:
1、禁止緩存、
2、對volatile變量相關的指令不做重排序。

有些處理器沒有提供寫單個字節的功能。在這樣的處理器上更新byte數組,若只是簡單地讀取整個內容,更新對應的字節,然後將整個內容在寫回內存,將是不合法的。
這個問題有時候被稱爲“字分裂(word tearing)”,更新單個字節有難度的處理器,就要尋求其它方式來解決問題。
因此,編程人員需要注意,儘量不要對byte[]中的元素進行重新賦值,更不要在多線程程序中這樣做。

《java語言規範》中說道:建議程序員將共享的64位值(long、double)用volatile修飾或正確同步其程序以避免可能的複雜情況。

Atomic相關類和CAS機制

CAS的三個問題
1、 循環+CAS,自旋的實現讓所有線程都處於高頻運行,爭搶CPU執行時間的狀態。如果操作長時間不成功,會帶來很大的CPU資源消耗。
2、 僅針對單個變量的操作,不能用於多個變量來實現原子操作。
3、 ABA問題。

線程安全

只有當多個線程更新共享資源時,纔會發生競態條件,可能會出現線程安全問題。

棧封閉時,不會在線程之間共享變量,都是線程安全的。
局部對象引用本身不共享,但是引用的對象存儲在共享堆中。如果方法內創建的對象,只是在方法中傳遞,並且不對其他線程可用,那麼也是線程安全的。
不可變的共享對象來保證對象在線程間共享時不會被修改,從而實現線程安全。實例被創建,value變量就不能再被修改,這就是不可變性。
使用Threadlocal時,相當於不同的線程操作的是不同的資源,所以不存在線程安全問題。

synchronized vs Lock

Synchronized
優點: 1、使用簡單,語義清晰。
2、由JVM提供,提供了多種優化方案(鎖粗化、鎖消除、偏向鎖、輕量級鎖)
3、鎖的釋放由虛擬機來完成,不用人工干預,也降低了死鎖的可能性
缺點:無法實現一些鎖的高級功能如:公平鎖、中斷鎖、超時鎖、讀寫鎖、共享鎖等。
Lock
優點: 1、所有synchronized的缺點
2、可以實現更多的功能
缺點:需手動釋放鎖unlock,新手使用不當可能造成死鎖

ReadWriteLock

概念:維護一對關聯鎖,一個只用於讀操作,一個只用於寫操作;讀鎖可以由多個讀線程同時持有,寫鎖是排他的。同一時間,兩把鎖不能被不同線程持有。
適用場景:適合讀取操作多於寫入操作的場景,改進互斥鎖的性能,比如:集合的併發線程安全性改造、緩存組件

高併發網絡編程

NIO網絡編程

BIO的缺點:阻塞導致在處理網絡I/o時,一個線程只能處理一個網絡連接。
NIO中有三個核心組件:Buffer緩存區、Channel通道、Selector選擇器。
在文章《Scalable IO in Java》中,介紹了使用NIO與Reactor模式相結合來實現高伸縮性網絡的模型。在這個模型中,mianReactor負責處理client的請求;subReactor可以有多個,每個subReactor都會在一個獨立線程中執行。

Netty

因爲網絡編程本身的複雜性,以及JDK API開發的難度較高,所以在開源社區中,涌出來很多對JDK NIO進行封裝、增強後的網絡編程框架,例如:Netty、Mina等。
Netty是一個高性能、高可擴展性的異步事件驅動的網絡應用程序框架,它極大地簡化了TCP和UDP客戶端和服務器開發等網絡編程。
Netty重要的四個內容:
1、Reactor線程模型:一種高性能的多線程程序設計思路。
2、Netty中自己定義的Channel概念:增強版的通道概念。
3、ChannelPipeline職責鏈設計模式:事件處理機制,保證了Netty的高度可擴展性。
4、內存管理:增強的ByteBuf緩衝區

Netty相關優化

核心三個要點:線程模型、職責鏈、ByteBuf。
1、業務上的耗時操作,通過單獨創建線程池進行處理,不要佔用寶貴I/O線程。
2、Pipeline機制,有一項需要優化:handler複用。
3、Netty本身就是有很多優化 – 例如ByteBuf(零拷貝、對象複用、內存複用)。
4、大數據數據寫入, 分成多批次,小數據包的方式。

Java系統性能調優

垃圾回收機制

垃圾收集器:
1、串行收集器 - Serial GC -XX:+UseSerialGC
單個線程來執行所有垃圾收集工作,適合單處理器機器。
2、串行收集器 - Serial Old -XX:+UseSerialOldGC
可以在老年代使用,它採用了標記-整理算法,區別於新生代的複製算法。
3、並行收集器 - Parallel GC -XX:+UseParallelGC
並行收集器 - Parallel Old GC -XX:+UseParallelOldGC
server模式JVM的默認GC選擇,整體算法和Serial比較相似,區別是新生代和老年代GC都是並行進行;也稱爲吞吐量優先的GC。
4、併發收集器 - CMS GC -XX:+UseConcMarkSweepGC
專用老年代,基於標記-清除算法,設計目標是儘量減少停頓時間。減少了停頓時間,這一點對於互聯網web等對時間敏感的系統非常重要。
5、並行收集器 - ParNew GC -XX:+UseParNewGC
新生代GC實現,它實際是Serial GC的多線程版本。最常見的應用場景是配合老年代的CMS GC工作。
6、併發收集器 - G1 -XX:+UseG1GC
針對大堆內存設計的收集器,兼顧吞吐量和停頓時間,JDK9後爲默認選項,目標是替代CMS。

JVM參數及調優

調優基本概念
在調整性能時,JVM有三個組件:
1、堆大小調整
2、垃圾收集器調整
3、JIT編譯器
大多數調優選項都與調整堆大小和爲您的情況選擇最合適的垃圾收集器有關。JIT編譯器對性能也有很大影響,但很少需要使用較新版本的JVM進行調優。

通常,在調優Java應用程序時,重點是以下兩個主要目標之一:
響應性:應用程序或系統對請求的數據進行響應的速度,對於專注於響應性的應用程序,長的暫停時間是不可接受的,重點是在短時間內做出迴應。
吞吐量:側重於在特定時間段內最大化應用程序的工作量,對於專注於吞吐量的應用程序,高暫停時間是可接受的。由於高吞吐量應用程序在較長時間內專注於基準測試,因此不需要考慮快速響應時間。

GC調優思路
1、分析場景
2、確定目標
3、收集日誌
4、分析日誌
5、調整參數

Tomcat參數調優

線程數調爲多少合適?
理想的線程數量=(1+代碼阻塞時間/代碼執行時間)*CPU數量
實際情況是跑起代碼,壓測環境進行調試。不斷調整線程數,將CPU打到80-90%的利用率。

發佈了6 篇原創文章 · 獲贊 0 · 訪問量 531
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章