2017-12-12 11 views
0

내 자신의 클래스에서 Jackson JSON 프로세서를 래핑하는 중이고 공유 인스턴스를 처리하기위한 확실한 스레드 안전성 및 성능이 뛰어난 방법을 찾지 못했습니다.잭슨 JSON 독자를위한 최고의 처리?

는 문서에 따르면, ObjectMapper, ObjectWriterObjectReader 모든 스레드 안전 내가 우선적으로 ObjectWriterObjectReader를 사용해야합니다. ObjectWriter 처리

꽤 솔직하지만, 심지어 Jackson Data-bind docsObjectReader에 대한 명확한 지침이 없다 그리고 내가 ObjectReaders 모든 내 자신을 생성하고 캐시해야 함을 암시한다. 이것이 내가 한 일입니다.

import com.fasterxml.jackson.databind.ObjectMapper; 
import com.fasterxml.jackson.databind.ObjectReader; 
import com.fasterxml.jackson.databind.ObjectWriter; 

import java.util.HashMap; 
import java.util.Map; 

public class JacksonEngine extends JsonProcessor { 

    private ObjectMapper mapper = new ObjectMapper(); 
    private ObjectWriter writer = mapper.writer();  
    private Map<Class, ObjectReader> readers = new HashMap<>(); 

    public String serialize(Object object) throws Exception { 
     return writer.writeValueAsString(object); 
    } 

    public <T> T deserialize(String json, Class<T> classOfT) 
      throws Exception { 
     if (!readers.containsKey(classOfT)) { 
      readers.put(classOfT, mapper.readerFor(classOfT)); 
     } 
     return readers.get(classOfT).readValue(json); 
    } 

} 

이것은 작동할까요?

해시 맵 키에 대해 Class이 아닌 제네릭으로 변환해야한다고 가정하면 Class<T>이 괜찮을 것이라고 가정합니다.

if not present then addif not present then add 멀티 쓰레드 조건에서 캐시에 새로운 Readers을 넣는 작업은 중복 된 리더 또는 2의 잠재적 생성을 제외하고는 문제가되지 않는다고 가정합니다.

마지막으로 성능 향상을 위해 Readers의 캐싱을 수행 할 가치가 있다고 가정합니다. 온라인에서 사람들이 사용하는 예제를 찾지 못했지만 말입니다.

답변

1

은 재사용, 공유, 캐시 될 수 있습니다. 스레드 안전성과 인스턴스가 상대적으로 가벼우므로 둘 다 가능합니다.

ObjectReader javadoc

당신의 접근 방식에 관계없이 ObjectReader가 스레드 안전 여부 올바르지 않습니다. 첫째, 귀하의 필드는 최종해야합니다; 최종없이 스레드간에 JacksonEngine을 공유하면 읽기가 완료 될 때 상태가 완전히 초기화되지 않고 손상된 상태로 표시 될 수 있습니다. 둘째로, "존재하지 않는다면 다음에 덧붙임"에 대한 가정도 부정확합니다. 원 자성이 필요하지 않더라도 HashMap의 사용은 다른 스레드에 대해 업데이트되지 않을 수 있으며, 모든 호출이 실패하거나 HashMap에 캐시 된 것을 볼 수 없을 수도 있습니다. 효과없이 캐싱하고 있습니다.

그냥 ObjectReader를 상수에 저장하면 괜찮습니다. 그렇지 않으면 ThreadLocal을 사용하는 것이 좋습니다.

+0

'최종'및 v.흥미로운 점은 해시 맵이 메모리에 어디에 위치하는지에 관한 것입니다. - JacksonEngine이 싱글 톤이 될 것이고, 해시 맵 내용의 가시성이 개별 CPU 캐시에만 국한되지는 않을 것이라고 생각했기 때문에 - 멤버 변수입니다. – Adam

+0

싱글 톤이라는 사실은 맵 데이터가 캐시 된 위치와 아무 관련이 없습니다. 나는 심지어 당신이 캐시 미스가 있기를 원한다고 말하면서 CPU가 업데이트 된 값을 검색 할 것이라고 말할 것이다. 동기화하지 않으면 CPU에 업데이트 된 내용이 표시되거나 처음부터 업데이트가 표시되지 않을 수 있습니다. – xTrollxDudex

+0

그러나 'ObjectReaders'는 캐시되어야하며 스레드 로컬도 아닙니다. 어떻게 그걸 성취합니까? 'ConcurrentHashMap' 작업을하거나 동기화해야합니까? – Adam

1

실제로 여기에 ObjectReaderObjectWriter이 필요하지 않다고 생각합니다. 스레드로부터 안전하므로 공유 할 수 있습니다. 당신은 독자와 작가를 공유하지 않기 때문에, 당신은 간단하게 할 수 있습니다

public class JacksonEngine extends JsonProcessor { 

    private final static ObjectMapper mapper = new ObjectMapper(); 

    public String serialize(Object object) throws Exception { 
     return mapper.writeValueAsString(object); 
    } 

    public <T> T deserialize(String json, Class<T> classOfT) 
      throws Exception { 
     return mapper.readValue(json, classOfT); 
    } 

} 

UPDATE : OP로

성능에 대한 우려를 그 때마다 새로운 리더를 만들어으로 ObjectMapper.readValue()를 호출 할 때. 소스 코드를 보면 다음과 같지 않습니다.

public <T> T readValue(String content, Class<T> valueType) { 
    return (T) _readMapAndClose(_jsonFactory.createParser(content), _typeFactory.constructType(valueType)); 
} 

protected Object _readMapAndClose(JsonParser p0, JavaType valueType) { 
    try { 
     Object result; 
     // ... 
     JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, valueType); 
     // ... 
     result = deser.deserialize(p, ctxt); 
     // ... 
     return result; 
    } 
} 
+0

음질은 간단하지만 음질은 분명하지만 성능을 해칠 것이라고 생각했습니다. 매번 mapper.readValue()가 매번 mapper.readerFor (classOfT)를 실행하지 않습니까? 캐싱은 훨씬 더 빠를 것입니다. – Adam

+0

'ObjectReader'는'ObjectMapper'의 deserialization 설정의 불변 캡슐입니다. 'ObjectMapper.readValue()'는 독자를 생성하지 않습니다. 루트 디시리얼라이저를 찾고이를 위임 한 다음 결과를 반환합니다. – Christian

+0

'ObjectMapper'를 잠깐 살펴보면 매번 어떤 종류의'typeFactory.constructType (classOfT)'을 수행 할 것입니다. 필자는 Jackson이 자체적으로 그렇게하지 않는 것처럼 보이기 때문에'typeFactory'에서 'JavaType'을 캐시하도록 코드를 리팩터링했습니다. – Adam