在JDK中使用SimpleDateFormat的时候都会遇到线程安全的问题,在JDK文档中也说明了该类是线程非安全的,建议对于每个线程都创建一个SimpleDateFormat对象。如下面一个Case中,多个线程去调用SimpleDateFormat中得parse方法:

@Test
public void testUnThreadSafe() throws Exception {
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,S"); final String[] dateStrings = {
"2014-04-30 18:51:01,61",
"2014-04-30 18:51:01,461",
"2014-04-30 18:51:01,361",
"2014-04-30 18:51:01,261",
"2014-04-30 18:51:01,161",
};
int threadNum = 5;
Thread[] parseThreads = new Thread[threadNum];
for (int i=0; i<threadNum; i++) {
parseThreads[i] = new Thread(new Runnable() {
public void run() {
for (int j=0; j<dateStrings.length; j++) {
try {
System.out.println(Thread.currentThread().getName() + " " + sdf.parse(dateStrings[j]));
} catch (ParseException e) {
e.printStackTrace();
}
}
}
});
parseThreads[i].start();
} for (int i=0; i<threadNum; i++) {
parseThreads[i].join();
}
}

将会抛出异常:java.lang.NumberFormatException: multiple points

通常的解决办法有:

1. 使用synchronized

synchronized (sdf) {
  System.out.println(Thread.currentThread().getName() + " " + sdf.parse(dateStrings[j]));
}

2. 每用一次实例化一次

try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,S");
System.out.println(Thread.currentThread().getName() + " " + sdf.parse(dateStrings[j]));
} catch (ParseException e) {
e.printStackTrace();
}

3. 使用ThreadLocal

    @Test
public void testUnThreadSafe() throws Exception {
// final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,S");
final ThreadLocal<SimpleDateFormat> localSdf = new ThreadLocal<SimpleDateFormat>();
final String[] dateStrings = {
"2014-04-30 18:51:01,61",
"2014-04-30 18:51:01,461",
"2014-04-30 18:51:01,361",
"2014-04-30 18:51:01,261",
"2014-04-30 18:51:01,161",
};
int threadNum = 5;
Thread[] parseThreads = new Thread[threadNum];
for (int i=0; i<threadNum; i++) {
parseThreads[i] = new Thread(new Runnable() {
public void run() {
for (int j=0; j<dateStrings.length; j++) {
try {
SimpleDateFormat sdf = localSdf.get();
if (sdf == null) {
sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,S");
localSdf.set(sdf);
}
System.out.println(Thread.currentThread().getName() + " " + sdf.parse(dateStrings[j]));
} catch (ParseException e) {
e.printStackTrace();
}
}
}
});
parseThreads[i].start();
} for (int i=0; i<threadNum; i++) {
parseThreads[i].join();
}
}

第一种和第二种解决方案对于一个工具类来说都会带来昂贵的资源开销,建议使用ThreadLocal创建一个对单个线程来说全局的变量,保证线程安全,当然可以使用第三方工具类如Apache commons 里的FastDateFormat或者Joda-Time类库来处理。

