`

Java DateFormat并发实现

    博客分类:
  • Java
阅读更多

    根据javadocs描述,DateFormat类是非线程安全的。通过多线程并发测试,也证实了这一点。

 

    通过此次测试得出的一些经验:

  • 经多线程并发UT验证,DateFormat的format一直都正确运行(基于StringBuffer实现),但parse经常出问题(未使用任何并发技术)
  • 即使DateFormat的parse运行正常结束,最终结果也可能不对!
  • DateFormatThreadLocal、DateTimeFormatterWrapper与FastDateFormatWrapper都正确运行
  • 通过500并发量+5个线程的性能测试来看,对于format操作,DateTimeFormatterWrapper 最优,FastDateFormatWrapper 次之,DateFormatThreadLocal 最差;对于parse操作,三者差距微乎其微

 

/**
 * 可格式化的日期接口定义。
 * 
 * @author  Bert Lee
 * @version 2014-8-16
 */
public interface DateFormattable {

	/**
	 * 默认日期格式
	 */
	String PATTERN = "yyyy-MM-dd";

	/**
	 * Formats a Date into a date/time string.
	 * 
	 * @param date
	 * @return
	 * @see java.text.DateFormat#format(Date)
	 */
	String format(Date date);

	/**
	 * Parses text from the beginning of the given string to produce a date.
	 * 
	 * @param source
	 * @return
	 * @throws ParseException
	 * @see java.text.DateFormat#parse(String)
	 */
	Date parse(String source) throws ParseException;

}

 

/**
 * {@link DateFormat} wrapper.
 *
 * @author	Bert Lee
 * @version 2014-8-16
 */
public class DateFormatWrapper implements DateFormattable {

	private final DateFormat format;

	public DateFormatWrapper() {
		this(PATTERN);
	}

	public DateFormatWrapper(String pattern) {
		this.format = new SimpleDateFormat(pattern);
	}

	/**
	 * <font color="red">经多线程UT验证,format都正确运行(基于StringBuffer实现)!</font>
	 */
	@Override
	public String format(Date date) {
		return this.format.format(date);
	}

	/**
	 * <font color="red">经多线程UT验证,parse经常出问题(未使用任何并发技术)!</font>
	 */
	@Override
	public Date parse(String source) throws ParseException {
		return this.format.parse(source);
	}

}

 

/**
 * Date parse task.
 *
 * @author	Bert Lee
 * @version 2014-8-16
 */
public class DateParseCallable implements Callable<Date> {

	private DateFormattable formatter;

	private String source;

	public DateParseCallable(DateFormattable formatter, String source) {
		this.formatter = formatter;
		this.source = source;
	}

	@Override
	public Date call() throws Exception {
		return this.formatter.parse(source);
	}

}

 

/**
 * Concurrent thread {@link java.util.concurrent.Executor Executor} wrapper.
 *
 * @author	Bert Lee
 * @version 2014-8-16
 */
public class ConcurrentThreadExecutorWrapper<T> {

	private ExecutorService executor;
	
	private Callable<T> task;

	private List<Future<T>> results;
	
	private TestScale runTimes;
	
	public ConcurrentThreadExecutorWrapper(Callable<T> task) {
		this(task, TestScale.BASIC);
	}
	
	public ConcurrentThreadExecutorWrapper(Callable<T> task, TestScale runTimes) {
		// pool with 5 threads
		this.executor = Executors.newFixedThreadPool(5);
		
		this.task = task;
		this.runTimes = runTimes;
	}
	
	public void run() {
		results = new ArrayList<Future<T>>();
		
		// perform 10 times date conversions
		for (int i = 0; i < runTimes.getValue(); i++) {
			results.add(executor.submit(task));
		}
		executor.shutdown();
	}

	public void printResults() throws Exception {
		this.run();
		
		// look at the results
		for (Future<T> result : results) {
			out.println(result.get());
		}
	}

}

 

/**
 * Test for {@link ConcurrentThreadExecutorWrapper}.
 * <p>
 * 参考并优化实现了<a href="http://stackoverflow.com/questions/4021151/java-dateformat-is-not-threadsafe-what-does-this-leads-to">
 * “Java DateFormat is not thread-safe” what does this leads to?</a>
 *
 * @author	Bert Lee
 * @version 2014-8-16
 */
public class DateFormatExecutorTest {

	// 日期转换格式
	private static final String pattern = "yyyy-MM-dd HH:mm";
	
	/*
	 * 经多线程UT验证,DateFormat的format一直都正确运行(基于StringBuffer实现),但parse经常出问题(未使用任何并发技术)!
	 * 即使DateFormat的parse运行正常结束,最终结果也可能不对!
	 * 
	 * DateFormatThreadLocal、DateTimeFormatterWrapper与FastDateFormatWrapper都正确运行。
	 */
	@Test(dataProvider = "parse", groups = "parse")
	public void parse(DateFormattable formatter, String source) throws Exception {
		Callable<Date> task = new DateParseCallable(formatter, source);
		ConcurrentThreadExecutorWrapper<Date> executor = new ConcurrentThreadExecutorWrapper<Date>(task);
		out.println(formatter.getClass().getSimpleName() + " parse result:");
		executor.printResults();
	}
	@DataProvider(name = "parse")
	protected static final Object[][] parseTestData() {
		Object[][] testData = new Object[][] {
//				{ new DateFormatWrapper(pattern), "2014-08-16 08:23:07" }, // 经常报错,即使运行结束,最终结果也可能不对!
				{ new DateFormatThreadLocal(pattern), "2014-08-16 08:23:07" },
				{ new DateTimeFormatterWrapper(pattern), "2014-08-16 08:23" },
				{ new FastDateFormatWrapper(pattern), "2014-08-16 08:23:07" },
		};
		return testData;
	}
	
	@Test(dataProvider = "format", groups = "format")
	public void format(DateFormattable formatter) throws Exception {
		Date date = new Date();
		Callable<String> task = new DateFormatCallable(formatter, date);
		ConcurrentThreadExecutorWrapper<String> executor = new ConcurrentThreadExecutorWrapper<String>(task);
		out.println(formatter.getClass().getSimpleName() + " format result:");
		executor.printResults();
	}
	@DataProvider(name = "format")
	protected static final Object[][] formatTestData() {
		Object[][] testData = new Object[][] {
				{ new DateFormatWrapper(pattern) },
				{ new DateFormatThreadLocal(pattern) },
				{ new DateTimeFormatterWrapper(pattern) },
				{ new FastDateFormatWrapper(pattern) },
		};
		return testData;
	}

}

 

     为了解决并发问题,参考了StackOverflow上的这篇文章《“Java DateFormat is not thread-safe” what does this leads to?》和《Java Best Practices – DateFormat in a Multithreading Environment》,在此基础上进行了重构及性能测试。文章提供了三种解决方案:

  1. 基于ThreadLocal实现:使用一个ThreadLocal变量来持有DateFormat对象
  2. 直接使用Joda-Time的DateTimeFormatter
  3. 直接使用Apache Commons Lang 3.x的FastDateFormat

     通过500并发量+5个线程的性能测试来看,对于format操作,DateTimeFormatterWrapper 最优,FastDateFormatWrapper 次之,DateFormatThreadLocal 最差;对于parse操作,三者差距微乎其微。

 

/**
 * {@link DateFormat} thread-local.
 *
 * @author	Bert Lee
 * @version 2014-8-16
 */
public class DateFormatThreadLocal implements DateFormattable {

	private final ThreadLocal<DateFormat> format;

	public DateFormatThreadLocal() {
		this(PATTERN);
	}

	public DateFormatThreadLocal(final String pattern) {
		this.format = new ThreadLocal<DateFormat>() {
			@Override
			protected DateFormat initialValue() {
				return new SimpleDateFormat(pattern);
			}
		};
	}

	@Override
	public String format(Date date) {
		return this.format.get().format(date);
	}

	@Override
	public Date parse(String source) throws ParseException {
		return this.format.get().parse(source);
	}

}
 
/**
 * {@link DateTimeFormatter} wrapper.
 *
 * @author	Bert Lee
 * @version 2014-8-19
 */
public class DateTimeFormatterWrapper implements DateFormattable {

	private final DateTimeFormatter format;
	
	public DateTimeFormatterWrapper() {
		this(PATTERN);
	}
	
	public DateTimeFormatterWrapper(String pattern) {
		this.format = DateTimeFormat.forPattern(pattern);
	}
	
	@Override
	public String format(Date date) {
		return this.format.print(date.getTime());
	}

	/**
	 * <font color="red">日期字符串表示要与日期模式完全匹配,不然会抛异常!</font>
	 */
	@Override
	public Date parse(String source) throws ParseException {
		DateTime dt = this.format.parseDateTime(source);
		return dt.toDate();
	}

}
 
/**
 * {@link FastDateFormat} is a fast and thread-safe version of
 * {@link java.text.SimpleDateFormat}.<p>
 * 
 * FastDateFormat implements the behavior of Java 7.
 *
 * @author	Bert Lee
 * @version 2014-8-26
 */
public class FastDateFormatWrapper implements DateFormattable {

	private final FastDateFormat format;
	
	public FastDateFormatWrapper() {
		this(PATTERN);
	}
	
	public FastDateFormatWrapper(String pattern) {
		this.format = FastDateFormat.getInstance(pattern);
	}
	
	@Override
	public String format(Date date) {
		return this.format.format(date);
	}

	@Override
	public Date parse(String source) throws ParseException {
		return this.format.parse(source);
	}

}
 
public class DateFormatExecutorTest {

	// 日期转换格式
	private static final String pattern = "yyyy-MM-dd HH:mm";
	
	/*
	 * 通过500并发量+5个线程的性能测试来看,
	 * 对于format操作,DateTimeFormatterWrapper 最优,FastDateFormatWrapper 次之,DateFormatThreadLocal 最差;
	 * 对于parse操作,三者差距微乎其微。
	 */
	@Test(dataProvider = "profileParse", groups = "profile")
	public void profileParse(DateFormattable formatter, String source) throws Exception {
		String className = formatter.getClass().getSimpleName() + "'s parse";
		RunTimeStats timeStats = new RunTimeStats(className);
		
		Callable<Date> task = new DateParseCallable(formatter, source);
		ConcurrentThreadExecutorWrapper<Date> executor = new ConcurrentThreadExecutorWrapper<Date>(task, TestScale.SMALL);
		executor.run();
		
		timeStats.print();
	}
	@DataProvider(name = "profileParse")
	protected static final Object[][] profileParseTestData() {
		Object[][] testData = new Object[][] {
				{ new DateFormatThreadLocal(pattern), "2014-08-16 08:23:07"},
				{ new DateTimeFormatterWrapper(pattern), "2014-08-16 08:23" },
				{ new FastDateFormatWrapper(pattern), "2014-08-16 08:23:07" },
		};
		return testData;
	}
	
	@Test(dataProvider = "profileFormat", groups = "profile")
	public void profileFormat(DateFormattable formatter) throws Exception {
		String className = formatter.getClass().getSimpleName() + "'s format";
		RunTimeStats timeStats = new RunTimeStats(className);
		
		Date date = new Date();
		Callable<String> task = new DateFormatCallable(formatter, date);
		ConcurrentThreadExecutorWrapper<String> executor = new ConcurrentThreadExecutorWrapper<String>(task, TestScale.MIDDLE);
		executor.run();
		
		timeStats.print();
	}
	@DataProvider(name = "profileFormat")
	protected static final Object[][] profileFormatTestData() {
		Object[][] testData = new Object[][] {
				{ new DateFormatThreadLocal(pattern) },
				{ new DateTimeFormatterWrapper(pattern) },
				{ new FastDateFormatWrapper(pattern) },
		};
		return testData;
	}

}
 

    完整的源码见附件啦~

 

 

玩得开心!^_^

分享到:
评论

相关推荐

    Java并发编程实战(中文版).7z

    《Java并发编程实战》是Java开发者深入理解和掌握并发编程的一本经典著作。这本书全面地介绍了如何在Java平台上高效、安全地编写多线程程序,帮助读者理解并解决并发编程中的各种挑战。 首先,我们需要理解“并发”...

    Java在并发环境中SimpleDateFormat多种解决方案

    Java在并发环境中SimpleDateFormat多种解决方案 Java在并发环境中使用SimpleDateFormat时,可能会遇到线程安全问题。下面将介绍六种解决方案来解决这个问题。 方法一:使用局部变量 在需要执行格式化的地方都新建...

    Java核心源代码

    7. **java.concurrent**: 并发编程包,包含线程(Thread)、线程池(ExecutorService)和并发工具类(如Semaphore、CyclicBarrier),帮助开发者编写多线程应用。 8. **java.sql**: 提供了与关系数据库交互的接口,...

    DateFormat多线程问题

    在Java编程语言中,`DateFormat`类是处理日期和时间格式化的重要工具,但它的线程安全性是一个常见的问题。在多线程环境下,不恰当的使用`DateFormat`可能导致数据不一致、性能下降甚至程序崩溃。这篇博客将深入探讨...

    Java代理服务器的实现

    java.text.DateFormat cal = java.text.DateFormat.getDateTimeInstance(); System.out.println(cal.format(new java.util.Date()) + "-" + url + "" + sock.getInetAddress() + "&lt;BR&gt;"); return host; } // ...

    Java多线程编程中使用DateFormat类

    根据Java官方文档的建议,每个线程应该拥有自己独立的日期格式实例,或者在访问`DateFormat`时进行适当的同步控制,以避免并发问题。 首先,我们来看一个简单的例子,展示`DateFormat`非线程安全的问题。在给定的...

    java1.6API文档可搜索

    `java.text` 包中的 `NumberFormat` 和 `DateFormat` 用于格式化数字和日期,`ResourceBundle` 用于管理不同语言的文本资源。 10. **安全性** Java 1.6 引入了安全管理器(Security Manager),用于控制程序的访问...

    Java_API_1.6中文版

    8. **国际化与本地化**:java.text和java.util包提供了对国际化和本地化的支持,如NumberFormat和DateFormat类用于格式化数字和日期,ResourceBundle用于根据地区加载不同的资源。 9. **Java标准库**:包括诸如Math...

    JAVA2 SDK 类库详解

    10. **java.concurrent**:多线程和并发处理相关类,如Thread、ExecutorService和Semaphore,支持高级并发控制和并行计算。 11. **javax.xml**:处理XML文档的解析、生成和验证,如DOM、SAX和StAX API。 12. **...

    Java各种工具类

    7. **java.util.concurrent**: 并发工具包,提供了线程池、并发容器(如ConcurrentHashMap)、锁机制(如ReentrantLock)以及其他并发工具类,为多线程编程提供了强大支持。 8. **java.net**: 网络编程相关类,如...

    javaAPI

    8. **java.concurrent**:多线程处理的包,包含Thread、ExecutorService、Semaphore等类,方便实现并发编程。 9. **java.text**:处理文本格式化,如数字、日期、货币等,包含NumberFormat和DateFormat类。 10. **...

    多国时间显示 java

    在Java编程语言中,实现多国时间显示的功能主要涉及到日期和时间处理,国际化(i18n)以及线程管理...对于初学者来说,这样的项目不仅能够锻炼编程技能,还能深入理解Java中的并发编程、GUI设计以及国际化的实现方式。

    Java28个相关包

    7. **java.text**:处理文本格式化和解析,如DateFormat、NumberFormat、MessageFormat等。 8. **java.concurrent**:多线程和并发编程包,包括Thread、ExecutorService、Future、Semaphore等工具类。 9. **java....

    JAVA语言API类库

    10. **java.concurrent**:并发和多线程处理包,包括Thread、ExecutorService、Semaphore等,有助于在多核环境下提高程序性能。 11. **java.lang.reflect**:反射机制,允许程序在运行时检查和修改自身行为,实现...

    Java2 类库 Java2 类库

    8. **国际化**:java.text和java.util包提供了支持多语言环境的工具,如DateFormat、NumberFormat和MessageFormat,便于处理日期、数字和消息的格式化。 9. **XML处理**:Java2类库包含了DOM(Document Object ...

    java-api.rar_java api

    5. **多线程**:Java通过`java.lang.Thread`和`java.util.concurrent`包提供多线程编程的支持,可以创建和管理线程,实现并发和同步。 6. **反射**:`java.lang.reflect`包允许程序在运行时检查和修改类、接口、...

    Java_api_6开发帮助文档中文网页版(java_api_6_zh_cn)

    4. **多线程**:Java提供了Thread类和Runnable接口来实现并发执行。synchronized关键字用于同步访问共享资源,wait()、notify()和notifyAll()方法用于线程间通信。 5. **I/O流**:Java的I/O流体系结构包括字节流...

    JAVA基础课程讲义

    JAVA提供了Socket和ServerSocket类实现TCP/IP通信,DatagramSocket和DatagramPacket处理UDP通信。网络编程还涉及到IP、端口、URL的概念。 第十一章 **JAVA多媒体编程** JAVA支持多媒体处理,如字体、颜色、图形...

Global site tag (gtag.js) - Google Analytics