Java之——SimpleDateFormat 线程不安全问题及解决方法

转载请注明出处:http://blog.csdn.net/l1028386804/article/details/53727922

今天,给大家带来一篇Java SimpleDateFormat在多线程环境下不安全的文章,Java SimpleDateFormat 是线程不安全的,当在多线程环境下使用一个DateFormat的时候是有问题的,如下面的例子

package com.lya.date;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * SimpleDateFormat在多线程下会变得不安全
 * @author liuyazhuang
 *
 */
public class DateFormatTest {
	private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	public static void main(String[] args) throws Exception {
		Callable<Date> task = new Callable<Date>() {
			public Date call() throws Exception {
				return format.parse("2016-12-18 15:00:00");
			}
		};
		// 创建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) {
			System.out.println(result.get());
		}
	}
}

一、问题

异常1:

Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.NumberFormatException: multiple points
	at java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.util.concurrent.FutureTask.get(FutureTask.java:192)
	at com.lya.date.DateFormatThreadSafe.main(DateFormatThreadSafe.java:35)
Caused by: 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.lya.date.DateFormatThreadSafe$1.call(DateFormatThreadSafe.java:23)
	at com.lya.date.DateFormatThreadSafe$1.call(DateFormatThreadSafe.java:1)
	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)
异常2

Sun Dec 18 15:00:00 CST 2016
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.NumberFormatException: For input string: "E.2122"
	at java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.util.concurrent.FutureTask.get(FutureTask.java:192)
	at com.lya.date.DateFormatThreadSafe.main(DateFormatThreadSafe.java:35)
Caused by: java.lang.NumberFormatException: For input string: "E.2122"
	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:1869)
	at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
	at java.text.DateFormat.parse(DateFormat.java:364)
	at com.lya.date.DateFormatThreadSafe$1.call(DateFormatThreadSafe.java:23)
	at com.lya.date.DateFormatThreadSafe$1.call(DateFormatThreadSafe.java:1)
	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)
异常3

Sun Dec 18 15:00:00 CST 2016
Sun Jan 14 00:00:00 CST 4317
Exception in thread "main" 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 com.lya.date.DateFormatThreadSafe.main(DateFormatThreadSafe.java:35)
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:1869)
	at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
	at java.text.DateFormat.parse(DateFormat.java:364)
	at com.lya.date.DateFormatThreadSafe$1.call(DateFormatThreadSafe.java:23)
	at com.lya.date.DateFormatThreadSafe$1.call(DateFormatThreadSafe.java:1)
	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)
二、解决方案

我们可以将把DateFormat 放到ThreadLocal中解决线程安全问题,如下所示

package com.lya.date;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 解决SimpleDateFormat线程安全问题
 * @author liuyazhuang
 *
 */
public class DateFormatThreadSafe {
	private static final ThreadLocal<DateFormat> DF = new ThreadLocal<DateFormat>() {
		@Override
		protected DateFormat initialValue() {
			return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		}
	};

	public static Date convert(String source) throws ParseException {
		Date d = DF.get().parse(source);
		return d;
	}
	
}
测试代码如下:

package com.lya.date;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 测试线程安全的DateFormat
 * @author liuyazhuang
 *
 */
public class DateTest {
	public static void main(String[] args) throws Exception {
		Callable<Date> task = new Callable<Date>() {
			public Date call() throws Exception {
				return DateFormatThreadSafe.convert("2016-12-18 15:00:00");
			}
		};
		// 创建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) {
			System.out.println(result.get());
		}
	}
}
输出结果如下:

Sun Dec 18 15:00:00 CST 2016
Sun Dec 18 15:00:00 CST 2016
Sun Dec 18 15:00:00 CST 2016
Sun Dec 18 15:00:00 CST 2016
Sun Dec 18 15:00:00 CST 2016
Sun Dec 18 15:00:00 CST 2016
Sun Dec 18 15:00:00 CST 2016
Sun Dec 18 15:00:00 CST 2016
Sun Dec 18 15:00:00 CST 2016
Sun Dec 18 15:00:00 CST 2016
至此,Java中的SimpleDateFormat就是线程安全的了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰 河

可以吃鸡腿么?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值