3

I have a simple JsonSerializer in my Spring project:

public class JsonDateTimeSerializer extends JsonSerializer<Date> {
    private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Override
    public void serialize(Date value, JsonGenerator gen, SerializerProvider sp) throws IOException {
        gen.writeString(DATE_FORMAT.format(value));
    }
}

And use it like:

@JsonSerialize(using = JsonDateTimeSerializer.class)
public Date getDate() {
    return date;
}

Do I have to take care of thread-safety and make the DATE_FORMAT synchronized (as SimpleDateFormat is not thread-safe)? I am not sure how exactly @JsonSerialize works - does it create just single serialized instance across all threads? Or does it create separate instance for each transformation?

M. Justin
  • 14,487
  • 7
  • 91
  • 130
Laimoncijus
  • 8,615
  • 10
  • 58
  • 81

2 Answers2

4

If JsonDateTimeSerializer.serialize might be called from multiple threads, then this use of SimpleDateFormat is not safe. The common approach to avoid inefficient synchronization on SimpleDateFormat is explained well in this other answer. Adapting to your use case:

public class JsonDateTimeSerializer extends JsonSerializer<Date> {

    private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    };

    @Override
    public void serialize(Date value, JsonGenerator gen, SerializerProvider sp) throws IOException {
        gen.writeString(formatter.get().format(value));
    }
}
Community
  • 1
  • 1
janos
  • 120,954
  • 29
  • 226
  • 236
  • 1
    I have had that same multithread issue, and when I added the above code, it went away. So I think correct answer is this for the issue. – SHAKU Nov 16 '19 at 17:13
  • Note that `ThreadLocal` may introduce a memory leak; see https://rules.sonarsource.com/java/RSPEC-5164 – Robert Hume Aug 09 '23 at 15:13
1

When Jackson sees your type for the first time, (depending on the type) it will build a BeanSerializer with the appropriate JsonSerializers for each property. This BeanSerializer is cached and reused for future serializations of the same Type.

As such, a single instance of JsonDateTimeSerializer (per type), which will have been registered with the JsonDateTimeSerializer, will be reused for all serializations. It must therefore be thread safe if you plan to use the ObjectMapper across multiple threads. (You should since the ObjectMapper itself is thread safe.)

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724