您现在的位置是:群英 > 开发技术 > 编程语言
线程不安全的原因是什么,怎样解决?
Admin发表于 2022-02-07 18:19:10662 次浏览

    这篇文章我们来了解为什么说线程不安全的原因,下文介绍了SimpleDateFormat线程安全测试及线程安全解决方法,下文有详细的介绍,有需要的朋友可以参考,接下来就跟随小编来一起学习一下吧!

    本教程操作环境: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

    现在对于为什么说线程不安全大家应该都清楚了吧,希望大家阅读完这篇文章能有所收获。最后,想要了解更多Java线程的相关内容,大家可以关注群英网络其它相关文章。

文本转载自PHP中文网

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

标签: 线程不安全
相关信息推荐
2022-06-06 17:11:17 
摘要:下面由golang教程栏目给大家详解Go 中方法与函数的区别,希望对需要的朋友有所帮助!这篇文章将介绍Go中函数和方法之间的主要区别,以及如何最佳使用。
2022-01-18 17:59:59 
摘要:这篇文章我们来了解反射的相关内容,有一些朋友在学习Java时,对于什么是反射,反射的功能及具体的使用这些不是很了解,对此这篇就给大家来详细的介绍一下,对大家学习和理解Java反射机制会有帮助。
2022-01-30 17:26:45 
摘要:HTML中字体删除线的效果怎样做?字体删除线的效果也就是字体横向中间带有横向,可以用于一些要删除但又看用户看到的内容,例如产品的原价场景应用,小编觉得挺实用的,因此分享给大家做个参考,文中的示例代码介绍得很详细,有需要的朋友可以参考,接下来就跟随小编一起了解看看吧。
云活动
推荐内容
热门关键词
热门信息
群英网络助力开启安全的云计算之旅
立即注册,领取新人大礼包
  • 联系我们
  • 24小时售后:4006784567
  • 24小时TEL :0668-2555666
  • 售前咨询TEL:400-678-4567

  • 官方微信

    官方微信
Copyright  ©  QY  Network  Company  Ltd. All  Rights  Reserved. 2003-2019  群英网络  版权所有   茂名市群英网络有限公司
增值电信经营许可证 : B1.B2-20140078   粤ICP备09006778号
免费拨打  400-678-4567
免费拨打  400-678-4567 免费拨打 400-678-4567 或 0668-2555555
微信公众号
返回顶部
返回顶部 返回顶部