线程安全问题
java.text中的三大格式化类:
1、NumberFormat
2、MessageFormat
3、DateFormat(SimpleDateFormat)
除了NumberFormat外,其他两个都不是线程安全的。
NumberFormat中使用的属性都是不变的,而SimpleDateFormat等却使用了可变但没有同步的属性,所以在多线程访问的条件下会产生线程安全问题,即格式不正确的问题。
假设我们要提供一个供并发方法的格式化工具方法,需要提供线程安全和高性能,如何做呢?
不恰当的使用
1、Stack局部变量方式,如
public static String format(Date date) {
SimpleDateFormat formater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return formater.format(date);
}
这样在高并发方法下是线程安全的,因为format是stack上分配的变量,不会和其他线程共享,然后不足就是每一次执行格式化,都需要new一个SimpleDateFormat,这样在高并发上,内存消耗不容忽视
2、类上静态常量
private static final SimpleDateFormat formater = new SimpleDateFormat();
......
public static String format(Date date) {
return formater.format(date);
}
在单线程测试下,格式化1000万记录,第一种方式花费17秒多,而第二种方式仅为7秒多。但在多线程的条件下,这样一来就线程不安全了,怎么办呢?首先想到的时候synchronized,如下所示:
public static synchronized String format(Date date) {
//SimpleDateFormat formater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return formater.format(date);
}
但这样处理的话,是线程安全了,但使所有并发访问变成了串行化访问,而一个简单的格式化类,用脚趾想也应该是无状态的,支持高性能访问。
正确、高效使用
综合考虑上面的两种情况,比较完善的实现应该具备以下特征:
- 不能每一次格式化都new SimpleDateFormat,但也不能所有线程共用一个SimpleDateFormat实例
- 格式化方法不能增加synchronized限制,增加了synchronized必然降低吞吐量
因为 SimpleDateFormat是mutable(可变)的并且共享在多个线程,为了安全访问,必然要用同步(synchronization),这样看来上面两个特征是互相矛盾,不竟然。SimpleDateFormat是mutable,这个除了改写SimpleDateFormat外,如果还想用SimpleDateFormat的话是没法改变的,那现在既然因为SimpleDateFormat共享引起的,那我不共享可不可以?可以。
下面是并发技术领域技术泰斗、JCP专家、JUC(java.util.concurrent)实现者Doug Lea经典书籍《Java concurrency in practice》中的一段话(3.3 Thread Confinement):
Accessing shared, mutable data requires using synchronization; one way to avoid this requirement is to not share. If data is only accessed from a single thread, no synchronization is needed. This technique, thread confinement, is one of the simplest ways to achieve thread safety. When an object is confined to a thread, such usage is automatically thread-safe even if the confined object itself is not |
通过上面的描述,我们得知,虽然SimpleDateFormat不是线程安全的,但通过把对SimpleDateFormat的访问局限在单个线程,对SimpleDateFormat的使用自然也就线程安全,这种技术叫Thead Configment,Java提供的实现是ThreadLocal。
改进
改进的方法就是一个线程一个SimpleDateFormat实例,这样每一个线程访问format都是串行化,对SimpleDateFormat的访问自然就线程安全了。同时因应用的所能支持的线程数是有限的,如一般都低于1万(按照实践经验准则支持1万线程高效运行,而不是context switch的话,CPU数目少说也的50核吧),这样SimpleDateFormat的实例也不会太多,对内存的消耗也可以忽略。
最后实现方式如下:
private static ThreadLocal<SimpleDateFormat> formaterHolder
= new ThreadLocal<SimpleDateFormat>() {
public SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
public static String format(Date date) {
return formaterHolder.get().format(date);
}
formaterHolder是formater的各个线程的持有者,不同的线程调用formaterHolder.get()获的自己线程的formater。各个线程的formater通过initialValue在线程第一次使用时初始化
总结
正确写出一个程序比较难,而正确写出一个并发程序更难,而且还不是同一数量级的难度。写出一个好的高并发程序,掌握一门语言仅是一个必要条件,而理解和领会并发编程艺术才是充分条件。
相关推荐
SimpleDateFormat类的线程安全问题是因为它使用了缓存机制来提高解析和格式化的性能。缓存机制使用了一个缓存数组来存储解析和格式化的结果,但是这个缓存数组是共享的,这意味着在多线程环境中,多个线程可能会同时...
DateFormat和SimpleDateFormat类都是Java中用于日期字符串解析和格式化输出的类,但是它们的使用场景和实现机理不同。DateFormat类是一个抽象类,而SimpleDateFormat类是DateFormat类的一个实现类。 知识点7: ...
在实际开发中,如果你不想每次都创建新的`SimpleDateFormat`实例,可以考虑使用`ThreadLocal<SimpleDateFormat>`来存储线程局部的`SimpleDateFormat`,避免多线程环境下的同步问题。 总结起来,`java.text....
例如,`SimpleDateFormat` 是非线程安全的,因为它内部使用了一个`Calendar`对象,多个线程同时使用一个`SimpleDateFormat`实例进行解析或格式化日期时,可能会出现线程A操作未完成而线程B已经开始的问题,导致数据...
在Java编程语言中,`SimpleDateFormat`类是一个广泛使用的日期时间格式化工具,但它的线程安全性是一个常常被开发者忽视的问题。标题指出的"simpleDateFormat是线程不安全的",意味着在多线程环境下,如果多个线程...
SimpleDateFormat类是Java中常用的日期格式化类,但是它并不是线程安全的。在多线程环境下,如果多个线程同时使用同一个SimpleDateFormat对象,可能会出现日期格式化错误的问题。 2. 使用ThreadLocal解决线程安全...
SimpleDateFormat是Java中一个常用的日期时间格式化类,但是它却存在线程安全问题。在多线程环境下,如果多个线程同时使用同一个SimpleDateFormat对象,可能会导致日期时间格式化结果不正确或抛出异常。 问题的根源...
在Java编程语言中,`DateFormat`类是处理日期和时间格式化的重要工具,但它的线程安全性是一个常见的问题。在多线程环境下,不恰当的使用`DateFormat`可能导致数据不一致、性能下降甚至程序崩溃。这篇博客将深入探讨...
在Java SE中,传统的日期格式化常常涉及到线程安全问题,特别是当多个线程共享同一实例的`SimpleDateFormat`时。这是因为`SimpleDateFormat`不是线程安全的,它内部使用了可变的状态来处理日期和时间格式化。下面将...
在Java开发过程中,`SimpleDateFormat`是被广泛使用的日期格式化工具类。然而,在多线程环境下,`SimpleDateFormat`存在非线程安全的问题,这可能会导致程序运行时出现异常或者错误的结果。 #### 二、问题分析 ###...
此外,Java 8引入了`java.time`包,其中的`DateTimeFormatter`类提供了更高效、更安全的日期和时间格式化功能,并且是线程安全的。因此,对于新的项目或者需要升级的代码,推荐使用`java.time` API,而不是传统的`...
在上述示例中,`ThreadLocalNormalUsage05`类展示了如何使用ThreadLocal为每个线程创建独立的日期格式化对象。通过ThreadLocal,每个线程都可以安全地访问其自己的`SimpleDateFormat`实例,而不会产生竞态条件。 ...
推荐使用`Double-Check Locking`或者`java.util.concurrent.atomic.AtomicReference`来实现线程安全的延迟初始化。 遵循这些规范,开发者可以编写出更健壮、高效的并发代码,避免潜在的线程安全问题,提高程序的...
1. **线程安全问题**:`SimpleDateFormat`不是线程安全的,在多线程环境中应该避免共享实例,或者使用`ThreadLocal`来管理。 2. **异常处理**:在进行字符串到`Date`的转换时,一定要注意捕获并处理`ParseException`...
Java 1.6支持国际化的字符串和日期格式化,通过Locale类和ResourceBundle类可以方便地实现本地化资源的加载。 九、XML处理 DOM、SAX和JAXB等API用于解析和生成XML文档,JAXP提供了XPath和XSLT支持,便于XML数据的...
- 通过@Multithread注解来指定一个类是线程安全的,但这是第三方提供的,并不是Java SE标准部分。 在JavaScript编程部分,文档提到了表单验证的脚本代码。具体地,文档描述了当一个文本框失去焦点时,如果输入的值...
通过使用`ThreadLocal`、每次使用时创建新实例或切换到`java.time`包中的类,可以有效地避免这类问题,提高程序的稳定性和可靠性。在实际开发中,应尽量选择线程安全的方式处理日期时间,以确保程序的正确性。
在Java多线程编程中,`DateFormat`类是一个常见的日期和时间格式化工具,但它并不是线程安全的。这意味着在多线程环境下直接共享`DateFormat`实例可能会导致数据不一致或者异常。根据Java官方文档的建议,每个线程...
解决这一问题的方法有多种,包括将 SimpleDateFormat 定义为局部变量、使用 synchronized 或 Lock 进行同步控制、利用 ThreadLocal 创建线程私有的实例以及使用 JDK 8 中的 DateTimeFormatter。根据实际应用场景,...
Java是一种广泛使用的编程语言,它的灵活性和强大的功能使其在软件开发领域占有一席之地。为了提高开发效率,Java提供了一系列的工具类,这些类通常包含了各种实用方法,可以帮助开发者简化代码,提高代码的可读性...