一、

线程不安全验证:

/**
* SimpleDateFormat线程安全测试
* 〈功能详细描述〉
*
* @author 17090889
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class SimpleDateFormatTest {
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 100, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(1000), new MyThreadFactory("SimpleDateFormatTest")); public void test() {
while (true) {
poolExecutor.execute(new Runnable() {
@Override
public void run() {
String dateString = simpleDateFormat.format(new Date());
try {
Date parseDate = simpleDateFormat.parse(dateString);
String dateString2 = simpleDateFormat.format(parseDate);
System.out.println(dateString.equals(dateString2));
} catch (ParseException e) {
e.printStackTrace();
}
}
});
}
}

输出:

  true
  false
  true
  true
  false

出现了false,说明线程不安全

1、format方法

public StringBuffer format(Date date, StringBuffer toAppendTo,
FieldPosition pos)
{
pos.beginIndex = pos.endIndex = 0;
return format(date, toAppendTo, pos.getFieldDelegate());
} // 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;
}
 protected Calendar calendar;

可以看到,多个线程之间共享变量calendar,并修改calendar。因此在多线程环境下,当多个线程同时使用相同的SimpleDateFormat对象(如static修饰)的话,如调用format方法时,多个线程会同时调用calender.setTime方法,导致time被别的线程修改,因此线程是不安全的。

此外,parse方法也是线程不安全的,parse方法实际调用的是CalenderBuilder的establish来进行解析,其方法中主要步骤不是原子操作。

解决方案:

  1、将SimpleDateFormat定义成局部变量

  2、 加一把线程同步锁:synchronized(lock)

  3、使用ThreadLocal,每个线程都拥有自己的SimpleDateFormat对象副本。如:

/**
* SimpleDateFormat线程安全测试
* 〈功能详细描述〉
*
* @author 17090889
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class SimpleDateFormatTest {
private static final ThreadLocal<SimpleDateFormat> THREAD_LOCAL = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
// private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 100, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(1000), new MyThreadFactory("SimpleDateFormatTest")); public void test() {
while (true) {
poolExecutor.execute(new Runnable() {
@Override
public void run() {
SimpleDateFormat simpleDateFormat = THREAD_LOCAL.get();
if (simpleDateFormat == null) {
simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
String dateString = simpleDateFormat.format(new Date());
try {
Date parseDate = simpleDateFormat.parse(dateString);
String dateString2 = simpleDateFormat.format(parseDate);
System.out.println(dateString.equals(dateString2));
} catch (ParseException e) {
e.printStackTrace();
} finally {
local.remove();
}
}
});
}
}
}

  4、使用DateTimeFormatter代替SimpleDateFormat

  DateTimeFormatter是线程安全的,默认提供了很多格式化方法,也可以通过ofPattern方法创建自定义格式化方法。

  (1)格式化日期示例:

 LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime); // 2019-11-20T15:04:29.017
DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
String strDate=localDateTime.format(dtf);
System.out.println(strDate); // 2019/23/20 15:23:46

  (2)解析日期

 DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime localDateTime=LocalDateTime.parse("2019/11/20 15:23:46",dtf);
System.out.println(localDateTime); // 2019-11-20T15:23:46

二、使用JDK8全新的日期和时间API

  1、LocalDate、LocalTime、LocalDateTime

  Date如果不格式化,打印出的日期可读性极差,可使用LocalDateTime代替。

  (1)LocalDateTime:获取年月日时分秒等于LocalDate+LocalTime

 LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime); // 2019-11-20T15:04:29.017
LocalDate localDate = localDateTime.toLocalDate();
LocalTime localTime = localDateTime.toLocalTime();

  (2)LocalDate:获取年月日

 LocalDate localDate=LocalDate.now();
System.out.println(localDate); // 2019-11-20

  (3)LocalTime:获取时分秒

 LocalTime localTime = LocalTime.now();
System.out.println(localTime); // 15:14:17.081
int hour = localTime.getHour();
int minute = localTime.getMinute();
int second = localTime.getSecond();

  2、Instant

SimpleDateFormat线程不安全原因及解决方案的更多相关文章

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

    1.什么是线程不安全? 线程不安全也叫非线程安全,是指多线程执行中,程序的执行结果和预期的结果不符的情况就叫做线程不安全. ​ 线程不安全的代码 SimpleDateFormat 就是一个典型的线程不 ...

  2. Java并发编程原理与实战八:产生线程安全性问题原因(javap字节码分析)

    前面我们说到多线程带来的风险,其中一个很重要的就是安全性,因为其重要性因此,放到本章来进行讲解,那么线程安全性问题产生的原因,我们这节将从底层字节码来进行分析. 一.问题引出 先看一段代码 packa ...

  3. iOS构建流畅的交互界面--CPU,GPU资源消耗的原因和解决方案

    CPU资源消耗的原因和解决方案对象创建轻量对象代替重量对象* 不需要响应触摸事件的控件:CALayer显示* 对象不涉及UI操作,则尽量放到后台线程创建* 包含有CALayer的控件只能在主线程创建和 ...

  4. 大多数人不知道的:HashMap链表成环的原因和解决方案

    引导语 在 JDK7 版本下,很多人都知道 HashMap 会有链表成环的问题,但大多数人只知道,是多线程引起的,至于具体细节的原因,和 JDK8 中如何解决这个问题,很少有人说的清楚,百度也几乎看不 ...

  5. SimpleDateFormat线程不安全及解决办法(转)

    以前没有注意到SimpleDateFormat线程不安全的问题,写时间工具类,一般写成静态的成员变量,不知,此种写法的危险性!在此讨论一下SimpleDateFormat线程不安全问题,以及解决方法. ...

  6. 玩转Windows服务系列——无COM接口Windows服务启动失败原因及解决方案

    将VS创建的Windows服务项目编译生成的程序,通过命令行 “服务.exe -Service”注册为Windows服务后,就可以通过服务管理器进行管理了. 问题 通过服务管理器进行启动的时候,发现服 ...

  7. PowerDesigner16.5 连64位MySQL,出错:SQLSTATE = IM014。原因及解决方案

  8. WPF [调用线程无法访问此对象,因为另一个线程拥有该对象。] 解决方案以及如何实现字体颜色的渐变

    本文说明WPF [调用线程无法访问此对象,因为另一个线程拥有该对象.] 解决方案以及如何实现字体颜色的渐变 先来看看C#中Timer的简单说明,你想必猜到实现需要用到Timer的相关知识了吧. C# ...

  9. NIOS II CPU复位异常的原因及解决方案

    NIOS II CPU复位异常的原因及解决方案   近期在用nios ii做项目时,发现一个奇怪的现象,在NIOS II EDS软件中编写好的代码,烧写到芯片中,第一次能够正常运行,但是当我按下板卡上 ...

随机推荐

  1. (转)利用VMware克隆虚拟机需要注意的问题

    第一步:关闭当前机器 第二步:在VMware中右键要克隆的机器 选择管理-->克隆 第三步:启动新克隆的虚拟机 修改主机名 如 hostname  slave2 第四步:修改克隆的虚拟机的ip地 ...

  2. vue2.X 与 vue1.X 的区别

    vue2.0: bower info vue http://vuejs.org/ 到了2.0以后,有哪些变化? 1. 在每个组件模板,不在支持片段代码 组件中模板: 之前: <template& ...

  3. PHP7.2中AES加密解密方法mcrypt_module_open()替换方案 Function mcrypt_get_block_size() is deprecated

    直接粘代码,该类是基于微信公众号消息加密解密所提供的PHP DEMO改造而来,目前使用于彬彬大学APP接口token校验中. php的mcrypt 扩展已经过时了大约10年,并且用起来很复杂.因此它被 ...

  4. python如何判断1个列表中所有的数据都是相等的?

    方法一: 元素两两比较,如果有数据不同,则r的值变为false #!/usr/bin/python a=[22,22,22,22] b = len(a) r=True for i in range(b ...

  5. 安装mysql数据库-centos7

    mysql官网下载地址:https://dev.mysql.com/downloads/mysql/ 参考安装:https://blog.51cto.com/snowlai/2140451?sourc ...

  6. [Linux] 020 RPM 包的命名原则与其依赖性

    1. RPM 包命名原则 例如:httpd-2.2.15-15.e16.centos.1.i686.rpm 字符 释义 httpd 软件包名 2.2.15 软件版本 15 软件发布的次数 e16.ce ...

  7. CF1105C Ayoub and Lost Array ——动态规划

    CF1105C Ayoub and Lost Array 题意:一个整数数组,满足: 1. 长度为n 2. 所有元素都在[l, r]范围内 3. 所有元素的和能被3整除给出n, l, r (1 ≤ n ...

  8. HRESULT是什么类型

    HRESULT 是一种简单的数据类型,可以判断函数执行的结果.HRESULT 常被用作COM调用的返回值.充分利用HRESULT返回信息可以帮助提高我们的代码质量,提供程序的健壮性. HRESULT ...

  9. Pandas处理缺失的数据

    处理丢失数据 有两种丢失数据: None np.nan(NaN) import numpy as np import pandas from pandas import DataFrame 1. No ...

  10. CodeChef Sereja and GCD

    Sereja and GCD   Problem code: SEAGCD   Submit All Submissions   All submissions for this problem ar ...