首页 > 其他分享 >类变量在高并发环境下引发的线程安全问题

类变量在高并发环境下引发的线程安全问题

时间:2024-02-27 11:22:21浏览次数:15  
标签:java 变量 SimpleDateFormat 并发 static 线程 date new

### 背景
生产环境中,登录接口出现偶发性的异常,排查发现是获取当前时间的工具类抛出异常,以下为代码片段:
``````java
/**
* 时间工具类
*/
public class DateUtil {
Logger logger= LoggerFactory.getLogger(this.getClass());
private final static SimpleDateFormat shortSdf = new SimpleDateFormat("yyyy-MM-dd");
private final static SimpleDateFormat longSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private final static SimpleDateFormat sdfYMD = new SimpleDateFormat("yyyyMMdd");
private final static SimpleDateFormat sdfHM= new SimpleDateFormat("yyyyMMddHHmm");
private final static SimpleDateFormat sdf= new SimpleDateFormat("yyyy/MM/dd");
static SimpleDateFormat sdfYM = new SimpleDateFormat("yyyy-MM"); // 日期格式
……
/**
* 获取当前日期
*
* @return Date 返回类型
*/
public static Date getNowDate() {
Date date = null;
try {
date = shortSdf.parse(shortSdf.format(new Date()));
} catch (ParseException e) {

}
return date;
}

````````

其中getNowDate()偶尔抛出异常:``java.lang.NumberFormatException: For input string: ""``

### 原因分析
SimpleDateFormat类本身是线程不安全的,当把SimpleDateFormat定义为类变量时,多个线程同时调用format和parse方法时,SimpleDateFormat的成员变量``protected Calendar calendar``,会被多个线程同时访问或修改,导致线程安全问题。

format方法会调用:``calendar.setTime(date);``
parse方法会调用:CalendarBuilder类的establish方法,其中会调用``cal.clear();``清空calendar实例的值。

### 解决方案

``SimpleDateFormat`` 类确实是线程不安全的,如果将其定义为类变量并在多线程环境下使用,可能会导致错误。有几种方法可以解决这个问题:
1. **局部变量**:将 ``SimpleDateFormat`` 作为局部变量使用。这样每个线程都会创建一个新的对象,避免了线程安全问题。
``````java
public String formatDate(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(date);
}
``````
2. **ThreadLocal**:使用 ``ThreadLocal`` 可以让每个线程都拥有自己的 ``SimpleDateFormat`` 实例,从而避免线程安全问题。
``````java
private static final ThreadLocal<SimpleDateFormat> dateFormatter = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
public String formatDate(Date date) {
return dateFormatter.get().format(date);
}
``````
3. **同步块**:使用同步块(synchronized block)来确保一次只有一个线程可以访问 ``SimpleDateFormat``,但这种方法可能会降低性能。
``````java
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
public String formatDate(Date date) {
synchronized (sdf) {
return sdf.format(date);
}
}
``````
4. **使用 Java 8 的 DateTimeFormatter**:Java 8 引入了一套新的时间日期API,其中包括线程安全的 ``DateTimeFormatter`` 类。推荐升级到 Java 8 并使用这个类来解决线程安全问题。
``````java
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
private static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
public String formatDate(LocalDateTime date) {
return dtf.format(date);
}
``````
以上四种方法都可以解决 ``SimpleDateFormat`` 的线程安全问题,可以根据项目需求和实际情况选择最适合的方法。
全能宝已使用方案四,对公共组件的DateUtil工具类进行统一改造。

### 引申

在 JDK 1.8 中,主要有以下几个可能引发线程安全问题的类:
1. **SimpleDateFormat**: 如之前讨论的,``SimpleDateFormat`` 类是非线程安全的。在处理日期和时间时,推荐使用 Java 8 的新日期时间 API(如 ``LocalDate``、``LocalTime``、``LocalDateTime`` 和 ``DateTimeFormatter``)。
2. **Calendar**: ``Calendar`` 类也是线程不安全的。Java 8 推出了新的日期时间 API 来替代它,包括 ``LocalDate``、``LocalTime`` 和 ``LocalDateTime`` 等。
3. **Random**: ``java.util.Random`` 类在多线程环境下可能存在竞争条件,导致随机数生成不符合预期。为了解决这个问题,可以使用 ``java.util.concurrent.ThreadLocalRandom`` 类,它提供了线程安全的随机数生成。
4. **DecimalFormat**: ``java.text.DecimalFormat`` 类同样是线程不安全的。如果需要在多线程环境中进行数字格式化,可以考虑使用 ``ThreadLocal<DecimalFormat>`` 或将其作为局部变量。
5. **StringBuilder**: ``java.lang.StringBuilder`` 是线程不安全的,在多线程环境下应该使用 ``java.lang.StringBuffer``。但请注意,``StringBuffer`` 的性能略低于 ``StringBuilder``,因此只有在确实需要共享缓冲区的情况下才应使用 ``StringBuffer``。
总之,在使用这些类时,需要注意线程安全性。在多线程环境下,可以考虑使用线程安全的替代方案。对于日期和时间处理,推荐使用 Java 8 的新日期时间 API。

标签:java,变量,SimpleDateFormat,并发,static,线程,date,new
From: https://www.cnblogs.com/wmy666/p/18036509

相关文章

