SimpleDateFormat和ThreadLocal联合使用

编程

SimpleDateFormat线程不安全问题

SimpleDateFormat大家都用过,日期与字符串转换的类,它的方法是线程不安全的。有同学就说了,这个方法不安全也没事啊,不就是做个日期转换,现编写一下代码

package com.huawei.test;

import java.text.ParseException;

import java.text.SimpleDateFormat;

import java.util.Locale;

public class ProveNotSafe {

public static void main(String[] args) {

for (int i=0;i<5;i++){

new Thread(new DateFormatTest()).start();

}

}

}

class DateFormatTest implements Runnable{

static SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd", Locale.US);

@Override

public void run() {

try {

System.out.println(Thread.currentThread().getName()+":"+df.parse("2020-09-11"));

} catch (ParseException e) {

e.printStackTrace();

}

}

}

运行完后,妥妥的报错了

Thread-4:Fri Sep 11 00:00:00 CST 2020

Exception in thread "Thread-1" java.lang.NumberFormatException: For input string: ""

at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)

at java.lang.Long.parseLong(Long.java:601)

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:2162)

at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)

at java.text.DateFormat.parse(DateFormat.java:364)

at com.huawei.test.DateFormatTest.run(ProveNotSafe.java:21)

at java.lang.Thread.run(Thread.java:748)

Exception in thread "Thread-2" Exception in thread "Thread-3" java.lang.NumberFormatException: multiple points

at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1890)

at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)

at java.lang.Double.parseDouble(Double.java:538)

at java.text.DigitList.getDouble(DigitList.java:169)

at java.text.DecimalFormat.parse(DecimalFormat.java:2056)

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.huawei.test.DateFormatTest.run(ProveNotSafe.java:21)

at java.lang.Thread.run(Thread.java:748)

java.lang.NumberFormatException: multiple points

at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1890)

at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)

at java.lang.Double.parseDouble(Double.java:538)

at java.text.DigitList.getDouble(DigitList.java:169)

at java.text.DecimalFormat.parse(DecimalFormat.java:2056)

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.huawei.test.DateFormatTest.run(ProveNotSafe.java:21)

at java.lang.Thread.run(Thread.java:748)

Thread-0:Fri Sep 11 00:00:00 CST 2020

Disconnected from the target VM, address: "127.0.0.1:56922", transport: "socket"

Process finished with exit code 0

没错就是java.lang.NumberFormatException: multiple points,不怎么常见,跟进异常堆栈,定位到的点是使用parse方法时会把继承自DateFormat类的类变量calendar被clear了正在设置新值,刚好其他线程进来时使用也调用了calendar.clear(),导致当前线程挂壁,则报错。

如果是用format方法倒是不会报错,但是会串其他线程的结果。原因也是一样:只有类变量Calendar多线程

解决方案

方案一:每次都new 一个SimpleDateFormat

每次线程使用新的实例对象,在本例也简单,把static去掉即可。但是消耗的内存过多

方案二:将对象存入ThreadLocal中

class DateFormatTest implements Runnable{

// 将SimpleDateFormat对象放入ThreadLocal,线程只用自己的对象,就不会出现线程共享脏用报错的问题

private ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>(){

public SimpleDateFormat initialValue(){

SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd", Locale.US);

return sdf;

}

};

@Override

public void run() {

try {

System.out.println(Thread.currentThread().getName()+":"+threadLocal.get().parse("2020-09-11"));

} catch (ParseException e) {

e.printStackTrace();

}

}

}

方案三:使用JDK8提供的DateTimeFormatter

DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss SSS");

LocalDateTime now = LocalDateTime.now();

String str = dtf.format(now);

方案四:将SimpleDateFormat对象用sychronized关键字修饰

synchronized (df) {

System.out.println(Thread.currentThread().getName() + ":" + df.parse("2020-09-11"));

}

以上是 SimpleDateFormat和ThreadLocal联合使用 的全部内容, 来源链接: utcz.com/z/518066.html

回到顶部