SimpleDateFormat线程不安全(转)
有三种方法可以解决以上安全问题。
1).使用同步
1 package com.bijian.study.date; 2 3 import java.text.ParseException; 4 import java.text.SimpleDateFormat; 5 import java.util.Date; 6 7 public class DateUtil02 implements DateUtilInterface { 8 9 private SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 10 11 @Override 12 public void format(Date date) { 13 System.out.println(dateformat.format(date)); 14 } 15 16 @Override 17 public void parse(String str) { 18 try { 19 synchronized(dateformat){ 20 System.out.println(dateformat.parse(str)); 21 } 22 } catch (ParseException e) { 23 e.printStackTrace(); 24 } 25 } 26 }
修改DateMainTest.java的DateUtilInterface dateUtil = new DateUtil01();为DateUtilInterface dateUtil = new DateUtil02();测试OK。
不过,当线程较多时,当一个线程调用该方法时,其他想要调用此方法的线程就要block,这样的操作也会一定程度上影响性能。
2).每次使用时,都创建一个新的SimpleDateFormat实例
1 package com.bijian.study.date; 2 3 import java.text.ParseException; 4 import java.text.SimpleDateFormat; 5 import java.util.Date; 6 7 public class DateUtil03 implements DateUtilInterface { 8 9 @Override 10 public void format(Date date) { 11 12 SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 13 System.out.println(dateformat.format(date)); 14 } 15 16 @Override 17 public void parse(String str) { 18 try { 19 SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 20 System.out.println(dateformat.parse(str)); 21 } catch (ParseException e) { 22 e.printStackTrace(); 23 } 24 } 25 }
修改DateMainTest.java的DateUtilInterface dateUtil = new DateUtil02();为DateUtilInterface dateUtil = new DateUtil03();测试OK。
3).借助ThreadLocal对象每个线程只创建一个实例
借助ThreadLocal对象每个线程只创建一个实例,这是推荐的方法
对于每个线程SimpleDateFormat不存在影响他们之间协作的状态,为每个线程创建一个SimpleDateFormat变量的拷贝或者叫做副本,代码如下:
1 package com.bijian.study.date; 2 3 import java.text.DateFormat; 4 import java.text.ParseException; 5 import java.text.SimpleDateFormat; 6 import java.util.Date; 7 8 public class DateUtil04 implements DateUtilInterface { 9 10 private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; 11 12 // 第一次调用get将返回null 13 private static ThreadLocal threadLocal = new ThreadLocal(){ 14 protected Object initialValue() { 15 return null;//直接返回null 16 } 17 }; 18 19 // 获取线程的变量副本,如果不覆盖initialValue,第一次get返回null,故需要初始化一个SimpleDateFormat,并set到threadLocal中 20 public static DateFormat getDateFormat() { 21 DateFormat df = (DateFormat) threadLocal.get(); 22 if (df == null) { 23 df = new SimpleDateFormat(DATE_FORMAT); 24 threadLocal.set(df); 25 } 26 return df; 27 } 28 29 @Override 30 public void parse(String textDate) { 31 32 try { 33 System.out.println(getDateFormat().parse(textDate)); 34 } catch (ParseException e) { 35 e.printStackTrace(); 36 } 37 } 38 39 @Override 40 public void format(Date date) { 41 System.out.println(getDateFormat().format(date)); 42 } 43 }
创建一个ThreadLocal类变量,这里创建时用了一个匿名类,覆盖了initialValue方法,主要作用是创建时初始化实例。
也可以采用下面方式创建。
1 package com.bijian.study.date; 2 3 import java.text.DateFormat; 4 import java.text.ParseException; 5 import java.text.SimpleDateFormat; 6 import java.util.Date; 7 8 public class DateUtil05 implements DateUtilInterface { 9 10 private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; 11 12 @SuppressWarnings("rawtypes") 13 private static ThreadLocal threadLocal = new ThreadLocal() { 14 protected synchronized Object initialValue() { 15 return new SimpleDateFormat(DATE_FORMAT); 16 } 17 }; 18 19 public DateFormat getDateFormat() { 20 return (DateFormat) threadLocal.get(); 21 } 22 23 @Override 24 public void parse(String textDate) { 25 26 try { 27 System.out.println(getDateFormat().parse(textDate)); 28 } catch (ParseException e) { 29 e.printStackTrace(); 30 } 31 } 32 33 @Override 34 public void format(Date date) { 35 System.out.println(getDateFormat().format(date)); 36 } 37 }
修改DateMainTest.java的DateUtilInterface dateUtil = new DateUtil03();为DateUtilInterface dateUtil = new DateUtil04();或者DateUtilInterface dateUtil = new DateUtil05();测试OK。
最后,当然也可以使用apache commons-lang包的DateFormatUtils或者FastDateFormat实现,apache保证是线程安全的,并且更高效。
附:Oracle官方bug说明: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6178997
转自 http://bijian1013.iteye.com/blog/1873336