Java ThreadLocal

96
i左撇子
2017.03.27 17:22* 字数 357

代码里边偶然发现一个bug,代码如下,

public class DateUtil{
    // format默认格式
    private static String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";

    private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() {
        protected synchronized DateFormat initialValue() {
            return new SimpleDateFormat(DATE_FORMAT);
        }
    };

    // 根据传入的format生成对应的SimpleDateFormat 
    public static SimpleDateFormat getDateFormatInstance(String format) {
        if (null != format && !format.equals("")) {
            DATE_FORMAT = format;
        } else {
            DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
        }
        return (SimpleDateFormat) threadLocal.get();
    }
}

这段代码是用来做日期的format的,考虑到性能问题,这里采用了ThreadLocal来获取SimpleDateFormat,而不是采用传统的new方式。在容器加载上述代码的时候,静态变量threadLocal 会被初始化,DateFormat的格式为默认,即"yyyy-MM-dd HH:mm:ss",所以如果当我们这样使用该工具类的时候就出了问题,如下:

DateUtil.getDateFormatInstance("yyyy-MM-dd").format(Calendar.getInstance().getTime());

得到的日期字符串格式不是我们想要的"yyyy-MM-dd"格式,而是"yyyy-MM-dd HH:mm:ss"默认格式。这个不难理解,因为threadLocal 只初始化了一次,并且DateFormat为默认格式。如果想format不同格式的日期,正确的姿势是这样的:

public class DateUtil{

    private static String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";

    // 锁对象
    private static final Object lockObj = new Object();
    private static Map<String, ThreadLocal<SimpleDateFormat>> threadLocalMap = new HashMap<String, ThreadLocal<SimpleDateFormat>>();

    public static SimpleDateFormat getDateFormatInstance(final String format) {
        ThreadLocal<SimpleDateFormat> tLocal = threadLocalMap.get(format);
        
        if (null == tLocal) {
            synchronized (lockObj) {
                if (null == tLocal) {
                    tLocal = new ThreadLocal<SimpleDateFormat>() {
                        @Override
                        protected SimpleDateFormat initialValue() {
                            if (null != format && !format.equals("")) {
                                DATE_FORMAT = format;
                            } else {
                                DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
                            }
                            return new SimpleDateFormat(DATE_FORMAT);
                        };
                    };
                    
                    threadLocalMap.put(format, tLocal);
                }
            }
        }

        return tLocal.get();
    }
 }

上边只是一个ThreadLocal的使用场景和姿势,那我就在想,ThreadLocal是什么,可以用来做什么?有什么优势?
有人这样解释的,我觉得挺好:
ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。具体参考[Java并发包学习七]解密ThreadLocal 这篇文章。

Java