`

SimpleDateFormat线程不安全

    博客分类:
  • J2EE
 
阅读更多

 

现象:

如果多个线程同时调用同一个SimpleDateFormat的实例方法,例如parse(String date),format(Date date)等方法,就可能发生线程不安全的问题:

 

private final static SimpleDateFormat sdf_d = new SimpleDateFormat(
      "yyyyMMdd");

  public static Date str2Date(String date) {
    Date d = null;
    try {
      d =  sdf_d.parse(date);
    } catch (ParseException e) {
      e.printStackTrace();
    }
    return d;
  }

抛出异常:

 

 

Exception in thread "Thread-7" java.lang.NumberFormatException: multiple points
        at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1110)
        at java.lang.Double.parseDouble(Double.java:540)
        at java.text.DigitList.getDouble(DigitList.java:168)
        at java.text.DecimalFormat.parse(DecimalFormat.java:1321)
        at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1791)
        at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1455)
        at java.text.DateFormat.parse(DateFormat.java:355)
        at com.wbkit.cobub.utils.DateUtil.str2Date(DateUtil.java:16)
        at com.wbkit.cobub.objects.ParticipationDegreeMarker.<init>(ParticipationDegreeMarker.java:34)
        at com.wbkit.cobub.mapreduce.BackFlowUser.getChecker(BackFlowUser.java:279)
        at com.wbkit.cobub.mapreduce.BackFlowUser.addFilter(BackFlowUser.java:258)
        at com.wbkit.cobub.mapreduce.BackFlowUser.run(BackFlowUser.java:69)

  

 

分析:

SimpleDateFormate的继承关系:

Format <-- DateFormat <-- SimpleDateFormat

 

在DateFormat中:

protected Calendar calendar;
protected Numberformat  numberFormat;
/**
     * Formats a Date into a date/time string.
     * @param date the time value to be formatted into a time string.
     * @return the formatted time string.
     */
    public final String format(Date date)
    {
        return format(date, new StringBuffer(),
                      DontCareFieldPosition.INSTANCE).toString();
    }

 

在SimpleDateFormat.java中,当不同线程调用同时调用format时,calendar的值被改变:

// Called from Format after creating a FieldDelegate
    private StringBuffer format(Date date, StringBuffer toAppendTo,
                                FieldDelegate delegate) {
        // Convert input date to time field list
        calendar.setTime(date);

        boolean useDateFormatSymbols = useDateFormatSymbols();

        for (int i = 0; i < compiledPattern.length; ) {
            int tag = compiledPattern[i] >>> 8;
            int count = compiledPattern[i++] & 0xff;
            if (count == 255) {
                count = compiledPattern[i++] << 16;
                count |= compiledPattern[i++];
            }

            switch (tag) {
            case TAG_QUOTE_ASCII_CHAR:
                toAppendTo.append((char)count);
                break;

            case TAG_QUOTE_CHARS:
                toAppendTo.append(compiledPattern, i, count);
                i += count;
                break;

            default:
                subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
                break;
            }
        }
        return toAppendTo;
    }

结论:

calendar.setTime(date)这条语句改变了calendar,稍后,calendar还会用到(在subFormat方法里),而这就是引发问题的根源。想象一下,在一个多线程环境下,有两个线程持有了同一个SimpleDateFormat的实例,分别调用format方法:
  线程1调用format方法,改变了calendar这个字段。
  中断来了。
  线程2开始执行,它也改变了calendar。
  又中断了。
  线程1回来了,此时,calendar已然不是它所设的值,而是走上了线程2设计的道路。如果多个线程同时争抢calendar对象,则会出现各种问题,时间不对,线程挂死等等。
  分析一下format的实现,我们不难发现,用到成员变量calendar,唯一的好处,就是在调用subFormat时,少了一个参数,却带来了这许多的问题。其实,只要在这里用一个局部变量,一路传递下去,所有问题都将迎刃而解。
  这个问题背后隐藏着一个更为重要的问题--无状态:无状态方法的好处之一,就是它在各种环境下,都可以安全的调用。衡量一个方法是否是有状态的,就看它是否改动了其它的东西,比如全局变量,比如实例的字段。format方法在运行过程中改动了SimpleDateFormat的calendar字段,所以,它是有状态的。

  这也同时提醒我们在开发和设计系统的时候注意下一下三点:

  1.自己写公用类的时候,要对多线程调用情况下的后果在注释里进行明确说明

  2.对线程环境下,对每一个共享的可变变量都要注意其线程安全性

  3.我们的类和方法在做设计的时候,要尽量设计成无状态的

  

 我们需要使用线程安全的策略让SimpleDateFormat变得线程安全

 实现方法有很多,一种是使用synchronized关键字对线程不安全的方法加锁

  private final static SimpleDateFormat sdf_d = new SimpleDateFormat(
      "yyyyMMdd");
  
  public static synchronized Date str2Date(String date) {
    Date d = null;
    try {
      d =  sdf_d.parse(date);
    } catch (ParseException e) {
      e.printStackTrace();
    }
    return d;
  }

 一种是使用ThreadLocal模式,为每一个线程创建自己的线程副本

private static final ThreadLocal<SimpleDateFormat> simpleDateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>() {
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("dd/MM/yyyy");
        }
    };
 
    public String formatDate(Date input) {
        if (input == null) {
            return null;
        }
 
        return simpleDateFormatThreadLocal.get().format(input);
    }
 
    public Date parseDate(String input) throws ParseException {
        if (input == null) {
            return null;
        }
        return simpleDateFormatThreadLocal.get().parse(input);
    }

 一种是使用joda-time 的java时间支持包

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

    SimpleDateFormat线程不安全的5种解决方案.md

    SimpleDateFormat线程不安全的5种解决方案.md

    SimpleDateFormat线程不安全的5种解决方案.docx

    JDK 8 引入了新的日期时间 API,其中的 DateTimeFormatter 是线程安全的,可以替代 SimpleDateFormat。以下是使用 DateTimeFormatter 的示例: ```java import java.time.LocalDateTime; import java.time.format....

    simpleDateFormat是线程不安全的

    在Java编程语言中,`SimpleDateFormat`类是一个广泛使用的日期时间格式化工具,但它的线程安全性是一个常常被开发者忽视的问题。标题指出的"simpleDateFormat是线程不安全的",意味着在多线程环境下,如果多个线程...

    详解SimpleDateFormat的线程安全问题与解决方案

    在Java编程中,`SimpleDateFormat`是一个常用的日期时间格式化工具类,但它的设计并不是线程安全的。本文将深入探讨`SimpleDateFormat`的线程安全问题及其解决方案。 ### 1. 线程安全问题的原因 `SimpleDateFormat...

    高并发之-SimpleDateFormat类的线程安全问题和解决方案.docx

    SimpleDateFormat类的线程安全问题和解决方案 SimpleDateFormat类的线程安全问题 SimpleDateFormat类是Java提供的日期时间转化类,用于将日期和时间类型的数据进行解析和格式化。在Java开发中,SimpleDateFormat类...

    ThreadLocal:如何优雅的解决SimpleDateFormat多线程安全问题

    目录SimpleDateFormat诡异bug复现SimpleDateFormat诡异bug字符串日期转Date日期(parse)Date日期转String类型(format)SimpleDateFormat出现...事项使用ThreadLocal解决SimpleDateFormat线程安全问题总结...

    深入理解Java:SimpleDateFormat安全的时间格式化

    但是,如果使用不小心会导致非常微妙和难以调试的问题,因为DateFormat和SimpleDateFormat类不是线程安全的。在多线程环境下调用format()和parse()方法应该使用同步代码来避免问题。 知识点1: SimpleDateFormat的非...

    Java SimpleDateFormat线程安全问题原理详解

    Java SimpleDateFormat线程安全问题原理详解 Java SimpleDateFormat线程安全问题是Java开发中一个常见的问题。SimpleDateFormat是Java中一个常用的日期时间格式化类,但是它却存在线程安全问题。在多线程环境下,...

    关于SimpleDateFormat的非线程安全问题及其解决方案.docx

    3. **使用不可变的日期时间API**:从Java 8开始,提供了新的日期时间API(如`java.time`包下的类),这些API设计为线程安全的,可以替代`SimpleDateFormat`。 ```java import java.time.LocalDate; import java....

    软件安全监测报告.pdf

    这份软件安全监测报告主要是监测java开发程序中出现的安全问题,找了一个项目程序监测后出现的原版报告显示,在使用DES加解密的情况下,会被检测...还有关于SimpleDateFormat线程不安全的问题,都有详细解释及处理方法

    Java多线程环境下SimpleDateFormat类安全转换

    SimpleDateFormat类是Java中常用的日期格式化类,但是它并不是线程安全的。在多线程环境下,如果多个线程同时使用同一个SimpleDateFormat对象,可能会出现日期格式化错误的问题。 2. 使用ThreadLocal解决线程安全...

    由浅入深解析 SimpleDateFormat

    SimpleDateFormat 详解 SimpleDateFormat 是 Java 语言中...然而,在使用时需要注意线程安全和资源消耗问题。通过合理地使用 SimpleDateFormat,我们可以实现日期和时间的高效格式化和解析,提高程序的性能和可读性。

    Java理论与实践:描绘线程安全性

    这类类在设计时就没有考虑线程安全,例如`SimpleDateFormat`,在1.4 JDK之前的版本中并未明确指出其线程不安全,导致许多开发者在并发场景中误用,引发错误。 在文档中清晰地记录类的线程安全性是至关重要的。如...

    Java多线程编程的线程安全性.docx

    Java标准库中有一些类,如ArrayList、HashMap和SimpleDateFormat,并未设计为线程安全,因此在多线程环境下直接使用可能导致数据不一致或其他问题。开发者应当了解每个类的线程安全特性,以便做出正确的选择和适当地...

    java SimpleDateFormat &Calendar

    需要注意的是,由于`SimpleDateFormat`不是线程安全的,所以在多线程环境中,建议为每个线程创建单独的实例。 `Calendar`类则是Java中更底层的日期和时间工具,它提供了一套完整的API来操作日期和时间,包括添加、...

    有关SimpleDateFormat的常用方法说明

    - `SimpleDateFormat`是线程不安全的,如果在多线程环境中使用,需要考虑同步问题。 - 日期和时间的格式化字符串要与实际情况对应,避免出现理解错误。 以上就是关于`SimpleDateFormat`的一些常见用法和注意事项,...

    Java线程安全的计数器简单实现代码示例

    Java线程安全的计数器简单实现代码示例 Java线程安全的计数器简单实现代码示例是一种在Java中实现线程安全的计数器的方法,该方法使用AtomicInteger和volatile关键字来保证计数器的线程安全性。该计数器可以每天从1...

    DateFormat多线程问题

    Java 8 引入了`java.time.format.DateTimeFormatter`,这个类是线程安全的,可以作为`SimpleDateFormat`的一个更优选择。它提供了与`SimpleDateFormat`类似的功能,但避免了线程安全问题。 5. **池化`DateFormat`...

    Java 实例 - 格式化时间SimpleDateFormat使用源代码-详细教程.zip

    `SimpleDateFormat`不是线程安全的,所以在多线程环境中,要么为每个线程创建单独的实例,要么在每次使用后进行同步。 8. **替代方案**: Java 8引入了`java.time`包,其中的`DateTimeFormatter`类提供了更现代且...

Global site tag (gtag.js) - Google Analytics