考虑下面的代码,假设有两个线程同时经过下面的两个方法对同一个对象df进行访问,因为DateFormat不是线程安全的,因此有可能形成数据错乱,返回非你指望的结果(你想要当前的日期,返回的确实1970年1月1号)。java
private DateFormat df = new SimpleDateFormat("MM/dd/yy"); public String formatCurrentDate() { return df.format(new Date()); } public String formatFirstOfJanyary1970() { return df.format(new Date(0)); }
使用传统的同步方法能够很好得避免多线程并发访问时的数据错乱,可是会形成线程阻塞,从而严重影响性能。仔细考虑这里的df的使用场景,它并不须要各个线程保持一致,也就是说不是多线程通讯的手段,所以能够在各个线程中各有一个‘副本’ , 这种状况下就能够使用ThreadLocal变量。编程
private DateFormat df = new SimpleDateFormat("MM/dd/yy"); public synchronized String formatCurrentDate() { return df.format(new Date()); } public synchronized String formatFirstOfJanyary1970() { return df.format(new Date(0)); }
改写后的用法,去掉了同步方法,这时不一样的线程访问df时, 它们使用的同一个df, 可是经过df.get() 获得的是不一样的DateFormat对象, 从而保证各个线程格式化日期时互不干扰:安全
public static ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { protected DateFormat initialValue() { return new SimpleDateFormat("MM/dd/yy"); } }; public String formatCurrentDate() { return df.get().format(new Date()); } public String formatFirstOfJanyary1970() { return df.get().format(new Date(0)); }
使用ThreadLocal要注意内存泄露的状况。考虑上面的情景,若是咱们使用的不是DateFormat对象,而是自定义的类对象,那么这个对象可能持有应用程序context或其余资源的引用。同时这个对象在不一样的线程各有一份,若是有某个线程在线程池中(不正常地)存活了很长时间,那么就可能形成内存泄露了。内存泄露的解决能够经过良好的编程习惯、使用WeakReference等手段解决。多线程
参考:https://plumbr.eu/blog/when-and-how-to-use-a-threadlocal并发