浅谈SimpleDateFormat的线程安全问题

浅谈SimpleDateFormat的线程安全问题

在实际项目中,我们经常需要将日期在String和Date之间做转化,此时需要使用SimpleDateFormat类。使用SimpleDateFormat类的parse方法,可以将满足格式要求的字符串转换成Date对象,使用SimpleDateFormat类的format方法,可以将Date类型的对象转换成一定格式的字符串!但是有一点需要特别注意,SimpleDateFormat并非是线程安全的,也就是说在并发环境下,如果考虑不周使用SimpleDateFormat方法可以会出现线程安全方面的问题,需要我们特别注意!

下面展示下,SimpleDateFormat在并发环境下出现的问题!

No BB,Show Code!

运行环境:JDK1.8,引入maven依赖


    
    
  1. <dependency>
  2. <groupId>com.google.guava</groupId>
  3. <artifactId>guava</artifactId>
  4. <version> 19.0</version>
  5. </dependency>

1-  SimpleDateFormat线程安全问题

使用ExecutorService提交多个任务的方式,模拟并发环境将字符串转换为日期即测试parse方法,代码如下:


    
    
  1. @Test
  2. public void testParse() {
  3. ExecutorService executorService = Executors.newCachedThreadPool();
  4. List<String> dateStrList = Lists.newArrayList(
  5. "2018-04-01 10:00:01",
  6. "2018-04-02 11:00:02",
  7. "2018-04-03 12:00:03",
  8. "2018-04-04 13:00:04",
  9. "2018-04-05 14:00:05"
  10. );
  11. SimpleDateFormat simpleDateFormat = new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss");
  12. for (String str : dateStrList) {
  13. executorService.execute(() -> {
  14. try {
  15. simpleDateFormat.parse(str);
  16. TimeUnit.SECONDS.sleep( 1);
  17. } catch (Exception e) {
  18. e.printStackTrace();
  19. }
  20. });
  21. }
  22. }

运行后,报错如下:


可见并发环境下使用SimpleDateFormat的parse方法有线程安全问题!

线程安全问题的原因:

在SimpleDateFormat转换日期是通过Calendar对象来操作的,SimpleDateFormat继承DateFormat类,DateFormat类中维护一个Calendar对象,代码如下:


通过DateFormat类中的注释可知:此处Calendar实例被用来进行日期-时间计算,既被用于format方法也被用于parse方法!

在parse方法的最后,会调用CalendarBuilder的establish方法,入参就是SimpleDateFormat维护的Calendar实例,在establish方法中会调用calendar的clear方法,如下:


可知SimpleDateFormat维护的用于format和parse方法计算日期-时间的calendar被清空了,如果此时线程A将calendar清空且没有设置新值,线程B也进入parse方法用到了SimpleDateFormat对象中的calendar对象,此时就会产生线程安全问题!

2-  解决方案

每一个使用SimpleDateFormat对象进行日期-时间进行formatparse方法的时候就创建一个新的SimpleDateFormat对象,用完就销毁即可!代码如下:


    
    
  1. /**
  2. * 模拟并发环境下使用SimpleDateFormat的parse方法将字符串转换成Date对象
  3. */
  4. @Test
  5. public void testParseThreadSafe() {
  6. ExecutorService executorService = Executors.newCachedThreadPool();
  7. List<String> dateStrList = Lists.newArrayList(
  8. "2018-04-01 10:00:01",
  9. "2018-04-02 11:00:02",
  10. "2018-04-03 12:00:03",
  11. "2018-04-04 13:00:04",
  12. "2018-04-05 14:00:05"
  13. );
  14. for (String str : dateStrList) {
  15. executorService.execute(() -> {
  16. try {
  17. //创建新的SimpleDateFormat对象用于日期-时间的计算
  18. SimpleDateFormat simpleDateFormat = new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss");
  19. simpleDateFormat.parse(str);
  20. TimeUnit.SECONDS.sleep( 1);
  21. simpleDateFormat = null; //销毁对象
  22. } catch (Exception e) {
  23. e.printStackTrace();
  24. }
  25. });
  26. }
  27. }

在运行可以发现不会报出之前的错误了!

综上所述,使用SimpleDateFormat对象进行日期-时间计算时,如果SimpleDateFormat是多个线程共享的就会有线程安全问题!应该让每一个线程都有一个独立的SimpleDateFormat对象用于日期-时间的计算!此时就可以使用ThreadLocal将SimpleDateFormat绑定到线程上,是的该线程上的日期-时间计算顺序的使用SimpleDateFormat对象,这样也可以避免线程安全问题!

转载:https://blog.csdn.net/weixin_38810239/article/details/79941964

