作為Java后端開發者,我們創作的許多代碼直接影響著用戶的使用體驗。如果后端代碼性能不佳,用戶在訪問網站時就必須花費更多時間等待服務器響應。這可能引發用戶投訴甚至用戶流失問題。
性能優化是一個廣泛而重要的話題。《Java程序性能優化》提到性能優化可分為五個層次:設計優化、代碼優化、JVM優化、數據庫優化、操作系統優化等。每個層次都涵蓋許多方法論和最佳實踐。本文無意進行全面詳盡的概述,只是列舉幾個常用的Java代碼優化方案,希望讀者閱讀后能實際應用到自己的代碼中。
在處理IO操作、數據庫連接、配置文件解析加載等耗費大量系統資源的任務時,我們必須限制這些實例的創建,或者始終使用一個共享的實例,以節約系統資源。這種情況下就需要使用單例模式。
若有100個請求,逐個執行顯然效率較低。將這100個請求合并為一個請求進行批量操作,則能大幅提升效率。
特別是在數據庫操作中,批量處理不僅比逐條執行效率更高,還能有效降低數據庫連接數,提升應用的QPS上限。
假設某項任務需花費一定時間執行,為避免無謂的等待,可先獲取一個“提貨單”——即Future,隨后繼續處理其他任務,直至“貨物”抵達,即任務執行完成并獲得結果。這時便可憑借“提貨單”提取物品,即通過Future對象獲取返回值。
偽代碼
public class RealData implements Callable<String> { protected String data; public RealData(String data) { this.data = data; } @Override public String call() throws Exception { // 通過sleep方法演示業務是緩慢的 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return data; }}public class Application { public static void main(String[] args) throws Exception { FutureTask<String> futureTask = new FutureTask<>(new RealData("name")); ExecutorService executor = Executors.newFixedThreadPool(1); // 使用線程池 // 執行FutureTask,相當于上例中的client.request("name")發送請求 executor.submit(futureTask); // 這里可以用一個sleep代替對其他業務邏輯的處理 // 在處理這些業務邏輯的同時,RealData也在創建,充分利用等待時間 Thread.sleep(2000); // 使用真實數據 // 如果call()沒有執行完成,仍會等待 System.out.println("數據=" + futureTask.get()); }}
合理運用線程池帶來三大益處。首先,降低資源消耗:通過重復利用已創建的線程,降低線程的創建與銷毀成本。其次,提高響應速度:任務到達時,無需等待線程創建即可立即執行。第三,提升線程可管理性:線程是珍貴資源,無節制地創建會消耗系統資源,降低系統穩定性;線程池能實現統一分配、優化和監控。
自 Java 5 開始,引入了并發編程新API,如Executor框架,內部采用線程池機制,位于java.util.concurrent包中。通過該框架控制線程的啟動、執行和關閉,可簡化并發編程操作。
偽代碼
public class MultiThreadTest { public static void main(String[] args) { ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("thread-%d").build(); ExecutorService executor = new ThreadPoolExecutor(2, 5, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory); executor.execute(new Runnable() { @Override public void run() { System.out.println("Hello, world!"); } }); System.out.println(" ===> Main Thread! "); }}
JDK自1.4版本起引入了新的I/O編程類庫,即NIO。NIO不僅帶來了高效的Buffer和Channel,還引入了基于Selector的非阻塞I/O機制,可以將多個異步I/O操作集中到一個或少數幾個線程中進行處理。使用NIO替代阻塞I/O能夠提高程序的并發吞吐能力,降低系統開銷。
針對每個請求,如果為其單獨開啟一個線程來處理邏輯,當客戶端數據傳輸是間歇性的而非連續的時,相應線程會處于I/O等待狀態,并頻繁進行上下文切換。利用NIO引入的Selector機制,可以提升程序的并發效率,改善這種狀況。
偽代碼
public class NioTest { static public void main( String args[] ) throws Exception { FileInputStream fin = new FileInputStream("D://test.txt"); // 獲取通道 FileChannel fc = fin.getChannel(); // 創建緩沖區 ByteBuffer buffer = ByteBuffer.allocate(1024); // 讀取數據到緩沖區 fc.read(buffer); buffer.flip(); while (buffer.remaining()>0) { byte b = buffer.get(); System.out.print(((char)b)); } fin.close(); } }
在并發場景中,頻繁使用鎖是很常見的情況。然而,鎖引發競爭,而競爭又會耗費大量資源。那么,在Java代碼中,如何優化鎖呢?我們可以考慮以下幾個方面:
在并發環境中使用Map時,最好選用ConcurrentHashMap替代HashTable和HashMap(ConcurrentHashMap采用分段鎖,鎖的粒度更細)。
分離鎖
普通鎖(例如synchronized)可能導致讀寫互相阻塞,可以嘗試將讀操作和寫操作分開。
鎖粗化
有時我們希望將多次鎖的請求合并成一個,以減少頻繁加鎖、同步和解鎖所帶來的性能損失。
鎖消除
鎖消除是指Java虛擬機在JIT編譯時,經過運行上下文的掃描,去除那些不會產生共享資源競爭的鎖。通過鎖消除,可以減少無謂的鎖請求時間。
在數據傳輸之前,壓縮數據是一種優化方式,可以減少網絡傳輸的數據量,提升傳輸速度。接收端可解壓數據,還原傳輸內容。壓縮后的數據能節省存儲介質(如磁盤或內存)空間和網絡帶寬,從而降低成本。然而,壓縮并非無成本之舉。數據壓縮需要大量CPU計算,并且根據壓縮算法的不同,計算復雜度和壓縮比都有顯著差異。通常需要根據業務情景選擇合適的壓縮算法。
對于相同的用戶請求,若每次都重復查詢數據庫、重復計算,將浪費大量時間和資源。將計算結果緩存至本地內存或使用分布式緩存,可節約寶貴的CPU計算資源,減少數據庫重復查詢或磁盤I/O。將原本需要磁頭物理轉動的操作轉化為內存中的電子運動,提高響應速度。同時,快速釋放線程也提升了應用的吞吐能力。
本文鏈接:http://www.www897cc.com/showinfo-26-81731-0.html一篇文章告訴你真實場景下服務端接口性能問題是如何解決的
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com