SimpleDateFormat是线程不安全的,不能多个线程共用。而FastDateFormat和Joda-Time都是线程安全的。
SimpleDateFormat是JDK提供的,不需要依赖第三方jar包,而其他两种都得依赖第三方jar包。FastDateFormat是apache的commons-lang3包提供的:
1 2 3 4 5 <dependency > <groupId > org.apache.commons</groupId > <artifactId > commons-lang3</artifactId > <version > 3.8.1</version > </dependency >
Joda-Time需要依赖以下maven的配置。
1 2 3 4 5 <dependency > <groupId > joda-time</groupId > <artifactId > joda-time</artifactId > <version > 2.10.1</version > </dependency >
SimpleDateFormat 和 FastDateFormat 主要都是对时间的格式化。
SimpleDateFormat在对时间进行格式化的方法format中,会先对 calendar 对象进行 setTime 的赋值,若是有多个线程同时操作一个 SimpleDateFormat 实例的话,就会对calendar的赋值进行覆盖,进而产生问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 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 是java.text.DateFormat中的属性
1 2 3 4 5 6 protected Calendar calendar;
一个SimpleDateFormat实例的 calendar 属性是多线程共享的。
有三种方法可以解决这个问题:
在每次需要使用的时候,进行SimpleDateFormat实例的创建,这种方式会导致创建多个对象实例,占用内存,不建议使用。
使用同步的方式,在调用方法的时候加上synchronized,这样可以让线程调用方法时,进行加锁,也就是会造成线程间的互斥,但对性能影响比较大。
使用ThreadLocal进行保存,相当于一个线程只会有一个实例,进而减少了实例数量,也防止了线程间的互斥,推荐使用这种方式。
FastDateFormat 是线程安全的,可以直接使用,不必考虑多线程的情况
FastDateFormat的实例是通过getInstance方法创建的。
1 2 FastDateFormat format = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss" ); System.out.println(format.format(new Date ()));
可以使用 org.apache.commons.lang3.time.DateFormatUtils 类来操作(也是commons.lang3提供的),方法里面也是使用的FastDateFormat类来实现的:
1 2 3 4 public static String format (final Date date, final String pattern, final TimeZone timeZone, final Locale locale) { final FastDateFormat df = FastDateFormat.getInstance(pattern, timeZone, locale); return df.format(date); }
使用方式
1 System.out.println(DateFormatUtils.format(new Date (), "yyyy-MM-dd HH:mm:ss" ));
Joda-Time Joda-Time 与以上两种有所区别,不仅仅可以对时间进行格式化输出,而且可以生成瞬时时间值,并与Calendar、Date等对象相互转化,极大的方便了程序的兼容性。
官网地址 https://www.joda.org/joda-time/
Joda-Time的类具有不可变性,因此他们的实例是无法修改的,就跟String的对象一样。这种不可变性提现在所有API方法中,这些方法返回的都是新的类实例,与原来实例不同。
以下是 Joda-Time 的一些使用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 Date currentDate = new Date (); DateTime dateTime = new DateTime (); System.out.println(currentDate.getTime()); System.out.println(dateTime.getMillis()); Date oneDate = new Date (1472457422728L ); DateTime oneDateTime = new DateTime (1472457422728L ); DateTime oneDateTime1 = new DateTime (2016 , 8 , 29 , 15 , 57 , 2 , 728 ); System.out.println(oneDate.toString()); System.out.println(oneDateTime.toString()); System.out.println(oneDateTime1.toString("MM/dd/yyyy hh:mm:ss.SSSa" )); Date convertDate = new Date (); DateTime dt1 = new DateTime (convertDate); System.out.println(dt1.toString()); Date d1 = dt1.toDate(); System.out.println(d1.toString()); Calendar c1 = Calendar.getInstance(); DateTime dt2 = new DateTime (c1); System.out.println(dt2.toString()); Calendar c2 = dt2.toCalendar(null ); System.out.println(c2.getTimeZone()); DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss" ); DateTime dt3 = DateTime.parse("2016-08-29 13:32:33" , formatter); System.out.println(dt3.toString()); DateTime dt4 = DateTime.parse("2016-08-29T" ); System.out.println(dt4.toString()); System.out.println(new DateTime ().toString("yyyy年MM月dd日 HH:mm:ss EE" , Locale.CHINESE)); LocalDate start = new DateTime ().toLocalDate(); LocalDate end = new LocalDate (2016 , 8 , 25 ); System.out.println(Days.daysBetween(start ,end).getDays()); DateTime dt5 = new DateTime (); dt5 = dt5.plusYears(1 ) .plusMonths(1 ) .plusDays(1 ) .minusHours(1 ) .minusMinutes(1 ) .minusSeconds(1 ); System.out.println(dt5.toString()); DateTime dt6 = new DateTime (); DateTime.Property month = dt6.monthOfYear(); System.out.println(month.isLeap());