428

Jackson 图书馆的ObjectMapper课程似乎是线程安全的。

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

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

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

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

6 回答 6

583

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

您引用的页面中唯一需要注意的是,一旦共享映射器,您就不能修改映射器的配置;但是您没有更改配置,所以这很好。如果您确实需要更改配置,则可以从静态块中执行此操作,也可以。

编辑:(2013/10)

对于 2.0 及更高版本,可以通过注意还有一种更好的方法来增强上述内容:使用ObjectWriterObjectReader对象,可以通过ObjectMapper. 它们是完全不可变的、线程安全的,这意味着理论上甚至不可能导致线程安全问题(ObjectMapper如果代码尝试重新配置实例,可能会发生这种情况)。

于 2010-10-11T20:49:19.010 回答
74

尽管 ObjectMapper 是线程安全的,但我强烈反对将其声明为静态变量,尤其是在多线程应用程序中。甚至不是因为这是一种不好的做法,而是因为您面临着很大的死锁风险。我是根据我自己的经验告诉它的。我创建了一个具有 4 个相同线程的应用程序,这些线程从 Web 服务获取和处理 JSON 数据。根据线程转储,我的应用程序经常在以下命令上停止:

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

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

于 2016-03-22T18:36:05.113 回答
9

如果您不想将其定义为静态最终变量但想节省一些开销并保证线程安全,我从这个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();
}

归功于作者。

于 2018-10-01T05:19:29.673 回答
4

尽管在线程安全方面声明静态 ObjectMapper 是安全的,但您应该知道在 Java 中构造静态 Object 变量被认为是不好的做法。有关更多详细信息,请参阅为什么静态变量被认为是邪恶的?(如果你愿意,我的回答

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

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

在 ObjectMapper 的情况下,它很好,但一般来说这是不好的做法,并且与使用单例模式或控制反转来管理长期存在的对象相比没有任何优势。

于 2013-04-24T16:39:06.853 回答
1

com.fasterxml.jackson.databind.type.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 的另一个原因

于 2017-04-28T10:16:08.530 回答
1

这个问题可能很老,但这就是我所做的。

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;
  }

}
于 2021-11-24T21:11:27.440 回答