温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

simpledateformat线程不安全的原因

发布时间:2021-05-10 15:04:05 来源:亿速云 阅读:411 作者:小新 栏目:编程语言

这篇文章将为大家详细讲解有关simpledateformat线程不安全的原因,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

原因:在多线程环境下,当多个线程同时使用相同的SimpleDateFormat对象(如static修饰)的话,如调用format方法时,多个线程会同时调用calender.setTime方法,导致time被别的线程修改,因此线程是不安全的。

本教程操作环境:windows7系统、java8版、DELL G3电脑。

线程不安全验证:

/**  * SimpleDateFormat线程安全测试  * 〈功能详细描述〉  *  * @author 17090889  * @see [相关类/方法](可选)  * @since [产品/模块版本] (可选)  */ public class SimpleDateFormatTest {     private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");     ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 100, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(1000), new MyThreadFactory("SimpleDateFormatTest"));     public void test() {         while (true) {             poolExecutor.execute(new Runnable() {                 @Override                 public void run() {                     String dateString = simpleDateFormat.format(new Date());                     try {                         Date parseDate = simpleDateFormat.parse(dateString);                         String dateString2 = simpleDateFormat.format(parseDate);                         System.out.println(dateString.equals(dateString2));                     } catch (ParseException e) {                         e.printStackTrace();                     }                 }             });         }     }

输出:

  true   false   true   true   false

出现了false,说明线程不安全

1、format方法

public StringBuffer format(Date date, StringBuffer toAppendTo,                                FieldPosition pos)     {         pos.beginIndex = pos.endIndex = 0;         return format(date, toAppendTo, pos.getFieldDelegate());     }     // Called from Format after creating a FieldDelegate     private StringBuffer format(Date date, StringBuffer toAppendTo,                                 FieldDelegate delegate) {         // Convert input date to time field list         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;     }
 protected Calendar calendar;

可以看到,多个线程之间共享变量calendar,并修改calendar。因此在多线程环境下,当多个线程同时使用相同的SimpleDateFormat对象(如static修饰)的话,如调用format方法时,多个线程会同时调用calender.setTime方法,导致time被别的线程修改,因此线程是不安全的。

此外,parse方法也是线程不安全的,parse方法实际调用的是CalenderBuilder的establish来进行解析,其方法中主要步骤不是原子操作。

解决方案:

  1、将SimpleDateFormat定义成局部变量

  2、 加一把线程同步锁:synchronized(lock)

  3、使用ThreadLocal,每个线程都拥有自己的SimpleDateFormat对象副本。如:

/**  * SimpleDateFormat线程安全测试  * 〈功能详细描述〉  *  * @author 17090889  * @see [相关类/方法](可选)  * @since [产品/模块版本] (可选)  */ public class SimpleDateFormatTest {         private static final ThreadLocal<SimpleDateFormat> THREAD_LOCAL = new ThreadLocal<SimpleDateFormat>() {         @Override         protected SimpleDateFormat initialValue() {             return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");         }     };     //    private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");     ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 100, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(1000), new MyThreadFactory("SimpleDateFormatTest"));     public void test() {         while (true) {             poolExecutor.execute(new Runnable() {                 @Override                 public void run() {                     SimpleDateFormat simpleDateFormat = THREAD_LOCAL.get();                     if (simpleDateFormat == null) {                         simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");                     }                     String dateString = simpleDateFormat.format(new Date());                     try {                         Date parseDate = simpleDateFormat.parse(dateString);                         String dateString2 = simpleDateFormat.format(parseDate);                         System.out.println(dateString.equals(dateString2));                     } catch (ParseException e) {                         e.printStackTrace();                     } finally {                         local.remove();                     }                 }             });         }     } }

  4、使用DateTimeFormatter代替SimpleDateFormat

  DateTimeFormatter是线程安全的,默认提供了很多格式化方法,也可以通过ofPattern方法创建自定义格式化方法。

  (1)格式化日期示例:

 LocalDateTime localDateTime = LocalDateTime.now();  System.out.println(localDateTime); // 2019-11-20T15:04:29.017  DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");  String strDate=localDateTime.format(dtf);  System.out.println(strDate); // 2019/23/20 15:23:46

  (2)解析日期

 DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");  LocalDateTime localDateTime=LocalDateTime.parse("2019/11/20 15:23:46",dtf);  System.out.println(localDateTime); // 2019-11-20T15:23:46

关于“simpledateformat线程不安全的原因”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI