SimpleDateFormat多线程下的安全问题
首先们知道SimpleDateFormat是线程不安全的,那么让SimpleDateFormat变得线程安全呢,接下来的三个实验一步一步教会你。
首先我们来看一下SimpleDateFormat是如何线程不安全的.
首先创建一个工具类把SimpleDateFormat的两个方法包成类中的方法供调用
public class DateUtil {
private static final String PATTERN = "yyyy-MM-dd HH-mm-ss";
private static DateFormat dateFormat = new SimpleDateFormat(PATTERN);
//SimpleDateFormat是线程不安全,不能作为成员变量使用
//Str --Date
public static Date strConvertToDate(String dateStr) {
Objects.requireNonNull(dateStr);
Date parse = null;
try {
parse = dateFormat.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return parse;
}
//Date转换str
public static String DateConvertToStr(Date date) {
Objects.requireNonNull(date);
String format = dateFormat.format(date);
return format;
}
}
创建一个类调用方法实现线程不安全.这个类中实现了两个进程一起进行
这就让两个进程都同时使用了SimpleDateFormat创建的dateFormat对象
public class DateTest {
public static void main(String[] args) {
String[] dateStr = {
"2020-12-12 12-12-12",
"2020-12-12 12-12-12",
"2020-12-12 12-12-12",
"2020-12-12 12-12-12",
"2020-12-12 12-12-12",
"2020-12-12 12-12-12"
};
//实在多线程的环境下会出现问题
new Thread(() -> {
for (String s : dateStr) {
Date date = DateUtil.strConvertToDate(s);
System.out.println(Thread.currentThread().getName() + date);
}
}).start();
new Thread(()->{
for (String s : dateStr) {
Date date = DateUtil.strConvertToDate(s);
System.out.println(Thread.currentThread().getName()+date);
}
}).start();
}
}
结果如下:此时报错
Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: -1
Thread-0Sat Dec 12 12:12:12 CST 2020
at java.text.DigitList.fitsIntoLong(DigitList.java:230)
Thread-0Sat Dec 12 12:12:12 CST 2020
Thread-0Sat Dec 12 12:12:12 CST 2020
Thread-0Sat Dec 12 12:12:12 CST 2020
at java.text.DecimalFormat.parse(DecimalFormat.java:2082)
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 exercise.DateUtil.strConvertToDate(DateUtil.java:28)
at exercise.DateTest.lambda$main$1(DateTest.java:36)
at java.lang.Thread.run(Thread.java:748)
Thread-0Sat Dec 12 12:12:12 CST 2020
Thread-0Sat Dec 12 12:12:12 CST 2020
-------------------------------------------------分隔符------------------------------------------------------
如何实现线程安全?
<mark>方法一</mark>:修改DateUtil中的dateFormat对象位置作为局部变量,在每一个方法中都添加一个dateFormat对象。此时线程安全但是创建 了大量的dateFormat对象(浪费空间)。每次实现一个方法都创建了一个dateFormat对象
public class DateUtil {
private static final String PATTERN = "yyyy-MM-dd HH-mm-ss";
//SimpleDateFormat是线程不安全,不能作为成员变量使用
//Str --Date
public static Date strConvertToDate(String dateStr) {
Objects.requireNonNull(dateStr);
Date parse = null;
try {
DateFormat dateFormat = new SimpleDateFormat(PATTERN);
parse = dateFormat.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return parse;
}
//Date转换str
public static String DateConvertToStr(Date date) {
Objects.requireNonNull(date);
DateFormat dateFormat = new SimpleDateFormat(PATTERN);
String format = dateFormat.format(date);
return format;
}
}
<mark>方法二</mark>:作为成员变量存在可以使用synchronized解决 但是效率极低
public class DateUtil {
private static final String PATTERN = "yyyy-MM-dd HH-mm-ss";
//SimpleDateFormat是线程不安全,不能作为成员变量使用
//Str --Date
public static synchronized Date strConvertToDate(String dateStr) {
Objects.requireNonNull(dateStr);
Date parse = null;
try {
DateFormat dateFormat = new SimpleDateFormat(PATTERN);
parse = dateFormat.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return parse;
}
//Date转换str
public static synchronized String DateConvertToStr(Date date) {
Objects.requireNonNull(date);
DateFormat dateFormat = new SimpleDateFormat(PATTERN);
String format = dateFormat.format(date);
return format;
}
}
<mark>方法三</mark>终极大法!解决内存过大和提高效率问题(ThreadLocal:以空间换时间)
每个线程各自有一个SimpleDateFormat对象 相互不干扰 代表着SimpleDateFormat的创建 删除 修改都交给ThreadLocal处理
2个线程 创建两个SimpleDateFormat对象
public class DateUtil {
private static final String PATTERN = "yyyy-MM-dd HH-mm-ss";
//SimpleDateFormat是线程不安全,不能作为成员变量使用
//Str --Date
//交给ThreadLocal管理
//默认执行initialValue
//ThreadLocal的hashcode是pattern的hahcode执行了两次方法,但是hashcode值是一样的
private static final ThreadLocal<SimpleDateFormat> THREAD_LOCAL = new ThreadLocal(){
//重写initialValue
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(PATTERN);
}
};
public static synchronized Date strConvertToDate(String dateStr) {
Objects.requireNonNull(dateStr);
Date parse = null;
try {
parse = THREAD_LOCAL.get().parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return parse;
}
//Date转换str
public static synchronized String DateConvertToStr(Date date) {
Objects.requireNonNull(date);
String format = THREAD_LOCAL.get().format(date);
return format;
}
}