使用面向Java的Lock Analyzen診斷同步和鎖問題

2021-01-11 IT168

  【IT168 技術文章】面向 Java™ 的 IBM® 鎖分析器可從 alphaWorks 獲得,可對運行中的 Java 應用程式進行實時鎖監視。鎖爭用會降低應用程式性能,該工具會突出顯示發生鎖爭用的線程。 開發人員可以使用該信息修改其應用程式以減少鎖爭用,從而提高性能。

  當今很多 Java 應用程式都通過使用線程來利用該語言的功能,從而支持並發編程。很多線程可以共享相同的數據對象,但是如果應用程式中控制線程的部分設計得不好,則您可能會遇到鎖爭用問題從而降低性能。

  例如,在多線程應用程式中,如果多個線程訪問相同的資源進行讀寫訪問, 則可能會出現線程同步問題。如果一個線程嘗試讀取某個文件,而另一個線程 對其進行寫訪問,則可能會損壞數據。為了解決這個問題,Java 語言允許一個線程獲得對對象的專有鎖, 從而防止其他任何線程訪問它。一旦該線程完成對該對象的處理之後,便釋放該對象,以便 其他線程可以進行訪問。但是,如果應用程式沒有進行過精心設計,則這種機制可能會導致更高級別的爭用。

  如果一個鎖正在使用中,而另一個線程嘗試獲取它,這時會發生鎖爭用。 如果鎖被頻繁獲得和/或持有時間較長,則可能會發生更高級別的爭用。 更高級別的鎖爭用(很多線程嘗試訪問它)將成為系統中的瓶頸, 因為每個運行的線程將暫停執行,直到它所需的鎖變為可用為止, 這樣便限制了應用程式的性能。

  使用 JLA 防止出現鎖問題

  面向 Java 的 IBM 鎖分析器 (JLA) 是 alphaWorks 中的新工具,它可以 在運行 IBM 提供的 Java SDK 或 JRE 版本 5.0 或更高版本的的任何平臺上運行, 對活動應用程式執行鎖分析,以突出顯示線程的活動並且提供對鎖爭用頻率的深入洞察。該視圖可以幫助解決鎖爭用問題以及性能問題。

  JLA 一邊運行現有的 Java 應用程式,一邊收集該應用程式的所有鎖信息。記錄每個鎖及其詳細信息,如保持的時間、是否爭用以及它阻塞線程嘗試獲得它的時間百分比。該工具由運行時庫和前端圖形用戶界面組成,前者必須在啟動所要監視的應用程式時加載,後者可以顯示結果和性能分析。

  JLA 設計詳細信息

  JLA 由以下兩個程序包組成:

  JLAagent。該程序包與平臺有關,由特定於平臺的庫文件和一組打包成 JAR 的 Java 類組成,它提供與 Java 虛擬機 (JVM) 的連接以收集有關運行的應用程式的鎖統計信息。

  JLAGui。該程序包是使用 Swing 編寫的,它與平臺無關,提供圖形用戶界面。

  JLAagent 中的本地庫是用 C 編寫的並且使用 Java 虛擬機工具界面 ( Java Virtual Machine Tool Interface,JVM TI)。該界面提供方法控制在 JVM 中運行的應用程式的執行。通過該界面,您可以利用 IBM 發布的 JVM 中可用的功能收集線程統計信息。還可以通過對您要分析的 Java 應用程式的啟動命令進行修改,讓 JVM 在啟動時加載此本地庫。然後本地庫使用與它一起打包的 Java 類創建一個平臺 MBean 伺服器,該伺服器提供允許 JLAGui 連接 JLAagent 的機制。

  啟動 MBean 伺服器之後, JLAGui 可以通過將該伺服器導航到本地庫中,然後導航到運行的 JVM 中,從而請求運行的應用程式的鎖統計信息。由於 JLAagent 和 JLAGui 通過 MBeans 連接,因此它們沒有必要在同一機器上運行;JLAGui 還可以從網絡上的任意位置連接到 MBean 伺服器。

  運行 JLA

  若要運行 JLA,您必須首先為您的應用程式配置 JLAagent。 然後您才可以啟動 JLAGui 開始監視鎖性能。如果您尚未這樣做, 請 下載 JLA 以便您可以跟隨本文的操作。

  配置應用程式以使用 JLA

  有很多版本的 JLAagent,IBM 提供的 JRE 支持的每個平臺使用一個版本;您必須確保使用的 JLAagent 與您要監視的應用程式將運行的平臺相對應。如果您使用了錯誤版本的 JLAagent,則嘗試監視的應用程式將無法加載並且將崩潰。根據將要啟動它的 JRE 的基礎架構,每個 JLAagent 程序包都具有一個名稱。

  在運行所要監視的應用程式的計算機上,將 JLAagent 程序包解壓縮到某個目錄。將該目錄添加到 Java 類路徑和系統路徑。屬性文件名為 JLAtiagent。屬性包含在該程序包中;它包含有關啟動 MBean 伺服器的連接信息。如果您想修改默認值,可以編輯該屬性。

  若要為將進行分析的應用程式加載 JLAagent,請修改該應用程式的啟動命令。 添加 -agentlib:JLAtiagent 參數以便它加載本地庫, 將添加的 JLAtiagent.jar 添加到類路徑, 並指定 JLAtiagent.properties 文件的位置。

  例如,啟動命令 java dummy.class 將需要進行如下修改:

