public class TimeUtils { public static String formatDate(Date date) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.format(date); } public static Date parse(String strDate) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); try { return sdf.parse(strDate); } catch (ParseException e) { e.printStackTrace(); } return null; } |
每次调用方都要new SimpleDateFormat(),每次处理一个时间信息的时候,就须要建立一个SimpleDateFormat实例对象,而后再丢弃这个对象。大量的对象就这样被建立出来,占用大量的内存和 jvm空间安全
因而 那我就建立一个静态的simpleDateFormat实例多线程
public class TimeUtils { private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static String formatDate(Date date) { return sdf.format(date); } public static Date parse(String strDate) { try { return sdf.parse(strDate); } catch (ParseException e) { e.printStackTrace(); } return null; } |
可是问题是:线程不安全,在format方法里,有这样一段代码:app
private StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) { calendar.setTime(date); boolean useDateFormatSymbols = useDateFormatSymbols(); for (int i = 0; i < compiledPattern.length; ) { int tag = compiledPattern[i] >>> 8; int count = compiledPattern[i++] & 0xff; if (count == 255) { count = compiledPattern[i++] << 16; count |= compiledPattern[i++]; } switch (tag) { case TAG_QUOTE_ASCII_CHAR: toAppendTo.append((char)count); break; case TAG_QUOTE_CHARS: toAppendTo.append(compiledPattern, i, count); i += count; break; default: subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols); break; } } return toAppendTo; } |
calendar不是方法局部变量而是SimpleDateFormat类的全局变量,而这就是引起问题的根源。想象一下,在一个多线程环境下,有两个线程持有了同一个SimpleDateFormat的实例,分别调用format方法:
线程1调用format方法,改变了calendar这个字段。
中断来了。
线程2开始执行,它也改变了calendar。
又中断了。
线程1回来了,此时,calendar已然不是它所设的值,而是走上了线程2设计的道路。若是多个线程同时争抢calendar对象,则会出现各类问题,时间不对,线程挂死等等。
分析一下format的实现,咱们不难发现,用到成员变量calendar,惟一的好处,就是在调用subFormat时,少了一个参数,却带来了这许多的问题。其实,只要在这里用一个局部变量,一路传递下去,全部问题都将迎刃而解。
1 SimpleDateFormat做为线程局部变量来使用。2使用对象锁,代码以下。3使用jodaTime(推荐)jvm
public class TimeUtils { private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static String formatDate(Date date) { synchronized(sdf){ return sdf.format(date); } } public static Date parse(String strDate) { synchronized(sdf){ try { return sdf.parse(strDate); } catch (ParseException e) { e.printStackTrace(); } return null; } } |
public class TimeUtils { public static String formatDate(Date date) { DateTime dateTime = new DateTime(date); String formatStr = "yyyy-MM-dd HH:mm:ss"; return dateTime.toString(formatStr); } public static Date parse(String strDate) { DateTimeFormatter dateTimeFormat = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"); DateTime dateTime = DateTime.parse(strDate, dateTimeFormat); dateTime = dateTime.plusDays(1); return dateTime.toDate(); } } |