文档章节

并发编程-SimpleDateFormat线程不安全及解决办法

h
 haidao1992
发布于 2017/08/05 19:27
字数 612
阅读 68
收藏 0

一. 为什么SimpleDateFormat不是线程安全的?

Java源码如下:

一. 为什么SimpleDateFormat不是线程安全的?

Java源码如下:

/**
* Date formats are not synchronized.
* It is recommended to create separate format instances for each thread.
* If multiple threads access a format concurrently, it must be synchronized
* externally.
*/
public class SimpleDateFormat extends DateFormat {
    
    public Date parse(String text, ParsePosition pos){
        calendar.clear(); // Clears all the time fields
        // other logic ...
        Date parsedDate = calendar.getTime();
    }
}
abstract class DateFormat{
    // other logic ...
    protected Calendar calendar;
    public Date parse(String source) throws ParseException{
        ParsePosition pos = new ParsePosition(0);
        Date result = parse(source, pos);
        if (pos.index == 0)
            throw new ParseException("Unparseable date: \"" + source + "\"" ,
                pos.errorIndex);
        return result;
    }
}

如果我们把SimpleDateFormat定义成static成员变量,那么多个thread之间会共享这个sdf对象, 所以Calendar对象也会共享。

假定线程A和线程B都进入了parse(text, pos) 方法, 线程B执行到calendar.clear()后,线程A执行到calendar.getTime(), 那么就会有问题。

二. 问题重现:

public class SdfThreadTest2{
    public static void main(String[] args) throws InterruptedException {
        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        final String[] date = new String[]{"2017-01-01","2017-02-02","2017-03-03","2017-04-04","2017-05-05",
                "2017-06-06","2017-07-07","2017-08-08","2017-09-09","2017-10-10","2017-11-11","2017-12-12"};
        final ExecutorService es2 = Executors.newFixedThreadPool(12);
        for(int i=0;i<12;i++){
            final int j = i;
            es2.execute(new Runnable(){

                @Override
                public void run() {
                    try {
                        System.out.println(j+ "->" + sdf.parse((date[j])));
                    } catch (ParseException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

三. 解决方案:

1. 解决方案a:

将SimpleDateFormat定义成局部变量:

    SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy", Locale.US);  

    String str1 = "01-Jan-2010";  

    String str2 = sdf.format(sdf.parse(str1));  

缺点:每调用一次方法就会创建一个SimpleDateFormat对象,方法结束又要作为垃圾回收。

2. 解决方案b:

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

public class SdfThreadTest2{
    public static void main(String[] args) throws InterruptedException {
        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        final String[] date = new String[]{"2017-01-01","2017-02-02","2017-03-03","2017-04-04","2017-05-05",
                "2017-06-06","2017-07-07","2017-08-08","2017-09-09","2017-10-10","2017-11-11","2017-12-12"};
        final ExecutorService es2 = Executors.newFixedThreadPool(12);
        for(int i=0;i<12;i++){
            final int j = i;
            es2.execute(new Runnable(){

                @Override
                public void run() {
                    try {
                        synchronized(sdf){
                            System.out.println(j+ "->" + sdf.parse((date[j])));
                        }
                    } catch (ParseException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

缺点:性能较差,每次都要等待锁释放后其他线程才能进入

3. 解决方案c: (推荐)

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

写一个工具类:

public class DateUtil {
    private static ThreadLocal<SimpleDateFormat> local = new ThreadLocal<SimpleDateFormat>();

    public static Date parse(String str) throws Exception {
        SimpleDateFormat sdf = local.get();
        if (sdf == null) {
            sdf = new SimpleDateFormat("dd-MMM-yyyy", Locale.US);
            local.set(sdf);
        }
        return sdf.parse(str);
    }
    
    public static String format(Date date) throws Exception {
        SimpleDateFormat sdf = local.get();
        if (sdf == null) {
            sdf = new SimpleDateFormat("dd-MMM-yyyy", Locale.US);
            local.set(sdf);
        }
        return sdf.format(date);
    }
}

© 著作权归作者所有

h
粉丝 1
博文 26
码字总数 27021
作品 0
浦东
程序员
私信 提问
线程不安全的SimpleDateFormat

8.5 SimpleDateFormat是线程不安全的 SimpleDateFormat是Java提供的一个格式化和解析日期的工具类,日常开发中应该经常会用到,但是由于它是线程不安全的,多线程公用一个SimpleDateFormat实...

今天你不奋斗明天你就落后
2017/12/14
0
0
java并发编程——线程不安全的类

什么是线程不安全的类? 如果一个类的对象同时被多个线程访问,如果不做特殊的同步或并发处理,很容易表现出线程不安全的现象,比如抛出异常、逻辑处理错误等,这种类我们就称为线程不安全的...

长头发-dawn
2018/10/08
12
0
Java并发编程笔记之SimpleDateFormat源码分析

SimpleDateFormat 是 Java 提供的一个格式化和解析日期的工具类,日常开发中应该经常会用到,但是由于它是线程不安全的,多线程公用一个 SimpleDateFormat 实例对日期进行解析或者格式化会导...

狂小白
2018/07/12
0
0
Java并发编程之SimpleDateFormat源码分析

SimpleDateFormat 是 Java 提供的一个格式化和解析日期的工具类,日常开发中应该经常会用到,但是由于它是线程不安全的,多线程公用一个 SimpleDateFormat 实例对日期进行解析或者格式化会导...

狂小白
2018/07/12
0
0
Spring单例与线程安全小结

一、Spring单例模式与线程安全 Spring框架里的bean,或者说组件,获取实例的时候都是默认的单例模式,这是在多线程开发的时候要尤其注意的地方。 单例模式的意思就是只有一个实例。单例模式确...

勇敢的蜗牛_Z
2016/04/07
115
0

没有更多内容

加载失败,请刷新页面

加载更多

关于AsyncTask的onPostExcute方法是否会在Activity重建过程中调用的问题

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/XG1057415595/article/details/86774575 假设下面一种情况...

shzwork
今天
6
0
object 类中有哪些方法?

getClass(): 获取运行时类的对象 equals():判断其他对象是否与此对象相等 hashcode():返回该对象的哈希码值 toString():返回该对象的字符串表示 clone(): 创建并返此对象的一个副本 wait...

happywe
今天
6
0
Docker容器实战(七) - 容器中进程视野下的文件系统

前两文中,讲了Linux容器最基础的两种技术 Namespace 作用是“隔离”,它让应用进程只能看到该Namespace内的“世界” Cgroups 作用是“限制”,它给这个“世界”围上了一圈看不见的墙 这么一...

JavaEdge
今天
8
0
文件访问和共享的方法介绍

在上一篇文章中,你了解到文件有三个不同的权限集。拥有该文件的用户有一个集合,拥有该文件的组的成员有一个集合,然后最终一个集合适用于其他所有人。在长列表(ls -l)中这些权限使用符号...

老孟的Linux私房菜
今天
7
0
面试套路题目

作者:抱紧超越小姐姐 链接:https://www.nowcoder.com/discuss/309292?type=3 来源:牛客网 面试时候的潜台词 抱紧超越小姐姐 编辑于 2019-10-15 16:14:56APP内打开赞 3 | 收藏 4 | 回复24 ...

MtrS
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部