1 java -Dcom.sun.management.config.file=JLAtiagent.properties
2      -agentlib:JLAtiagent -cp .;JLAagent.jar dummy.class

  如果此時出現異常,請進行檢查以確保使用正確的 JLAagent 版本。如果一切正常,將會在命令行上顯示消息 JLA 客戶機正在註冊 MBeanServer。

  此時,JLAagent 已經指導 VM 開始記錄鎖詳細信息,即使 JLAGui 未運行也是如此。這意味著當啟動 JLAGui 時,您將能夠查看整個應用程式生命周期中的鎖統計信息。

  JLAGui

  您可以使用以下命令啟動 JLAGui:

  默認情況下, JLAGui 將查找在本地主機埠 1972 上運行的 MBean 伺服器;同樣不需要指定這些值。但是如果您想使用不同的值,則可以在命令行上修改主機或埠值,如表 1 所示:

  表 1. JLAGui 的連接選項

設置默認值說明-Dcom.ibm.jla.port1972需要匹配 JLAtiagent 使用的 com.sun.management.jmxremote.port 值-Dcom.ibm.jla.hostname本地主機如果想監視在遠程機器上運行的應用程式,可以設置為 IP 地址或有效的主機名

  當啟動 GUI 時,它將嘗試通過機器上運行的 MBean 伺服器以及在啟動選項中指定的埠號連接到代理程序。 如果連接成功,則圖 1 中的屏幕將顯示:

  圖 1. 初始 JLA 屏幕

  如果連接未成功,則會顯示錯誤消息,並提供 GUI 嘗試連接的詳細信息。仔細查看錯誤消息以確保連接正確。如有必要,您可以關閉 GUI,然後用修改後的命令行選項重新啟動。您還可以進行檢查以確保要分析的應用程式正在運行附帶的本機代理程序。

  單擊 Create Report 按鈕即可創建正在監視的應用程式當前線程統計信息的快照報告。您將看到三個面板(參見圖 2),它們顯示 Java 監視程序(應用程式創建的那些監視程序)、系統監視程序(VM 創建的那些監視程序)以及摘要報告頁。應用程式創建或使用的任何監視程序在表和圖中都有一個條目。每個監視程序只有一個條目可見,即使有多個線程訪問它也是如此。將在監視程序級別而不是線程級別記錄該數據。

  圖 2. JLA 報告示例

  每一列的高度基於慢鎖計數的值,它與圖中的所有列有關。當請求的監視程序已經被另一個線程擁有並且請求線程被阻塞時會發生緩慢計數。每個條狀圖形的顏色基於 %MISS 列的值(參見表 2),從紅色 (100%) 逐漸過渡到黃色 (50%),並且最終變為綠色 (0%)。紅色條表示每次請求監視程序時線程都會阻塞,而綠色條表示線程從不阻塞。對圖進行快速掃描將顯示執行得不太好的那些監視程序。

  表 2 解釋報告表中各列值的說明:

  表 2. 報告表圖例

