SimpleDateFormat是非线程安全的
private static final ThreadLocal<DateFormat> DATE_FORMAT_THREAD_LOCAL = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
在《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数据类型