首页> 搜索结果页
"java SimpleDateFormat" 检索
共 2331 条结果
SimpleDateFormat in Java is not Thread-Safe Use Carefully
SimpleDateFormat in Java  very common and used to format Date to String and parse String into Date in Java but it can cause very subtle and hard to debug issues if not used carefully because DateFormat and SimpleDateFormat both are not thread-safe and buggy. call to format() and parse() method mutate state of DateFormat class and should be synchronized externally in order to avoid any issue. here are few points which you should take care while using SimpleDateFormat in Java: SimpleDateFormat in Java is not Thread-Safe 1) Use local DateFormat or SimpleDateFormat objects for converting or formatting dates in Java. Making them local ensure that they will not be shared between multiple Threads.   2) If you are sharing Date for SimpleDateFormat class in Java then you need to externally synchronize call to format() and parse() method as they mutate state of DateFormat object and can create subtle and hard to fix bugs while formatting Strings or creating dates in Java. Best is to avoid sharing of DateFormat class altogether.   3) If you have option use JODA date time library for your date and time related operation. its easy to understand and portable with Java Date API and solves all thread-safety issues associated with SimpleDateFormat in Java.   4) Another good alternative of SimpleDateFormat in Java is Apaches' commons.lang package which hold a class called  FastDateFormat utility class and thread-safe alternative of SimpleDateFormat in Java.   5) Another approach of synchronizing DateFormat and SimpleDateFormat is using ThreadLocal, which create SimpleDateFormat on per Thread basis but it can be source of severe memory leak and java.lang.OutOfMemoryError if not used carefully. so avoid until you don't have any other option.   That’s all on SimpleDateFormat in Java. Many people has pointed out flaw on design of Format class but unfortunately Sun or Oracle has not addressed them in any release not even in JDK7. SimpleDateFormat is still first choice because of its availability on standard library but should be used carefully. Read more: http://javarevisited.blogspot.com/2012/03/simpledateformat-in-java-is-not-thread.html#ixzz3QDZXPBbq
文章
Java
2015-01-29
SimpleDateFormat 非线程安全
@[TOC]前言类 SimpleDateFormat 主要负责日期的转换与格式化,但在多线程的环境中,使用此类容易造成数据转换及处理的不准确,因为 SimpleDateFormat 类并不是线程安全的。1.出现异常  本实例将实现使用类 SimpleDateFormat 类在多线程的环境中处理日期但得出的结果却是错误的情况,这也是再多线程环境开发中容易遇到的问题。  创建名称为7.4.1项目。  创建MyThread.java类代码如下:import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class MyThread extends Thread{ private SimpleDateFormat sdf; private String dateString; public MyThread(SimpleDateFormat sdf, String dateString) { super(); this.sdf = sdf; this.dateString = dateString; } @Override public void run() { try { Date dateRef = sdf.parse(dateString); String newDateString = sdf.format(dateRef).toString(); if (!newDateString.equals(dateString)) { System.out.println("ThreadName= " + this.getName() + "报错了 日期字符串: " + dateString + " 转换成的日期为: " + newDateString); } } catch (ParseException e) { e.printStackTrace(); } } }  创建运行类Test.java代码如下:import java.text.SimpleDateFormat; public class Test { public static void main(String[] args) { //共享变量SimpleDateFormat 导致 java.lang.NumberFormatException: multiple points SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String[] dateStringArray = new String[]{"2000-01-01", "2001-02-02", "2003-03-03", "2004-04-04","2004-04-05","2004-04-06","2004-04-07"}; MyThread[] threadArray = new MyThread[7]; for (int i = 0; i < dateStringArray.length; i++) { threadArray[i] = new MyThread(sdf, dateStringArray[i]); } for (int i = 0; i < dateStringArray.length; i++) { threadArray[i].start(); } } }  程序运行后的结果如下所示:ThreadName= Thread -2 报错了 日期字符串: 2003 -03 -03 转换成的日期为: 2300 -04 -07 ThreadName= Thread -4 报错了 日期字符串: 2004 -04 -05 转换成的日期为: 2300 -04 -07  从控制台中打印的结果来看,使用单例的 SimpleDateFormat 类在多线程的环境中处理日期,极易出现日期转换错误的情况。2.解决异常方法 1  创建名称为7.4.2项目。  创建MyThread.java类代码如下:import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class MyThread extends Thread{ private SimpleDateFormat sdf; private String dateString; public MyThread(SimpleDateFormat sdf, String dateString) { super(); this.sdf = sdf; this.dateString = dateString; } @Override public void run() { try { Date dateRef = DateTools.parse("yyyy-MM-dd", dateString); String newDateString = DateTools.format("yyyy-MM-dd", dateRef); if (!newDateString.equals(dateString)) { System.out.println("ThreadName= " + this.getName() + "报错了 日期字符串: " + dateString + " 转换成的日期为: " + newDateString); } } catch (ParseException e) { e.printStackTrace(); } } }  创建类DateTools.java代码如下:import java.text.SimpleDateFormat; import java.util.Date; import java.text.ParseException; public class DateTools { public static Date parse(String formatPattern, String dateString) throws ParseException { return new SimpleDateFormat(formatPattern).parse(dateString); } public static String format(String formatPattern, Date date) { return new SimpleDateFormat(formatPattern).format(date).toString(); } }  创建运行类Test.java代码如下:import java.text.SimpleDateFormat; public class Test { public static void main(String[] args) { //共享变量SimpleDateFormat 导致 java.lang.NumberFormatException: multiple points SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String[] dateStringArray = new String[]{"2000-01-01", "2001-02-02", "2003-03-03", "2004-04-04","2004-04-05","2004-04-06","2004-04-07"}; MyThread[] threadArray = new MyThread[7]; for (int i = 0; i < dateStringArray.length; i++) { threadArray[i] = new MyThread(sdf, dateStringArray[i]); } for (int i = 0; i < dateStringArray.length; i++) { threadArray[i].start(); } }  程序运行后的结果是控制台输出空白。  控制台没有输出任何异常,解决处理错误的方法就是创建了多个 SimpleDateFormat 类的实例。3.解决异常方法 2  ThreadLocal类能使线程绑定到指定的对象。使用该类也可以解决多线程环境下SimpleDateFormat类处理错误的情况。  创建名称为7.4.3项目。  创建MyThread.java类代码如下:import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class MyThread extends Thread{ private SimpleDateFormat sdf; private String dateString; public MyThread(SimpleDateFormat sdf, String dateString) { super(); this.sdf = sdf; this.dateString = dateString; } @Override public void run() { try { Date dateRef = DateTools.getSimpleDateFormat("yyyy-MM-dd").parse(dateString); String newDateString = DateTools.getSimpleDateFormat("yyyy-MM-dd").format(dateRef).toString(); if (!newDateString.equals(dateString)) { System.out.println("ThreadName= " + this.getName() + "报错了 日期字符串: " + dateString + " 转换成的日期为: " + newDateString); } } catch (ParseException e) { e.printStackTrace(); } } }  创建类DateTools.java代码如下:import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class DateTools { public static ThreadLocal<SimpleDateFormat> t1 = new ThreadLocal<SimpleDateFormat>(); public static SimpleDateFormat getSimpleDateFormat(String datePattern) { SimpleDateFormat sdf = null; sdf = t1.get(); if (null == sdf) { sdf = new SimpleDateFormat(datePattern); t1.set(sdf); } return sdf; } }  创建运行类Test.java代码如下:import java.text.SimpleDateFormat; public class Test { public static void main(String[] args) { //共享变量SimpleDateFormat 导致 java.lang.NumberFormatException: multiple points SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String[] dateStringArray = new String[]{"2000-01-01", "2001-02-02", "2003-03-03", "2004-04-04","2004-04-05","2004-04-06","2004-04-07"}; MyThread[] threadArray = new MyThread[7]; for (int i = 0; i < dateStringArray.length; i++) { threadArray[i] = new MyThread(sdf, dateStringArray[i]); } for (int i = 0; i < dateStringArray.length; i++) { threadArray[i].start(); } } }  程序运行后的结果是控制台输出空白。  控制台没有输出任何异常,没有任何信息被输出,看来运行结果是正确的
文章
安全
2023-02-03
Java SimpleDateFormat 线程不安全问题及解决方法
Java SimpleDateFormat 是线程不安全的,当在多线程环境下使用一个DateFormat的时候是有问题的,如下面的例子: package com.heaven.threadpool; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class DateFormatThreadSafe { public static void main(String[] args) throws Exception { final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd"); Callable<Date> task = new Callable<Date>() { public Date call() throws Exception { return format.parse("20101022"); } }; // 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++) { results.add(exec.submit(task)); } exec.shutdown(); // look at the results for (Future<Date> result : results) { System.out.println(result.get()); } } } 上面代码可能会引起如下问题: 问题1: Fri Oct 22 00:00:00 CST 2010 Fri Oct 22 00:00:00 CST 2010 Fri Oct 22 00:00:00 CST 2010 Mon Nov 22 00:00:00 CST 2010 Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.NumberFormatException: For input string: "" at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222) at java.util.concurrent.FutureTask.get(FutureTask.java:83) at com.heaven.threadpool.DateFormatThreadSafe.main(DateFormatThreadSafe.java:49) Caused by: java.lang.NumberFormatException: For input string: "" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48) at java.lang.Long.parseLong(Long.java:424) at java.lang.Long.parseLong(Long.java:461) at java.text.DigitList.getLong(DigitList.java:177) at java.text.DecimalFormat.parse(DecimalFormat.java:1298) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1589) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1312) at java.text.DateFormat.parse(DateFormat.java:335) at com.heaven.threadpool.DateFormatThreadSafe$1.call(DateFormatThreadSafe.java:31) at com.heaven.threadpool.DateFormatThreadSafe$1.call(DateFormatThreadSafe.java:1) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) at java.util.concurrent.FutureTask.run(FutureTask.java:138) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:619) 问题二:Fri Oct 22 00:00:00 CST 2010 Fri Oct 22 00:00:00 CST 2010 Fri Oct 22 00:00:00 CST 2010 Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.ArrayIndexOutOfBoundsException: -1 at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222) at java.util.concurrent.FutureTask.get(FutureTask.java:83) at com.heaven.threadpool.DateFormatThreadSafe.main(DateFormatThreadSafe.java:32) Caused by: java.lang.ArrayIndexOutOfBoundsException: -1 at java.text.DigitList.fitsIntoLong(DigitList.java:212) at java.text.DecimalFormat.parse(DecimalFormat.java:1296) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1589) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1312) at java.text.DateFormat.parse(DateFormat.java:335) at com.heaven.threadpool.DateFormatThreadSafe$1.call(DateFormatThreadSafe.java:19) at com.heaven.threadpool.DateFormatThreadSafe$1.call(DateFormatThreadSafe.java:1) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) at java.util.concurrent.FutureTask.run(FutureTask.java:138) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)问题三:Sat Oct 01 00:00:00 CST 1 Sat Oct 01 00:00:00 CST 1 Sun Oct 30 00:00:00 CST 2016 Sun Oct 30 00:00:00 CST 2016 Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.NumberFormatException: For input string: "E.122E22" at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222) at java.util.concurrent.FutureTask.get(FutureTask.java:83) at com.heaven.threadpool.DateFormatThreadSafe.main(DateFormatThreadSafe.java:32) Caused by: java.lang.NumberFormatException: For input string: "E.122E22" at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1224) at java.lang.Double.parseDouble(Double.java:510) at java.text.DigitList.getDouble(DigitList.java:151) at java.text.DecimalFormat.parse(DecimalFormat.java:1303) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1936) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1312) at java.text.DateFormat.parse(DateFormat.java:335) at com.heaven.threadpool.DateFormatThreadSafe$1.call(DateFormatThreadSafe.java:19) at com.heaven.threadpool.DateFormatThreadSafe$1.call(DateFormatThreadSafe.java:1) at java.u 解决方法一:把DateFormat 放到ThreadLocal中 package com.heaven.threadpool; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class DateFormatTest { private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd HH"); } }; public Date convert(String source) throws ParseException { Date d = df.get().parse(source); return d; } } 解决方法二:使用线程安全的DateFormat,在Github上已经有人写了一个线程安全的,我们可以直接拿来用,请点击下面链接查看 点击打开链接
文章
Java  ·  安全
2014-05-07
java date String 类型相互转换
这种转换要用到java.text.SimpleDateFormat类 字符串转换成日期类型: 方法1: 也是最简单的方法 Date date=new Date("2008-04-14"); 方法2: SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");//小写的mm表示的是分钟 String dstr="2008-4-24"; java.util.Date date=sdf.parse(dstr); 日期转换成字符串: SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd"); java.util.Date date=new java.util.Date(); String str=sdf.format(date);
文章
Java
2015-10-17
【百问百答】Java开发手册灵魂15问之禁止SimpleDateFormat定义成static变量
1. 代码描述在Java语言中,多线程中CountDownLatch的用法 2. 简述在java语言开发中,使用SimpleDateFormat 要注意存在的什么问题 3. 代码描述在java语言开发中,如何实现通过局部变量,解决SimpleDateFormat 使用过程中的线程不安全的问题 4. 代码描述在java语言开发中,如何实现通过加同步锁,解决SimpleDateFormat 使用过程中的线程不安全的问题 5. 代码描述在java语言开发中,如何实现通过ThreadLocal,解决SimpleDateFormat 使用过程中的线程不安全的问题 6. 代码描述在java语言开发中,如何实现通过JDK8使用DateTimeFormatter,替换SimpleDateFormat 解决使用过程中的线程不安全的问题 7. 简述在Java语言开发中,如何解决使用过程中的线程不安全的问题
问答
Java
2021-01-14
深入理解Java:SimpleDateFormat安全的时间格式化
想必大家对SimpleDateFormat并不陌生。SimpleDateFormat 是 Java 中一个非常常用的类,该类用来对日期字符串进行解析和格式化输出,但如果使用不小心会导致非常微妙和难以调试的问题,因为 DateFormat 和 SimpleDateFormat 类不都是线程安全的,在多线程环境下调用 format() 和 parse() 方法应该使用同步代码来避免问题。下面我们通过一个具体的场景来一步步的深入学习和理解SimpleDateFormat类。   一.引子  我们都是优秀的程序员,我们都知道在程序中我们应当尽量少的创建 SimpleDateFormat 实例,因为创建这么一个实例需要耗费很大的代价。在一个读取数据库数据导出到excel文件的例子当中,每次处理一个时间信息的时候,就需要创建一个 SimpleDateFormat实例对象,然后再丢弃这个对象。大量的对象就这样被创建出来,占用大量的内存和 jvm空间。代码如下: package com.peidasoft.dateformat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class DateUtil { public static String formatDate(Date date)throws ParseException{ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.format(date); } public static Date parse(String strDate) throws ParseException{ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.parse(strDate); } }   你也许会说,OK,那我就创建一个静态的simpleDateFormat实例,然后放到一个DateUtil类(如下)中,在使用时直接使用这个实例进行操作,这样问题就解决了。改进后的代码如下: package com.peidasoft.dateformat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class DateUtil { private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static String formatDate(Date date)throws ParseException{ return sdf.format(date); } public static Date parse(String strDate) throws ParseException{ return sdf.parse(strDate); } }   当然,这个方法的确很不错,在大部分的时间里面都会工作得很好。但当你在生产环境中使用一段时间之后,你就会发现这么一个事实:它不是线程安全 的。在正常的测试情况之下,都没有问题,但一旦在生产环境中一定负载情况下时,这个问题就出来了。他会出现各种不同的情况,比如转化的时间不正确,比如报 错,比如线程被挂死等等。我们看下面的测试用例,那事实说话: package com.peidasoft.dateformat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class DateUtil { private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static String formatDate(Date date)throws ParseException{ return sdf.format(date); } public static Date parse(String strDate) throws ParseException{ return sdf.parse(strDate); } } package com.peidasoft.dateformat; import java.text.ParseException; import java.util.Date; public class DateUtilTest { public static class TestSimpleDateFormatThreadSafe extends Thread { @Override public void run() { while(true) { try { this.join(2000); } catch (InterruptedException e1) { e1.printStackTrace(); } try { System.out.println(this.getName()+":"+DateUtil.parse("2013-05-24 06:02:20")); } catch (ParseException e) { e.printStackTrace(); } } } } public static void main(String[] args) { for(int i = 0; i < 3; i++){ new TestSimpleDateFormatThreadSafe().start(); } } }   执行输出如下: Exception in thread "Thread-1" java.lang.NumberFormatException: multiple points at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1082) at java.lang.Double.parseDouble(Double.java:510) at java.text.DigitList.getDouble(DigitList.java:151) at java.text.DecimalFormat.parse(DecimalFormat.java:1302) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1589) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1311) at java.text.DateFormat.parse(DateFormat.java:335) at com.peidasoft.orm.dateformat.DateNoStaticUtil.parse(DateNoStaticUtil.java:17) at com.peidasoft.orm.dateformat.DateUtilTest$TestSimpleDateFormatThreadSafe.run(DateUtilTest.java:20) Exception in thread "Thread-0" java.lang.NumberFormatException: multiple points at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1082) at java.lang.Double.parseDouble(Double.java:510) at java.text.DigitList.getDouble(DigitList.java:151) at java.text.DecimalFormat.parse(DecimalFormat.java:1302) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1589) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1311) at java.text.DateFormat.parse(DateFormat.java:335) at com.peidasoft.orm.dateformat.DateNoStaticUtil.parse(DateNoStaticUtil.java:17) at com.peidasoft.orm.dateformat.DateUtilTest$TestSimpleDateFormatThreadSafe.run(DateUtilTest.java:20) Thread-2:Mon May 24 06:02:20 CST 2021 Thread-2:Fri May 24 06:02:20 CST 2013 Thread-2:Fri May 24 06:02:20 CST 2013 Thread-2:Fri May 24 06:02:20 CST 2013   说明:Thread-1和Thread-0报java.lang.NumberFormatException: multiple points错误,直接挂死,没起来;Thread-2 虽然没有挂死,但输出的时间是有错误的,比如我们输入的时间是:2013-05-24 06:02:20 ,当会输出:Mon May 24 06:02:20 CST 2021 这样的灵异事件。   二.原因   作为一个专业程序员,我们当然都知道,相比于共享一个变量的开销要比每次创建一个新变量要小很多。上面的优化过的静态的 SimpleDateFormat版,之所在并发情况下回出现各种灵异错误,是因为SimpleDateFormat和DateFormat类不是线程安 全的。我们之所以忽视线程安全的问题,是因为从SimpleDateFormat和DateFormat类提供给我们的接口上来看,实在让人看不出它与线 程安全有何相干。只是在JDK文档的最下面有如下说明:   SimpleDateFormat中的日期格式不是同步的。推荐(建议)为每个线程创建独立的格式实例。如果多个线程同时访问一个格式,则它必须保持外部同步。   JDK原始文档如下:  Synchronization:  Date formats are not synchronized.   It is recommended to create separate format instances for each thread.   If multiple threads access a format concurrently, it must be synchronized externally.   下面我们通过看JDK源码来看看为什么SimpleDateFormat和DateFormat类不是线程安全的真正原因:   SimpleDateFormat继承了DateFormat,在DateFormat中定义了一个protected属性的 Calendar类的对象:calendar。只是因为Calendar累的概念复杂,牵扯到时区与本地化等等,Jdk的实现中使用了成员变量来传递参 数,这就造成在多线程的时候会出现错误。   在format方法里,有这样一段代码: 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; ) { int tag = compiledPattern[i] >>> 8; int count = compiledPattern[i++] & 0xff; if (count == 255) { count = compiledPattern[i++] << 16; count |= compiledPattern[i++]; } 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; }   calendar.setTime(date)这条语句改变了calendar,稍后,calendar还会用到(在subFormat方法里),而这就是引发问题的根源。想象一下,在一个多线程环境下,有两个线程持有了同一个SimpleDateFormat的实例,分别调用format方法:  线程1调用format方法,改变了calendar这个字段。  中断来了。  线程2开始执行,它也改变了calendar。  又中断了。  线程1回来了,此时,calendar已然不是它所设的值,而是走上了线程2设计的道路。如果多个线程同时争抢calendar对象,则会出现各种问题,时间不对,线程挂死等等。  分析一下format的实现,我们不难发现,用到成员变量calendar,唯一的好处,就是在调用subFormat时,少了一个参数,却带来了这许多的问题。其实,只要在这里用一个局部变量,一路传递下去,所有问题都将迎刃而解。   这个问题背后隐藏着一个更为重要的问题--无状态:无状态方法的好处之一,就是它在各种环境下,都可以安全的调用。衡量一个方法是否是有状态的,就看它 是否改动了其它的东西,比如全局变量,比如实例的字段。format方法在运行过程中改动了SimpleDateFormat的calendar字段,所 以,它是有状态的。   这也同时提醒我们在开发和设计系统的时候注意下一下三点:   1.自己写公用类的时候,要对多线程调用情况下的后果在注释里进行明确说明   2.对线程环境下,对每一个共享的可变变量都要注意其线程安全性   3.我们的类和方法在做设计的时候,要尽量设计成无状态的   三.解决办法   1.需要的时候创建新实例: package com.peidasoft.dateformat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class DateUtil { public static String formatDate(Date date)throws ParseException{ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.format(date); } public static Date parse(String strDate) throws ParseException{ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.parse(strDate); } }   说明:在需要用到SimpleDateFormat 的地方新建一个实例,不管什么时候,将有线程安全问题的对象由共享变为局部私有都能避免多线程问题,不过也加重了创建对象的负担。在一般情况下,这样其实对性能影响比不是很明显的。   2.使用同步:同步SimpleDateFormat对象 package com.peidasoft.dateformat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class DateSyncUtil { private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static String formatDate(Date date)throws ParseException{ synchronized(sdf){ return sdf.format(date); } } public static Date parse(String strDate) throws ParseException{ synchronized(sdf){ return sdf.parse(strDate); } } }   说明:当线程较多时,当一个线程调用该方法时,其他想要调用此方法的线程就要block,多线程并发量大的时候会对性能有一定的影响。   3.使用ThreadLocal:  package com.peidasoft.dateformat; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class ConcurrentDateUtil { private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); } }; public static Date parse(String dateStr) throws ParseException { return threadLocal.get().parse(dateStr); } public static String format(Date date) { return threadLocal.get().format(date); } }   另外一种写法: package com.peidasoft.dateformat; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class ThreadLocalDateUtil { private static final String date_format = "yyyy-MM-dd HH:mm:ss"; private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>(); public static DateFormat getDateFormat() { DateFormat df = threadLocal.get(); if(df==null){ df = new SimpleDateFormat(date_format); threadLocal.set(df); } return df; } public static String formatDate(Date date) throws ParseException { return getDateFormat().format(date); } public static Date parse(String strDate) throws ParseException { return getDateFormat().parse(strDate); } }   说明:使用ThreadLocal, 也是将共享变量变为独享,线程独享肯定能比方法独享在并发环境中能减少不少创建对象的开销。如果对性能要求比较高的情况下,一般推荐使用这种方法。   4.抛弃JDK,使用其他类库中的时间格式化类:   1.使用Apache commons 里的FastDateFormat,宣称是既快又线程安全的SimpleDateFormat, 可惜它只能对日期进行format, 不能对日期串进行解析。   2.使用Joda-Time类库来处理时间相关问题       做一个简单的压力测试,方法一最慢,方法三最快,但是就算是最慢的方法一性能也不差,一般系统方法一和方法二就可以满足,所以说在这个点很难成为你系统的瓶颈所在。从简单的角度来说,建议使用方法一或者方法二,如果在必要的时候,追求那么一点性能提升的话,可以考虑用方法三,用ThreadLocal做缓存。   Joda-Time类库对时间处理方式比较完美,建议使用。   参考资料:   1.http://dreamhead.blogbus.com/logs/215637834.html   2.http://www.blogjava.net/killme2008/archive/2011/07/10/354062.html
文章
安全  ·  Java
2016-08-17
Java SimpleDateFormat 线程不安全问题及解决方法
Java SimpleDateFormat 是线程不安全的,当在多线程环境下使用一个DateFormat的时候是有问题的,如下面的例子: package com.heaven.threadpool; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class DateFormatThreadSafe { public static void main(String[] args) throws Exception { final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd"); Callable<Date> task = new Callable<Date>() { public Date call() throws Exception { return format.parse("20101022"); } }; // 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++) { results.add(exec.submit(task)); } exec.shutdown(); // look at the results for (Future<Date> result : results) { System.out.println(result.get()); } } } 上面代码可能会引起如下问题: 问题1: Fri Oct 22 00:00:00 CST 2010 Fri Oct 22 00:00:00 CST 2010 Fri Oct 22 00:00:00 CST 2010 Mon Nov 22 00:00:00 CST 2010 Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.NumberFormatException: For input string: "" at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222) at java.util.concurrent.FutureTask.get(FutureTask.java:83) at com.heaven.threadpool.DateFormatThreadSafe.main(DateFormatThreadSafe.java:49) Caused by: java.lang.NumberFormatException: For input string: "" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48) at java.lang.Long.parseLong(Long.java:424) at java.lang.Long.parseLong(Long.java:461) at java.text.DigitList.getLong(DigitList.java:177) at java.text.DecimalFormat.parse(DecimalFormat.java:1298) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1589) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1312) at java.text.DateFormat.parse(DateFormat.java:335) at com.heaven.threadpool.DateFormatThreadSafe$1.call(DateFormatThreadSafe.java:31) at com.heaven.threadpool.DateFormatThreadSafe$1.call(DateFormatThreadSafe.java:1) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) at java.util.concurrent.FutureTask.run(FutureTask.java:138) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:619) 问题二:Fri Oct 22 00:00:00 CST 2010 Fri Oct 22 00:00:00 CST 2010 Fri Oct 22 00:00:00 CST 2010 Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.ArrayIndexOutOfBoundsException: -1 at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222) at java.util.concurrent.FutureTask.get(FutureTask.java:83) at com.heaven.threadpool.DateFormatThreadSafe.main(DateFormatThreadSafe.java:32) Caused by: java.lang.ArrayIndexOutOfBoundsException: -1 at java.text.DigitList.fitsIntoLong(DigitList.java:212) at java.text.DecimalFormat.parse(DecimalFormat.java:1296) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1589) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1312) at java.text.DateFormat.parse(DateFormat.java:335) at com.heaven.threadpool.DateFormatThreadSafe$1.call(DateFormatThreadSafe.java:19) at com.heaven.threadpool.DateFormatThreadSafe$1.call(DateFormatThreadSafe.java:1) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) at java.util.concurrent.FutureTask.run(FutureTask.java:138) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)问题三:Sat Oct 01 00:00:00 CST 1 Sat Oct 01 00:00:00 CST 1 Sun Oct 30 00:00:00 CST 2016 Sun Oct 30 00:00:00 CST 2016 Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.NumberFormatException: For input string: "E.122E22" at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222) at java.util.concurrent.FutureTask.get(FutureTask.java:83) at com.heaven.threadpool.DateFormatThreadSafe.main(DateFormatThreadSafe.java:32) Caused by: java.lang.NumberFormatException: For input string: "E.122E22" at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1224) at java.lang.Double.parseDouble(Double.java:510) at java.text.DigitList.getDouble(DigitList.java:151) at java.text.DecimalFormat.parse(DecimalFormat.java:1303) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1936) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1312) at java.text.DateFormat.parse(DateFormat.java:335) at com.heaven.threadpool.DateFormatThreadSafe$1.call(DateFormatThreadSafe.java:19) at com.heaven.threadpool.DateFormatThreadSafe$1.call(DateFormatThreadSafe.java:1) at java.u 解决方法一:把DateFormat 放到ThreadLocal中 package com.heaven.threadpool; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class DateFormatTest { private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd HH"); } }; public Date convert(String source) throws ParseException { Date d = df.get().parse(source); return d; } } 解决方法二:使用线程安全的DateFormat,在Github上已经有人写了一个线程安全的,我们可以直接拿来用,请点击下面链接查看 点击打开链接
文章
Java  ·  安全
2014-05-07
java的SimpleDateFormat线程不安全出问题了,项目不支持java8,虚竹教你这一招
目录一、场景二、SimpleDateFormat线程为什么是线程不安全的呢?验证SimpleDateFormat线程不安全三、FastDateFormat源码分析实践四、结论一、场景在java8以前,要格式化日期时间,就需要用到SimpleDateFormat。但我们知道SimpleDateFormat是线程不安全的,处理时要特别小心,要加锁或者不能定义为static,要在方法内new出对象,再进行格式化。很麻烦,而且重复地new出对象,也加大了内存开销。后来Apache 在commons-lang 包中扩展了FastDateFormat对象,它是一个线程安全的,可以用来完美替换SimpleDateFormat。二、SimpleDateFormat线程为什么是线程不安全的呢?来看看SimpleDateFormat的源码//FastDateFormatpublic static FastDateFormat getInstance(final String pattern) { return CACHE.getInstance(pattern, null, null);} 如图可证,是使用了ConcurrentMap 做缓存。且key值是格式,时区和locale(语境)三者都相同为相同的key。四、结论java8之前,可使用FastDateFormat 替换SimpleDateFormat,达到线程安全的目的;java8及以上的,java8提供了一套新的日期时间API,可以使用DateTimeFormatter来代替SimpleDateFormat。具体的源码分析,可以看这里,传送门:万字博文教你搞懂java源码的日期和时间相关用法
文章
缓存  ·  安全  ·  Java  ·  API  ·  Apache
2022-01-09
时间串变成Date类型的数据
java.text.SimpleDateFormat sf = new java.text.SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); java.util.Date d = sf.parse("2000-11-11 14:23:20"); System.out.println(d); String s = sf.format(d); System.out.println(s);    java.text.DateFormat   怎么把字符串转化成Date类型? DataFormat.getDateInstance().parse("2006-6-6") 当前时间 DataFormat.getDateTimeInstance().format(new Date())       Date date =new Date();       DateFormat   df   =  new  SimpleDateFormat("yyyy年MM月dd日");      java.text.SimpleDateFormat formatter =new java.text.SimpleDateFormat("yyyy-MM-dd-H-mm-ss");       Date date =new Date();     java.text.SimpleDateFormat formatter =new java.text.SimpleDateFormat("yyyy-MM-dd H:mm:ss");           System.out.println(formatter.format(date));     时间的类型是java.util.Date   Date date = new Date(); System.out.println(date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds()); 
文章
2013-03-04
根据时间的变化修改表名
package com.XX.utils; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; /** * Created by apple on 2017/10/1. */ public class DateUtil { private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static String format(Date date) { return sdf.format(date); } public static Date getTimeByMinusHour(Date date, int n) { SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Calendar cal = Calendar.getInstance(); cal.setTime(date); cal.add(Calendar.MINUTE, -1*n); return cal.getTime(); } public static String dateToStr(java.util.Date dateDate) { SimpleDateFormat formatter = new SimpleDateFormat("yyMMdd"); String dateString = formatter.format(dateDate); return dateString; } public static void main(String[] args) { Date date = new Date(); String dateString = dateToStr(date); String talbe = "DD"+dateString; System.out.print(talbe); } }
文章
2017-09-22
...
跳转至:
开发与运维
5622 人关注 | 131425 讨论 | 301869 内容
+ 订阅
  • 记CentOS7.6 zabix5.0-0 —agent2监控Mysql数据库
  • 【算法日记】快速幂:关于我知道答案却做不出来这档事
  • 【Java实用技术】java中关于整数的几个冷知识,总有一个你不知道
查看更多 >
安全
1191 人关注 | 23954 讨论 | 81266 内容
+ 订阅
  • 阿里云国际版账号注册常见问题汇总
  • 阿里云国际站注册下单流程
  • 阿里云国际站版账号购买云服务器:国际阿里云分销商,注册USDT充值教程
查看更多 >