java.text.DateFormat 多线程并发问题
在日常开发中,java.text.DateFormat 应该算是使用频率比较高的一个工具类,经常会使用它 将 Date 对象转换成字符串日期,或者将字符串日期转化成 Date 对象。先来看一段眼熟的代码:
public abstract class DateUtils { private static final DateFormat dateFormatForDay = new SimpleDateFormat("yyyyMMdd"); public static String formatForDay(Date date){ return dateFormatForDay.format(date); }} |
类 DateUtils 的方法 formatForDay() 在多线程的情况下,可能会得不到想要的结果。给一段多线程并发访问 formatForDay() 方法的示例:
public class DateFormatExample { static final class RunThread implements Runnable{ private int i; public RunThread(int i) { this.i = i; } @Override public void run() { int count = 0; while(true){ // create a date Calendar c = Calendar.getInstance(); c.add(Calendar.DAY_OF_MONTH, -i); Date d = c.getTime(); // expect string String origianlDate = new SimpleDateFormat("yyyyMMdd").format(d); // DateUtils format string String afterDate = DateUtils.formatForDay(d); if(!origianlDate.equals(afterDate)){ System.out.println("RunThread["+i+"]["+count+"] origianlDate = "+origianlDate +", afterDate = "+afterDate); System.exit(0); } count++; } } } public static void main(String[] args) { for(int i=0; i<100; i++){ new Thread(new RunThread(i)).start(); } } |
示例代码,创建了100个 RunThread 线程,每个线程根据 i 的值创建不同的日期,并将日期格式化成字符串,和原日期进行对比,若不相等,则打印退出。某次执行的结果见下:
RunThread[85][1] origianlDate = 20170128, afterDate = 20170122RunThread[91][5] origianlDate = 20170122, afterDate = 20170222RunThread[32][7] origianlDate = 20170322, afterDate = 20170114RunThread[60][3] origianlDate = 20170222, afterDate = 20170202RunThread[1][0] origianlDate = 20170422, afterDate = 20170401 |
从结果可以看出,格式化后的日期和传给 format() 的日期不一致。
原因分析
类 DateFormat 有个 Calendar 成员变量:
public abstract class DateFormat extends Format { protected Calendar calendar; // ... other code} |
调用 format() 方法,会调用实现类 SimpleDateFormat 的 format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) 方法:
public class SimpleDateFormat extends DateFormat { //... other code // Called from Format after creating a FieldDelegate private StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) { // Convert input date to time field list calendar.setTime(date); //... other code }} |
每次调用 format() 方法,会将要格式化的日期设置到成员变量 calendar 中,然后再对其进行格式化,此类实现未进行线程同步,是非线程安全的。
当然,调用 parse() 方法,将 字符串转化成日期,也会有同样的非线程安全问题。
解决方案
- 每次进行格式化日期调用时,均 new 一个 SimpleDateFormat 对象;缺点是在高并发的情况下,就会频繁创建和销毁对旬,造成开销。
- 使用 synchronized 关键字 或 Lock 给静态方法加上同步;缺点是在高并发的情况下,所有的线程在此处会引起资源的竞争。
- 使用 ThreadLocal 对象创建静态 DateFormat 。这样在高并必情况下,有多少个线程,就会创建多少个 DateFormat 对象,既不会无限制创建、销毁对象,也不会引起对象的多线程竞争,如下:
public abstract class DateUtils { private static final ThreadLocal<DateFormat> dateFormatForDay = new ThreadLocal<DateFormat>(); public static String formatForDay(Date date){ if(dateFormatForDay.get() == null){ dateFormatForDay.set(new SimpleDateFormat("yyyyMMdd")); } return dateFormatForDay.get().format(date); }} |
java.text.DateFormat 多线程并发问题的更多相关文章
- java.text.DateFormat 日期格式化
一: java.text.DateFormat <%@ page language="java" contentType="text/html; charset=u ...
- 常用类一一时间处理相关类一一java.util.Tomezone(java.util.Calendar , java.util.Date , java.text.DateFormat)
时间处理相关类 时间是一个一维的东东.所以,我们需要一把刻度尺来区表达和度量时间.在计算机世界,我们把1970 年 1 月 1 日 00:00:00定为基准时间,每个度量单位是毫秒(1秒的千分之一). ...
- java.text.DateFormat 线程不安全问题
java.text下的 DateFormat 是线程不安全的: 建议1: 1.使用threadLocal包装DateFormat(太复杂,不推荐) 2.使用org.apache.commons.lan ...
- java 多线程:线程安全问题,示例DateFormat多线程执行冲突解决方案ThreadLocal、方法内变量
SimpleDateFormat多线程中执行报错 java.lang.NumberFormatException: For input string: "" import ja ...
- 【java】Date与String之间的转换及Calendar类:java.text.SimpleDateFormat、public Date parse(String source) throws ParseException和public final String format(Date date)
package 日期日历类; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util. ...
- java.text.ParseException: Unparseable date: "2015-06-09 hh:56:19"
1.错误描述 [DEBUG:]2015-06-09 16:56:19,520 [-------------------transcation start!--------------] java.te ...
- java.text.SimpleDateFormat使用介绍
java.text.SimpleDateFormat的使用 java.lang.Object | +----java.text.Format | +-- ...
- java.text.ParseException: Failed to parse date ["未知']
先把"未知"替换为"" 直接new 出来的Gson 对象是无法解析为""的Date属性的,需要通过GsonBuilder来进行创建 Gson ...
- java.text.ParseException: Unparseable date: "2015-06-09 hh:56:19"
1.错误描写叙述 [DEBUG:]2015-06-09 16:56:19,520 [-------------------transcation start!--------------] java. ...
随机推荐
- Rwordseg使用
#用于下载安装rJava 和 Rwordseg,如果安装了就注释掉 install.packages("rJava") install.packages("Rwordse ...
- C#:几种数据库的大数据批量插入
在之前只知道SqlServer支持数据批量插入,殊不知道Oracle.SQLite和MySql也是支持的,不过Oracle需要使用Orace.DataAccess驱动,今天就贴出几种数据库的批量插入解 ...
- Reactor-反应器模式
Reactor模式:反应器模式,是高性能网络服务器中最为常用的一种模式,libevent,muduo,libuv等网络库都是以 Reactor模式构建.Reactor模式由同步事件多路分解器和具体事件 ...
- 通过回调函数的理解来进一步理解ajax及其注意的用法
一,再一次理解回调函数 (function($){ $.fn.shadow = function(opts){ //定义的默认的参数 var defaults = { copies: 5, opaci ...
- SpringMVC源码情操陶冶-RequestMappingHandlerAdapter适配器
承接前文SpringMVC源码情操陶冶-HandlerAdapter适配器简析.RequestMappingHandlerAdapter适配器组件是专门处理RequestMappingHandlerM ...
- 认识大前端html+css+js
认识大前端:前端就是将效果图生成网页,利用html+css+js等技术. 如果把前端比作一台汽车,那么html就是车的骨架,css就是完整的车的模型,而js就充当着车的发动机... 建议: 刚刚开 ...
- [Luogu3377]【模板】左偏树(可并堆)
题面戳我 题目描述 如题,一开始有N个小根堆,每个堆包含且仅包含一个数.接下来需要支持两种操作: 操作1: 1 x y 将第x个数和第y个数所在的小根堆合并(若第x或第y个数已经被删除或第x和第y个数 ...
- canvas练手项目(二)——各种操作基础
想想应该在canvas上面作画了,那么就不得不提到事件了. (打着canvas的旗号,写着mouse事件.挂羊头卖狗肉!哈哈哈哈哈~) 先来看一看HTML事件属性,我们要用的就是Mouse事件,就先研 ...
- 未来已来,腾讯AI计算网络
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 作者:由鹅厂网事发表在云+社区 "鹅厂网事"由深圳市腾讯计算机系统有限公司技术工程事业群网络平台部运营,我们希望与业界各位志同道合的伙伴交流 ...
- [设计]GUI设计规范的规范
1. 前言 很多著名的软件企业都有GUI设计规范,如 Apple 和 Google,得益于这些设计规范(或者叫指南),设计者才可以开发出符合平台视觉及交互要求的UI.其实除了这些大公司,软件公司都最好 ...