  • 多线程系列(十) -ReadWriteLock用法详解
    一、摘要在上篇文章中,我们讲到ReentrantLock可以保证了只有一个线程能执行加锁的代码。但是有些时候,这种保护显的有点过头,比如下面这个方法,它仅仅就是只读取数据,不修改数据,它实际上允许多个线程同时调用的。publicclassCounter{privatefinalLocklock=newReentra......
  • 深入解析Python并发编程的多线程和异步编程
    本文分享自华为云社区《Python并发编程探秘:多线程与异步编程的深入解析》,作者:柠檬味拥抱。在Python编程中,多线程是一种常用的并发编程方式,它可以有效地提高程序的执行效率,特别是在处理I/O密集型任务时。Python提供了threading模块,使得多线程编程变得相对简单。本文将深入探讨thre......
  • 设置PHP最大连接数及php-fpm -static高并发
    设置PHP最大连接数及php-fpm高并发参数调整 服务器中找到php-fpm.conf配置(有的会在引入的www.conf中)1234567891011121314151617181920212223[global]pid=/usr/local/php/var/run/php-fpm.piderror_log=/usr/local/php/var/log/ph......
  • STEP: 用于多变量时间序列预测的预训练增强时空图神经网络《Pre-training Enhanced Sp
    2023年12月27日,看一篇老师给的论文。论文:Pre-trainingEnhancedSpatial-temporalGraphNeuralNetworkforMultivariateTimeSeriesForecasting或者是:Pre-trainingEnhancedSpatial-temporalGraphNeuralNetworkforMultivariateTimeSeriesForecastingGitHub:https:......
  • QT多线程实现-----问题解决及实现方式
    一、概述恰巧正在做一个多线程通信的项目,具体功能是与下位机交互和文件发送,在子线程中实现命令发送和文件传输。使用movetothread主要遇到以下几个问题:1.Socketnotifierscannotbeenabledordisabledfromanotherthread。2.子线程完成文件传输,发送信号......
  • Java 基础变量
    基本数据类型:字符型:char,数字类型:整数型:Byte,short,int,long(long 类型的数据一定要在数值后面加上 L,否则将作为整型解析)         浮点型:float,double布尔型:boolean引用类型:String定义变量,变量作用域包装类型:八种基本类型都有对应的包装类分别为:Byte、Sh......
  • 利用IO复用技术Epoll与线程池实现多线程的Reactor高并发模型
    Reactor模型是一种常见的高并发设计模式,特别是在网络编程中。在Reactor模型中,一个或多个输入同时传递给一个或多个服务处理程序。服务处理程序对输入进行处理,然后将结果传递给相应的输出处理程序。使用IO复用技术(如epoll)和线程池,可以实现多线程的Reactor高并发模型。下面是一个简......
  • 多线程系列(九) -ReentrantLock常用方法详解
    一、简介在上一篇文章中,我们介绍了ReentrantLock类的一些基本用法,今天我们重点来介绍一下ReentrantLock其它的常用方法,以便对ReentrantLock类的使用有更深入的理解。二、常用方法介绍2.1、构造方法ReentrantLock类有两个构造方法,核心源码内容如下:/***默认创建非公平锁*/......
  • 【译】代码更快、更好,借助 GitHub Copilot 的新功能:斜杠命令和上下文变量
    你是否曾经希望有一个人工智能助手可以帮助你更快更好地编写代码?那就是VisualStudioCopilotChat为您提供的:一个人工智能驱动的结对程序员,可以回答您的问题,建议代码片段,解释代码逻辑,并与您讨论您的项目。您可以使用Copilot更快更好地编写代码,因为它可以帮助您避免错误并......
  • 类变量和类方法、代码块、单例设计模式、final关键字、抽象类、接口、内部类
    类变量和类方法类变量-提出问题说:有一群小孩在玩堆雪人,不时有新的小孩加入,请问如何知道现在共有多少人在玩?,编写程序解决。传统的方法来解决思路在main方法中定义一个变量count当一个小孩加入游戏后count++,最后个count就记录有多少小孩玩游戏小孩是一个类,有名字属......