目录
前言
正文
1.出现异常
2.解决方法1
3.解决方法2
总结
SimpleDateFormat
类是 Java 中处理日期和时间格式化和解析的类,但它并不是线程安全的。这意味着多个线程不能安全地共享一个SimpleDateFormat
实例进行日期和时间的解析和格式化。当多个线程共享同一个SimpleDateFormat
实例时,会因为SimpleDateFormat
内部维护的日历字段(例如:Calendar
对象)等的竞争条件而导致解析和格式化错误。
类 SimpleDataFormat 的可以对日期进行解析与格式化,但在使用时如果不想使用 0 进行填充,比如 2000-01-02 只想转换成 2002-1-2 ,我们需要在代码上进行处理,示例代码如下。
package org.example.SimpleDataFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class Run {
public static void main(String[] args) throws ParseException {
String dataString1 = "2000-1-1";
String dataString2 = "2000-11-18";
SimpleDateFormat format1 = new SimpleDateFormat("yyyy-M-d");
SimpleDateFormat format2 = new SimpleDateFormat("yyyy-MM-dd");
//先按照日期模式将字符串解析成日期再格式化成时间字符串。
System.out.println(format1.format(format1.parse(dataString1)));
System.out.println(format2.format(format2.parse(dataString1)));
System.out.println(format1.format(format1.parse(dataString2)));
System.out.println(format2.format(format2.parse(dataString2)));
}
}
打印结果如下:
但 SimpleDateFormat 在多线程环境中使用类容易造成数据转换及处理不准确,因为类 SimpleDateFormat 并不是线程安全的。
本示例将展示使用类 SimpleDataFormat 在多线程环境中处理日期时得到错误结果,这也是在多线程环境中开发经常遇到的问题。
ackage org.example.SimpleDataFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class formatError {
static class MyThread extends Thread {
private SimpleDateFormat sdf;
private String dateString;
public MyThread(SimpleDateFormat sdf, String dateString) {
this.sdf = sdf;
this.dateString = dateString;
}
@Override
public void run() {
try {
Date dateRef = sdf.parse(dateString);
String newDataString = sdf.format(dateRef);
if (!newDataString.equals(dateString)) {
System.out.println("ThreadName=" + this.getName()
+ "报错了 日期字符串:" + dateString + " 转换成的日期为:"
+ newDataString);
}
} catch (ParseException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String[] dateStringArray = new String[]{"2000-01-01",
"2000-01-02","2000-01-03","2000-01-04",
"2000-01-05","2000-01-06","2000-01-07",
"2000-01-08","2000-01-09","2000-01-10",
};
MyThread[] threads = new MyThread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new MyThread(sdf,dateStringArray[i]);
}
for (int i = 0; i < 10; i++) {
threads[i].start();
}
}
}
运行结果如图:
从打印的结果来看,使用单例的类 SimpleDateFormat 在多线程环境中处理日期极易出现转换错误的情况。
甚至由于竞争导致解析逻辑的数字处理部分冲突,控制台照成了错误输出 。
第一种解决办法的原理是满足竞争,创建多个类 SimpleDateFormat 的实例。
package org.example.SimpleDataFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class formatOK1 {
static 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);
}
}
static class MyThread extends Thread {
private SimpleDateFormat sdf;
private String dateString;
public MyThread(SimpleDateFormat sdf, String dateString) {
this.sdf = sdf;
this.dateString = dateString;
}
@Override
public void run() {
try {
Date dateRef = DateTools.parse("yyyy-MM-dd",dateString);
String newDataString = DateTools.format("yyyy-MM-dd",dateRef);
if (!newDataString.equals(dateString)) {
System.out.println("ThreadName=" + this.getName()
+ "报错了 日期字符串:" + dateString + " 转换成的日期为:"
+ newDataString);
}
} catch (ParseException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String[] dateStringArray = new String[]{"2000-01-01",
"2000-01-02","2000-01-03","2000-01-04",
"2000-01-05","2000-01-06","2000-01-07",
"2000-01-08","2000-01-09","2000-01-10",
};
MyThread[] threads = new MyThread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new MyThread(sdf,dateStringArray[i]);
}
for (int i = 0; i < 10; i++) {
threads[i].start();
}
}
}
运行结果如图:
控制台没有异常信息输出。
还有一种更简单的方法,那就是使用 ThreadLocal 包装SimpleDateFormat。ThreadLocal 可以为每个线程提供一个单独的 SimpleDateFormat 实例,能使线程绑定到指定对象。使用该类也可以解决多线程环境中类 SimpleDateFormat 处理日期时出现错误的问题。
package org.example.SimpleDataFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class formatOK2 {
static class DateTools {
private static ThreadLocal t1 = new ThreadLocal<>();
public static SimpleDateFormat getSimpleDateFormat(String datePattern) {
SimpleDateFormat sdf = null;
sdf = t1.get();
if (sdf == null) {
sdf = new SimpleDateFormat(datePattern);
t1.set(sdf);
}
return sdf;
}
}
static class MyThread extends Thread {
private SimpleDateFormat sdf;
private String dateString;
public MyThread(SimpleDateFormat sdf, String dateString) {
this.sdf = sdf;
this.dateString = dateString;
}
@Override
public void run() {
try {
Date dateRef = DateTools.getSimpleDateFormat("yyyy-MM-dd").parse(dateString);
String newDataString = DateTools.getSimpleDateFormat("yyyy-MM-dd")
.format(dateRef);
if (!newDataString.equals(dateString)) {
System.out.println("ThreadName=" + this.getName()
+ "报错了 日期字符串:" + dateString + " 转换成的日期为:"
+ newDataString);
}
} catch (ParseException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String[] dateStringArray = new String[]{"2000-01-01",
"2000-01-02", "2000-01-03", "2000-01-04",
"2000-01-05", "2000-01-06", "2000-01-07",
"2000-01-08", "2000-01-09", "2000-01-10",
};
MyThread[] threads = new MyThread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new MyThread(sdf, dateStringArray[i]);
}
for (int i = 0; i < 10; i++) {
threads[i].start();
}
}
}
运行结果:
控制台没有异常信息输出,说明 ThreadLocal 解决了 SimpleDateFormat 非线程安全问题。
加油!!!!