测试代码:
public class Main { public static void main(String[] args) { for (int k = 0; k < 10; k++) { Runnable target = new Runnable() { @Override public void run() { Object obj = dateFormatter.get(); System.out.println(Thread.currentThread().getName() + " => " + obj); obj = dateFormatter.get(); } }; new Thread(target, k+"").start(); } } private static final ThreadLocal<SimpleDateFormat> dateFormatter = new ThreadLocal<SimpleDateFormat>() { @Override protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd"); } }; }
输出结果:
8 => [email protected]
5 => [email protected]
6 => [email protected]
...
7 => [email protected]
咦?怎么全是f67a0200一个实例?跟踪ThreadLocal代码寻找原因,百思不得其解。最后突然怀疑是SimpleDateFormat中toString方法的问题,SimpleDateFormat#toString源码如下:
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
SimpleDateFormat#hashCode代码如下,其中pattern变量是SimpleDateFormat的格式字符串值( public SimpleDateFormat(String pattern))。
@Override public int hashCode() { return pattern.hashCode(); // just enough fields for a reasonable distribution }
所以如上原因可以得知,由于自己实现的initialValue方法的SimpleDateFormat的pattern都一样,所以不同sdf实例的toString最终输出相同。
protected SimpleDateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd"); }
产生困惑原因:
通常子类在没有重写toString方法时,我们都可以简单的根据toString值进行判断是否是一个实例,但是由于SimpleDateFormat自己实现了toString所以这个规则不在生效。
提醒:
以后尽可能不要简单的将toString输出用来判断是否是一个实例,如果需要这么判断的话一定要检查toString方法。
拓展:
在java.lang.ThreadLocal#getMap方法中可以发现原来java的Thread对线程局部变量自身就有支持,在Thread中有一个ThreadLocalMap的成员变量。java.lang.ThreadLocal#getMap源码如下:
ThreadLocalMap getMap(Thread t) { //threadLocals是一个默认修饰符成员变量 return t.threadLocals; }
原文地址:https://www.cnblogs.com/leodaxin/p/8458127.html