使用SimpleDateFormat在多线程下处理日期但得出的结果却是错误的,这是因为SimpleDateFormat方法是非线程安全的。
示例代码:
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 date = sdf.parse(dateString);
String dateStr = sdf.format(date);
if(!dateStr.equals(dateString)) {
System.out.println("ThreadName=" + this.getName() + "报错了,日期字符串:" + dateString + ",转换成的日期字符串:" + dateStr);
} else {
System.out.println("ThreadName=" + this.getName() + "成功,日期字符串:" + dateString);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String[] dateString = {"2017-11-05","2017-11-06","2017-11-07","2017-11-08","2017-11-09","2017-11-10","2017-11-11","2017-11-12","2017-11-13","2017-11-14"};
Thread[] threads = new Thread[10];
for (int i = 0; i < threads.length; i++) {
threads[i] = new MyThread(sdf, dateString[i]);
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
}
}
输出结果:
ThreadName=Thread-5成功,日期字符串:2017-11-10
ThreadName=Thread-0报错了,日期字符串:2017-11-05,转换成的日期字符串:2017-11-10
ThreadName=Thread-1成功,日期字符串:2017-11-06
ThreadName=Thread-4报错了,日期字符串:2017-11-09,转换成的日期字符串:2017-11-10
ThreadName=Thread-9成功,日期字符串:2017-11-14
ThreadName=Thread-3成功,日期字符串:2017-11-08
ThreadName=Thread-8成功,日期字符串:2017-11-13
ThreadName=Thread-7成功,日期字符串:2017-11-12
ThreadName=Thread-6报错了,日期字符串:2017-11-11,转换成的日期字符串:0007-11-11
ThreadName=Thread-2报错了,日期字符串:2017-11-07,转换成的日期字符串:0007-11-11
通过输出结果可以看出不是所有线程都成功了,有些线程转换的字符串很奇怪,这都是因为多线程导致的,多线程可能会出现很多我们意想不到的结果,每次运行以上代码都可能会得到不一样的结果。
解决方法一:
在使用SimpleDateFormat对象时,每次都新创建一个。
示例代码:
class MyThread1 extends Thread {
private String dateString;
public MyThread1(String dateString) {
this.dateString = dateString;
}
@Override
public void run() {
try {
Date date = DateTools.parse("yyyy-MM-dd", dateString);
String dateStr = DateTools.format("yyyy-MM-dd", date);
if(!dateStr.equals(dateString)) {
System.out.println("ThreadName=" + this.getName() + "报错了,日期字符串:" + dateString + ",转换成的日期字符串:" + dateStr);
} else {
System.out.println("ThreadName=" + this.getName() + "成功,日期字符串:" + dateString);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class DateTools {
public static String format(String format, Date date) {
return new SimpleDateFormat(format).format(date);
}
public static Date parse(String format, String dateStr) throws ParseException {
return new SimpleDateFormat(format).parse(dateStr);
}
}
public class Test1 {
public static void main(String[] args) {
String[] dateString = {"2017-11-05","2017-11-06","2017-11-07","2017-11-08","2017-11-09","2017-11-10","2017-11-11","2017-11-12","2017-11-13","2017-11-14"};
Thread[] threads = new Thread[10];
for (int i = 0; i < threads.length; i++) {
threads[i] = new MyThread1(dateString[i]);
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
}
}
输出结果:
ThreadName=Thread-7成功,日期字符串:2017-11-12
ThreadName=Thread-4成功,日期字符串:2017-11-09
ThreadName=Thread-6成功,日期字符串:2017-11-11
ThreadName=Thread-5成功,日期字符串:2017-11-10
ThreadName=Thread-1成功,日期字符串:2017-11-06
ThreadName=Thread-2成功,日期字符串:2017-11-07
ThreadName=Thread-3成功,日期字符串:2017-11-08
ThreadName=Thread-9成功,日期字符串:2017-11-14
ThreadName=Thread-8成功,日期字符串:2017-11-13
ThreadName=Thread-0成功,日期字符串:2017-11-05
通过输出结果可以看出,多线程日期转换的过程中没有出现异常,通过避免多线程同时使用一个SimpleDateFormat对象而带来的问题,可以为每个线程都创建一个新的SimpleDateFormat对象,所以就不会出现线程安全的问题了。
解决方法二:
使用ThreadLocal类,ThreadLocal类能使线程绑定到指定的对象上。
示例代码:
class MyThread2 extends Thread {
private String dateString;
public MyThread2(String dateString) {
this.dateString = dateString;
}
@Override
public void run() {
try {
Date date = DateTools2.getSimpleDateFormat("yyyy-MM-dd").parse(dateString);
String dateStr = DateTools2.getSimpleDateFormat("yyyy-MM-dd").format(date);
if(!dateStr.equals(dateString)) {
System.out.println("ThreadName=" + this.getName() + "报错了,日期字符串:" + dateString + ",转换成的日期字符串:" + dateStr);
} else {
System.out.println("ThreadName=" + this.getName() + "成功,日期字符串:" + dateString);
}
} catch(Exception e) {
e.printStackTrace();
}
}
}
class DateTools2 {
private static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<>();
public static SimpleDateFormat getSimpleDateFormat(String format) {
SimpleDateFormat sdf = null;
sdf = threadLocal.get();
if (sdf == null) {
sdf = new SimpleDateFormat(format);
threadLocal.set(sdf);
}
return sdf;
}
}
public class Test2 {
public static void main(String[] args) {
String[] dateString = {"2017-11-05","2017-11-06","2017-11-07","2017-11-08","2017-11-09","2017-11-10","2017-11-11","2017-11-12","2017-11-13","2017-11-14"};
Thread[] threads = new Thread[10];
for (int i = 0; i < threads.length; i++) {
threads[i] = new MyThread2(dateString[i]);
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
}
}
运行结果:
ThreadName=Thread-1成功,日期字符串:2017-11-06
ThreadName=Thread-6成功,日期字符串:2017-11-11
ThreadName=Thread-4成功,日期字符串:2017-11-09
ThreadName=Thread-8成功,日期字符串:2017-11-13
ThreadName=Thread-7成功,日期字符串:2017-11-12
ThreadName=Thread-5成功,日期字符串:2017-11-10
ThreadName=Thread-0成功,日期字符串:2017-11-05
ThreadName=Thread-9成功,日期字符串:2017-11-14
ThreadName=Thread-3成功,日期字符串:2017-11-08
ThreadName=Thread-2成功,日期字符串:2017-11-07
通过结果可以看出,解决了SimpleDateFormat多线程访问异常的问题。
总结:
为什么SimpleDateFormat类多线程访问时会出现异常,原因是因为SimpleDateFormat中的方法不是线程安全的,多线程访问会出现意想不到的结果。解决的思路就是,避免多线程使用同一个SimpleDateFormat对象。
原文:https://blog.csdn.net/u014740338/article/details/78544821