一、

线程不安全验证:

/**
* 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. oracle基础sql

    二.SQL Structur query language 结构化查询语言,是操作关系型数据库中的对象. DDL(Data definition language 数据定义语言),用于建表或删表操作, ...

  2. ES命令

    基础概念 Elasticsearch有几个核心概念.从一开始理解这些概念会对整个学习过程有莫大的帮助.   接近实时(NRT)        Elasticsearch是一个接近实时的搜索平台.这意味 ...

  3. vs2017或vs2019添加引用时报错

    我先安装的是vs2019,进入VS命令提示符里后一直说:gacutil 不是有效的命令,一直没能解决,然后直接装了vs2017后,该命令可以使用了, 还是用VS2017吧,2019的版本感觉还有点问题 ...

  4. 像计算机科学家一样思考python-第2章 变量、表达式和语句

    感想: 1.程序出现语义错误时,画状态图是一个很好的调试办法.打印出关键变量在不同代码处理后值的变化,就能发现问题的蛛丝马迹. 2.每当学习新语言特性时,都应当在交互模式中进行尝试,并故意犯下错误,看 ...

  5. Mybatis入门(附源码压缩包下载)

    首先,来个项目全景预览,文章尾部附上Demo下载链接 [1]pom.xml配置(加入jar包) <project xmlns="http://maven.apache.org/POM/ ...

  6. SqL语句基础之增删改查

    增查删改的SQL语句,如此的实用,下面我就来简单介绍一下它简单的用法. 1.什么是SQL? SQL是用于访问和处理数据库的标准的一种计算机语言. 2.SQL可以做什么?  (1)可以向数据库进行查询 ...

  7. JSP基础--三大指令

    JSP指令 1       JSP指令概述 JSP指令的格式:<%@指令名 attr1=”” attr2=”” %>,一般都会把JSP指令放到JSP文件的最上方,但这不是必须的. JSP中 ...

  8. ubantu 安装软件

    一.解压后bin文件夹里有setup.py 进入到setup.py的目录,执行命令: sudo python3 setup.py install 二.以.whl结尾的文件 直接运行命令: sudo p ...

  9. Jenkins windows 执行批量cmd命令XCOPY 提示'XCOPY' 不是内部或外部命令,也不是可运行的程序 或批处理文件。

    由于Jenkins没有配置环境变量造成 打开Jenkins=>Manage Jenkins =>Configure System =>全局属性 新增全局变量 健: Path 值: % ...

  10. uoj396 [NOI2018]屠龙勇士

    [NOI2018]屠龙勇士 描述 小 D 最近在网上发现了一款小游戏.游戏的规则如下: 游戏的目标是按照编号 1∼n 顺序杀掉 n 条巨龙,每条巨龙拥有一个初始的生命值 ai .同时每条巨龙拥有恢复能 ...