`

Java三大格式化类的线程安全问题以及ThreadLocal的使用

    博客分类:
  • Java
阅读更多
线程安全问题

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);
}
但这样处理的话,是线程安全了,但使所有并发访问变成了串行化访问,而一个简单的格式化类,用脚趾想也应该是无状态的,支持高性能访问。
正确、高效使用
综合考虑上面的两种情况,比较完善的实现应该具备以下特征:
  1. 不能每一次格式化都new SimpleDateFormat,但也不能所有线程共用一个SimpleDateFormat实例
  2. 格式化方法不能增加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在线程第一次使用时初始化
总结
正确写出一个程序比较难,而正确写出一个并发程序更难,而且还不是同一数量级的难度。写出一个好的高并发程序,掌握一门语言仅是一个必要条件,而理解和领会并发编程艺术才是充分条件。
分享到:
评论

相关推荐

    java ThreadLocal多线程专属的变量源码

    java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多...

    简单分析Java线程编程中ThreadLocal类的使用共

    简单分析Java线程编程中ThreadLocal类的使用共4页.pdf.zip

    java中ThreadLocal类的使用

    NULL 博文链接:https://justsee.iteye.com/blog/791919

    Java多线程 之 临界区、ThreadLocal.docx

    synchronized关键字不属于方法特征签名的一部分,所以可以在覆盖方法的时候加上去。...如果在一个类内部都是使用synchronized关键字定义了方法f(),g()。那么当使用这个类的实例调用f()时,就不能再调用g()方法。

    ThreadLocal:如何优雅的解决SimpleDateFormat多线程安全问题

    目录SimpleDateFormat诡异bug复现SimpleDateFormat诡异bug字符串日期转Date日期(parse)Date日期转String类型(format)SimpleDateFormat出现bug...ThreadLocal注意事项使用ThreadLocal解决SimpleDateFormat线程安全问题总结...

    java线程详解

    六、线程安全类 七、线程死锁 八、线程同步小结 Java线程:线程的交互 Java线程:线程的调度-休眠 Java线程:volatile关键字 Java线程:新特征-线程池 一、固定大小的线程池 二、单任务线程池 三、可变尺寸...

    TestNG多线程安全吗?ThreadLocal:有我还能不安全?

    目录一、背景介绍二、TestNG多线程详解2.1 TestNG多线程实现2.2 TestNG多线程效果演示三、ThreadLocal3.1 ThreadLocal概念3.2 具体实现 ...所以就使用到ThreadLocal这个类去保证线程安全。 二、TestN

    java事务 - threadlocal

    ThreadLocal保证一个类的实例变量在各个线程中都有一份单独的拷贝, 从而不会影响其他线程中的实例变量

    java 简单的ThreadLocal示例

    java 简单的ThreadLocal示例

    java多线程安全性基础介绍.pptx

    java多线程安全性基础介绍 线程安全 正确性 什么是线程安全性 原子性 竞态条件 i++ 读i ++ 值写回i 可见性 JMM 由于cpu和内存加载速度的差距,在两者之间增加了多级缓存导致,内存并不能直接对cpu可见。 ...

    JAVA ThreadLocal类深入

    深入研究java.lang.ThreadLocal类。ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是 threadlocalvariable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。

    Java多线程编程中ThreadLocal类的用法及深入

    早在 JDK 1.2 的时代,java.lang.ThreadLocal 就诞生了,它是为了解决多线程并发问题而设计的,只不过设计得有些难用,所以至今没有得到广泛使用。其实它还是挺有用的,不相信的话,我们一起来看看这个例子吧。 一个...

    Java中ThreadLocal的设计与使用

    Java中ThreadLocal的设计与使用.doc

    Java ThreadLocal 线程安全问题解决方案

    主要介绍了Java ThreadLocal 线程安全问题解决方案的相关资料,需要的朋友可以参考下

    java中ThreadLocal详解

    详解java底层实现原理,ThreadLocal底层实现的数据结构,为什么不会导致内存泄露

    使用Java ThreadLocal.docx

    我们可以看到,通过这段代码实例化了一个ThreadLocal对象。我们只需要实例化对象一次,并且也不需要知道它是被哪个线程实例化。虽然所有的线程都能访问到这个ThreadLocal实例,但是每个线程却只能访问到自己通过调用...

    ThreadLocal

    ThreadLocal入门教程。 讲解了线程安全和ThreadLocal的使用的基本知识。

    线程安全你还在用synchronized?

    你还在用synchronized?线程安全相关知识深入剖析

    Java高级程序设计-多线程(二).pptx

    掌握同步方法的使用 理解线程死锁 掌握 ThreadLocal 类的使用 使用多线程模拟猴子采花 使用同步方法模拟购票 使用多线程模拟购物订单生成 使用 ThreadLocal 类模拟银行取款 Java高级程序设计-多线程(二)全文共34页,...

    Java中的线程同步与ThreadLocal无锁化线程封闭实现

    主要介绍了Java中的线程同步与ThreadLocal无锁化线程封闭实现,Synchronized关键字与ThreadLocal变量的使用是Java中线程控制的基础,需要的朋友可以参考下

Global site tag (gtag.js) - Google Analytics