SimpleDataFormat 非线程安全

目录

前言 

正文 

1.出现异常 

2.解决方法1 

3.解决方法2 

总结


前言 

SimpleDateFormat 类是 Java 中处理日期和时间格式化和解析的类,但它并不是线程安全的。这意味着多个线程不能安全地共享一个 SimpleDateFormat 实例进行日期和时间的解析和格式化。当多个线程共享同一个 SimpleDateFormat 实例时,会因为 SimpleDateFormat 内部维护的日历字段(例如:Calendar 对象)等的竞争条件而导致解析和格式化错误。


正文 

类 SimpleDataFormat 的可以对日期进行解析与格式化,但在使用时如果不想使用 0 进行填充,比如 2000-01-02 只想转换成 2002-1-2 ,我们需要在代码上进行处理,示例代码如下。 

package org.example.SimpleDataFormat;

import java.text.ParseException;
import java.text.SimpleDateFormat;

public class Run {
    public static void main(String[] args) throws ParseException {
        String dataString1 = "2000-1-1";
        String dataString2 = "2000-11-18";
        SimpleDateFormat format1 = new SimpleDateFormat("yyyy-M-d");
        SimpleDateFormat format2 = new SimpleDateFormat("yyyy-MM-dd");
        //先按照日期模式将字符串解析成日期再格式化成时间字符串。
        System.out.println(format1.format(format1.parse(dataString1)));
        System.out.println(format2.format(format2.parse(dataString1)));
        System.out.println(format1.format(format1.parse(dataString2)));
        System.out.println(format2.format(format2.parse(dataString2)));

    }
}

打印结果如下:

SimpleDataFormat 非线程安全_第1张图片

但 SimpleDateFormat 在多线程环境中使用类容易造成数据转换及处理不准确,因为类 SimpleDateFormat 并不是线程安全的。  

1.出现异常 

本示例将展示使用类 SimpleDataFormat 在多线程环境中处理日期时得到错误结果,这也是在多线程环境中开发经常遇到的问题。 

ackage org.example.SimpleDataFormat;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class formatError {
    static class MyThread extends Thread {
        private SimpleDateFormat sdf;
        private String dateString;

        public MyThread(SimpleDateFormat sdf, String dateString) {
            this.sdf = sdf;
            this.dateString = dateString;
        }

        @Override
        public void run() {
            try {
                Date dateRef = sdf.parse(dateString);
                String newDataString = sdf.format(dateRef);
                if (!newDataString.equals(dateString)) {
                    System.out.println("ThreadName=" + this.getName()
                            + "报错了 日期字符串:" + dateString + " 转换成的日期为:"
                            + newDataString);
                }
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String[] dateStringArray = new String[]{"2000-01-01",
                "2000-01-02","2000-01-03","2000-01-04",
                "2000-01-05","2000-01-06","2000-01-07",
                "2000-01-08","2000-01-09","2000-01-10",
        };
        MyThread[] threads = new MyThread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new MyThread(sdf,dateStringArray[i]);
        }
        for (int i = 0; i < 10; i++) {
            threads[i].start();
        }
    }
}

运行结果如图:

SimpleDataFormat 非线程安全_第2张图片  

从打印的结果来看,使用单例的类 SimpleDateFormat 在多线程环境中处理日期极易出现转换错误的情况。 

甚至由于竞争导致解析逻辑的数字处理部分冲突,控制台照成了错误输出 。

SimpleDataFormat 非线程安全_第3张图片

2.解决方法1 

第一种解决办法的原理是满足竞争,创建多个类 SimpleDateFormat 的实例。 

package org.example.SimpleDataFormat;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class formatOK1 {

    static class DateTools{

        public static Date parse(String formatPattern, String dateString) throws ParseException {
            return new SimpleDateFormat(formatPattern).parse(dateString);
        }


        public static String format(String formatPattern, Date date) {
            return new SimpleDateFormat(formatPattern).format(date);
        }
    }
    static class MyThread extends Thread {
        private SimpleDateFormat sdf;
        private String dateString;

        public MyThread(SimpleDateFormat sdf, String dateString) {
            this.sdf = sdf;
            this.dateString = dateString;
        }

        @Override
        public void run() {
            try {
                Date dateRef = DateTools.parse("yyyy-MM-dd",dateString);
                String newDataString = DateTools.format("yyyy-MM-dd",dateRef);
                if (!newDataString.equals(dateString)) {
                    System.out.println("ThreadName=" + this.getName()
                            + "报错了 日期字符串:" + dateString + " 转换成的日期为:"
                            + newDataString);
                }
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String[] dateStringArray = new String[]{"2000-01-01",
                "2000-01-02","2000-01-03","2000-01-04",
                "2000-01-05","2000-01-06","2000-01-07",
                "2000-01-08","2000-01-09","2000-01-10",
        };
        MyThread[] threads = new MyThread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new MyThread(sdf,dateStringArray[i]);
        }
        for (int i = 0; i < 10; i++) {
            threads[i].start();
        }
    }
}

运行结果如图:

SimpleDataFormat 非线程安全_第4张图片

控制台没有异常信息输出。

3.解决方法2 

还有一种更简单的方法,那就是使用 ThreadLocal 包装SimpleDateFormat。ThreadLocal 可以为每个线程提供一个单独的 SimpleDateFormat 实例,能使线程绑定到指定对象。使用该类也可以解决多线程环境中类 SimpleDateFormat 处理日期时出现错误的问题。

package org.example.SimpleDataFormat;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class formatOK2 {

    static class DateTools {
        private static ThreadLocal t1 = new ThreadLocal<>();

        public static SimpleDateFormat getSimpleDateFormat(String datePattern) {
            SimpleDateFormat sdf = null;
            sdf = t1.get();
            if (sdf == null) {
                sdf = new SimpleDateFormat(datePattern);
                t1.set(sdf);
            }
            return sdf;
        }
    }

    static class MyThread extends Thread {
        private SimpleDateFormat sdf;
        private String dateString;

        public MyThread(SimpleDateFormat sdf, String dateString) {
            this.sdf = sdf;
            this.dateString = dateString;
        }

        @Override
        public void run() {
            try {
                Date dateRef = DateTools.getSimpleDateFormat("yyyy-MM-dd").parse(dateString);
                String newDataString = DateTools.getSimpleDateFormat("yyyy-MM-dd")
                        .format(dateRef);
                if (!newDataString.equals(dateString)) {
                    System.out.println("ThreadName=" + this.getName()
                            + "报错了 日期字符串:" + dateString + " 转换成的日期为:"
                            + newDataString);
                }
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String[] dateStringArray = new String[]{"2000-01-01",
                "2000-01-02", "2000-01-03", "2000-01-04",
                "2000-01-05", "2000-01-06", "2000-01-07",
                "2000-01-08", "2000-01-09", "2000-01-10",
        };
        MyThread[] threads = new MyThread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new MyThread(sdf, dateStringArray[i]);
        }
        for (int i = 0; i < 10; i++) {
            threads[i].start();
        }
    }
}

运行结果:

SimpleDataFormat 非线程安全_第5张图片

控制台没有异常信息输出,说明 ThreadLocal 解决了 SimpleDateFormat 非线程安全问题。  


总结

 加油!!!!

你可能感兴趣的:(多线程,多线程,java)