add concurrentmap tests (#1051)

This commit is contained in:
Tian Baoqiang 2017-01-27 21:10:49 +08:00 committed by Grzegorz Piwowarek
parent fb61d1b187
commit 5d2abc9031
4 changed files with 416 additions and 0 deletions

View File

@ -0,0 +1,80 @@
package com.baeldung.java.concurrentmap;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
public class ConcurrentMapAggregateStatusTest {
private ExecutorService executorService;
private Map<String, Integer> concurrentMap;
private List<Integer> mapSizes;
private int MAX_SIZE = 100000;
@Before
public void init() {
executorService = Executors.newFixedThreadPool(2);
concurrentMap = new ConcurrentHashMap<>();
mapSizes = new ArrayList<>(MAX_SIZE);
}
@Test
public void givenConcurrentMap_whenSizeWithoutConcurrentUpdates_thenCorrect() throws InterruptedException {
Runnable collectMapSizes = () -> {
for (int i = 0; i < MAX_SIZE; i++) {
concurrentMap.put(String.valueOf(i), i);
mapSizes.add(concurrentMap.size());
}
};
Runnable retrieveMapData = () -> {
for (int i = 0; i < MAX_SIZE; i++) {
concurrentMap.get(String.valueOf(i));
}
};
executorService.execute(retrieveMapData);
executorService.execute(collectMapSizes);
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.MINUTES);
for (int i = 1; i <= MAX_SIZE; i++) {
assertEquals("map size should be consistently reliable", i, mapSizes
.get(i - 1)
.intValue());
}
assertEquals(MAX_SIZE, concurrentMap.size());
}
@Test
public void givenConcurrentMap_whenUpdatingAndGetSize_thenError() throws InterruptedException {
Runnable collectMapSizes = () -> {
for (int i = 0; i < MAX_SIZE; i++) {
mapSizes.add(concurrentMap.size());
}
};
Runnable updateMapData = () -> {
for (int i = 0; i < MAX_SIZE; i++) {
concurrentMap.put(String.valueOf(i), i);
}
};
executorService.execute(updateMapData);
executorService.execute(collectMapSizes);
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.MINUTES);
assertNotEquals("map size collected with concurrent updates not reliable", MAX_SIZE, mapSizes
.get(MAX_SIZE - 1)
.intValue());
assertEquals(MAX_SIZE, concurrentMap.size());
}
}

View File

