繁体   English   中英

Java-静态方法的线程安全

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM