SimpleDateFormat线程不安全(转)

有三种方法可以解决以上安全问题。
  1).使用同步

 1 package com.bijian.study.date;
 2
 3 import java.text.ParseException;
 4 import java.text.SimpleDateFormat;
 5 import java.util.Date;
 6
 7 public class DateUtil02 implements DateUtilInterface {
 8
 9     private SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
10
11     @Override
12     public void format(Date date) {
13         System.out.println(dateformat.format(date));
14     }
15
16     @Override
17     public void parse(String str) {
18         try {
19             synchronized(dateformat){
20                 System.out.println(dateformat.parse(str));
21             }
22         } catch (ParseException e) {
23             e.printStackTrace();
24         }
25     }
26 }

修改DateMainTest.java的DateUtilInterface dateUtil = new DateUtil01();为DateUtilInterface dateUtil = new DateUtil02();测试OK。

不过,当线程较多时,当一个线程调用该方法时,其他想要调用此方法的线程就要block,这样的操作也会一定程度上影响性能。

2).每次使用时,都创建一个新的SimpleDateFormat实例

 1 package com.bijian.study.date;
 2
 3 import java.text.ParseException;
 4 import java.text.SimpleDateFormat;
 5 import java.util.Date;
 6
 7 public class DateUtil03 implements DateUtilInterface {
 8
 9     @Override
10     public void format(Date date) {
11
12         SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
13         System.out.println(dateformat.format(date));
14     }
15
16     @Override
17     public void parse(String str) {
18         try {
19             SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
20             System.out.println(dateformat.parse(str));
21         } catch (ParseException e) {
22             e.printStackTrace();
23         }
24     }
25 }

修改DateMainTest.java的DateUtilInterface dateUtil = new DateUtil02();为DateUtilInterface dateUtil = new DateUtil03();测试OK。

3).借助ThreadLocal对象每个线程只创建一个实例

借助ThreadLocal对象每个线程只创建一个实例,这是推荐的方法

对于每个线程SimpleDateFormat不存在影响他们之间协作的状态,为每个线程创建一个SimpleDateFormat变量的拷贝或者叫做副本,代码如下:

 1 package com.bijian.study.date;
 2
 3 import java.text.DateFormat;
 4 import java.text.ParseException;
 5 import java.text.SimpleDateFormat;
 6 import java.util.Date;
 7
 8 public class DateUtil04 implements DateUtilInterface {
 9
10     private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
11
12     // 第一次调用get将返回null
13     private static ThreadLocal threadLocal = new ThreadLocal(){
14         protected Object initialValue() {
15             return null;//直接返回null
16         }
17     };
18
19     // 获取线程的变量副本,如果不覆盖initialValue,第一次get返回null,故需要初始化一个SimpleDateFormat,并set到threadLocal中
20     public static DateFormat getDateFormat() {
21         DateFormat df = (DateFormat) threadLocal.get();
22         if (df == null) {
23             df = new SimpleDateFormat(DATE_FORMAT);
24             threadLocal.set(df);
25         }
26         return df;
27     }
28
29     @Override
30     public void parse(String textDate) {
31
32         try {
33             System.out.println(getDateFormat().parse(textDate));
34         } catch (ParseException e) {
35             e.printStackTrace();
36         }
37     }
38
39     @Override
40     public void format(Date date) {
41         System.out.println(getDateFormat().format(date));
42     }
43 }

创建一个ThreadLocal类变量,这里创建时用了一个匿名类,覆盖了initialValue方法,主要作用是创建时初始化实例。

也可以采用下面方式创建。

 1 package com.bijian.study.date;
 2
 3 import java.text.DateFormat;
 4 import java.text.ParseException;
 5 import java.text.SimpleDateFormat;
 6 import java.util.Date;
 7
 8 public class DateUtil05 implements DateUtilInterface {
 9
10     private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
11
12     @SuppressWarnings("rawtypes")
13     private static ThreadLocal threadLocal = new ThreadLocal() {
14         protected synchronized Object initialValue() {
15             return new SimpleDateFormat(DATE_FORMAT);
16         }
17     };
18
19     public DateFormat getDateFormat() {
20         return (DateFormat) threadLocal.get();
21     }
22
23     @Override
24     public void parse(String textDate) {
25
26         try {
27             System.out.println(getDateFormat().parse(textDate));
28         } catch (ParseException e) {
29             e.printStackTrace();
30         }
31     }
32
33     @Override
34     public void format(Date date) {
35         System.out.println(getDateFormat().format(date));
36     }
37 }

修改DateMainTest.java的DateUtilInterface dateUtil = new DateUtil03();为DateUtilInterface dateUtil = new DateUtil04();或者DateUtilInterface dateUtil = new DateUtil05();测试OK。

最后,当然也可以使用apache commons-lang包的DateFormatUtils或者FastDateFormat实现,apache保证是线程安全的,并且更高效。

附:Oracle官方bug说明: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6178997

  转自 http://bijian1013.iteye.com/blog/1873336

时间: 2024-11-08 03:15:00

SimpleDateFormat线程不安全(转)的相关文章

SimpleDateFormat线程不安全原因及解决方案

线程不安全验证: /** * SimpleDateFormat线程安全测试 * 〈功能详细描述〉 * * @author 17090889 * @see [相关类/方法](可选) * @since [产品/模块版本] (可选) */ public class SimpleDateFormatTest { private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Thr

并发条件下SimpleDateFormat线程不安全及解决方案

1.使用线程池创建并发环境解析日期 package com.zh.time; 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.ExecutionException; import java.util

SimpleDateFormat线程不安全及解决的方法

一. 为什么SimpleDateFormat不是线程安全的? Java源代码例如以下: /** * Date formats are not synchronized. * It is recommended to create separate format instances for each thread. * If multiple threads access a format concurrently, it must be synchronized * externally. */

SimpleDateFormat线程安全

private static final ThreadLocal<SimpleDateFormat> simpleDateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>() { protected SimpleDateFormat initialValue() { return new SimpleDateFormat("dd/MM/yyyy"); } }; public String formatDa

SimpleDateFormat线程安全问题解决方案

package com.itheima.netty_chat.util; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class DateUtils { private static final Map<Str

2017.12.11SimpleDateFormat的线程安全性讨论

转载来自:http://blog.csdn.net/zxh87/article/details/19414885 1.结论 DateFormat和SimpleDateFormat都不是线程安全的.在多线程环境中调用format()和parse()应处理线程安全的问题. 2.错误示例 (1)错误示例1 每次处理一个时间信息,都新建一个SimpleDateFormat实例,然后再丢弃.造成内存的浪费. 1 package com.peidasoft.dateformat; 2 3 import ja

为什么阿里巴巴禁止把SimpleDateFormat定义为static类型的?

在日常开发中,我们经常会用到时间,我们有很多办法在Java代码中获取时间.但是不同的方法获取到的时间的格式都不尽相同,这时候就需要一种格式化工具,把时间显示成我们需要的格式.最常用的方法就是使用SimpleDateFormat类.这是一个看上去功能比较简单的类,但是,一旦使用不当也有可能导致很大的问题.在阿里巴巴Java开发手册中,有如下明确规定: 那么,本文就围绕SimpleDateFormat的用法.原理等来深入分析下如何以正确的姿势使用它.SimpleDateFormat用法SimpleD

Java日期时间API系列4-----Jdk7及以前的日期时间类的线程安全问题

1.Date类为可变的,在多线程并发环境中会有线程安全问题. (1)可以使用锁来处理并发问题. (2)使用JDK8  Instant 替代. 2.Calendar的子类为可变的,在多线程并发环境中会有线程安全问题. (1)可以使用锁来处理并发问题. (2)使用JDK8  LocalDateTime 替代. 3.DateFormat和SimpleDateFormat不是线程安全的原因 (1)DateFormat中calendar是共享变量,其子类SimpleDateFormat中也是共享变量. D

理解java中的ThreadLocal(转)

一.对ThreadLocal概术 JDK API 写道: 该类提供了线程局部 (thread-local) 变量.这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本.ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联. 二.结合源码理解 可以看到ThreadLocal类中的变量只有这3个int型: private f