浅谈SimpleDateFormat的线程安全问题

在实际项目中,我们经常需要将日期在String和Date之间做转化,此时需要使用SimpleDateFormat类。使用SimpleDateFormat类的parse方法,可以将满足格式要求的字符串转换成Date对象,使用SimpleDateFormat类的format方法,可以将Date类型的对象转换成一定格式的字符串!但是有一点需要特别注意,SimpleDateFormat并非是线程安全的,也就是说在并发环境下,如果考虑不周使用SimpleDateFormat方法可以会出现线程安全方面的问题,需要我们特别注意!

下面展示下,SimpleDateFormat在并发环境下出现的问题!

No BB,Show Code!

运行环境:JDK1.8,引入maven依赖


  
  
  1. <dependency>
  2. <groupId>com.google.guava</groupId>
  3. <artifactId>guava</artifactId>
  4. <version> 19.0</version>
  5. </dependency>

1-  SimpleDateFormat线程安全问题

使用ExecutorService提交多个任务的方式,模拟并发环境将字符串转换为日期即测试parse方法,代码如下:


  
  
  1. @Test
  2. public void testParse() {
  3. ExecutorService executorService = Executors.newCachedThreadPool();
  4. List<String> dateStrList = Lists.newArrayList(
  5. "2018-04-01 10:00:01",
  6. "2018-04-02 11:00:02",
  7. "2018-04-03 12:00:03",
  8. "2018-04-04 13:00:04",
  9. "2018-04-05 14:00:05"
  10. );
  11. SimpleDateFormat simpleDateFormat = new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss");
  12. for (String str : dateStrList) {
  13. executorService.execute(() -> {
  14. try {
  15. simpleDateFormat.parse(str);
  16. TimeUnit.SECONDS.sleep( 1);
  17. } catch (Exception e) {
  18. e.printStackTrace();
  19. }
  20. });
  21. }
  22. }

运行后,报错如下:


可见并发环境下使用SimpleDateFormat的parse方法有线程安全问题!

线程安全问题的原因:

在SimpleDateFormat转换日期是通过Calendar对象来操作的,SimpleDateFormat继承DateFormat类,DateFormat类中维护一个Calendar对象,代码如下:


通过DateFormat类中的注释可知:此处Calendar实例被用来进行日期-时间计算,既被用于format方法也被用于parse方法!

在parse方法的最后,会调用CalendarBuilder的establish方法,入参就是SimpleDateFormat维护的Calendar实例,在establish方法中会调用calendar的clear方法,如下:


可知SimpleDateFormat维护的用于format和parse方法计算日期-时间的calendar被清空了,如果此时线程A将calendar清空且没有设置新值,线程B也进入parse方法用到了SimpleDateFormat对象中的calendar对象,此时就会产生线程安全问题!

2-  解决方案

每一个使用SimpleDateFormat对象进行日期-时间进行formatparse方法的时候就创建一个新的SimpleDateFormat对象,用完就销毁即可!代码如下:


  
  
  1. /**
  2. * 模拟并发环境下使用SimpleDateFormat的parse方法将字符串转换成Date对象
  3. */
  4. @Test
  5. public void testParseThreadSafe() {
  6. ExecutorService executorService = Executors.newCachedThreadPool();
  7. List<String> dateStrList = Lists.newArrayList(
  8. "2018-04-01 10:00:01",
  9. "2018-04-02 11:00:02",
  10. "2018-04-03 12:00:03",
  11. "2018-04-04 13:00:04",
  12. "2018-04-05 14:00:05"
  13. );
  14. for (String str : dateStrList) {
  15. executorService.execute(() -> {
  16. try {
  17. //创建新的SimpleDateFormat对象用于日期-时间的计算
  18. SimpleDateFormat simpleDateFormat = new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss");
  19. simpleDateFormat.parse(str);
  20. TimeUnit.SECONDS.sleep( 1);
  21. simpleDateFormat = null; //销毁对象
  22. } catch (Exception e) {
  23. e.printStackTrace();
  24. }
  25. });
  26. }
  27. }

在运行可以发现不会报出之前的错误了!

综上所述,使用SimpleDateFormat对象进行日期-时间计算时,如果SimpleDateFormat是多个线程共享的就会有线程安全问题!应该让每一个线程都有一个独立的SimpleDateFormat对象用于日期-时间的计算!此时就可以使用ThreadLocal将SimpleDateFormat绑定到线程上,是的该线程上的日期-时间计算顺序的使用SimpleDateFormat对象,这样也可以避免线程安全问题!

猜你喜欢

转载自blog.csdn.net/q1054261752/article/details/88360332