你在项目中写如下一行代码来格式化时间会有啥危险?
/**
* 定义一个全局的SimpleDateFormat
*/
private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
1.在阿里巴巴Java开发手册中,有如下明确规定:
2.SimpleDateFormat的线程安全性:
3.SimpleDateFormat为神马会有线程安全性问题?
找到SimpleDateFormat类的format方法一看便知,他使用了一个全局的Calendar 变量导致:
protected Calendar calendar;
由于我们在声明SimpleDateFormat的时候,使用的是static定义的。那么这个SimpleDateFormat就是一个共享变量,随之,SimpleDateFormat中的calendar也就可以被多个线程访问到
4.如何解决
4.1使用局部变量(略)
4.2加同步锁
synchronized (simpleDateFormat) {
,,,,,,,,,,,,,,,,,,
}
可以只对simpleDateFormat.format这一行加锁,这样效率更高一些
4.3使用ThreadLocal
/**
* 使用ThreadLocal定义一个全局的SimpleDateFormat
*/
private static ThreadLocal<SimpleDateFormat> simpleDateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
//用法
String dateString = simpleDateFormatThreadLocal.get().format(calendar.getTime());
4.4使用DateTimeFormatter
可以使用DateTimeFormatter代替SimpleDateFormat,这是一个线程安全的格式化工具类。就像官方文档中说的,这个类 simple beautiful strong immutable thread-safe
5.工具类
package xxxxx(自己的项目路径);
import cn.hutool.core.collection.CollectionUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.List;
/**
* @author zlf
* @description:
* @time: 2021/10/10 16:27
*/
@Component
@Slf4j
public class DateUtils {
private static final DateTimeFormatter df1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final DateTimeFormatter df2 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final DateTimeFormatter df3 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS");
private static final DateTimeFormatter df4 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
/**
* 传入一个开始日期和结束日期(开始日期小于结束日期),返回这之间的日期天数,可以结合xxl-job等定时任务跑批处理开始时间至结束时间段的每一天额数据
* @param startTime
* @param endTime
* @return
*/
public List<String> calculationDays(String startTime, String endTime) {
List<String> allDays = new ArrayList<>();
DateTimeFormatter dateTimeFormatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate startDt = LocalDate.parse(startTime, dateTimeFormatter1);
int start_Y = startDt.getYear();
int start_M = startDt.getMonth().getValue();
int start_D = startDt.getDayOfMonth();
log.info("Y=" + start_Y + ",M=" + start_M + ",D=" + start_D);
LocalDate endDt = LocalDate.parse(endTime, dateTimeFormatter1);
int start_Y1 = endDt.getYear();
int start_M1 = endDt.getMonth().getValue();
int start_D1 = endDt.getDayOfMonth();
log.info("Y1=" + start_Y1 + ",M1=" + start_M1 + ",D1=" + start_D1);
if (startDt.compareTo(endDt) > 1) {
//开始时间大于结束时间返回空!
return null;
}
String endTimeStr = dateTimeFormatter1.format(endDt);
Period period = Period.between(LocalDate.parse(startTime), endDt);
StringBuffer sb = new StringBuffer();
sb.append(period.getYears()).append(",")
.append(period.getMonths()).append(",")
.append(period.getDays());
log.info("=======时间分量:=======" + sb.toString());
int py = start_Y1 - start_Y;
int Y = 12;
detailYear(allDays, start_Y, py, Y);
log.info("=======allDays--size()=======" + allDays.size());
if (CollectionUtil.isNotEmpty(allDays)) {
for (int i = 0; i < allDays.size(); i++) {
log.info(allDays.get(i) + "--------allDays------>第" + (i + 1) + "天");
}
List<String> okResult = getOkResult(allDays, startTime, endTimeStr);
System.out.println("=======okResult--size()=======" + okResult.size());
for (int i = 0; i < okResult.size(); i++) {
log.info(okResult.get(i) + "--------okResult-------->第" + (i + 1));
}
return okResult;
}
return null;
}
/**
* 获取正确的List
*
* @param startTime
* @param allDays
* @return
*/
private static List<String> getOkResult(List<String> allDays, String startTime, String endTime) {
List<String> result = new ArrayList<>();
int indexStart = 0;
int indexEnd = 0;
for (int i = 0; i < allDays.size(); i++) {
if (allDays.get(i).equals(startTime)) {
indexStart = i;
}
}
for (int i = 0; i < allDays.size(); i++) {
if (allDays.get(i).equals(endTime)) {
indexEnd = i;
}
}
result = allDays.subList(indexStart, indexEnd + 1);
return result;
}
/**
* 处理整年
*
* @param allDays
* @param start_Y
* @param py
* @param y
*/
private void detailYear(List<String> allDays, int start_Y, int py, int y) {
//处理年的天
for (int i = start_Y; i < start_Y + py + 1; i++) {
for (int j = 1; j <= y; j++) {
String fst = "";
if (j <= 9) {
fst = i + "-0" + j + "-01";
} else {
fst = i + "-" + j + "-01";
}
int diff_day = getDiff_day(fst);
for (int k = 1; k <= diff_day + 1; k++) {
String d = "";
if (j <= 9) {
d = i + "-0" + j;
if (k <= 9) {
d += "-0" + k;
} else if (k > 9) {
d += "-" + k;
}
} else if (j > 9) {
d = i + "-" + j;
if (k <= 9) {
d += "-0" + k;
} else if (k > 9) {
d += "-" + k;
}
}
allDays.add(d);
}
}
}
}
/**
* 根据当月第一天计算本月的开始天和结束天
*
* @param fst
* @return
*/
private int getDiff_day(String fst) {
LocalDate fstLd = LocalDate.parse(fst);
//获取月的第一天
LocalDate fstLd_fd = fstLd.with(TemporalAdjusters.firstDayOfMonth());
//获取月的最后一天
LocalDate fstLd_ld = fstLd.with(TemporalAdjusters.lastDayOfMonth());
Period period2 = Period.between(fstLd_fd, fstLd_ld);
int diff_day = period2.getDays();
return diff_day;
}
/**
* LocalDateTime转String,格式是:yyyy-MM-dd HH:mm:ss
* @param localDateTime
* @return
*/
public static String localDateTimeToString(LocalDateTime localDateTime) {
String format = df1.format(localDateTime);
return format;
}
/**
* LocalDateTime转String,格式是:yyyy-MM-dd
* @param localDateTime
* @return
*/
public static String localDateTimeToStringToYMD(LocalDateTime localDateTime) {
String format = df2.format(localDateTime);
return format;
}
/**
* String转LocalDateTime,格式是:yyyy-MM-dd HH:mm:ss
* @param dateTimeStr
* @return
*/
public static LocalDateTime stringToLocalDateTime(String dateTimeStr) {
LocalDateTime localDateTime = LocalDateTime.parse(dateTimeStr, df1);
return localDateTime;
}
/**
* String转LocalDateTime,格式是:yyyy-MM-dd
* @param dateTimeStr
* @return
*/
public static LocalDateTime stringIncludeTStrToLocalDateTime(String dateTimeStr) {
LocalDateTime localDateTime = LocalDateTime.parse(dateTimeStr, df3);
return localDateTime;
}
/**
* LocalDateTime转String,格式是:yyyy-MM-dd'T'HH:mm:ss.SSS
* @param localDateTime
* @return
*/
public static String localDateTimeToStringT(LocalDateTime localDateTime) {
String format = df3.format(localDateTime);
return format;
}
/**
* LocalDateTime转String,格式是:yyyy-MM-dd'T'HH:mm:ss
* @param localDateTime
* @return
*/
public static String localDateTimeToStringT2(LocalDateTime localDateTime) {
String format = df4.format(localDateTime).replace("T", " ");
return format;
}
/**
* data转String
* @param date
* @return
*/
public static String DateToString(Date date) {
Instant instant = date.toInstant();
ZoneId zoneId = ZoneId.systemDefault();
ZonedDateTime zonedDateTime = instant.atZone(zoneId);
LocalDateTime localDateTime = zonedDateTime.toLocalDateTime();
// 或者直接使用LocalDateTime.ofInstant
LocalDateTime localDateTime1 = LocalDateTime.ofInstant(instant, zoneId);
DateTimeFormatter dateTimeFormatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return dateTimeFormatter1.format(localDateTime);
}
}
这个DateUtils工具类是我在项目中每次都会遇到JDK8新日期的API转换操作的接口代码整理,日积月累还是挺方便的。