JDK中的SimpleDateFormat线程非安全的更多相关文章

  1. 如何在一个线程环境中使用一个线程非安全的java类

    在开发过程中 当我们拿到一个线程非安全的java类的时候,我们可以额外创建这个类的管理类 并在管理类中控制同步 比如 一个非线程安全的Pair类 package test.thread.sx.test ...

  2. SimpleDateFormat,Calendar 线程非安全的问题

    SimpleDateFormat是Java中非常常见的一个类,用来解析和格式化日期字符串.但是SimpleDateFormat在多线程的环境并不是安全的,这个是很容易犯错的部分,接下来讲一下这个问题出 ...

  3. 深入理解JDK中的Reference原理和源码实现

    前提 这篇文章主要基于JDK11的源码和最近翻看的<深入理解Java虚拟机-2nd>一书的部分内容,对JDK11中的Reference(引用)做一些总结.值得注意的是,通过笔者对比一下JD ...

  4. JDK中线程池参详细解析

    在jdk中为我们提供了三种创建线程池的方式,但是在阿里的编码规范里面都是明确禁止使用这三种api去创建线程池,推荐我们去自定义线程池.为什么? 要回答为什么,我们需要明白创建线程池时,各参数的作用: ...

  5. JDK 中的证书生成和管理工具 keytool

    参考资料 该文中的内容来源于 Oracle 的官方文档 Java SE Tools Reference .Oracle 在 Java 方面的文档是非常完善的.对 Java 8 感兴趣的朋友,可以直接找 ...

  6. VC++ GetModuleFileName()获取路径字符串中带波浪线~

    GetModuleFileName()获取的字符串中带波浪线,不是完整的路径显示. 原因:获取的是短路径,进行了缩写 解决:还原长路径 TCHAR }; GetLongPathName( strTem ...

  7. 深入理解JDK中的I/O

    深入理解JDK中的I/O 目 录 java内存模型GCHTTP协议事务隔离级并发多线程设计模式清楚redis.memcache并且知道区别mysql分表分库有接口幂等性了解jdk8稍微了解一下特性 j ...

  8. C++模拟实现JDK中的ArrayList和LinkedList

    Java实现ArrayList和LinkedList的方式采用的是数组和链表.以下是用C++代码的模拟: 声明Collection接口: #ifndef COLLECTION_H_ #define C ...

  9. JDK中的设计模式

    Creational(创建模式) Abstract factory: 创建一组有关联的对象实例.这个模式在JDK中也是相当的常见,还有很多的framework例如Spring.我们很容易找到这样的实例 ...

随机推荐

  1. Babylon.js官方性能优化文档中文翻译

    在这里列出Babylon.js官方性能优化文档的中英文对照,并在CardSimulate项目里对其中的一些优化方法进行实践. How To 如何 Optimize your scene 优化你的场景 ...

  2. python基础学习笔记(一)

    最好有点c++基础来看,,每天都更新一篇吧 这一篇是一些基础东西 1.运算符2.变量3.基本输入输出4.字符串5.列表6.元组7.字典8.集合9.简单的说下循环啥的 1.运算符 特别的 a / b:为 ...

  3. golang应用打包成docker镜像

    golang编译的应用是不需要依赖其他运行环境的,那么为什么还需要打包成docker镜像呢?当需要附带配置和日志等文件时可以更方便的移植和运行,下面介绍从dockerfile编译成镜像. 在项目根目录 ...

  4. Java中的==符号与equals()的使用(测试两个变量是否相等)

    Java 程序中测试两个变量是否相等有两种方式:一种是利用 == 运算符,另一种是利用equals()方法. 当使用 == 来判断两个变量是否相等时,如果两个变量是基本类型变量,且都是数值类型(不一定 ...

  5. 查看linux端口对应的进程id

    例如:查看占用4040端口的进程 ss -lptn 'sport = :4040'

  6. caffe/blob.hpp:9:34: fatal error: caffe/proto/caffe.pb.h: 没有那个文件或目录

    You need to generate caffe.pb.h manually using protoc as follows. # In the directory you installed C ...

  7. JSBridge实现示例

    前言 参考来源 前人栽树,后台乘凉,本文参考了以下来源 Hybrid APP架构设计思路 marcuswestin/WebViewJavascriptBridge 楔子 本文介绍JSBridge的完整 ...

  8. 在js中保存数据

    localStorage: localStorage.setItem("key", "value"); localStorage.getItem("k ...

  9. Java第二次实验20135204

    一.实验过程: 1.先创建一个学号命名的文档: 2.一个百分制成绩转化为等级: 3.新建一个包,另一个测试: 4.打开UML,建模软件umbrello进行建模: 相关程序: 5.我的保存: 二.遇到的 ...

  10. 2017-2018-2 1723 『Java程序设计』课程 结对编程练习-四则运算-最后阶段

    2017-2018-2 1723 『Java程序设计』课程 结对编程练习-四则运算-最后阶段 最后的一周,时间越来越紧张,因为之前的拖沓和一些事情的耽误,导致了如今的紧张,这一周应该是我们小组效率最高 ...