非线程安全类SimpleDateFormat
来源:互联网 发布:小鸟云服务器知乎 编辑:程序博客网 时间:2024/05/24 03:45
SimpleDateFormat是非线程安全的,写处理日期的工具类时候请注意。
问题背景:
项目组的同事在新项目里写了一个DateUtil专门处理日期格式化的工具。线上运行后台日志偶然发生莫名其妙的错误:
java.lang.NumberFormatException: multiple points
java.lang.NumberFormatException: For input string: “”
java.lang.NumberFormatException: For input string: “.31023102EE22”
例如:
java.lang.NumberFormatException: multiple points at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1110) at java.lang.Double.parseDouble(Double.java:540) at java.text.DigitList.getDouble(DigitList.java:168) at java.text.DecimalFormat.parse(DecimalFormat.java:1321) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1793) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1455) at java.text.DateFormat.parse(DateFormat.java:355)或者java.lang.NumberFormatException: For input string: "" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) at java.lang.Long.parseLong(Long.java:453) at java.lang.Long.parseLong(Long.java:483) at java.text.DigitList.getLong(DigitList.java:194) at java.text.DecimalFormat.parse(DecimalFormat.java:1316) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1793) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1455) at java.text.DateFormat.parse(DateFormat.java:355)
原因分析:
根据错误日志搜来问题代码:
public class DateUtil { private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); ... ... public static Date parse(String strDate) throws ParseException{ return sdf.parse(strDate); }}
再分析项目调用该代码的场景:
Service A (线程1)某方法执行DateUtil.parse(“2017-02-12”)
Service B (线程2)某方法执行DateUtil.parse(“2017-03-12”)
并且当service A和service B同时触发上面代码时候就出问题了。
JDK源码分析:
调用链:
SimpleDateFormat里parse(String strDate)
=>DateFormat里parse(source, pos);
public class SimpleDateFormat extends DateFormat { ... transient private char[] compiledPattern; ... ... public Date parse(String text, ParsePosition pos) { checkNegativeNumberExpression(); int start = pos.index; int oldStart = start; int textLength = text.length(); boolean[] ambiguousYear = {false}; CalendarBuilder calb = new CalendarBuilder(); for (int i = 0; i < compiledPattern.length; ) { int tag = compiledPattern[i] >>> 8; int count = compiledPattern[i++] & 0xff; if (count == 255) { count = compiledPattern[i++] << 16; count |= compiledPattern[i++]; } ... ... ...
在DateUtil里变量SimpleDateFormat被定义为static,因而所有线程调用DateUtil时候都共享了该变量。
一看源码就直觉知道SimpleDateFormat是一个有状态的对象了,因为它拥有很多成员变量,而且变量和很多方法都没有加锁同步处理。
例如状态变量compiledPattern>>>8这句,假设多个线程同时修改该方法值,那各个线程间就互相影响了,从而SimpleDateFormat的parse方法肯定出问题。
解决方案:
- 去掉全局静态变量SimpleDateFormat,在每个parse方法里new SimpleDateFormat
public class DateUtil { ... ... public static Date parse(String strDate) throws ParseException{ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); return sdf.parse(strDate); }}
- 在parse方法前加synchronized同步
public class DateUtil { private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); ... ... public static synchronized Date parse(String strDate) throws ParseException{ return sdf.parse(strDate); }}
- 使用线程封闭的ThreadLocal实现同一线程内共享,不同线程间隔离 (此方法不推荐,详细解释留意下一篇文章详解ThreadLocal)
- 非线程安全类SimpleDateFormat
- SimpleDateFormat非线程安全
- SimpleDateFormat非线程安全
- SimpleDateFormat 非线程安全
- SimpleDateFormat非线程安全
- SimpleDateFormat非线程安全
- 非线程安全类SimpleDateFormat的禁忌
- SimpleDateFormat 解决非线程安全
- ThreadLocal 解决SimpleDateFormat非线程安全
- ThreadLocal 解决SimpleDateFormat非线程安全
- JDK中的SimpleDateFormat线程非安全
- ThreadLocal 解决SimpleDateFormat非线程安全
- Java多线程编程7--SimpleDateFormat非线程安全处理
- SimpleDateFormat不是线程安全的类
- SimpleDateFormat 转为线程安全
- 线程安全之SimpleDateFormat
- SimpleDateFormat线程安全
- java SimpleDateFormat线程安全
- LeetCode 500. Keyboard Row-Java
- TCP多进程并发服务器
- CppPrimer笔记 Chapter9 顺序容器
- 信息组织与提取方法
- 1105. Spiral Matrix (25)
- 非线程安全类SimpleDateFormat
- Microsoft .NET Framework 4.5.1 无法安装(Win7无法访问Windows Installer服务的解决方法)
- 并查集及优化(C++)
- Problem F: 时间类的加、减法赋值运算
- LUbuntu16.10安装kears(Ubuntu类似)
- CppPrimer笔记 Chapter10 泛型算法
- 进程、线程及调优策略
- c++学习笔记 动态内存分配 对象、对象数组的动态创建和释放
- Mac上配置maven+eclipse+spark开发环境