文章目录
1. 异常测试
public class SimpleDateFormatTest {
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5, 5, 1, TimeUnit.MINUTES,
new LinkedBlockingQueue<>(1000), new MyThreadFactory("SimpleDateFormatTest"));
@Test
public void test() {
while (true) {
poolExecutor.execute(new Runnable() {
@Override
public void run() {
String dateString = simpleDateFormat.format(new Date());
try {
Date parseDate = simpleDateFormat.parse(dateString);
String dateString2 = simpleDateFormat.format(parseDate);
System.out.println(dateString.equals(dateString2));
} catch (ParseException e) {
System.out.println("res=" + dateString);
poolExecutor.shutdownNow();
e.printStackTrace();
}
}
});
}
}
}
public class MyThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
public MyThreadFactory(String threadName) {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
namePrefix = "pool-" + poolNumber.getAndIncrement() + "-" + threadName + "-thread-";
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
部分打印结果:
true
false
false
false
true
Exception in thread "pool-1-SimpleDateFormatTest-thread-4" java.lang.NumberFormatException: For input string: "1212E"
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:1869)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at com.hzq.myapp.thread.dateFormat.SimpleDateFormatTest$1.run(SimpleDateFormatTest.java:25)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
2. 非线程安全原因
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;
}
// 共享的实例
protected Calendar calendar;
protected NumberFormat numberFormat;
多个线程之间共享变量calendar
,并修改calendar
。因此在多线程环境下,当多个线程同时使用相同的SimpleDateFormat
对象(如static
修饰)的话,如调用format
方法时,多个线程会同时调用calender.setTime()
方法,导致time
被别的线程修改,因此线程是不安全的。
3. 解决方法
3.1 将SimpleDateFormat定义成局部变量
public class DateTools {
public static Date parse(String formatPattern, String dateString) throws ParseException {
return new SimpleDateFormat(formatPattern).parse(dateString);
}
public static String format(String formatPattern, Date date) {
return new SimpleDateFormat(formatPattern).format(date);
}
}
3.2 使用ThreadLocal
每个线程都拥有自己的SimpleDateFormat
对象副本。
public class DateTools {
private static ThreadLocal<SimpleDateFormat> t1 = new ThreadLocal<SimpleDateFormat>();
public static SimpleDateFormat getSimpleDateFormat(String datePattern) {
SimpleDateFormat sdf = t1.get();
if (sdf == null) {
sdf = new SimpleDateFormat(datePattern);
t1.set(sdf);
}
return sdf;
}
}
3.3 使用DateTimeFormatter代替SimpleDateFormat
DateTimeFormatter
是线程安全的,默认提供了很多格式化方法,也可以通过ofPattern
方法创建自定义格式化方法。
- 格式化日期示例
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime); // 2020-12-21T19:46:14.399
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
String strDate = localDateTime.format(dtf);
System.out.println(strDate); // 2020/12/21 19:46:14
- 解析日期
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime localDateTime = LocalDateTime.parse("2020/12/21 19:50:14", dtf);
System.out.println(localDateTime); // 2019-11-20T15:23:46
ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime localDateTime = LocalDateTime.parse("2020/12/21 19:50:14", dtf);
System.out.println(localDateTime); // 2019-11-20T15:23:46