簡體   English   中英

以線程安全的方式更新ConcurrentHashMap

[英]Update the ConcurrentHashMap in a thread safe way

我正在對客戶端代碼中的幾種方法進行基准測試,以查看這些方法花費了多少時間。 因此,我編寫了一個多線程程序,該程序將產生多個線程,然后我將測量這些方法占用客戶端代碼和服務器端代碼的時間。

我有一個ConcurrentHashMap聲明為

public static ConcurrentHashMap<Long, Long> map = new ConcurrentHashMap<Long, Long>();

現在,我試圖找出X毫秒內返回了多少呼叫,因此我將這些數字存儲在上圖中。 因此,上面的地圖將存儲這樣的內容-

KEY will be Milliseconds
and VALUE will be Number of calls came back in those milliseconds.

下面是我得到的代碼。

final long start = System.nanoTime();

method.getInformation();

final long end = System.nanoTime() - start;
final long key = end / 1000000L;
boolean done = false;
while(!done) {
    Long oldValue = map.putIfAbsent(key, 1L);
    if(oldValue != null) {
        done = map.replace(key, oldValue, oldValue + 1);
    } else {
        done = true;
    }
}

我正在嘗試查看上面的代碼是否有問題?

我要問的是我是否正在運行Multithreading program ,所以server CPU usage通常達到80-90% 但是,如果我從服務器端代碼中刪除上述基准測試代碼,則CPU usage不會達到80-90% 所以這就是原因,我正在嘗試查看是否有更好的方法來編寫可以滿足上述相同情況的上述基准測試代碼?

謝謝您的幫助。

更新:-

我在unix-中使用TOP命令監視服務器端的CPU和內存使用情況

top - 17:13:18 up 25 days, 23:24,  4 users,  load average: 1.72, 1.43, 1.04
Tasks: 114 total,   1 running, 111 sleeping,   0 stopped,   2 zombie
Cpu0  : 79.2%us,  5.8%sy,  0.0%ni, 23.1%id,  0.0%wa,  0.0%hi,  1.9%si,  0.0%st
Cpu1  : 83.7%us,  3.7%sy,  0.0%ni, 40.7%id,  0.0%wa,  0.0%hi,  1.9%si,  0.0%st
Mem:   6127684k total,  5122736k used,  1004948k free,   240436k buffers
Swap:  1331196k total,        0k used,  1331196k free,  2485984k cached

下面是運行時的快照,我剛剛捕獲了

因此,我對您的時間檢查代碼進行了快速測試,並能夠在我的四核2013 Macbook Pro上在8.8秒內運行其中的一百萬 這意味着每個調用的成本不到8ns(因為我的計時是掛鍾,並且考慮了線程的問題)。 這是非常便宜的IMO。 我使用100個線程將0到1000000的隨機值固定下來,最終得到了631k條目的堆大小,這似乎占用了不到20-30mb的內核。

在我看來,您的通話時間代碼不是很大的性能問題。 我想知道每個線程是否都占用大量內存,而您的代碼唯一要做的就是遇到內存障礙。 volatile字段的單個更新替換代碼或強制設置障礙以查看是否獲得相同的行為可能會很有趣。


我不會立即看到您的代碼有任何問題,也不會認為這將是一個很大的性能問題。

我確實想知道這張地圖有多大。 似乎如果長時間運行它並在映射中有大量條目,則內存需求將變得非常重要。 您可以嘗試使用-Xmx參數增加內存。

如果是內存,則CPU問題可能與GC有關。 我將使用Jconsole來查看內存是否不足。

這里存在一些潛在的性能問題。

  1. 您的基准測試代碼是向每個請求添加對System.nanoTime()兩個調用。 根據請求中涉及的實際工作量,這些調用可能很重要。

  2. 當您嘗試使用ConcurrentHashMap減少並發瓶頸時,並不能完全消除它。 確實,如果您有很多請求花費相同的毫秒數,則該特定計數器上將出現瓶頸。 如果在特定計數器上存在爭用,則將導致“旋轉”,因為不同的線程競爭對其進行更新。

  3. 如果(假設)地圖很大,您可能會開始遇到與內存使用有關的問題; 例如,更大的工作集,增加的緩存和TLB爭用,抖動。

但最重要的是,您添加的任何性能評估代碼都可能會改變系統的性能特征。

性能/資源使用問題:

  • 在每次調用期間讀寫HashMap可能會很昂貴,而並發的hashmap則更是如此。 不是必需的
  • 您還可以調用System.nanoTime(),這可能會很昂貴,並且每次迭代都需要這樣做。 不是必需的

我的建議

  • 在執行方法的次數上聲明一個靜態運行計數器
  • 在同步塊中,增加計數器
  • 當計數器達到門限(例如1000), 然后確定在毫秒經過時間
  • 在列表中記錄經過的時間

     static List<long> methodElapsedMillisList = new ArrayList<long>(); final int methodCountBlock = 1000; static long methodCountStartMillis = System.currentTimeMillis(); static int methodCount = 0; static Object methodCountMonitorObj = new Object(); // within instrumented method: synchronised (methodCountMonitorObj) { methodCount++; if (methodCount > methodCountBlock ) { long newMethodCountStartMillis = System.currentTimeMillis(); long elapsedMillis = newMethodCountStartMillis - methodCountStartMillis; methodCountStartMillis = newMethodCounterStartMillis; methodElapsedMillisList.add(elapsedMillis); methodCount = 0; } } 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM