我试图通过使用代码来评估这些概念。这就是我最终的结果
public void runCWith3Threads() {
// mesure add with 3 threads
for (int i = 0; i < 10; i++) {
Map<Integer, Person> shm = Collections.synchronizedMap(new HashMap<Integer, Person>());
Map<Integer, Person> chm = new ConcurrentHashMap<Integer, Person>();
MapThread sm1 = new MapThread(shm, 0, 20000, "sm1");
MapThread sm2 = new MapThread(shm, 20000, 30000, "sm2");
MapThread sm3 = new MapThread(shm, 30000, 50000, "sm3");
sm1.start();sm2.start();sm3.start();
while (true) {
try {
sm1.join();
sm2.join();
sm3.join();
break;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
long secondMax = sm1.time > sm2.time ? sm1.time : sm2.time;
long firstMax = secondMax > sm3.time ? secondMax : sm3.time;
System.out.println("Millisec of SynchronizedMap cost: " + firstMax);
MapThread m1 = new MapThread(chm, 0, 20000, "m1");
MapThread m2 = new MapThread(chm, 20000, 30000, "m2");
MapThread m3 = new MapThread(chm, 30000, 50000, "m3");
m1.start();m2.start();m3.start();
while (true) {
try {
m1.join();
m2.join();
m3.join();
break;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
secondMax = m1.time > m2.time ? m1.time : m2.time;
firstMax = secondMax > m3.time ? secondMax : m3.time;
System.out.println("Millisec of ConcurrentHashMap cost: " + firstMax);
System.out.println();
}
}
public class MapThread extends Thread {
Map<Integer, Person> map;
int from;
int to;
long time;
String name;
public MapThread(Map<Integer, Person> map, int from, int to, String name) {
this.map = map;
this.from = from;
this.to = to;
this.name = name;
}
public void run() {
long start = System.currentTimeMillis();
for (int i = from; i < to; i++) {
map.put(i, new Person());
}
long end = System.currentTimeMillis();
time = end - start;
return;
}
}
我期望的是,在代码运行后,ConcurrentHashMap
的结果会更快,因为它允许多次插入地图。对于SynchronizedMap
,由于每个线程都在等待前一个线程完成(映射已同步),因此一旦运行单个线程环境,代码将起到相同的作用
然而,结果并没有完全反映出我的预期
Millisec of SynchronizedMap cost: 250
Millisec of ConcurrentHashMap cost: 203
Millisec of SynchronizedMap cost: 171
Millisec of ConcurrentHashMap cost: 172
Millisec of SynchronizedMap cost: 172
Millisec of ConcurrentHashMap cost: 188
Millisec of SynchronizedMap cost: 171
Millisec of ConcurrentHashMap cost: 172
Millisec of SynchronizedMap cost: 187
Millisec of ConcurrentHashMap cost: 172
Millisec of SynchronizedMap cost: 171
Millisec of ConcurrentHashMap cost: 189
Millisec of SynchronizedMap cost: 187
Millisec of ConcurrentHashMap cost: 171
Millisec of SynchronizedMap cost: 188
Millisec of ConcurrentHashMap cost: 171
Millisec of SynchronizedMap cost: 172
Millisec of ConcurrentHashMap cost: 172
Millisec of SynchronizedMap cost: 171
Millisec of ConcurrentHashMap cost: 188
为什么?
更新
Map<Integer, Person> chm = new ConcurrentHashMap<Integer, Person>(100000, 10, 3);
我有结果
Millisec of SynchronizedMap cost: 208
Millisec of ConcurrentHashMap cost: 216
Millisec of SynchronizedMap cost: 255
Millisec of ConcurrentHashMap cost: 196
Map<Integer, Person> chm = new ConcurrentHashMap<Integer, Person>(100000);
我有结果
Millisec of SynchronizedMap cost: 204
Millisec of ConcurrentHashMap cost: 283
Millisec of SynchronizedMap cost: 203
Millisec of ConcurrentHashMap cost: 200
答案 0 :(得分:1)
如果你正在做基准测试,你应该:
如果我创建类似于您的基准测试的基准测试,我会得到以下结果:
Warmup...
Benchmark...
4 * 500000: 0.22s / 0.04s
4 * 1000000: 0.55s / 0.10s
4 * 1500000: 1.10s / 0.16s
4 * 2000000: 0.90s / 0.19s
4 * 2500000: 1.68s / 0.25s
第一个数字表示线程数,第二个数字表示int范围的大小,第三个数字表示 synchronized Map 的持续时间,第四个数字表示 ConcurrentHashMap的持续时间。如您所见, ConcurrentHashMap 在所有情况下都要快得多。
您可以在下面找到整个Java代码。请注意,它使用了Java 8中的功能。但是,这应该对结果没有影响:
public static void main(String... args) {
System.out.println("Warmup...");
for (int i = 0; i < 10000; ++i) {
test(Collections.synchronizedMap(new HashMap<>()), 2, 1000);
test(new ConcurrentHashMap<>(), 2, 1000);
}
System.out.println("Benchmark...");
for (int i = 0; i < 5; ++i) {
int threads = 4;
int range = 500000 * (i + 1);
System.out.printf("%2d * %7d: %s / %s\n",
threads, range,
test(Collections.synchronizedMap(new HashMap<>()), threads, range),
test(new ConcurrentHashMap<>(), threads, range));
}
}
public static String test(Map<Integer,Object> map, int threads, int range) {
long duration = IntStream.range(0, 10)
.mapToLong(i -> execute(
IntStream.range(0, threads)
.<Runnable>mapToObj(t -> () -> bulkPut(map, t * range, (t + 1) * range, new Object()))
.toArray(Runnable[]::new)))
.min().getAsLong();
return String.format("%4.2fs",
duration / 1000.0, threads, range);
}
public static <T> void bulkPut(Map<Integer,T> map, int from, int to, T value) {
for (int i = from; i < to; ++i) {
map.put(i, value);
}
}
public static long execute(Runnable... runnables) {
List<Thread> threads = new ArrayList<>();
AtomicLong duration = new AtomicLong();
for (Runnable runnable : runnables) {
Thread thread = new Thread(() -> {
long start = System.currentTimeMillis();
try {
runnable.run();
} finally {
long elapsed = System.currentTimeMillis() - start;
duration.accumulateAndGet(elapsed, Math::max);
}
});
thread.start();
threads.add(thread);
}
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
return duration.get();
}