[alibaba/fastjson]fastjson的IdentityHashMap并发时会出错

2025-11-12 89 views
8

并发的往IdentityHashMap中put数据时,可能会没有put进去。 可以用下面这段groovy脚本验证。

两个线程同时往map中put字符串,立刻又get这个字符串,key是同一个对象,但有时会返回null。 如果uncomment掉synchronize语句就不会有这个问题。

import com.alibaba.fastjson.util.IdentityHashMap;

String key = "kkkkkkkkkkkkkkkk"

IdentityHashMap map = new IdentityHashMap(2) 
Random ran = new Random()

new Thread() {
    public void run() {
        while(true) {
            String kk = key + ran.nextInt(2)
//          synchronized(map) {
                map.put(kk, kk);
//          }
            Object val = map.get(kk)
            if(val == null) {
                println "found"
            }
        }
    }
}.start();

new Thread() {
    public void run() {
        while(true) {
            String kk = key + ran.nextInt(2)
//          synchronized(map) {
                map.put(kk, kk);
//          }
            Object val = map.get(kk)
            if(val == null) {
                println "found"
            }
        }
    }
}.start();

回答

5

由于上述这个问题,fastjson有可能会出现如下NPE: Caused by: java.lang.NullPointerException at com.alibaba.fastjson.serializer.FieldSerializer.writeValue(FieldSerializer.java:222) ~[fastjson-1.2.29.jar:?] at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:296) ~[fastjson-1.2.29.jar:?] at com.alibaba.fastjson.serializer.FieldSerializer.writeValue(FieldSerializer.java:222) ~[fastjson-1.2.29.jar:?] at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:296) ~[fastjson-1.2.29.jar:?] at com.alibaba.fastjson.serializer.JSONSerializer.write(JSONSerializer.java:275) ~[fastjson-1.2.29.jar:?]

2

这是个IdentityHashMap,所以你要做这个处理:

String kk = (key + ran.nextInt(2)).intern();

这样就不会报错了。至于NPE估计是别的愿你因,你升级到新版本试试看 1.2.36

1

改成这样String kk = (key + ran.nextInt(2)).intern(); 出错的概率会降低;不管怎样我刚put一个对象进去,就立刻取出来,kk是同一个对象,返回null是有问题的。

1

嗯,设计就是这样的,使用的时候避免put再get,应该是get == null, create, put,有一个地方使用不当,我已提交代码修复。

7
1279

那这个解决了么 @wenshao

3

`private final static Type typeReference = new TypeReference<ESResultVO>() { }.getType();

static {
    ParserConfig.getGlobalInstance().getDeserializer(typeReference);
}

@Test
public void json() {
    ExecutorService executorService = ThreadUtil.newExecutor();
    while (true) {
        for (int j = 0; j < 10; j++) {
            executorService.submit(this::test);
        }
    }
}

private void test() {
    ESResultVO<TaskLogVO> json = JSON
        .parseObject("{\n" + "  \"code\": 0,\n" + "  \"message\": null,\n" + "  \"result\": {\n" + "    \"took\": 16,\n" + "    \"count\": 1,\n" + "    \"list\": [\n"
                     + "      {\n" + "        \"appId\": 4,\n" + "        \"bizNo\": \"\",\n" + "        \"elapsed\": 0,\n" + "        \"endTime\": 1567044743242,\n"
                     + "        \"gmtCreate\": 1567044743242,\n" + "        \"gmtUpdate\": 1567044743242,\n" + "        \"guid\": \"1fac95e9-081e-4e7a-965e-3279b306f1c3\",\n"
                     + "        \"id\": \"bc261686-11ef-461d-9ca0-a7aab09112b2\",\n" + "        \"startTime\": 1567044743242,\n" + "        \"status\": 0,\n"
                     + "        \"taskGuid\": \"3c704084-bc66-448d-86ff-f707f3d38317\",\n" + "        \"taskId\": 74,\n"
                     + "        \"traceId\": \"7b979533f94d48908d8fdd0d386a8400\",\n" + "        \"_score\": 0\n" + "      }\n" + "    ]\n" + "  }\n" + "}",
            typeReference);
}`

这么做可以解决~