线程安全,作何解?
多线程环境中,当涉及到共享数据时候,就要考虑线程安全问题。要进行同步,就是加锁,关键字synchronized 。就是同一时刻只能有一个线程访问共享资源。
举例说明,引用来自
http://www.importnew.com/23010.html?replytocom=539047#respond
高并发情况下,使用工具类
public class DateUtil {
private DateUtil(){}
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static Date parse(String date) throws ParseException {
return DATE_FORMAT.parse(date);
}
由此而造成异常
java.lang.NumberFormatException: For input string: ""
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
at java.lang.Long.parseLong(Long.java:431)
at java.lang.Long.parseLong(Long.java:468)
at java.text.DigitList.getLong(DigitList.java:177)
at java.text.DecimalFormat.parse(DecimalFormat.java:1297)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1589)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1311)
at java.text.DateFormat.parse(DateFormat.java:335)
at com.xxxxxxx.core.common.util.DateUtil.parseTimestamp(DateUtil.java:95)
at com.xxxxxxx.core.common.util.DateUtil.parse(DateUtil.java:84)
at com.xxxxxxx.hbase.generator.LogRowKeyGenerator.generate(LogRowKeyGenerator.java:21)
... 22 more
那么为什么出现这个问题??
查看SimpleDateFormat的源码会发现,最前面的注释
* Date formats are not synchronized.
* It is recommended to create separate format instances for each thread.
* If multiple threads access a format concurrently, it must be synchronized
* externally.
翻译过来就是
日期格式化的类是非同步的,建议为每一个线程创建独立的格式化实例。如果多个线程并发访问同一个格式化实例,就必须在外部添加同步机制
说明这样做是线程不安全的(把DateFormat DATE_FORMAT = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”) 作为了全局变量,是一个共享资源)。
所以正确做法是
public class DateUtil {
private DateUtil(){}
public static Date parse(String date) throws ParseException {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(date);
}
}
总结
把一个非同步 *Date formats are not synchronized的作为全局变量,也就是一个共享资源,没有加上锁,在高并发的情况下,出现意想不到的bug。现在的改法是把其变成非共享的资源,也就是为每一个线程创建独立的格式化实例,这样就不用使用锁来解决了。
提示
时间相关的操作建议使用Java 8新添加的Time包
共享对象时, 基本都会去看看类是不是线程安全的。谷歌guava库, joda-time, jackson这些常用的工具包, 在类的注释上都会标明是不是thread safe