Jackson库的ObjectMapper类似乎是线程安全的。
这是否意味着我应该像这样将ObjectMapper声明为静态字段
class Me {
private static final ObjectMapper mapper = new ObjectMapper();
}
而不是像这样的实例级字段?
class Me {
private final ObjectMapper mapper = new ObjectMapper();
}
Jackson库的ObjectMapper类似乎是线程安全的。
这是否意味着我应该像这样将ObjectMapper声明为静态字段
class Me {
private static final ObjectMapper mapper = new ObjectMapper();
}
而不是像这样的实例级字段?
class Me {
private final ObjectMapper mapper = new 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的另一个原因
尽管从线程安全的角度来说,声明静态ObjectMapper是安全的,但您应该意识到,在Java中构造静态Object变量被认为是一种糟糕的做法。有关更多详细信息,请参见为什么静态变量被认为是邪恶的?(如果你愿意,我的答案)
简而言之,应该避免静态,因为它使编写简洁的单元测试变得困难。例如,使用静态最终ObjectMapper,您不能将JSON序列化替换为虚拟代码或无操作。
此外,静态final可以防止您在运行时重新配置ObjectMapper。您现在可能还没有想到这样做的原因,但是如果您将自己锁定在一个静态的最终模式中,那么只有拆除类加载器才能让您重新初始化它。
在ObjectMapper的情况下,它很好,但一般来说,这是一种糟糕的实践,使用单例模式或反转控制来管理长生命期对象并没有什么优势。
这个问题可能很老了,但我是这么做的。
将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;
}
}
是的,这是安全的,也是推荐的。
您所提到的页面中唯一需要注意的是,一旦共享了映射器,您就不能修改它的配置;但是你没有改变构型,所以这很好。如果你确实需要改变配置,你可以从静态块来做,这也会很好。
编辑:(2013/10)
在2.0及以上版本中,可以通过注意有一种更好的方法来增强上述内容:使用ObjectWriter和ObjectReader对象,它们可以由ObjectMapper构造。 它们是完全不可变的、线程安全的,这意味着理论上甚至不可能导致线程安全问题(如果代码试图重新配置实例,则ObjectMapper可能会出现线程安全问题)。
尽管ObjectMapper是线程安全的,但我强烈反对将其声明为静态变量,特别是在多线程应用程序中。 甚至不是因为这是一种糟糕的实践,而是因为您面临着严重的死锁风险。我是根据自己的经验讲的。我创建了一个具有4个相同线程的应用程序,用于从web服务获取和处理JSON数据。 根据线程转储,我的应用程序经常在执行以下命令时暂停:
Map aPage = mapper.readValue(reader, Map.class);
除此之外,表现并不好。 当我用基于实例的变量替换静态变量时,失速消失了,性能翻了两番。也就是说,240万个JSON文档在40分56秒内被处理。而不是之前的2.5小时。