【问题标题】:ThreadLocal and SimpleDateFormat arrayThreadLocal 和 SimpleDateFormat 数组
【发布时间】:2012-07-07 15:39:05
【问题描述】:

使用与recent question 中描述的模式非常相似的模式,对于多线程应用程序,我得到了奇怪的日期值(例如,像 2025 或 2035 这样的年份,而源数据中显然不存在这样的值)。似乎出现了并发问题。

源代码看起来像

// Various Java DateFormat patterns, e.g. "yyyy-MM-dd".
private static final String[] DATE_PATTERNS = new String[] {...};

private static SimpleDateFormat[] getFormats(final String[] patterns)
{
    ThreadLocal<SimpleDateFormat[]> LOCAL_FORMATS = new ThreadLocal<SimpleDateFormat[]>()
    {
        @Override
        protected SimpleDateFormat[] initialValue()
        {
            List<SimpleDateFormat> formatList = new ArrayList<SimpleDateFormat>();

            for (String pattern:patterns)
            {
                formatList.add(new SimpleDateFormat(pattern));
            }

            return formatList.toArray(new SimpleDateFormat[formatList.size()]);
        }
    };

    return LOCAL_FORMATS.get(); // Create a thread-local copy
}

private static final SimpleDateFormat[] DATE_FORMATS = getFormats(DATE_PATTERNS);

在其静态初始化之后,DATE_FORMATS 数组被许多类访问,这些类又使用数组的 SimpleDateFormat 对象来解析或格式化多个日期字符串。

这样的使用场景会不会有并发问题,尤其是考虑到ThreadLocal的使用?

【问题讨论】:

    标签: java thread-safety simpledateformat thread-local


    【解决方案1】:

    是的,可能存在并发问题。您的线程局部变量没有任何用途。它仅在类初始化时使用,用于临时存储日期格式数组,该数组会立即检索并存储在静态常量中。

    之后,所有线程总是同时使用相同的日期格式实例,而不是从任何线程局部变量中获取它们。

    代码应该是:

    private static final String[] DATE_PATTERNS = new String[] {...};
    private static final ThreadLocal<SimpleDateFormat[]> DATE_FORMATS = 
        new ThreadLocal<SimpleDateFormat[]>() {
            @Override
            protected SimpleDateFormat[] initialValue() {
                List<SimpleDateFormat> formatList = new ArrayList<SimpleDateFormat>();
    
                for (String pattern : DATE_PATTERNS)
                {
                    formatList.add(new SimpleDateFormat(pattern));
                }
    
                return formatList.toArray(new SimpleDateFormat[formatList.size()]);
            }
        };
    
    public static SimpleDateFormat[] getDateFormats() {
        return DATE_FORMATS.get();
    }
    

    为了更安全,我还会使用不可修改的 List&lt;SimpleDateFormat&gt; 而不是数组。

    【讨论】:

    • 我实际上是在询问您在编辑答案时建议的解决方案是否正确。感谢您的详细信息!因此,每个调用 getDateFormats() 的方法总是会为同一个线程获得相同的 SimpleDateFormat[] 数组,而对于每个新线程,该数组只会被初始化一次,对吗?
    • 当然可以使用 DATE_FORMATS.get() 来代替 getDateFormats()。如果 SimpleDateFormat[] 数组从未被写入,则它应该是线程安全的。再次感谢您的回答!
    • 即使写入,它也是线程安全的,因为每个线程都有自己的副本。但是,如果您想防止数组成员意外重新排序或无效,您最好将数组设为不可修改的列表。
    【解决方案2】:
    // Various Java DateFormat patterns, e.g. "yyyy-mm-dd".
    

    “yyyy-mm-dd”格式可能会给您带来奇怪的结果,因为“mm”是分钟而不是月。来自 javadoc:

    M   Month in year   Month   July; Jul; 07
    ...
    m   Minute in hour  Number  30
    

    【讨论】:

    • 没有实际使用,只是上面评论中的一个错字。已修改,感谢发现! :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-31
    • 1970-01-01
    • 1970-01-01
    • 2011-11-25
    相关资源
    最近更新 更多