@ -0,0 +1,160 @@
package com.baeldung.java.concurrentmap;
import org.junit.Before;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static org.junit.Assert.assertNull;
public class ConcurrentMapNullKeyValueTest {
ConcurrentMap<String, Object> concurrentMap;
@Before
public void setup() {
concurrentMap = new ConcurrentHashMap<>();
}
@Test(expected = NullPointerException.class)
public void givenConcurrentHashMap_whenGetWithNullKey_thenThrowsNPE() {
concurrentMap.get(null);
}
@Test(expected = NullPointerException.class)
public void givenConcurrentHashMap_whenGetOrDefaultWithNullKey_thenThrowsNPE() {
concurrentMap.getOrDefault(null, new Object());
}
@Test(expected = NullPointerException.class)
public void givenConcurrentHashMap_whenPutWithNullKey_thenThrowsNPE() {
concurrentMap.put(null, new Object());
}
@Test(expected = NullPointerException.class)
public void givenConcurrentHashMap_whenPutNullValue_thenThrowsNPE() {
concurrentMap.put("test", null);
}
@Test(expected = NullPointerException.class)
public void givenConcurrentHashMapAndKeyAbsent_whenPutWithNullKey_thenThrowsNPE() {
concurrentMap.putIfAbsent(null, new Object());
}
@Test(expected = NullPointerException.class)
public void givenConcurrentHashMapAndMapWithNullKey_whenPutNullKeyMap_thenThrowsNPE() {
Map<String, Object> nullKeyMap = new HashMap<>();
nullKeyMap.put(null, new Object());
concurrentMap.putAll(nullKeyMap);
}
@Test(expected = NullPointerException.class)
public void givenConcurrentHashMapAndMapWithNullValue_whenPutNullValueMap_thenThrowsNPE() {
Map<String, Object> nullValueMap = new HashMap<>();
nullValueMap.put("test", null);
concurrentMap.putAll(nullValueMap);
}
@Test(expected = NullPointerException.class)
public void givenConcurrentHashMap_whenReplaceNullKeyWithValues_thenThrowsNPE() {
concurrentMap.replace(null, new Object(), new Object());
}
@Test(expected = NullPointerException.class)
public void givenConcurrentHashMap_whenReplaceWithNullNewValue_thenThrowsNPE() {
Object o = new Object();
concurrentMap.replace("test", o, null);
}
@Test(expected = NullPointerException.class)
public void givenConcurrentHashMap_whenReplaceOldNullValue_thenThrowsNPE() {
Object o = new Object();
concurrentMap.replace("test", null, o);
}
@Test(expected = NullPointerException.class)
public void givenConcurrentHashMap_whenReplaceWithNullValue_thenThrowsNPE() {
concurrentMap.replace("test", null);
}
@Test(expected = NullPointerException.class)
public void givenConcurrentHashMap_whenReplaceNullKey_thenThrowsNPE() {
concurrentMap.replace(null, "test");
}
@Test(expected = NullPointerException.class)
public void givenConcurrentHashMap_whenReplaceAllMappingNull_thenThrowsNPE() {
concurrentMap.put("test", new Object());
concurrentMap.replaceAll((s, o) -> null);
}
@Test(expected = NullPointerException.class)
public void givenConcurrentHashMap_whenRemoveNullKey_thenThrowsNPE() {
concurrentMap.remove(null);
}
@Test(expected = NullPointerException.class)
public void givenConcurrentHashMap_whenRemoveNullKeyWithValue_thenThrowsNPE() {
concurrentMap.remove(null, new Object());
}
@Test(expected = NullPointerException.class)
public void givenConcurrentHashMap_whenMergeNullKeyWithValue_thenThrowsNPE() {
concurrentMap.merge(null, new Object(), (o, o2) -> o2);
}
@Test(expected = NullPointerException.class)
public void givenConcurrentHashMap_whenMergeKeyWithNullValue_thenThrowsNPE() {
concurrentMap.put("test", new Object());
concurrentMap.merge("test", null, (o, o2) -> o2);
}
@Test(expected = NullPointerException.class)
public void givenConcurrentHashMapAndAssumeKeyAbsent_whenComputeWithNullKey_thenThrowsNPE() {
concurrentMap.computeIfAbsent(null, s -> s);
}
@Test(expected = NullPointerException.class)
public void givenConcurrentHashMapAndAssumeKeyPresent_whenComputeWithNullKey_thenThrowsNPE() {
concurrentMap.computeIfPresent(null, (s, o) -> o);
}
@Test(expected = NullPointerException.class)
public void givenConcurrentHashMap_whenComputeWithNullKey_thenThrowsNPE() {
concurrentMap.compute(null, (s, o) -> o);
}
@Test
public void givenConcurrentHashMap_whenMergeKeyRemappingNull_thenRemovesMapping() {
Object oldValue = new Object();
concurrentMap.put("test", oldValue);
concurrentMap.merge("test", new Object(), (o, o2) -> null);
assertNull(concurrentMap.get("test"));
}
@Test
public void givenConcurrentHashMapAndKeyAbsent_whenComputeWithKeyRemappingNull_thenRemainsAbsent() {
concurrentMap.computeIfPresent("test", (s, o) -> null);
assertNull(concurrentMap.get("test"));
}
@Test
public void givenKeyPresent_whenComputeIfPresentRemappingNull_thenMappingRemoved() {
Object oldValue = new Object();
concurrentMap.put("test", oldValue);
concurrentMap.computeIfPresent("test", (s, o) -> null);
assertNull(concurrentMap.get("test"));
}
@Test
public void givenKeyPresent_whenComputeRemappingNull_thenMappingRemoved() {
Object oldValue = new Object();
concurrentMap.put("test", oldValue);
concurrentMap.compute("test", (s, o) -> null);
assertNull(concurrentMap.get("test"));
}
}

View File

