[英]Java - Thread Safety of a static method
我的應用程序並行處理一堆文件,並且由於每個文件都有刪除文件的日期作為其名稱的一部分,因此我使用以下方法提取刪除日期:
public static Optional<Date> getFileDropDate(String filename) {
/* Example: my_file-1_20171002.xml */
if (StringUtils.isEmpty(filename) || !filename.matches(Constants.FILE_DATE_PATTERN)) {
return Optional.empty();
}
String[] segments = filename.split(UNDERSCORE);
try {
// Filename definitely ends with _yyyyMMdd.xml as it matches the pattern. Split by underscore, get the last segment and take out the .xml
return Optional.of(yyyymmdd.parse(segments[segments.length - 1].replace(Constants.XML_EXTENSION, "")));
} catch (ParseException e) {
logger.error("Unable to parse filename to find load date");
return Optional.empty();
}
}
雖然功能相當可靠並且我的測試全部成功,但是當我在多線程安裝程序中運行它時,它由於日期解析異常而失敗,並且該方法嘗試解析的字符串完全奇怪。
例如:
java.lang.NumberFormatException: For input string: "E.21616"
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:2162)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at mycompany.importer.parser.ParserUtil.getFileDropDate(ParserUtil.java:308)
at mycompany.dataprocessors.FileProcessor.lambda$importData$2(FileProcessor.java:181)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:291)
at java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:731)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
2017-10-21 22:11:48.550 [ForkJoinPool.commonPool-worker-2] ERROR a.c.t.m.m.a.m.i.d.FileProcessor - Error in parsing files and building datamap. Feed file is: /files/my_file_1-db_20171016.xml
java.lang.NumberFormatException: For input string: "E.21616E2"
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:2162)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at mycompany.importer.parser.ParserUtil.getFileDropDate(ParserUtil.java:308)
at mycompany.dataprocessors.FileProcessor.lambda$importData$2(FileProcessor.java:181)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:291)
at java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:731)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
2017-10-21 22:11:48.550 [ForkJoinPool.commonPool-worker-3] ERROR a.c.t.m.m.a.m.i.d.FileProcessor - Error in parsing files and building datamap. Feed file is: /files/my_file_2-db_20171020.xml
java.lang.NumberFormatException: For input string: "20172017E4"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Long.parseLong(Long.java:589)
at java.lang.Long.parseLong(Long.java:631)
at java.text.DigitList.getLong(DigitList.java:195)
at java.text.DecimalFormat.parse(DecimalFormat.java:2051)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1867)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at mycompany.importer.parser.ParserUtil.getFileDropDate(ParserUtil.java:308)
at mycompany.dataprocessors.FileProcessor.lambda$importData$2(FileProcessor.java:181)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:291)
at java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:731)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
使方法同步可以解決問題,但是鑒於此方法不使用或不修改任何實例變量(並且如果不明顯,則Constant中的所有變量都是public static final),並且每個線程從技術上講都有自己的局部堆棧變量,為什么此方法不是線程安全的?
原因是此yyyymmdd
不是線程安全的。 從文檔中 :
日期格式不同步。 建議為每個線程創建單獨的格式實例。 如果多個線程同時訪問一種格式,則必須在外部進行同步。
不要在線程之間共享單個SimpleDateFormat,因為它不是線程安全的。 在方法中創建一個新的SimpleDateFormat,或者使用ThreadLocal,或者java.time
yyyymmdd
所有訪問同步,或者更好的是,使用java.time
類,它們是線程安全的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.