【Java线程】SimpleDateFormat的线程安全性实验二

【实验目的】

从SimpleDateFormat着手来探讨怎样的代码会导致线程不安全。

 

【代码】

用于将java.util.Date变成时间字符串的DateUtil类:

复制代码
package unsafesdf2;

import java.text.SimpleDateFormat;
import java.util.Date;

public class DateUtil {
    private static SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
    
    public static String format(Date date) {
        return sdf.format(date);
    }
}
复制代码

 

使用无限循环取日期的测试线程类:

复制代码
package unsafesdf2;

import java.util.Date;

public class TestThread extends Thread{
    private Date date;
    
    private String expect;
    
    public TestThread(Date date) {
        this.date=date;
        this.expect=DateUtil.format(date);
    }
    
    public void run() {
        while(true) {
            String actual=DateUtil.format(date);
            if(actual.equals(expect)==false) {
                System.out.println("expect:"+expect+" actual:"+actual+" @"+this.getName());
            }
        }
    }
}
复制代码

 

程序启动类:

复制代码
package unsafesdf2;

import java.util.Calendar;

public class Test {
    public static void main(String[] args) {
        Calendar clder=Calendar.getInstance();
        
        clder.set(2022, 7-1, 23);        
        TestThread th1=new TestThread(clder.getTime());
        th1.setName("线程一");
        
        clder.set(2022, 8-1, 24);        
        TestThread th2=new TestThread(clder.getTime());
        th2.setName("线程二");
        
        th1.start();
        th2.start();
    }
}
复制代码

 

【程序输出节选】

复制代码
expect:2022-08-24 actual:2022-08-23 @线程二
expect:2022-07-23 actual:2022-08-24 @线程一
expect:2022-08-24 actual:2022-08-23 @线程二
expect:2022-07-23 actual:2022-08-24 @线程一
expect:2022-08-24 actual:2022-07-23 @线程二
expect:2022-07-23 actual:2022-07-24 @线程一
expect:2022-08-24 actual:2022-08-23 @线程二
expect:2022-07-23 actual:2022-07-24 @线程一
expect:2022-08-24 actual:2022-07-23 @线程二
复制代码

点评:

在Test类代码中,线程一被赋予了2022-07-23的时间,而线程二被赋予了2022-08-24的时间。

但实际输出中发现,除了年份都是2022没有发生错乱,月份日子都乱了。

需要注意的是,编写TestThread时是知道会发生错乱而把实际和预期不一致的情况输出,而在实际生产中,如果不知道可能发生并发赋值问题或是忽略了,出错了一段事件后才会由人工觉察出来,但此时很多入库的紊乱日期已经不可能还原了。

 

【原因】

SimpleDateFormat有一个类变量canlendar,在格式化日期时,canlendar变量会首先设置为用户的输入值。

这样当多个线程同时调用format函数,就有可能引发calendar并发赋值问题,即线程A处理完毕准备返回canlendar变量的值时,线程B闯入修改了canlendar变量的值,导致线程A取得了线程B带入的新值。

 

【什么样的代码会导致并发赋值问题】

void func(T value){

    T v=value;

    processV....

    return v;

}

只要类似上面的,有从参数取值,处理值,返回值三步的,在多线程环境下,都有可能出问题。

即使是i++,++i这类代码,其内部实现也是分取值,设值,返回三步的,一样存在并发赋值问题。

总结下来就是:分取值,设值,返回三步曲的函数,如果不设置并发锁,任由线程进入的话,就存在并发赋值问题。

 

【参考资料】

《Java系统性能优化实战》P56-57 李家智 张世敏著 电子工业出版社出版。

END

posted @   逆火狂飙  阅读(59)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
历史上的今天:
2016-08-23 启用CentOS6.5 64位安装时自带的MySQL数据库服务器
2016-08-23 【Canvas与艺术】环形橄榄枝纹饰
2016-08-23 如何修改MyEclipse项目的web context-root
2016-08-23 【高中数学/基本不等式】已知:正实数a,b皆大于0,且a+b=1 求:1/a+1/ab的最小值
2013-08-23 如何解决Win7将任务栏程序自动分组的困扰
2013-08-23 安装Ubuntu 桌面版 12.04 LTS 过程之记录
2013-08-23 #lspci | grep Eth
生当作人杰 死亦为鬼雄 至今思项羽 不肯过江东
点击右上角即可分享
微信分享提示