A ObjectMapper
classe da biblioteca Jackson parece ser segura para threads .
Isso significa que devo declarar meu ObjectMapper
como um campo estático como este
class Me {
private static final ObjectMapper mapper = new ObjectMapper();
}
em vez de um campo no nível da instância como este?
class Me {
private final ObjectMapper mapper = new ObjectMapper();
}
ObjectMapper
ainda é seguro para threads depois deObjectMapper#setDateFormat()
ser chamado. Sabe-se queSimpleDateFormat
não é seguro para threads , portantoObjectMapper
, não será, a menos que seja clonado, por exemplo,SerializationConfig
antes de cada umwriteValue()
(duvido). Você poderia desmascarar meu medo?DateFormat
é de fato clonado sob o capô. Boa suspeita, mas você está coberto. :)ObjectMapper
?! Métodos são nomeadoswriter()
ereader()
(e algunsreaderFor()
,writerFor()
).mapper.with()
chamada (uma vez que "with" em Jackson implica construção de uma nova instância e execução segura de thread). Mas com relação às alterações de configuração: nenhuma verificação é feita, portanto, o acesso à configuraçãoObjectMapper
deve ser protegido. Quanto a "copy ()": sim, isso cria uma nova cópia nova que pode ser totalmente (re) configurada, de acordo com as mesmas regras: configure-a totalmente primeiro, depois use, e isso é bom. Existe um custo não trivial associado (já que a cópia não pode usar nenhum dos manipuladores em cache), mas é o caminho seguro, sim.Embora o ObjectMapper seja seguro para threads, eu desencorajaria fortemente de declará-lo como uma variável estática, especialmente em aplicativos multithread. Nem mesmo porque é uma prática ruim, mas porque você corre um grande risco de conflito. Estou contando isso por experiência própria. Criei um aplicativo com 4 threads idênticos que estavam obtendo e processando dados JSON de serviços da web. Meu aplicativo estava com frequência parado no seguinte comando, de acordo com o despejo de threads:
Além disso, o desempenho não foi bom. Quando substituí a variável estática pela variável baseada em instância, a paralisação desapareceu e o desempenho quadruplicou. Ou seja, 2,4 milhões de documentos JSON foram processados em 40min.56seg., Em vez de 2,5 horas anteriormente.
fonte
ObjectMapper
instância para cada instância da classe pode impedir bloqueios, mas pode ser muito pesada no GC posteriormente (imagine uma instância do ObjectMapper para cada instância da classe que você criar). Uma abordagem do caminho do meio pode ser, em vez de manter apenas umaObjectMapper
instância estática (pública) em todo o aplicativo, você pode declarar uma instância estática (privada) deObjectMapper
todas as classes . Isso reduzirá um bloqueio global (distribuindo a carga em classe) e também não criará nenhum novo objeto; portanto, também haverá luz no GC.ObjectPool
é claro, manter um é o melhor caminho a seguir - fornecendo o melhorGC
e as melhoresLock
performances. Você pode consultar o link a seguir paraObjectPool
implementação do apache-common . commons.apache.org/proper/commons-pool/api-1.6/org/apache/…ObjectMapper
algum lugar, mas apenas obtenhaObjectReader
/ObjectWriter
instâncias (por meio de métodos auxiliares), mantenha referências a outras em outros lugares (ou chame dinamicamente). Esses objetos de leitor / gravador não são apenas reconfiguração wrt totalmente segura para thread, mas também muito leves (instâncias do mapeador wrt). Portanto, manter milhares de referências não adiciona muito uso de memória.Embora seja seguro declarar um ObjectMapper estático em termos de segurança de encadeamento, você deve estar ciente de que a construção de variáveis estáticas de Objeto em Java é considerada uma prática ruim. Para mais detalhes, consulte Por que variáveis estáticas são consideradas más? (e se você quiser, minha resposta )
Em resumo, a estática deve ser evitada, pois dificulta a criação de testes de unidade concisos. Por exemplo, com um ObjectMapper final estático, você não pode trocar a serialização JSON por código fictício ou não operacional.
Além disso, uma final estática impede que você reconfigure o ObjectMapper em tempo de execução. Você pode não imaginar uma razão para isso agora, mas se você se prender a um padrão final estático, nada além de derrubar o carregador de classes permitirá que você o reinicialize.
No caso do ObjectMapper, tudo bem, mas, em geral, é uma prática ruim e não há vantagem em usar um padrão singleton ou inversão de controle para gerenciar seus objetos de vida longa.
fonte
readerFor
ewriterFor
criaObjectReader
eObjectWriter
instâncias sob demanda. Então, eu diria que coloque o mapeador com a configuração inicial em algum lugar estático e obtenha leitores / gravadores com a configuração por caso, conforme necessário.Um truque que aprendi com este PR se você não deseja defini-lo como uma variável final estática, mas deseja economizar um pouco de sobrecarga e garantir a segurança do thread.
crédito ao autor.
fonte
ObjectMapper
ele será anexado ao encadeamento que pode fazer parte de um pool.com.fasterxml.jackson.databind.type.TypeFactory._hashMapSuperInterfaceChain (HierarchicType)
O método _hashMapSuperInterfaceChain na classe com.fasterxml.jackson.databind.type.TypeFactory é sincronizado. Estou vendo contenda no mesmo em altas cargas.
Pode ser outro motivo para evitar um ObjectMapper estático
fonte
TypeReference
seja algo um pouco caro de usar: se possível, resolvê-loJavaType
evitaria um pouco de processamento (TypeReference
s não pode ser - infelizmente - armazenado em cache por razões que não vou cavar aqui), pois eles são "totalmente resolvidos" (supertipo, digitação genérica etc.).