非线程安全的类-SimpleDateFormat
类SimpleDateFormat主要负责日期的转换与格式化,但在多线程的环境中,使用此类容易造成数据转换及处理的不正确,因为SimpleDateFormat并不是线程安全的,我们看一段代码:
package chapter7.testsimpledateformat; import java.text.SimpleDateFormat; import java.util.Date; public 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 { super.run(); Date dateRef = sdf.parse(dateString); String newDateStr = sdf.format(dateRef).toString(); if(!newDateStr.equals(dateString)) { System.out.println("ThreadName="+this.getName()+"报错了 日期字符串:"+dateString+" 转换成的日期为:"+newDateStr); } } catch (Exception e) { e.printStackTrace(); } } }
package chapter7.testsimpledateformat; import java.text.SimpleDateFormat; public class Test { public static void main(String[] args) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String[] dateStr = 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, dateStr[i]); } for(int i=0;i<10;i++) { threads[i].start(); } } }
运行结果:
ThreadName=Thread-5报错了 日期字符串:2000-01-06 转换成的日期为:2000-01-02
ThreadName=Thread-6报错了 日期字符串:2000-01-07 转换成的日期为:2000-01-01
ThreadName=Thread-2报错了 日期字符串:2000-01-03 转换成的日期为:2000-01-05
ThreadName=Thread-3报错了 日期字符串:2000-01-04 转换成的日期为:0010-01-10
ThreadName=Thread-4报错了 日期字符串:2000-01-05 转换成的日期为:2000-01-10
解决办法1:
package chapter7.testsimpledateformat; import java.util.Date; public class MyThread extends Thread{ private String dateString; public MyThread(String dateString) { this.dateString = dateString; } @Override public void run() { try { super.run(); Date dateRef = DateTools.parse(dateString); String newDateStr = DateTools.format(dateRef); if(!newDateStr.equals(dateString)) { System.out.println("ThreadName="+this.getName()+"报错了 日期字符串:"+dateString+" 转换成的日期为:"+newDateStr); } } catch (Exception e) { e.printStackTrace(); } } }
package chapter7.testsimpledateformat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class DateTools { private static final String DATE_FORMAT = "yyyy-MM-dd"; public static Date parse(String dateStr) throws ParseException { return new SimpleDateFormat(DATE_FORMAT).parse(dateStr); } public static String format(Date date){ return new SimpleDateFormat(DATE_FORMAT).format(date); } }
package chapter7.testsimpledateformat; public class Test { public static void main(String[] args) { try { String[] dateStr = 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(dateStr[i]); } for(int i=0;i<10;i++) { threads[i].start(); } } catch (Exception e) { e.printStackTrace(); } } }
运行结果:没有打印错误,说明问题解决,解决处理错误的原理其实就是创建多个SimpleDateFormat。
解决方法2:
package chapter7.testsimpledateformat; import java.util.Date; public class MyThread extends Thread{ private String dateString; public MyThread(String dateString) { this.dateString = dateString; } @Override public void run() { try { super.run(); Date dateRef = DateTools.getSimpleDateFormat().parse(dateString); String newDateStr = DateTools.getSimpleDateFormat().format(dateRef); if(!newDateStr.equals(dateString)) { System.out.println("ThreadName="+this.getName()+"报错了 日期字符串:"+dateString+" 转换成的日期为:"+newDateStr); } } catch (Exception e) { e.printStackTrace(); } } }
package chapter7.testsimpledateformat; import java.text.SimpleDateFormat; public class DateTools { private static final String DATE_FORMAT = "yyyy-MM-dd"; private static ThreadLocal<SimpleDateFormat> tlLocal = new ThreadLocal<SimpleDateFormat>(); public static SimpleDateFormat getSimpleDateFormat() { SimpleDateFormat sdf = null; sdf = tlLocal.get(); if(sdf == null) { sdf = new SimpleDateFormat(DATE_FORMAT); tlLocal.set(sdf); } return sdf; } }
运行结果:没有输出错误,说明用ThreadLocal可以解决问题,ThreadLocal类能使线程绑定到指定的对象。