Jackson库的ObjectMapper类似乎是线程安全的。

这是否意味着我应该像这样将ObjectMapper声明为静态字段

class Me {
    private static final ObjectMapper mapper = new ObjectMapper();
}

而不是像这样的实例级字段?

class Me {
    private final ObjectMapper mapper = new ObjectMapper();
}

是的,这是安全的,也是推荐的。

您所提到的页面中唯一需要注意的是,一旦共享了映射器,您就不能修改它的配置;但是你没有改变构型,所以这很好。如果你确实需要改变配置,你可以从静态块来做,这也会很好。

编辑:(2013/10)

在2.0及以上版本中,可以通过注意有一种更好的方法来增强上述内容:使用ObjectWriter和ObjectReader对象,它们可以由ObjectMapper构造。 它们是完全不可变的、线程安全的,这意味着理论上甚至不可能导致线程安全问题(如果代码试图重新配置实例,则ObjectMapper可能会出现线程安全问题)。


尽管从线程安全的角度来说,声明静态ObjectMapper是安全的,但您应该意识到,在Java中构造静态Object变量被认为是一种糟糕的做法。有关更多详细信息,请参见为什么静态变量被认为是邪恶的?(如果你愿意,我的答案)

简而言之,应该避免静态,因为它使编写简洁的单元测试变得困难。例如,使用静态最终ObjectMapper,您不能将JSON序列化替换为虚拟代码或无操作。

此外,静态final可以防止您在运行时重新配置ObjectMapper。您现在可能还没有想到这样做的原因,但是如果您将自己锁定在一个静态的最终模式中,那么只有拆除类加载器才能让您重新初始化它。

在ObjectMapper的情况下,它很好,但一般来说,这是一种糟糕的实践,使用单例模式或反转控制来管理长生命期对象并没有什么优势。


尽管ObjectMapper是线程安全的,但我强烈反对将其声明为静态变量,特别是在多线程应用程序中。 甚至不是因为这是一种糟糕的实践,而是因为您面临着严重的死锁风险。我是根据自己的经验讲的。我创建了一个具有4个相同线程的应用程序,用于从web服务获取和处理JSON数据。 根据线程转储,我的应用程序经常在执行以下命令时暂停:

Map aPage = mapper.readValue(reader, Map.class);

除此之外,表现并不好。 当我用基于实例的变量替换静态变量时,失速消失了,性能翻了两番。也就是说,240万个JSON文档在40分56秒内被处理。而不是之前的2.5小时。


com .杰克逊fasterxml。databind TypeFactory _hashMapSuperInterfaceChain (HierarchicType)型。

com.fasterxml.jackson.databind.type.TypeFactory._findSuperInterfaceChain(Type, Class)
  com.fasterxml.jackson.databind.type.TypeFactory._findSuperTypeChain(Class, Class)
     com.fasterxml.jackson.databind.type.TypeFactory.findTypeParameters(Class, Class, TypeBindings)
        com.fasterxml.jackson.databind.type.TypeFactory.findTypeParameters(JavaType, Class)
           com.fasterxml.jackson.databind.type.TypeFactory._fromParamType(ParameterizedType, TypeBindings)
              com.fasterxml.jackson.databind.type.TypeFactory._constructType(Type, TypeBindings)
                 com.fasterxml.jackson.databind.type.TypeFactory.constructType(TypeReference)
                    com.fasterxml.jackson.databind.ObjectMapper.convertValue(Object, TypeReference)

类com.fasterxml.jackson.databind.type.TypeFactory中的_hashMapSuperInterfaceChain方法是同步的。 我在高负载时看到了同样的争用。

可能是避免使用静态ObjectMapper的另一个原因


如果你不想将它定义为静态final变量,但又想节省一点开销并保证线程安全,我从这个PR中学到了一个技巧。

private static final ThreadLocal<ObjectMapper> om = new ThreadLocal<ObjectMapper>() {
    @Override
    protected ObjectMapper initialValue() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        return objectMapper;
    }
};

public static ObjectMapper getObjectMapper() {
    return om.get();
}

归功于作者。


这个问题可能很老了,但我是这么做的。

将ObjectMapper实例保存在线程安全的单例中:

public final class JacksonObjectMapperHolder {

  private static volatile JacksonObjectMapperHolder INSTANCE;

  private static final Object MUTEX = new Object();

  public static JacksonObjectMapperHolder getInstance() {
    JacksonObjectMapperHolder instance = INSTANCE;

    if(instance == null) {
      synchronized(MUTEX) {
        instance = INSTANCE;

        if(instance == null) {
          INSTANCE = instance = new JacksonObjectMapperHolder();
        }
      }
    }

    return instance;
  }

  private final ObjectMapper objectMapper = new ObjectMapper();

  private JacksonObjectMapperHolder() {
    super();
  }

  public final ObjectMapper getObjectMapper() {
    return objectMapper;
  }

}