Java 并发访问用于计算过期天数的静态方法

Java 并发访问用于计算过期天数的静态方法,java,concurrency,calendar,Java,Concurrency,Calendar,我有一个具有以下签名的静态方法: private static volatile SimpleDateFormat payDayFormat = new SimpleDateFormat("yyyyMMdd"); public static int overdueDays(String repayDay){ try { Date billDate = payDayFormat.parse(repayDay); Calendar startDate = Ca

我有一个具有以下签名的静态方法:

private static volatile SimpleDateFormat payDayFormat = new SimpleDateFormat("yyyyMMdd");
public static int overdueDays(String repayDay){
    try {
        Date billDate = payDayFormat.parse(repayDay);
        Calendar startDate = Calendar.getInstance();
        startDate.setTime(billDate);
        Calendar endDate = Calendar.getInstance();
        long end = endDate.getTimeInMillis();
        long start = startDate.getTimeInMillis();
        Long days = TimeUnit.MILLISECONDS.toDays(Math.abs(end - start));
        return days.intValue();
    } catch (ParseException e) {
        logger.error("判断是否逾期解析时间出错");
    }
    return 0;
}
当同时使用同一参数对上述方法进行多个调用时,结果会不同

56
56
1279716
56
56
56
5
736387
56
-1226645849
56
正确答案只有56个。
但是,当我将日历替换为Joda Time时,它会返回正确的答案。我应该如何更改此代码而不使用Joda Timelockunlock并获得正确答案。

这是因为共享
payDayFormat
,这是一个:

日期格式不同步。建议为每个线程创建单独的格式实例。如果多个线程同时访问一种格式,则必须在外部对其进行同步

它使用Jodatime的原因是它的日期格式化程序没有共享的可变状态,因为它是合理的

因此,您可以:

  • 向方法签名添加
    synchronized
  • 在使用
    payDayFormat
    的位置附近添加一个
    synchronized
    块:

    Date billDate;
    synchronized (payDayFormat) {
      billDate = payDayFormat.parse(repayDay);
    }
    
  • 在方法内部创建一个新的
    SimpleDataFormat
    实例

  • payDayFormat
    a
    ThreadLocal
    ,这样每个线程都有自己的副本

您可以将方法标记为“已同步”;但是你可能不需要-这个方法中使用了任何静态变量吗?payDayFormat的类型是什么?如果你有Java 8,你可以使用Java时间(相当于Joda时间)@Jango请你扩展一下“完全反对”这句话好吗?请注意,这不是我会采取的方法。你指的是第67项“避免过度同步”@YinbinWang我要指出的是,你没有以正确的方式进行计算:你混淆了瞬间(毫秒)和24小时长的周期(通过日期字符串)。在Jodatime(或java8)中使用
LocalDate
,这样做会更好。