日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

提高Java的锁性能

發布時間:2023/12/3 java 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 提高Java的锁性能 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Plumbr是唯一通過解釋應用程序性能數據來自動檢測Java性能問題的根本原因的解決方案。

幾個月前,我們在Plumbr中引入了鎖定線程檢測之后,我們開始收到類似于“嘿,太好了,現在我知道是什么導致了性能問題,但是現在應該做什么?”這樣的查詢。

我們正在努力將解決方案說明構建到我們自己的產品中,但是在這篇文章中,我將分享幾種獨立于檢測鎖的工具可以應用的常見技術。 這些方法包括鎖拆分,并發數據結構,保護數據(而不是代碼)和減少鎖范圍。

鎖不是邪惡的,鎖爭用是

每當您遇到線程代碼的性能問題時,就有機會開始指責鎖。 畢竟,常見的“知識”是鎖很慢并且限制了可伸縮性。 因此,如果您具備了這種“知識”并開始優化代碼并擺脫了鎖,那么您最終有可能引入令人討厭的并發錯誤,這些錯誤將在以后出現。

因此,了解競爭鎖和非競爭鎖之間的區別非常重要。 當一個線程試圖進入另一個線程當前執行的同步塊/方法時,發生鎖爭用。 現在,第二個線程被迫等待,直到第一個線程完成執行同步塊并釋放監視器。 當一次僅一個線程試圖執行同步代碼時,鎖保持無競爭狀態。

實際上,JVM中的同步針對無競爭的情況進行了優化,并且對于絕大多數應用程序而言,無競爭的鎖幾乎在執行期間沒有開銷。 因此,它不是性能應歸咎的鎖,而是爭執的鎖。 有了這些知識,讓我們看看如何減少爭用的可能性或減少爭用的時間。

保護數據而不是代碼

實現線程安全的一種快速方法是鎖定對整個方法的訪問。 例如,請看以下示例,該示例說明了構建在線撲克服務器的幼稚嘗試:

class GameServer {public Map<<String, List<Player>> tables = new HashMap<String, List<Player>>();public synchronized void join(Player player, Table table) {if (player.getAccountBalance() > table.getLimit()) {List<Player> tablePlayers = tables.get(table.getId());if (tablePlayers.size() < 9) {tablePlayers.add(player);}}}public synchronized void leave(Player player, Table table) {/*body skipped for brevity*/}public synchronized void createTable() {/*body skipped for brevity*/}public synchronized void destroyTable(Table table) {/*body skipped for brevity*/} }

作者的意圖一直很好–當新玩家加入桌臺時,必須保證坐在桌旁的玩家人數不會超過9人。

但是,只要這樣的解決方案實際上負責使玩家坐在桌子上,即使是在流量適中的撲克場所,該系統注定要通過等待釋放鎖的線程不斷觸發爭用事件。 鎖定塊包含帳戶余額和表限制檢查,這可能會涉及昂貴的操作,從而增加爭用的可能性和持續時間。

解決方案的第一步是通過將同步從方法聲明移到方法主體,確保我們保護數據,而不是代碼。 在上面的簡約示例中,它起初可能沒有太大變化。 但是讓我們考慮整個GameServer接口,而不僅僅是單個join()方法:

class GameServer {public Map<String, List<Player>> tables = new HashMap<String, List<Player>>();public void join(Player player, Table table) {synchronized (tables) {if (player.getAccountBalance() > table.getLimit()) {List<Player> tablePlayers = tables.get(table.getId());if (tablePlayers.size() < 9) {tablePlayers.add(player);}}}}public void leave(Player player, Table table) {/* body skipped for brevity */}public void createTable() {/* body skipped for brevity */}public void destroyTable(Table table) {/* body skipped for brevity */} }

原本似乎是很小的更改,但現在影響了整個班級的行為。 每當玩家加入表時,先前同步的方法就會鎖定在GameServer實例( this )上,并向試圖同時離開 table ()表的玩家引入爭用事件。 將鎖從方法簽名移到方法主體可推遲鎖定并減少爭用可能性。

