题外话:
傍晚晚饭过后,总喜欢和朋友吹吹牛,和大多数人一样,我们的话题很日常,无非就是女人、前途和生活琐碎。如果某一个人感性一点或许会将话题会升华到事业以及自己对未来的畅想和规划。而作为同是搞计算机编程的,代码是必然会聊到的主题。今天我想分享的就是一个看似简单却不那么简单的问题,SimpleDateFormat的线程安全问题。
问题:
你知道SimpleDateFormat是否线程安全吗?假如你知道,那么它为什么线程不安全?假如你又知道,那么它的线程不安全问题怎么解决?
接下来我们一探究竟。
现象:
图中使用了多个线程同时访问同一个全局变量SimpleDateFormat,接下来我们看下运行结果:
根据程序我们知道,正常输出应该是true,但是却出现了false,我们从程序运行结果可知:SimpleDateFormat作为全局变量是线程不安全的。
原因分析:
1.调出format方法源码我们可以看到:
2.根据图示,传入的date参数是存到了calender变量的,我们来看下calender变量:
3.显而易见:calender是一个全局变量,当多线程异步调用的时候,我们对calender变量是无法控制的,A线程修改了calender,同时B线程也可能修改calender,从而导致calender不一致,也就导致了format方法的最终结果不一致。
结论:
SimpleDateFormat线程不安全。
解决方案:
1.将SimpleDateFormat变量定义为局部变量,因为我们知道,JVM栈是线程独享的,生命周期与线程同步,每个线程在执行format方法时,所有栈信息都是该线程独享的,包括方法内部创建的局部变量,因此是线程安全的。此处不做详细解释,具体可参考JVM内存模型。
2.加一把线程同步锁 synchronized(lock),该方法比较简 单,此处不做解释。
3.使用DateTimeFormatter。
4.使用使用ThreadLocal,每个线程都拥有自己的对象副 本。
运行结果:
此时我们可以看到结果均为true ,即实现了局部变量的线程安全。此时我们也许还有个疑问,为什么ThreadLocal可以实现线程安全?
如图:
当我们第一次从ThreadLocal对象取value时,会获取到当前线程缓存ThreadLocalMap,此时ThreadLocalMap为空,执行setInitialValue方法将值保存到ThreadLocalMap中:
第一次从ThreadLocalMap中取value时为空,此时创建一个新的
ThreadLocalMap将value值与当前线程形成key->value的映射保存。
当我们下一次从ThreadLocalMap中取出我们存入的value时,即每个线程最终获取到的value值都属于当前线程,也就是线程之间互相隔离互不影响,从而实现了线程安全。
当你觉得你什么都不知道的时候,其实你已经知道了很多
扫码关注公众号
回复”资料“领取更多编程学习资料,阿里等面试题集。
ANTI DRUG DAY
扫码关注我