我有一个名为statusCountMap的Java HashMap
调用size()会产生30.
但如果我手动计算条目,那就是31
这是我的一个TestNG单元测试。下面的结果来自Eclipse的显示窗口(类型代码 - >突出显示 - >点击评估所选文本的显示结果)。
statusCountMap.size() (int) 30 statusCountMap.keySet().size() (int) 30 statusCountMap.values().size() (int) 30 statusCountMap (java.util.HashMap) {40534-INACTIVE=2, 40526-INACTIVE=1, 40528-INACTIVE=1, 40492-INACTIVE=3, 40492-TOTAL=4, 40513-TOTAL=6, 40532-DRAFT=4, 40524-TOTAL=7, 40526-DRAFT=2, 40528-ACTIVE=1, 40524-DRAFT=2, 40515-ACTIVE=1, 40513-DRAFT=4, 40534-DRAFT=1, 40514-TOTAL=3, 40529-DRAFT=4, 40515-TOTAL=3, 40492-ACTIVE=1, 40528-TOTAL=4, 40514-DRAFT=2, 40526-TOTAL=3, 40524-INACTIVE=2, 40515-DRAFT=2, 40514-ACTIVE=1, 40534-TOTAL=3, 40513-ACTIVE=2, 40528-DRAFT=2, 40532-TOTAL=4, 40524-ACTIVE=3, 40529-ACTIVE=1, 40529-TOTAL=5} statusCountMap.entrySet().size() (int) 30
是什么给出的?有没有人经历过这个? 我很确定statusCountMap此时没有被修改 有两种方法(让我们称之为methodA和methodB)通过重复调用incrementCountInMap来修改statusCountMap 并发。
private void incrementCountInMap(Map map, Long id, String qualifier) { String key = id + "-" + qualifier; if (map.get(key) == null) { map.put(key, 0); } synchronized (map) { map.put(key, map.get(key).intValue() + 1); } }方法D是我遇到问题的地方。 methodD有一个TestNG @dependsOnMethods = {“methodA”,“methodB”}所以当methodD正在执行时,statusCountMap已经非常静态了。 我提到这个是因为它可能是TestNG中的一个错误 我正在使用Sun JDK 1.6.0_24。 TestNG是testng-5.9-jdk15.jar
嗯...重读我的帖子之后,可能是因为同步块外同时执行map.get(key)== null& map.put(key,0)导致了这个问题?
答案 0 :(得分:12)
我相信如果在将密钥添加到HashMap后修改密钥,则可以实现此目的。
但是在您的情况下,似乎只是在没有正确同步的情况下在两个线程中修改相同映射的情况。例如在线程A中,map.put(key,0),线程B map.put(key2,0)可以产生1或2的大小。如果对remove执行相同操作,则最终可能会比您大应该。
答案 1 :(得分:4)
嗯...重读我的帖子之后,可能是因为同步块外同时执行map.get(key)== null& map.put(key,0)导致了这个问题?
总之......是的。
HashMap不是线程安全的。因此,如果有两个线程在没有正确同步的情况下更新HashMap的任何点,则映射可能会进入不一致状态。即使其中一个线程只是在读取,该线程也可以看到地图的不一致状态。
编写该方法的正确方法是:
private void incrementCountInMap(Map map, Long id, String qualifier) {
String key = id + "-" + qualifier;
synchronized (map) {
Integer count = map.get(key);
map.put(key, count == null ? 1 : count + 1);
}
}
答案 2 :(得分:4)
如果您使用默认的初始容量16并以非线程安全的方式访问它们,那么您的成熟状态就会变得不稳定。大小是在输入每个项目时更新地图中的州成员(大小++)。这是因为地图本身是一个链表的数组,并不能真正返回其实际大小,因为它不表示它包含的项目数。一旦Map达到初始容量的百分比(load_factor),它就必须调整自身大小以容纳更多项目。如果流氓线程正在尝试添加项目,因为地图正在调整大小,谁知道地图将处于什么状态。
答案 3 :(得分:2)
第一个map.put(..)未同步的问题。要么同步它,要么使用Collections.synchronizedMap(..)
。测试用例:
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class Test {
public static void main(String... args) throws InterruptedException {
final Random random = new Random();
final int max = 10;
for (int j = 0; j < 100000; j++) {
// final Map<String, Integer> map = Collections.synchronizedMap(new HashMap<String, Integer>());
final HashMap<String, Integer> map = new HashMap<String, Integer>();
Thread t = new Thread() {
public void run() {
for (int i = 0; i < 100; i++) {
incrementCountInMap(map, random.nextInt(max));
}
}
};
t.start();
for (int i = 0; i < 100; i++) {
incrementCountInMap(map, random.nextInt(max));
}
t.join();
if (map.size() != max) {
System.out.println("size: " + map.size() + " entries: " + map);
}
}
}
static void incrementCountInMap(Map<String, Integer> map, int id) {
String key = "k" + id;
if (map.get(key) == null) {
map.put(key, 0);
}
synchronized (map) {
map.put(key, map.get(key).intValue() + 1);
}
}
}
我得到了一些结果:
size: 11 entries: {k3=24, k4=20, k5=16, k6=30, k7=16, k8=18, k9=11, k0=18, k1=16, k1=13, k2=18}
size: 11 entries: {k3=18, k4=19, k5=21, k6=20, k7=18, k8=26, k9=20, k0=16, k1=25, k2=15}
size: 11 entries: {k3=25, k4=20, k5=27, k6=15, k7=17, k8=17, k9=24, k0=21, k1=16, k1=1, k2=17}
size: 11 entries: {k3=13, k4=21, k5=18, k6=21, k7=13, k8=17, k9=25, k0=20, k1=23, k2=28}
size: 11 entries: {k3=21, k4=25, k5=19, k6=12, k7=17, k8=14, k9=23, k0=24, k1=26, k2=18}
size: 9 entries: {k3=13, k4=17, k5=23, k6=24, k7=18, k8=19, k9=28, k0=21, k1=17, k2=20}
size: 9 entries: {k3=15, k4=24, k5=21, k6=18, k7=21, k8=30, k9=20, k0=17, k1=15, k2=19}
size: 11 entries: {k3=15, k4=13, k5=21, k6=21, k7=15, k8=19, k9=23, k0=30, k1=15, k2=27}
size: 11 entries: {k3=29, k4=15, k5=19, k6=19, k7=15, k8=23, k9=14, k0=31, k1=18, k2=12}
size: 11 entries: {k3=17, k4=18, k5=20, k6=11, k6=13, k7=20, k8=22, k9=30, k0=12, k1=21, k2=16}