[英]java map concurrent update
我正在嘗試使用int
值創建Map
並通過多個線程增加它們。 兩個或多個線程可能會增加相同的密鑰。
ConcurrentHashMap
文檔對我來說非常不清楚,因為它表示:
Retrieval operations (including get) generally do not block, so may overlap with update operations (including put and remove)
我想知道使用ConcurrentHashMap
的以下代碼是否可以正常工作 :
myMap.put(X, myMap.get(X) + 1);
如果沒有,我該怎么辦呢?
並發映射不會幫助您的代碼的線程安全。 你仍然可以得到比賽條件:
Thread-1: x = 1, get(x)
Thread-2: x = 1, get(x)
Thread-1: put(x + 1) => 2
Thread-2: put(x + 1) => 2
發生了兩次增量,但你仍然只獲得+1。 只有當您的目標是修改地圖本身而不是其內容時,才需要並發地圖。 即使最簡單的HashMap也是並發讀取的線程安全 ,因為地圖不再變異。
因此,不需要基本類型的線程安全映射,您需要該類型的線程安全包裝器。 來自java.util.concurrent.atomic
東西,或者如果需要任意類型,請滾動自己的鎖定容器。
一個想法是將ConcurrentMap與AtomicInteger相結合,后者具有增量方法。
AtomicInteger current = map.putIfAbsent(key, new AtomicInteger(1));
int newValue = current == null ? 1 :current.incrementAndGet();
或者(更有效地,感謝@Keppil)使用額外的代碼保護來避免不必要的對象創建:
AtomicInteger current = map.get(key);
if (current == null){
current = map.putIfAbsent(key, new AtomicInteger(1));
}
int newValue = current == null ? 1 : current.incrementAndGet();
最佳實踐。 您可以使用HashMap和AtomicInteger。 測試代碼:
public class HashMapAtomicIntegerTest {
public static final int KEY = 10;
public static void main(String[] args) {
HashMap<Integer, AtomicInteger> concurrentHashMap = new HashMap<Integer, AtomicInteger>();
concurrentHashMap.put(HashMapAtomicIntegerTest.KEY, new AtomicInteger());
List<HashMapAtomicCountThread> threadList = new ArrayList<HashMapAtomicCountThread>();
for (int i = 0; i < 500; i++) {
HashMapAtomicCountThread testThread = new HashMapAtomicCountThread(
concurrentHashMap);
testThread.start();
threadList.add(testThread);
}
int index = 0;
while (true) {
for (int i = index; i < 500; i++) {
HashMapAtomicCountThread testThread = threadList.get(i);
if (testThread.isAlive()) {
break;
} else {
index++;
}
}
if (index == 500) {
break;
}
}
System.out.println("The result value should be " + 5000000
+ ",actually is"
+ concurrentHashMap.get(HashMapAtomicIntegerTest.KEY));
}
}
class HashMapAtomicCountThread extends Thread {
HashMap<Integer, AtomicInteger> concurrentHashMap = null;
public HashMapAtomicCountThread(
HashMap<Integer, AtomicInteger> concurrentHashMap) {
this.concurrentHashMap = concurrentHashMap;
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
concurrentHashMap.get(HashMapAtomicIntegerTest.KEY)
.getAndIncrement();
}
}
}
結果:
結果值應為5000000,實際為50000
或者HashMap和synchronized,但比前者慢得多
public class HashMapSynchronizeTest {
public static final int KEY = 10;
public static void main(String[] args) {
HashMap<Integer, Integer> hashMap = new HashMap<Integer, Integer>();
hashMap.put(KEY, 0);
List<HashMapSynchronizeThread> threadList = new ArrayList<HashMapSynchronizeThread>();
for (int i = 0; i < 500; i++) {
HashMapSynchronizeThread testThread = new HashMapSynchronizeThread(
hashMap);
testThread.start();
threadList.add(testThread);
}
int index = 0;
while (true) {
for (int i = index; i < 500; i++) {
HashMapSynchronizeThread testThread = threadList.get(i);
if (testThread.isAlive()) {
break;
} else {
index++;
}
}
if (index == 500) {
break;
}
}
System.out.println("The result value should be " + 5000000
+ ",actually is" + hashMap.get(KEY));
}
}
class HashMapSynchronizeThread extends Thread {
HashMap<Integer, Integer> hashMap = null;
public HashMapSynchronizeThread(
HashMap<Integer, Integer> hashMap) {
this.hashMap = hashMap;
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
synchronized (hashMap) {
hashMap.put(HashMapSynchronizeTest.KEY,
hashMap
.get(HashMapSynchronizeTest.KEY) + 1);
}
}
}
}
結果:
結果值應為5000000,實際為50000
使用ConcurrentHashMap會得到錯誤的結果。
public class ConcurrentHashMapTest {
public static final int KEY = 10;
public static void main(String[] args) {
ConcurrentHashMap<Integer, Integer> concurrentHashMap = new ConcurrentHashMap<Integer, Integer>();
concurrentHashMap.put(KEY, 0);
List<CountThread> threadList = new ArrayList<CountThread>();
for (int i = 0; i < 500; i++) {
CountThread testThread = new CountThread(concurrentHashMap);
testThread.start();
threadList.add(testThread);
}
int index = 0;
while (true) {
for (int i = index; i < 500; i++) {
CountThread testThread = threadList.get(i);
if (testThread.isAlive()) {
break;
} else {
index++;
}
}
if (index == 500) {
break;
}
}
System.out.println("The result value should be " + 5000000
+ ",actually is" + concurrentHashMap.get(KEY));
}
}
class CountThread extends Thread {
ConcurrentHashMap<Integer, Integer> concurrentHashMap = null;
public CountThread(ConcurrentHashMap<Integer, Integer> concurrentHashMap) {
this.concurrentHashMap = concurrentHashMap;
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
concurrentHashMap.put(ConcurrentHashMapTest.KEY,
concurrentHashMap.get(ConcurrentHashMapTest.KEY) + 1);
}
}
}
結果:
結果值應為5000000,實際為11759
您可以將操作放在synchronized (myMap) {...}
塊中。
您當前的代碼會同時更改地圖的值,因此無法使用。
如果多個線程可以put
值放入映射中,則必須使用ConcurrentHashMap等並發映射以及非線程安全值(如Integer
。 然后, ConcurrentMap.replace將執行您想要的操作(或使用AtomicInteger
來簡化代碼)。
如果您的線程只會更改地圖的值(而不是添加/更改鍵),那么您可以使用標准地圖存儲線程安全值,如AtomicInteger 。 然后你的線程將調用: map.get(key).incrementAndGet()
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.