一、场景描述:
有一个业务涉及异步使用ConcurrentHashMap里面的entry对象。类型是Entry<String,-String>获取他的key和value进行业务操作。
内容涉及到map的迭代,当我通过迭代获取到entry对象的时候,发现后面无论设置什么值。之前获取到的entry对象,通过getValue方法得到的结果一直不变。
二、先说原因:
ConcurrentHashMap 内部迭代 entry 实现的迭代器是
// java.util.concurrent.ConcurrentHashMap.EntryIterator
static final class EntryIterator<K,V> extends BaseIterator<K,V>
implements Iterator<Map.Entry<K,V>> {
EntryIterator(Node<K,V>[] tab, int index, int size, int limit,
ConcurrentHashMap<K,V> map) {
super(tab, index, size, limit, map);
}
public final Map.Entry<K,V> next() {
Node<K,V> p;
if ((p = next) == null)
throw new NoSuchElementException();
K k = p.key;
V v = p.val;
lastReturned = p;
advance();
return new MapEntry<K,V>(k, v, map);
}
}
他的 next() 方法是获取到 key 和 value 以后重新构造一个 entry 对象返回的,所以迭代获取到的对象和map里面一直操作的对象不一样的,所以后面表现出的行为也不一样了。
另外new ArrayList() 内部也会调用 toArray() 操作集合内元素。ConcurrentHashMap 实现的 toArray() 方法,也是通过迭代去给数组赋值,所以获取到的数组元素都是通过上面 new MapEntry获取得到的新对象。
public final Object[] toArray() {
long sz = map.mappingCount();
if (sz > MAX_ARRAY_SIZE)
throw new OutOfMemoryError(oomeMsg);
int n = (int)sz;
Object[] r = new Object[n];
int i = 0;
for (E e : this) {
if (i == n) {
if (n >= MAX_ARRAY_SIZE)
throw new OutOfMemoryError(oomeMsg);
if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1)
n = MAX_ARRAY_SIZE;
else
n += (n >>> 1) + 1;
r = Arrays.copyOf(r, n);
}
r[i++] = e;
}
return (i == n) ? r : Arrays.copyOf(r, i);
}
三、测试代码如下:
public class MapTest {
public static void main(String[] args) throws InterruptedException {
test3();
}
private static void test3() throws InterruptedException {
test1();
int i = 0;
Thread.sleep(1000L);
// List<Map.Entry<String, String>> list = new ArrayList<>(map.entrySet());
Map.Entry<String, String>[] arr = map.entrySet().toArray(new Map.Entry[0]);
Object a1 = null;
while (true) {
i++;
System.out.println("第"+(i)+"次循环开始");
String value = "123";
for(Map.Entry<String, String> entry:arr) {
if("empty".equals(entry.getKey()) && null == a1) {
value = entry.getValue();
a1 = entry;
}
}
Object a2 = null;
for(Map.Entry<String, String> entry:map.entrySet()) {
if("empty".equals(entry.getKey())) {
a2 = entry;
break;
}
}
System.out.println("第"+(i)+"次循环结束:" + value + ", really is " + map.get("empty"));
System.out.println("==========》a1:" + a1);
System.out.println("==========》a2:" + a2);
}
}
private static ConcurrentHashMap<String, String> map = new java.util.concurrent.ConcurrentHashMap<>();
private static void test1() {
ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 1, TimeUnit.SECONDS, new LinkedBlockingDeque<>(2000),
new ThreadFactoryBuilder().setNameFormat("qianp-test-thread-%d").get(),
new ThreadPoolExecutor.DiscardPolicy());
try {
String key = "empty";
executor.execute(()->{
int i = 0;
while (true) {
map.put(key, (i++) + "");
map.put("123"+System.nanoTime(), "123");
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}