【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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需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