ThreadLocal SimpleDateFormat in an Enum?

≯℡__Kan透↙ 提交于 2020-01-04 06:08:58

问题


I am doing some refactoring of our date formatting code because we've managed to introduce numerous inconsistencies for various reasons. I understand that it's best to have a ThreadLocal SimpleDateFormat. After discussing here we are unsure if ThreadLocal is even required when using in an Enum because we never change the instance and don't expose it so it can't be mutated? if it is still required does being an Enum, like this, break anything? Is anything else broken or not doing what I think it should? Basically I haven't had to work much with ThreadLocal and I'm unsure of the implications, especially in how it interacts with Enum.

public enum DateFormat {
DATE(newThreadLocalSimpleDateFormat( "MM/dd/yyyy" )),
LONG_DATE(newThreadLocalSimpleDateFormat("MMMM dd, yyyy")),
TIMESTAMP(newThreadLocalSimpleDateFormat( "MM/dd/yyyy hh:mm:ss aa" ));

private transient final ThreadLocal<SimpleDateFormat> formatter;

DateFormat( final ThreadLocal<SimpleDateFormat> formatter ) {
    this.formatter = formatter;
}


public  String format ( final Date date ) {
    return this.formatter.get().format( date );
}

private static ThreadLocal<SimpleDateFormat> newThreadLocalSimpleDateFormat( final String frmtString ) {
    return new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat( frmtString );
        }
    };
}

}

回答1:


Here the enum is a global constant that gets used as a convenient way to retrieve the right formatter from the threadlocal variables. The enum is immutable, but if it references things that have mutable state, those things can still have issues.

When you say

we never change the instance and don't expose it so it can't be mutated

you are misunderstanding the nature of the thread safety issues. The problem is that SimpleDateFormat has internal state that is not protected against access by multiple threads, so that when multiple threads access the same formatter instance, any of those threads can change the state being manipulated by other concurrent threads, corrupting the results.

Your choices for handling this formatter are:

  • leaving the ThreadLocal intact, so that each thread gets its own copy of the formatter; in some cases the biggest danger is that threadlocal objects may not get cleaned up properly, so that a thread chosen from the pool (you are using a thread pool, right?) may have a variable associated with a previous use, but in this case it is probably more like a feature: if all the threads need this anyway, it's preferable that the formatter sticks around,

  • making a new formatter for each call so that nothing is shared (fast, easy, and threadsafe, but this will create garbage as the formatters get thrown away; making the garbage collector work harder will reduce performance),

  • synchronizing access to the formatter so that threads can't access it concurrently (probably the least appealing choice, it's likely to cause a bottleneck)

  • using a different implementation of DateFormat, like FastDateFormat, that is threadsafe (the thread-safety may mean the implementation is either locking or copying state, so there may be downsides, it would take some testing to see the consequences).

Keeping the existing ThreadLocal or checking out the alternatives for threadsafe formatters would likely be the best choices here. Keeping what you have would seem like a safe choice.

Without a thread pool the Threadlocal becomes less appealing because the threads' lifetimes are shorter so there is less reuse of a given formatter. Thread pools are a good idea for multiple reasons (mainly making sure an error condition doesn't result in your application running itself out of threads so that your application can degrade in a controlled manner), if you aren't using them you should be.

To me the oddest thing about this code is that the enum has to be serializable, but the threadLocal is not serializable so it has to be declared as transient. If this thing did get serialized and consequently deserialized for some reason, the deserialized copy would have a null threadLocal. In practice this isn't something you'd want to serialize anyway (you wouldn't store this in an HttpSession or pass it to another JVM) so it seems like a small-to-nonexistent risk.




回答2:


Your current implementation is fine and you should not change it by removing the ThreadLocal. SimpleDateFormat is not thread safe. In other words, using the same instance across threads without external synchronization will not have the behavior you expect. The javadoc warns you about this.

ThreadLocal instances work by giving you a single instance per thread. This instance is not shared (unless you share it) and therefore can't cause any problems.

The fact that it is in an enum makes no difference.



来源:https://stackoverflow.com/questions/27065455/threadlocal-simpledateformat-in-an-enum

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!