列名說明%MISS當請求的線程被阻塞以等待監視程序時總 gets(獲取)的百分比。GETS成功獲取的總數。

NONREC

非遞歸獲取的總數。該數包括 SLOW gets。SLOW非遞歸獲取(導致阻塞請求線程以等待釋放監視程序)的總數。NONREC 中包含該數。REC遞歸獲取的總數。遞歸獲取表示請求的線程已經擁有監視程序 。TIER2支持三層自旋鎖的平臺上,層 2(內部自旋循環)迭代的總數。TIER3支持三層自旋鎖的平臺上,層 3(外部線程讓步循環)迭代的總數。%UTIL監視程序持有時間除以時間間隔。必須打開持有時間計數。AVER_HTM監視程序的平均持有時間;不包括遞歸獲取,因為遞歸獲取時已經擁有監視程序。MONITOR NAME監視程序名稱,如果不知道名稱,則為 NULL(空白)。

  運行中的 JLA

  一個簡單的應用程式示例將幫助您了解如何使用 JLA 幫助查找鎖爭用的區域。假設一個應用程式由兩個線程組成,而這兩個線程都嘗試訪問同一個對象。本例中,該對象就是一個名為 JLAsink 的類,該類包含兩個同步的方法,一個用於設置數據塊,一個用於檢索該數據塊。這兩個線程同時開始並且都在一個循環中運行以同時訪問 JLAsink。一個線程稱為 setter 方法,另一個線程稱為 retrieval 方法。

  圖 3 顯示在性能較差的示例應用程式中運行 JLA 的結果:

  圖 3. 最初的 JLA 快照

  可以從此屏幕截圖中看到,JLAsink 監視程序進行了 9,161 次 get,並且請求線程被阻塞,阻止它獲得 25% 的鎖時間。很明顯,當處理嘗試獲得鎖的線程時,應用程式的這部分性能將有所下降。

  圖 4 顯示在更高效的示例應用程式中運行的 JLA:

  圖 4. 在優化的應用程式中運行 JLA 的快照

  您可以看出 JLAsink 監視程序進行了 9,370 次 get,並且沒有阻塞任何一個線程。這表明在處理鎖爭用時,該監視程序運行得比較理想。現在優化的代碼比未優化的代碼運行速度更快,您可以通過比較每次運行的時間間隔看到這一點。 第二次運行訪問 JLAsink 對象的次數比第一次運行多,並且它花的時間較少。

  未來計劃

  隨著當前功能的完善,該工具的發展正逐漸穩定。將來,我們的團隊將在 JLA 的基礎上發布一個新工具,該工具將超越鎖統計信息,提供對活動 Java 應用程式的分析。該工具不僅僅具有 JLA 的功能,而且還具有 EVTK 和 Dump Analyzer 工具(在 本系列的前面部分文章 中您已經學習了這些工具)的功能。目前該工具還處於設計階段。

  結束語

  在本系列的下一篇文章中,您將回到在本系列的第 1 部分介紹的 Dump Analyzer 工具。 您將對該工具的擴展性獲得更深入的了解,並且還將學習如何構建您自己的分析模塊。

相關焦點

  • 使用Lock鎖:java多線程安全問題解決方案之Lock鎖
    實現提供了比使用 synchronized 方法和語句可獲得的更廣泛的鎖定操作,就來釋放鎖和獲取鎖來說,在使用synchronized 方法的時候,我們並不知道什麼時候獲取到了鎖,什麼時候釋放了鎖,而Lock接口不一樣,他提供了專門的獲取鎖和釋放鎖的方法。
  • Java中synchronized鎖和lock鎖的比較
    Java並發之顯式鎖和隱式鎖的區別在面試的過程中有可能會問到:在Java並發編程中,鎖有兩種實現:使用隱式鎖和使用顯示鎖分別是什麼?兩者的區別是什麼?所謂的顯式鎖和隱式鎖的區別也就是說說Synchronized(下文簡稱:sync)和lock(下文就用ReentrantLock來代之lock)的區別。
  • 教你使用BooleanLock,多個線程通過lock方法爭搶鎖的方法
    在使用該顯式鎖的時候,務必藉助於try finally語句來確保每次獲取到鎖之後都可以正常的釋放,使用BooleanLock,多個線程通過lock方法爭搶鎖示例代碼如下:package com.wangwenjun.concurrent.chapter05;import java.util.concurrent.TimeUnit
  • Java鎖的那些事兒
    用來實現鎖功能,它提供了與synchronized關鍵字類似的同步功能,只是在使用時需要顯式地獲取和釋放鎖。雖然它缺少了(通過synchronized塊或者方法)隱式獲取釋放鎖的便捷性,但是卻擁有了鎖獲取與釋放的可操作性、可中斷的獲取鎖以及超時獲取鎖等多種synchronized關鍵字所不具備的同步特性。Java鎖使用示例:Lock lock = new ReentrantLock();lock.lock();try { // ..
  • Java並發編程系列21|Condition-Lock的等待通知
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫我們知道 synchronized 鎖通過 Object 類的 wait()和 notify()方法實現線程間的等待通知機制,而比 synchronized 更靈活 Lock 鎖同樣也有實現等待通知機制的方式
  • Lock鎖 精講
    使用synchronized方法或語句可訪問與每個對象關聯的隱式監視器鎖,但會強制所有鎖的獲取和釋放以塊結構方式進行。當獲取多個鎖時,它們必須以相反的順序釋放鎖。雖然用於synchronized方法和語句的作用域機制使使用監視器鎖的編程變得更加容易,並且有助於避免許多常見的涉及鎖的編程錯誤,但在某些情況下,您需要以更靈活的方式使用鎖。
  • 「從入門到放棄-Java」並發編程-鎖-synchronized
    結果不符合預期:如上所示,作用於非靜態方法,鎖的是實例化對象,因此當sync和sync1同時運行時,還是會出現線程安全問題,因為鎖的是兩個不同的實例化對象。同步靜態方法結果符合預期:鎖靜態方法時,鎖的是類對象。因此在不同的線程中調用add1和add11依然會得到正確的結果。
  • Java面試的的時候你被提過哪些問題?
    Java面向對象的三個特徵與含義。17. Override和Overload的含義去區別。18. Interface與abstract類的區別。19. Static class 與non static class的區別。20. java多態的實現原理。21. 實現多線程的兩種方法:Thread與Runable。
  • Java中的文件鎖到底是怎麼回事?
    在下一節中,我們將看到Java如何處理這些類型的鎖。# Java中的文件鎖Java NIO庫支持在作業系統級別鎖定文件。FileChannel 中的lock() 和*tryLock()*方法就是為了這個而存在。
  • JAVA多線程 集合同步
    經典做法在迭代開始之前,使用synchronized手動同步所有針對List的修改操作(增加、刪除),保證迭代期間沒有線程修改List。此方法需要明確各個方法的同步,考慮的因素很多,不建議使用。B.Synchronized Wrappers(包裝器)使用java.util.Collections的工廠方法Collections.synchronizedXXX(collection),其保護代理(protectionproxy)模式,僅在正確的條件下才會調用原始List;需要明確指出的是,必須保證使用迭代器訪問List的時候要在同步代碼塊(synchronized block
  • 分布式鎖解決方案-Redis
    (一半以上)在使用,client就可以獲取和釋放鎖4.='methodName';```問題:1、因為是基於資料庫實現的,資料庫的可用性和性能將直接影響分布式鎖的可用性及性能,所以,資料庫需要雙機部署、數據同步、主備切換;2、不具備可重入的特性,因為同一個線程在釋放鎖之前,行數據一直存在,無法再次成功插入數據
  • Java 中15種鎖的介紹:公平鎖,可重入鎖,獨享鎖,互斥鎖,樂觀鎖,分段鎖,自旋鎖等等
    在Java中java.util.concurrent.atomic包下面的原子變量類就是使用了樂觀鎖的一種實現方式CAS實現的。分段鎖分段鎖其實是一種鎖的設計,並不是具體的一種鎖,對於ConcurrentHashMap而言,其並發的實現就是通過分段鎖的形式來實現高效的並發操作。
  • 不懂什麼是 Java 中的鎖?看看這篇你就明白了!
    在Java中java.util.concurrent.atomic包下面的原子變量類就是使用了樂觀鎖的一種實現方式 CAS 實現的。即不使用鎖的情況下實現多線程之間的變量同步,也就是在沒有線程被阻塞的情況下實現變量的同步,所以也叫非阻塞同步(Non-blocking SynchronizationJava 從 JDK1.5 開始支持,java.util.concurrent 包裡提供了很多面向並發編程的類,也提供了 CAS 算法的支持,一些以 Atomic 為開頭的一些原子類都使用 CAS 作為其實現方式。
  • 手把手教你定位常見Java性能問題
    概述性能優化一向是後端服務優化的重點,但是線上性能故障問題不是經常出現,或者受限於業務產品,根本就沒辦法出現性能問題,包括筆者自己遇到的性能問題也不多,所以為了提前儲備知識,當出現問題的時候不會手忙腳亂,我們本篇文章來模擬下常見的幾個Java性能故障,來學習怎麼去分析和定位。
  • 「原創」Java並發編程系列18|讀寫鎖(下)
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫本文為何適原創並發編程系列第 18 篇,文末有本系列文章匯總。,篇幅太多很不方便,而且文章太長閱讀體驗也不好,所以分成讀寫鎖(上)和讀寫鎖(下)兩篇。
  • 乾貨#Java常見面試問題匯總
    那麼,在JAVA如此盛行的時候,一般面試官都會考察我們哪些專業問題呢?筆者為計劃從事JAVA編程相關崗位的你們總結了一些面試官常問到的問題,希望給大家做個參考。Q:Synchronized 和 Lock 的區別原始構成Synchronized 是關鍵字,屬於JVM層面,底層是通過 monitorenter 和 monitorexit 完成,依賴於 monitor 對象來完成。由於 wait/notify 方法也依賴於 monitor 對象,因此只有在同步塊或方法中才能調用這些方法。
  • Java都為我們提供了各種鎖,為什麼還需要分布式鎖?
    就免不了資源的共享問題。既然是資源共享就免不了並發的問題。針對這些問題,redis也給出了一個很好的解決方案,那就是分布式鎖。這篇文章主要是針對為什麼需要使用分布式鎖這個話題來展開討論的。前一段時間在群裡有個兄弟問,既然分布式鎖能解決大部分生產問題,那麼java為我們提供的那些鎖有什麼用呢?直接使用分布式鎖不就結了嘛。針對這個問題我想了很多,一開始是在網上找找看看有沒有類似的回答。
  • 40個Java多線程問題總結
    使用volatile則會對禁止語義重排序,當然這也一定程度上降低了代碼執行效率從實踐角度而言,volatile的一個重要作用就是和CAS結合,保證了原子性,詳細的可以參見java.util.concurrent.atomic包下的類,比如AtomicInteger。
  • JAVA並發編程:concurrent提供了哪些比synchronized更高效的鎖
    在java.util.locks包下面提供了三種鎖,ReentrantLock、ReentrantReadWriteLock和StampedLock,下面一一講解這三種鎖的特點、實現方式以及應用。通用方法講解這三種鎖都實現了lock接口,先解讀一下Lock接口的方法及含義:void lock(); 方法,阻塞方法,直到可以獲取到鎖;void lockInterruptibly()方法,阻塞方法,直到可以獲取到鎖或者被其他線程中斷;boolean tryLock();嘗試獲取鎖,獲取不到立刻返回boolean
  • 徹底理解ReentrantLock可重入鎖的使用
    java除了使用關鍵字synchronized外,還可以使用ReentrantLock實現獨佔鎖的功能。而且ReentrantLock相比synchronized而言功能更加豐富,使用起來更為靈活,也更適合複雜的並發場景。這篇文章主要是從使用的角度來分析一下ReentrantLock。