博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java线程不安全类 SimpleDateFormat
阅读量:7235 次
发布时间:2019-06-29

本文共 3284 字,大约阅读时间需要 10 分钟。

不安全在什么地方?

前段时间在做系统数据清洗过程中,因为用到多线程及simpeldateformat,一开始没注意,遇到了线程安全问题,就在此描述解决办法。

// 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);    ....                                    }private void subFormat(int patternCharIndex, int count,                           FieldDelegate delegate, StringBuffer buffer,                           boolean useDateFormatSymbols)    {        int     maxIntCount = Integer.MAX_VALUE;        String  current = null;        int     beginOffset = buffer.length();        int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];        int value;        if (field == CalendarBuilder.WEEK_YEAR) {            if (calendar.isWeekDateSupported()) {                value = calendar.getWeekYear();//取值            } else {                // use calendar year 'y' instead                patternCharIndex = PATTERN_YEAR;                field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];                value = calendar.get(field);            }                    ...复制代码

可以看到在format代码中,将要被格式化的date设置到calendar实例中,这个实例是simpledateforamt的一个局部变量。

将设A线程调用了format,此时将A线程的(假设是2018-01-05 00:00:00)这个时间set到calendar,线程刮起,线程B进来,调用该方法(假设要格式的时间是2017-12-38 10:00:05),将该时间set到calendar。此时B线程挂起,A线程执行,通过subFormat方法获取格式化的数据,但是此时的calender里的信息是B线程的,所以这种情况,format的结果均为B线程的数据。这就是该方法线程不安全的地方。

下面是个测试方法,来体现该类线程不安全

public class Main {	@Test	public void testSimpleDateFormatThreadSafe() throws ParseException, InterruptedException {		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");		long now = System.currentTimeMillis();		long one = 1000 * 60 * 60 * 24;		long[] times = { now, now - one, now - one * 2, now - one * 3, now - one * 4, now - one * 5, now - one * 6,				now - one * 7, now - one * 8, now - one * 9, now - one * 10, now - one * 11, now - one * 12,				now - one * 13, now - one * 14, now - one * 15, now - one * 16, now - one * 17, now - one * 18,				now - one * 19, now - one * 20, now - one * 21, now - one * 22, now - one * 23, now - one * 24,				now - one * 25, now - one * 26, now - one * 27, now - one * 28, now - one * 29 };		ExecutorService pool = Executors.newFixedThreadPool(5);		for (int i = 0; i < 30; i++) {			pool.execute(new Work(sdf, times[i]));			// System.out.println(sdf.format(new Date(times[i])));//07		}		 Thread.sleep(10000);	}	public class Work implements Runnable {		SimpleDateFormat sdf;		long date;		public Work(SimpleDateFormat sdf, long date) {			this.sdf = sdf;			this.date = date;		}		@Override		public void run() {			System.out.println(sdf.format(new Date(date)));		}	}}复制代码

如何让它变的安全呢?

可以使用java的ThreadLocal类,该类会为每个线程实例化一个类,这样多线程之间就没有竞争对象了。这个类的原理很简单,其内部维护了一个map,key是线程的名字,value就是实例化对象。

ThreadLocal
safe = new ThreadLocal() { @Override protected Object initialValue() { return new SimpleDateFormat("yyyy-MM-dd"); } };pool.execute(new Work(safe.get(),times[i]));//线程安全复制代码

通过safe直接get得到的simpledateformat的对象,就是为每个线程独立创建的。这种做法在我们熟悉的tomcat为每个单独的访问保存其专有信息,就是使用的这种方法。

如果觉得为每个线程都实例化对象开销比较大或者造成gc,也可以维护一个对象池。

实例代码:https://github.com/yangzhenkun/learn/blob/master/src/main/java/com/yasin/threadsafe/Main.java

转载地址:http://wpgfm.baihongyu.com/

你可能感兴趣的文章
Cesium原理篇:3D Tiles(1)渲染调度
查看>>
neuroph Perceptron Sample
查看>>
关于navicat连接oracle 报 ORA-12737 set CHS16GBK错误的解决方案
查看>>
MEP自定义参数化风机盘管族
查看>>
(android控件)巧用background属性,实现图片可选择效果
查看>>
获取APK文件的签名信息,反射实现
查看>>
Hive On Spark hiveserver2方式使用
查看>>
如何在Android开发中让你的代码更有效率
查看>>
Visual Studio项目的生成事件代码
查看>>
android的消息处理机制(图文+源码分析)—Looper/Handler/Message[转]
查看>>
lightswitch conditional formatting change color
查看>>
發行項帶篩選的合併複製問題之----訂閱者更新導致部份數據丟失
查看>>
Linux/Ubuntu sudo不用输入密码的方法
查看>>
Kinect for Windows 正式发布
查看>>
不适合做管理的人zz
查看>>
数据库索引
查看>>
控件:ScrollView --- 滚动视图
查看>>
Synthetic
查看>>
HDU-1203 I NEED A OFFER! 动态规划 01背包
查看>>
C# WinForm 多线程使用委托操作窗体控件
查看>>