@ -0,0 +1,98 @@
package com.baeldung.java.concurrentmap;
import org.junit.Test;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
public class ConcurrentMapPerformanceTest {
@Test
public void givenMaps_whenGetPut500KTimes_thenConcurrentMapFaster() throws Exception {
Map<String, Object> hashtable = new Hashtable<>();
Map<String, Object> synchronizedHashMap = Collections.synchronizedMap(new HashMap<>());
Map<String, Object> concurrentHashMap = new ConcurrentHashMap<>();
long hashtableAvgRuntime = timeElapseForGetPut(hashtable);
long syncHashMapAvgRuntime = timeElapseForGetPut(synchronizedHashMap);
long concurrentHashMapAvgRuntime = timeElapseForGetPut(concurrentHashMap);
assertTrue(hashtableAvgRuntime > concurrentHashMapAvgRuntime);
assertTrue(syncHashMapAvgRuntime > concurrentHashMapAvgRuntime);
System.out.println(String.format("Hashtable: %s, syncHashMap: %s, ConcurrentHashMap: %s", hashtableAvgRuntime, syncHashMapAvgRuntime, concurrentHashMapAvgRuntime));
}
private long timeElapseForGetPut(Map<String, Object> map) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(4);
long startTime = System.nanoTime();
for (int i = 0; i < 4; i++) {
executorService.execute(() -> {
for (int j = 0; j < 500_000; j++) {
int value = ThreadLocalRandom
.current()
.nextInt(10000);
String key = String.valueOf(value);
map.put(key, value);
map.get(key);
}
});
}
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.MINUTES);
return (System.nanoTime() - startTime) / 500_000;
}
@Test
public void givenConcurrentMap_whenKeyWithSameHashCode_thenPerformanceDegrades() throws InterruptedException {
class SameHash {
@Override
public int hashCode() {
return 1;
}
}
int executeTimes = 5000;
Map<SameHash, Integer> mapOfSameHash = new ConcurrentHashMap<>();
ExecutorService executorService = Executors.newFixedThreadPool(2);
long sameHashStartTime = System.currentTimeMillis();
for (int i = 0; i < 2; i++) {
executorService.execute(() -> {
for (int j = 0; j < executeTimes; j++) {
mapOfSameHash.put(new SameHash(), 1);
}
});
}
executorService.shutdown();
executorService.awaitTermination(5, TimeUnit.SECONDS);
long mapOfSameHashDuration = System.currentTimeMillis() - sameHashStartTime;
Map<Object, Integer> mapOfDefaultHash = new ConcurrentHashMap<>();
executorService = Executors.newFixedThreadPool(2);
long defaultHashStartTime = System.currentTimeMillis();
for (int i = 0; i < 2; i++) {
executorService.execute(() -> {
for (int j = 0; j < executeTimes; j++) {
mapOfDefaultHash.put(new Object(), 1);
}
});
}
executorService.shutdown();
executorService.awaitTermination(5, TimeUnit.SECONDS);
long mapOfDefaultHashDuration = System.currentTimeMillis() - defaultHashStartTime;
assertEquals(executeTimes * 2, mapOfDefaultHash.size());
assertNotEquals(executeTimes * 2, mapOfSameHash.size());
System.out.println(String.format("same-hash: %s, default-hash: %s", mapOfSameHashDuration, mapOfDefaultHashDuration));
assertTrue("same hashCode() should greatly degrade performance", mapOfSameHashDuration > mapOfDefaultHashDuration * 10);
}
}

View File

@ -0,0 +1,78 @@
package com.baeldung.java.concurrentmap;
import org.junit.Test;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.*;
public class ConcurretMapMemoryConsistencyTest {
@Test
public void givenConcurrentMap_whenSumParallel_thenCorrect() throws Exception {
Map<String, Integer> map = new ConcurrentHashMap<>();
List<Integer> sumList = parallelSum100(map, 1000);
assertEquals(1, sumList
.stream()
.distinct()
.count());
long wrongResultCount = sumList
.stream()
.filter(num -> num != 100)
.count();
assertEquals(0, wrongResultCount);
}
@Test
public void givenHashtable_whenSumParallel_thenCorrect() throws Exception {
Map<String, Integer> map = new Hashtable<>();
List<Integer> sumList = parallelSum100(map, 1000);
assertEquals(1, sumList
.stream()
.distinct()
.count());
long wrongResultCount = sumList
.stream()
.filter(num -> num != 100)
.count();
assertEquals(0, wrongResultCount);
}
@Test
public void givenHashMap_whenSumParallel_thenError() throws Exception {
Map<String, Integer> map = new HashMap<>();
List<Integer> sumList = parallelSum100(map, 100);
assertNotEquals(1, sumList
.stream()
.distinct()
.count());
long wrongResultCount = sumList
.stream()
.filter(num -> num != 100)
.count();
assertTrue(wrongResultCount > 0);
}
private List<Integer> parallelSum100(Map<String, Integer> map, int executionTimes) throws InterruptedException {
List<Integer> sumList = new ArrayList<>(1000);
for (int i = 0; i < executionTimes; i++) {
map.put("test", 0);
ExecutorService executorService = Executors.newFixedThreadPool(4);
for (int j = 0; j < 10; j++) {
executorService.execute(() -> {
for (int k = 0; k < 10; k++)
map.computeIfPresent("test", (key, value) -> value + 1);
});
}
executorService.shutdown();
executorService.awaitTermination(5, TimeUnit.SECONDS);
sumList.add(map.get("test"));
}
return sumList;
}
}