为什么SimpleDateFormat线程不安全?
SimpleDateFormat中有个属性calendar(protected Calendar calendar;)继承自DataFormat, 所以当不同的线程如果用的是同一个SimpleDateFormat对象,则calendar对象也是完全相同的内存;
SimpleDateFormat的format方法中将传入的时间date set给了calendar属性(calendar.setTime(date););
当A线程传入时间a调用format方法进行格式化,format方法还没执行完,此时B线程传入时间b调用format方法进行格式化,则A线程中的calendar的time就变成了B线程传入的时间b了,当执行format方法中的subFormat方法时,获取calendar中的时间就线程不安全了。
java.text.SimpleDateFormat#format(java.util.Date, java.lang.StringBuffer, java.text.Format.FieldDelegate)源码如下:
private StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) { // Convert input date to time field list calendar.setTime(date); boolean useDateFormatSymbols = useDateFormatSymbols(); for (int i = 0; i < compiledPattern.length; ) { // 省略部分代码…… switch (tag) { case TAG_QUOTE_ASCII_CHAR: toAppendTo.append((char)count); break; case TAG_QUOTE_CHARS: toAppendTo.append(compiledPattern, i, count); i += count; break; default: subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols); break; } } return toAppendTo; }
java.text.SimpleDateFormat#subFormat 源码:
private void subFormat(int patternCharIndex, int count, FieldDelegate delegate, StringBuffer buffer, boolean useDateFormatSymbols) { int maxIntCount = Integer.MAX_VALUE; String current = null; int beginOffset = buffer.length(); int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex]; int value; if (field == CalendarBuilder.WEEK_YEAR) { if (calendar.isWeekDateSupported()) { value = calendar.getWeekYear(); } else { // use calendar year 'y' instead patternCharIndex = PATTERN_YEAR; field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex]; value = calendar.get(field); } } else if (field == CalendarBuilder.ISO_DAY_OF_WEEK) { value = CalendarBuilder.toISODayOfWeek(calendar.get(Calendar.DAY_OF_WEEK)); } // 省略部分代码……
解决方法
- 不同线程使用不同的SimpleDateFormat对象,每次new一个新的出来或者使用ThreadLocal,缺点消耗内存资源
- 要执行SimpleDateFormat之前进行加锁,缺点效率不高
- 使用其他时间类比如Java8线程安全的DateTimeFormatter,缺点可能是要重构老代码等
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架