咱們能夠用java.text.SimpleDateFormat類完成日期的轉換和格式化操做,如:java
import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** * @author wangmengjun * */ public class SimpleDateFormatExample { private static final DateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat( "yyyyMMdd"); public Date parseDate(String value) throws ParseException { return SIMPLE_DATE_FORMAT.parse(value); } }
import java.text.ParseException; import java.util.Date; /** * * @author wangmengjun * */ public class Main { public static void main(String[] args) throws ParseException { SimpleDateFormatExample example = new SimpleDateFormatExample(); Date date = example.parseDate("20161118"); //Fri Nov 18 00:00:00 CST 2016 System.out.println(date); } }
可是,同時,咱們也能從java.text.SimpleDateFormat類的javadoc中看到以下一句話。git
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.
Date formats沒有同步。apache
建議爲每個線程建立獨立的format對象。安全
若是多個線程併發訪問一個format,那麼,必定要在外部實現同步(synchronized)。多線程
也就是說,併發
在多線程下咱們須要作些額外的保護措施,去保證其正確處理,不然是不安全的。測試
讓咱們一塊兒來看一下,多線程下會出現什麼問題:spa
package my.format; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** * * @author wangmengjun * */ public class SimpleDateFormatExample { private static final DateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat( "yyyyMMdd"); public Date parseDate(String value) throws ParseException { return SIMPLE_DATE_FORMAT.parse(value); } }
多線程測試示例:線程
package my.format; import java.text.ParseException; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Main { public static void main(String[] args) throws InterruptedException, ExecutionException, ParseException { int availableProcessors = Runtime.getRuntime().availableProcessors(); ExecutorService exec = Executors .newFixedThreadPool(availableProcessors); List<Future<Date>> results = new ArrayList<>(); final SimpleDateFormatExample sdf = new SimpleDateFormatExample(); Callable<Date> parseDateTask = new Callable<Date>() { public Date call() throws Exception { return sdf.parseDate("20161118"); } }; for (int i = 0; i < 10; i++) { results.add(exec.submit(parseDateTask)); } exec.shutdown(); /** * 查看結果 */ for (Future<Date> result : results) { System.out.println(result.get()); } } }
運行結果主要包含以下幾個錯誤:code
Tue Nov 18 00:00:00 CST 2200 Tue Nov 18 00:00:00 CST 2200 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Thu Nov 18 00:00:00 CST 1 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016
如:
Fri Nov 18 00:00:00 CST 2016 Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.NumberFormatException: For input string: "" at java.util.concurrent.FutureTask.report(Unknown Source) at java.util.concurrent.FutureTask.get(Unknown Source) at my.format.Main.main(Main.java:40) Caused by: java.lang.NumberFormatException: For input string: "" at java.lang.NumberFormatException.forInputString(Unknown Source) at java.lang.Long.parseLong(Unknown Source) at java.lang.Long.parseLong(Unknown Source) at java.text.DigitList.getLong(Unknown Source) at java.text.DecimalFormat.parse(Unknown Source) at java.text.SimpleDateFormat.subParse(Unknown Source) at java.text.SimpleDateFormat.parse(Unknown Source) at java.text.DateFormat.parse(Unknown Source) at my.format.SimpleDateFormatExample.parseDate(SimpleDateFormatExample.java:14) at my.format.Main$1.call(Main.java:27) at my.format.Main$1.call(Main.java:1) at java.util.concurrent.FutureTask.run(Unknown Source) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.lang.Thread.run(Unknown Source)
再如:
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.NumberFormatException: For input string: "1111.E1111E22" at java.util.concurrent.FutureTask.report(Unknown Source) at java.util.concurrent.FutureTask.get(Unknown Source) at my.format.Main.main(Main.java:40) Caused by: java.lang.NumberFormatException: For input string: "1111.E1111E22" at sun.misc.FloatingDecimal.readJavaFormatString(Unknown Source) at java.lang.Double.parseDouble(Unknown Source) at java.text.DigitList.getDouble(Unknown Source) at java.text.DecimalFormat.parse(Unknown Source) at java.text.SimpleDateFormat.subParse(Unknown Source) at java.text.SimpleDateFormat.parse(Unknown Source) at java.text.DateFormat.parse(Unknown Source) at my.format.SimpleDateFormatExample.parseDate(SimpleDateFormatExample.java:14) at my.format.Main$1.call(Main.java:27) at my.format.Main$1.call(Main.java:1) at java.util.concurrent.FutureTask.run(Unknown Source) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.lang.Thread.run(Unknown Source)
那麼問題來了,如何保證運行正常呢?
其實,從SimpleDateFormat的javadoc中已經看到有處理的方法了。
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.
接下來,先從這個描述信息給出相關的解決方法。
改造SimpleDateFormatExample類,如:
package my.format; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** * * @author wangmengjun * */ public class SimpleDateFormatExample { public Date parseDate(String value) throws ParseException { return new SimpleDateFormat( "yyyyMMdd").parse(value); } }
執行上述Main.java類,獲得正確結果:
Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016
改造SimpleDateFormatExample類,如:
package my.format; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** * * @author wangmengjun * */ public class SimpleDateFormatExample { private static final DateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat( "yyyyMMdd"); public Date parseDate(String value) throws ParseException { synchronized (SIMPLE_DATE_FORMAT) { return SIMPLE_DATE_FORMAT.parse(value); } } }
或者在使用format對象的方法前添加synchronized修飾,如:
package my.format; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** * * @author wangmengjun * */ public class SimpleDateFormatExample { private static final DateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat( "yyyyMMdd"); public synchronized Date parseDate(String value) throws ParseException { return SIMPLE_DATE_FORMAT.parse(value); } }
一樣,執行上述Main.java類,能夠獲得正確結果:
Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016
改造SimpleDateFormatExample類,如:
package my.format; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** * * @author wangmengjun * */ public class SimpleDateFormatExample { private static final ThreadLocal<DateFormat> THREAD_LOCAL_DATE_FORMAT = new ThreadLocal<DateFormat>() { protected DateFormat initialValue() { return new SimpleDateFormat("yyyyMMdd"); } }; public Date parseDate(String value) throws ParseException { return THREAD_LOCAL_DATE_FORMAT.get().parse(value); } }
一樣,執行上述Main.java類,能夠獲得正確結果:
Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016
FastDateFormat類在Apache Common Langs包下面, 該類是線程安全的。
若是是Maven工程,其添加依賴包以下:
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.5</version> </dependency>
改造SimpleDateFormatExample類,如:
private static final FastDateFormat SIMPLE_DATE_FORMAT = FastDateFormat .getInstance("yyyyMMdd");
完成的類爲:
package my.format; import java.text.ParseException; import java.util.Date; import org.apache.commons.lang3.time.FastDateFormat; /** * * @author wangmengjun * */ public class SimpleDateFormatExample { private static final FastDateFormat SIMPLE_DATE_FORMAT = FastDateFormat .getInstance("yyyyMMdd"); public Date parseDate(String value) throws ParseException { return SIMPLE_DATE_FORMAT.parse(value); } }
一樣,執行上述Main.java類,能夠獲得正確結果:
Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016
DateTimeFormatter 類Joda-Time包下面, 該類是線程安全的。
若是是Maven工程,其添加依賴包以下:
<!-- https://mvnrepository.com/artifact/joda-time/joda-time --> <dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> <version>2.9.6</version> </dependency>
改造SimpleDateFormatExample類,如:
package my.format; import java.text.ParseException; import java.util.Date; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; /** * * @author wangmengjun * */ public class SimpleDateFormatExample { private static final DateTimeFormatter SIMPLE_DATE_FORMAT = DateTimeFormat .forPattern("yyyyMMdd"); public Date parseDate(String value) throws ParseException { return SIMPLE_DATE_FORMAT.parseDateTime(value).toDate(); } }
一樣,執行上述Main.java類,能夠獲得正確結果:
Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016 Fri Nov 18 00:00:00 CST 2016
本文首先給出了一個使用SimpleDateFormat的簡單示例;接着,給出了一個在多線程下使用SimpleDateFormat出現問題的例子;緊接着又給出了幾個解決SimpleDateFormat線程不安全使用的例子。
如今,Java 8中,已經對時間進行着改造,也已經逐漸向不可變和線程安全方向靠攏,有興趣的讀者能夠嘗試一下。