0
点赞
收藏
分享

微信扫一扫

SingleDateFormat--线程安全的操作方法--使用/实例


简介

        本文用示例介绍Java中的SingleDateFormat的线程安全的操作方法,也介绍SingleDateFormat为什么不是线程安全的。这两个问题也是面试中常见的问题。

线程不安全实例

package org.example.a;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;

public class Demo{
public static void main(String[] args) {
final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Callable<Date> task = new Callable<Date>() {
public Date call() throws Exception {
return dateFormat.parse("2016-12-18 15:00:34");
}
};

// 创建5个线程的线程池
ExecutorService exec = Executors.newFixedThreadPool(5);
List<Future<Date>> results = new ArrayList<Future<Date>>();
for (int i = 0; i < 10; i++) {
results.add(exec.submit(task));
}
exec.shutdown();
// 输出结果
for (Future<Date> result : results) {
try {
System.out.println(result.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}

执行结果(出现异常)

Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
java.util.concurrent.ExecutionException: java.lang.NumberFormatException: For input string: "E34"
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at org.example.a.Demo.main(Demo.java:29)
Caused by: java.lang.NumberFormatException: For input string: "E34"
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:2162)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at org.example.a.Demo$1.call(Demo.java:15)
at org.example.a.Demo$1.call(Demo.java:13)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
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)
java.util.concurrent.ExecutionException: java.lang.NumberFormatException: For input string: "E342"
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
Sun Dec 18 15:00:34 CST 2016 at org.example.a.Demo.main(Demo.java:29)
Caused by: java.lang.NumberFormatException: For input string: "E342"
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043)
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:2162)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at org.example.a.Demo$1.call(Demo.java:15)
at org.example.a.Demo$1.call(Demo.java:13)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
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)

java.util.concurrent.ExecutionException: java.lang.NumberFormatException: For input string: ""
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at org.example.a.Demo.main(Demo.java:29)
Caused by: 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 org.example.a.Demo$1.call(Demo.java:15)
at org.example.a.Demo$1.call(Demo.java:13)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
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)
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016

线程不安全原因

        SimpleDateFormat类内部有一个Calendar对象引用,它用来储存和这个SimpleDateFormat相关的日期信息,例如sdf.parse(dateStr),sdf.format(date) 诸如此类的方法参数传入的日期相关String,Date等等, 都是交由Calendar引用来储存的.这样就会导致一个问题:如果你的SimpleDateFormat是多个thread 之间共享的, 那么也共享这个Calendar引用。假定线程A和线程B都进入了parse(text, pos) 方法, 线程B执行到calendar.clear()后,线程A执行到calendar.getTime(), 那么就会有问题。

线程安全的方案

方案1:new局部变量(开销大)

缺点:每调用一次方法就会创建一个SimpleDateFormat对象,方法结束又要作为垃圾回收。

package org.example.a;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;

public class Demo{
public static void main(String[] args) {
Callable<Date> task = new Callable<Date>() {
public Date call() throws Exception {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return dateFormat.parse("2016-12-18 15:00:34");
}
};

// 创建5个线程的线程池
ExecutorService exec = Executors.newFixedThreadPool(5);
List<Future<Date>> results = new ArrayList<Future<Date>>();
for (int i = 0; i < 10; i++) {
results.add(exec.submit(task));
}
exec.shutdown();
// 输出结果
for (Future<Date> result : results) {
try {
System.out.println(result.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}

执行结果(正常)

Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016

方案2:方法加synchronized(性能差)

package org.example.a;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;

public class Demo{
public static void main(String[] args) {
final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Callable<Date> task = new Callable<Date>() {
public Date call() throws Exception {
synchronized (dateFormat) {
return dateFormat.parse("2016-12-18 15:00:34");
}
}
};

// 创建5个线程的线程池
ExecutorService exec = Executors.newFixedThreadPool(5);
List<Future<Date>> results = new ArrayList<Future<Date>>();
for (int i = 0; i < 10; i++) {
results.add(exec.submit(task));
}
exec.shutdown();
// 输出结果
for (Future<Date> result : results) {
try {
System.out.println(result.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}

执行结果(正常)

Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016

方案3:使用ThreadLocal(推荐) 

package org.example.a;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;

class DateFormatThreadSafe {
public static final ThreadLocal<DateFormat> THREAD_LOCAL = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
}

public class Demo{
public static void main(String[] args) {
Callable<Date> task = new Callable<Date>() {
public Date call() throws Exception {
DateFormat dateFormat = DateFormatThreadSafe.THREAD_LOCAL.get();
return dateFormat.parse("2016-12-18 15:00:34");
}
};

// 创建5个线程的线程池
ExecutorService exec = Executors.newFixedThreadPool(5);
List<Future<Date>> results = new ArrayList<Future<Date>>();
for (int i = 0; i < 10; i++) {
results.add(exec.submit(task));
}
exec.shutdown();
// 输出结果
for (Future<Date> result : results) {
try {
System.out.println(result.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}

执行结果(正常)

Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016
Sun Dec 18 15:00:34 CST 2016

方案4:针对JDK1.8

说明

        如果是JDK8的应用,可以使用Instant代替Date,LocalDateTime代替Calendar,DateTimeFormatter代替Simpledateformatter。

为什么DateTimeFormatter线程安全?

依据1:官方给出的解释:simple beautiful strong immutable thread-safe。

依据2:看DateTimeFormatter代码,它所有字段都是final类型。

实例

package org.example.a;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class Demo{
public static void main(String[] args) {
DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
Callable<LocalDateTime> task = new Callable<LocalDateTime>() {
public LocalDateTime call() throws Exception {
return LocalDateTime.parse("2016-12-18 15:00:34", dtf);
}
};

// 创建5个线程的线程池
ExecutorService exec = Executors.newFixedThreadPool(5);
List<Future<LocalDateTime>> results = new ArrayList<Future<LocalDateTime>>();
for (int i = 0; i < 10; i++) {
results.add(exec.submit(task));
}
exec.shutdown();
// 输出结果
for (Future<LocalDateTime> result : results) {
try {
System.out.println(result.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}

执行结果(正常)

2016-12-18T15:00:34
2016-12-18T15:00:34
2016-12-18T15:00:34
2016-12-18T15:00:34
2016-12-18T15:00:34
2016-12-18T15:00:34
2016-12-18T15:00:34
2016-12-18T15:00:34
2016-12-18T15:00:34
2016-12-18T15:00:34


举报

相关推荐

0 条评论