首页后端开发其他后端知识线程不安全的原因是什么,怎样解决?

线程不安全的原因是什么,怎样解决?

时间2024-03-28 18:30:03发布访客分类其他后端知识浏览926
导读:这篇文章我们来了解为什么说线程不安全的原因,下文介绍了SimpleDateFormat线程安全测试及线程安全解决方法,下文有详细的介绍,有需要的朋友可以参考,接下来就跟随小编来一起学习一下吧! 本教程操作环境:windows7系统、java...

这篇文章我们来了解为什么说线程不安全的原因,下文介绍了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 ThreadLocalSimpleDateFormat>
     THREAD_LOCAL = new ThreadLocalSimpleDateFormat>
() {

        @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中文网

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!


若转载请注明出处: 线程不安全的原因是什么,怎样解决?
本文地址: https://pptw.com/jishu/655120.html
Java中static关键字有什么作用,怎样理解? Jquery中绑定事件有几种方法,怎么理解

游客 回复需填写必要信息