JDK8 开始使用LcoalDateTime Insant DateTimeFormatter

SimpleDateFormat是非线程安全的

[强制] SimpleDateFormat 是线程不安全的类(主要是该类的方法非线程安全),一般不要定义为 static 变量,如果定义为 static , 必须加锁,或者使用 DateUtils 工具类。
正例: 注意线程安全,使用 DateUtils。亦推荐如下处理:
private static final ThreadLocal<DateFormat> DATE_FORMAT_THREAD_LOCAL = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
说明: 如果是 JDK8 的应用,可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar,
DateTimeFormatter 代替 SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe。

在《JAVA开发手册》中有提到上述的建议。我们就来详细解释下该建议的来龙去脉。

首先对SimpleDateFormat是非线程安全的进行一下解释。

最终调用的代码如下:

    // Called from Format after creating a FieldDelegate
    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)这句代码可以看出,SimpleDateFormat在format方法中将入参日期对象的时间set到calendar中calendar.setTime(date),calendar是全局变量,在SimpleDateFormat的多个方法中用到,一旦出现多线程调用的情况,calendar的值就会被修改,导致结果不正确甚至发生报错,所以SimpleDateFormat是线程不安全的.
 

DateTimeFormatter是线程安全的

首先来看一个DateTimeFormatter格式化的实例:

DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String format = dateTimeFormatter.format(LocalDateTime.now());
  • DateTimeFormatter#ofPattern

每次都会返回一个新的DateTimeFormatter对象

  • DateTimeFormatter#format
Formats a date-time object using this formatter.
This formats the date-time to a String using the rules of the formatter.

temporal  the temporal object to format, not null
the formatted string, not null
DateTimeException if an error occurs during formatting

public String format(TemporalAccessor temporal) {
      StringBuilder buf = new StringBuilder(32);
      formatTo(temporal, buf);
      return buf.toString();
}

  进入DateTimeFormatter#format函数的实现来看,其缓冲区每次调用都是新建的,进一步查看DateTimeFormatter#formatTo

  • DateTimeFormatter#formatTo

Formats a date-time object to an Appendable using this formatter.
     
This outputs the formatted date-time to the specified destination.
Appendable is a general purpose interface that is implemented by all
key character output classes including StringBuffer, StringBuilder,
PrintStream and Writer.
     
Although {@code Appendable} methods throw an {@code IOException}, this method does not.
Instead, any {@code IOException} is wrapped in a runtime exception.
     
temporal  the temporal object to format, not null
appendable  the appendable to format to, not null
DateTimeException if an error occurs during formatting
     
public void formatTo(TemporalAccessor temporal, Appendable appendable) {
    Objects.requireNonNull(temporal, "temporal");
    Objects.requireNonNull(appendable, "appendable");
    try {
        DateTimePrintContext context = new DateTimePrintContext(temporal, this);
        if (appendable instanceof StringBuilder) {
            printerParser.format(context, (StringBuilder) appendable);
        } else {
            // buffer output to avoid writing to appendable in case of error
            StringBuilder buf = new StringBuilder(32);
            printerParser.format(context, buf);
            appendable.append(buf);
          }
      } catch (IOException ex) {
          throw new DateTimeException(ex.getMessage(), ex);
       }
}

  在DateTimeFormatter#formatTo函数中重点注意DateTimePrintContext context = new DateTimePrintContext(temporal, this);这句代码表明传入的日期被封装到一个新的DateTimePrintContext对象中,所以你可以理解DateTimeFormatter#format函数就是一个简单的函数调用,并没有使用到共享数据;所以DateTimeFormatter#format是线程安全的。

MySQL 8 如何存储LocalDateTime

LocalDateTime 可以精确到纳秒(纳秒,十亿分之一秒),可是如果在数据库中使用datetime类型,那么会出先什么情况?

@Contract(value = "_,_,_,_,_,_,_->new", pure = true)  
@NotNull  
public static LocalDateTime of(     @Range(from = -999999999, to = 999999999)  int year,
    @Range(from = 1, to = 12)  int month,
    @Range(from = 1, to = 31)  int dayOfMonth,
    @Range(from = 0, to = 23)  int hour,
    @Range(from = 0, to = 59)  int minute,
    @Range(from = 0, to = 59)  int second,
    @Range(from = 0, to = 999999999)  int nanoOfSecond )
Obtains an instance of LocalDateTime from year, month, day, hour, minute, second and nanosecond.
This returns a LocalDateTime with the specified year, month, day-of-month, hour, minute, second and nanosecond. The day must be valid for the year and month, otherwise an exception will be thrown.
Params:
year – the year to represent, from MIN_YEAR to MAX_YEAR month – the month-of-year to represent, from 1 (January) to 12 (December) dayOfMonth – the day-of-month to represent, from 1 to 31 hour – the hour-of-day to represent, from 0 to 23 minute – the minute-of-hour to represent, from 0 to 59 second – the second-of-minute to represent, from 0 to 59 nanoOfSecond – the nano-of-second to represent, from 0 to 999,999,999
Returns:
the local date-time, not null

问题:存入数据库时只能精确到秒,所以会出现四舍五入的情况;例如2022-12-17T21:34:33.664475400,存入数据库是会被保存为2022-12-17T21:34:34。

解决方案:MySQL为了不丢失精度保存LocalDateTime,则需要使用bigint数据类型

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值