ThreadLocal SimpleDateFormat在一个枚举? [英] ThreadLocal SimpleDateFormat in an Enum?

查看:293
本文介绍了ThreadLocal SimpleDateFormat在一个枚举?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在对我们的日期格式代码进行重构,因为我们出于各种原因设法引入了大量的不一致。我知道最好有一个 ThreadLocal SimpleDateFormat 。在这里讨论后,我们不确定如果ThreadLocal在Enum中使用时是否是必需的,因为我们从不更改实例,并且不公开它,所以它不能被突变?如果仍然需要做一个枚举,像这样,打破任何东西?是否有什么其他破碎或不做我认为应该?基本上,我没有工作与ThreadLocal很多,我不确定的影响,特别是如何与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>格式化

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)
}
};
}

}


解决方案

这里枚举是一个全局常量,可以用来方便地从threadlocal变量中检索正确的格式化程序。枚举是不可变的,但如果它引用了具有可变状态的东西,那些东西仍然可能有问题。



当你说


我们从不更改实例,不公开它,因此不能改变。


你误解了线程安全问题的本质。问题是SimpleDateFormat的内部状态没有受到多个线程的访问的保护,所以当多个线程访问同一个格式化器实例时,任何这些线程都可以改变被其他并发线程操纵的状态,破坏结果。 p>

您对处理此格式化程序的选择是:




  • ,这样每个线程都有自己的格式化器副本;在某些情况下,最大的危险是,threadlocal对象可能无法正确清理,所以从池中选择的线程(您正在使用线程池,右?)可能有一个变量与以前的使用相关联,但在这种情况下它可能更像是一个功能:如果所有的线程都需要这个,最好是格式化程序坚持,


  • 为每个调用创建一个新的格式化没有什么是共享的(快速,简单,线程安全,但这将创建垃圾,因为格式化程序被扔掉;使垃圾收集器工作更难以降低性能),


  • 同步对格式化程序的访问,以便线程不能同时访问它(可能是最不吸引人的选择,很可能导致瓶颈)


  • 一个不同的DateFormat实现,像FastDateFormat,即threadafe(线程安全可能意味着实现是锁定或复制状态,因此可能有缺点,它需要一些测试来看到后果)。

    / li>


保持现有的ThreadLocal或检查线程安全格式化程序的替代方法可能是最好的选择。



没有线程池,Threadlocal变得不那么有吸引力了,因为线程的生命周期更短,所以一个给定的格式化器。线程池是一个好主意有多个原因(主要是确保错误条件不会导致您的应用程序运行自己的线程,以便您的应用程序可以以受控的方式降级),如果你不使用它们,你应该。



对我来说,这段代码最奇怪的是枚举必须是可序列化的,但是threadLocal不是可序列化的,所以必须声明为transient。如果这个东西被序列化,并因此由于某种原因反序列化,反序列化的副本将有一个null threadLocal。在实践中,这不是你想要序列化(你不会存储在一个HttpSession或传递给另一个JVM),所以它似乎是一个小到不存在的风险。


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 );
        }
    };
}

}

解决方案

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.

这篇关于ThreadLocal SimpleDateFormat在一个枚举?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