縮小鎖定范圍

現在,在確保它是我們實際保護的數據而不是代碼之后,我們應該確保我們的解決方案僅鎖定必要的內容,例如,當上面的代碼被重寫如下時:

public class GameServer {public Map<String, List<Player>> tables = new HashMap<String, List<Player>>();public void join(Player player, Table table) {if (player.getAccountBalance() > table.getLimit()) {synchronized (tables) {List<Player> tablePlayers = tables.get(table.getId());if (tablePlayers.size() < 9) {tablePlayers.add(player);}}}}//other methods skipped for brevity }

那么檢查玩家帳戶余額的潛在耗時操作(可能涉及IO操作)現在已超出鎖定范圍。 請注意,引入該鎖僅是為了防止超出表的容量,并且帳戶余額檢查也不是該保護措施的一部分。

分開鎖

當我們看最后一個代碼示例時,您可以清楚地注意到整個數據結構都受到同一鎖的保護。 考慮到我們可能在這種結構中容納成千上萬張撲克桌,由于我們必須分別保護每張桌子以防容量溢出,因此它仍然會帶來爭用事件的高風險。

為此,有一種簡單的方法可以在每個表中引入單個鎖,例如以下示例:

public class GameServer {public Map<String, List<Player>> tables = new HashMap<String, List<Player>>();public void join(Player player, Table table) {if (player.getAccountBalance() > table.getLimit()) {List<Player> tablePlayers = tables.get(table.getId());synchronized (tablePlayers) {if (tablePlayers.size() < 9) {tablePlayers.add(player);}}}}//other methods skipped for brevity }

現在,如果我們僅同步訪問同一表而不是所有表的訪問 ,則可以大大降低鎖爭用的可能性。 例如,在我們的數據結構中有100個表,爭用的可能性現在比以前小100倍。

使用并發數據結構

另一個改進是刪除傳統的單線程數據結構,并使用為并行使用而明確設計的數據結構。 例如,當選擇ConcurrentHashMap來存儲所有撲克表時,將導致類似于以下代碼:

public class GameServer {public Map<String, List<Player>> tables = new ConcurrentHashMap<String, List<Player>>();public synchronized void join(Player player, Table table) {/*Method body skipped for brevity*/}public synchronized void leave(Player player, Table table) {/*Method body skipped for brevity*/}public synchronized void createTable() {Table table = new Table();tables.put(table.getId(), table);}public synchronized void destroyTable(Table table) {tables.remove(table.getId());} }

由于我們需要保護單個表的完整性,因此join()和leave()方法中的同步仍然像我們之前的示例中那樣。 因此, ConcurrentHashMap在這方面沒有幫助。 但是,由于我們還在創建新表并在createTable()和destroyTable()方法中銷毀表,因此對ConcurrentHashMap的所有這些操作都是完全并發的,從而允許增加或減少并行表的數量。

其他提示和技巧

  • 降低鎖的可見性。 在上面的示例中,這些鎖被聲明為公共鎖,因此對于世界都是可見的,因此有可能其他人也會通過鎖定您精心挑選的監視器來破壞您的工作。
  • 請查看java.util.concurrent.locks,以查看在那里實施的任何鎖定策略是否都會改善解決方案。
  • 使用原子操作。 我們在上面的示例中實際進行的簡單計數器增加實際上并不需要鎖定。 用AtomicInteger替換計數跟蹤中的Integer最適合此示例。

希望本文能幫助您解決鎖爭用問題,而無論您使用的是Plumbr 自動鎖檢測解決方案還是從線程轉儲中手動提取信息。

Plumbr是唯一通過解釋應用程序性能數據來自動檢測Java性能問題的根本原因的解決方案。

翻譯自: https://www.javacodegeeks.com/2015/01/improving-lock-performance-in-java.html

總結

以上是生活随笔為你收集整理的提高Java的锁性能的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。