- 日期时间组件使用
java.util.Date:实现类,其对象具有时间、日期组件。
java.util.Calendar:抽象类,其对象具有时间、日期组件。
java.sql.Date:实现类,其对象具有日期组件。
java.sql.Time:实现类,其对象具有时间组件。
java.sql.Timestamp:实现类,其对象具有时间日期组件。
java.text.DateFormat:抽象类,其对象格式化时间日期。
java.text.DateFormatSymbols:实现类,其对象为格式化时间日期提供参数。 -
long currentTime = System.currentTimeMillis(); //获取当前时间戳,从1970-01-01以来的毫秒数 LOGGER.info("{}", currentTime); java.util.Date utilDate = new java.util.Date(currentTime); LOGGER.info("{}|{}", utilDate, utilDate.getTime()); Calendar calendar = Calendar.getInstance(); //除某些特殊地区外,默认等效于new GregorianCalendar() calendar.setTimeInMillis(currentTime); LOGGER.info("{}", calendar); java.sql.Date sqlDate = new java.sql.Date(currentTime); LOGGER.info("{}|{}", sqlDate, sqlDate.getTime()); java.sql.Time sqlTime = new java.sql.Time(currentTime); LOGGER.info("{}|{}", sqlTime, sqlTime.getTime()); java.sql.Timestamp sqlTimestamp = new java.sql.Timestamp(currentTime); LOGGER.info("{}|{}", sqlTimestamp, sqlTimestamp.getTime());
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //默认是SimpleDateFormat
LOGGER.info("{}", dateFormat.format(utilDate));
LOGGER.info("{}", dateFormat.parse("2017-05-25 23:00:00"));输出:
1495724884755 Thu May 25 23:08:04 CST 2017|1495724884755 java.util.GregorianCalendar[time=1495724884755,...,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai"...] 2017-05-25|1495724884755 23:08:04|1495724884755 2017-05-25 23:08:04.755|1495724884755 2017-05-25 23:08:04 Thu May 25 23:00:00 CST 2017
-
java.text.SimpleDateFormat的线程不安全问题
问题重现://final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); Callable<Date> task = new Callable<Date>() { @Override public Date call() throws Exception { LOGGER.info("executing task..."); try{ return new SimpleDateFormat("yyyy-MM-dd").parse("2017-05-25"); //return format.parse("2017-05-25"); //if use shared SimpleDateFormat, executing result is not expected. }catch (Exception ex){ LOGGER.error("Exception:", ex); return new Date(0); } } }; // pool with 5 threads ExecutorService exec = Executors.newFixedThreadPool(5); List<Future<Date>> results = new ArrayList<Future<Date>>(); // perform 10 date conversions for (int i = 0; i < 10; i++) { LOGGER.info("submit task[{}]", i); results.add(exec.submit(task)); } exec.shutdown(); LOGGER.info("shutdown"); // look at the results DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); for (Future<Date> result : results) { LOGGER.info("{}", dateFormat.format(result.get())); } LOGGER.info("completed");
输出(使用共享的SimpleDateFormat时,执行结果并不是预期结果):
submit task[0] submit task[1] submit task[2] executing task... submit task[3] executing task... submit task[4] executing task... executing task... submit task[5] executing task... submit task[6] submit task[7] submit task[8] submit task[9] shutdown executing task... executing task... executing task... executing task... executing task... Exception: java.lang.NumberFormatException: multiple points at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1890) at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110) at java.lang.Double.parseDouble(Double.java:538) at java.text.DigitList.getDouble(DigitList.java:169) at java.text.DecimalFormat.parse(DecimalFormat.java:2056) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514) at java.text.DateFormat.parse(DateFormat.java:364) at com.liq.DateTimeTest$1.call(DateTimeTest.java:65) at com.liq.DateTimeTest$1.call(DateTimeTest.java:59) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Exception: java.lang.NumberFormatException: empty String at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1842) at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110) at java.lang.Double.parseDouble(Double.java:538) at java.text.DigitList.getDouble(DigitList.java:169) at java.text.DecimalFormat.parse(DecimalFormat.java:2056) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514) at java.text.DateFormat.parse(DateFormat.java:364) at com.liq.DateTimeTest$1.call(DateTimeTest.java:65) at com.liq.DateTimeTest$1.call(DateTimeTest.java:59) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Exception: java.lang.NumberFormatException: For input string: "E.2505E2" at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043) at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110) at java.lang.Double.parseDouble(Double.java:538) at java.text.DigitList.getDouble(DigitList.java:169) at java.text.DecimalFormat.parse(DecimalFormat.java:2056) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514) at java.text.DateFormat.parse(DateFormat.java:364) at com.liq.DateTimeTest$1.call(DateTimeTest.java:65) at com.liq.DateTimeTest$1.call(DateTimeTest.java:59) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Exception: java.lang.NumberFormatException: For input string: "E.2505" at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043) at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110) at java.lang.Double.parseDouble(Double.java:538) at java.text.DigitList.getDouble(DigitList.java:169) at java.text.DecimalFormat.parse(DecimalFormat.java:2056) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514) at java.text.DateFormat.parse(DateFormat.java:364) at com.liq.DateTimeTest$1.call(DateTimeTest.java:65) at com.liq.DateTimeTest$1.call(DateTimeTest.java:59) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) 1970-01-01 08:00:00 1970-01-01 08:00:00 1970-01-01 08:00:00 1970-01-01 08:00:00 2017-05-25 00:00:00 2017-05-25 00:00:00 2017-05-25 00:00:00 2017-05-25 00:00:00 2017-05-25 00:00:00 2017-05-25 00:00:00 completed
产生原因:
SimpleDateFormat维护一个共享的Calendar对象,如果共享SimpleDateFomat时这个Calendar也是共享的。由多个线程并发进行操作就会有问题了。protected Calendar calendar;
解决:
(1)使用局部变量
(2)使用 ThreadLocalfinal ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd"); } }; //final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); Callable<Date> task = new Callable<Date>() { @Override public Date call() throws Exception { LOGGER.info("executing task..."); try{ //return new SimpleDateFormat("yyyy-MM-dd").parse("2017-05-25"); //return format.parse("2017-05-25"); //if use shared SimpleDateFormat, executing result is not expected. return df.get().parse("2017-05-25"); }catch (Exception ex){ LOGGER.error("Exception:", ex); return new Date(0); } } };
(3)同步代码块 synchronized(code)
(4)使用第三方的日期处理函数:比如使用 commons-lang 包中的 FastDateFormat 工具类
参考: