锁定老帖子 主题:ThreadLocal示例
精华帖 (2) :: 良好帖 (13) :: 新手帖 (13) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-09-07
本文借花献佛,引用Tim Cull的博文“SimpleDateFormat: Performance Pig”介绍下ThreadLocal的简单使用,同时也对SimpleDateFormat的使用有个深入的了解。 Tim Cull 写道
Just yesterday I came across this problem “in the wild” for the third time in my career so far: an application with performance problems creating tons of java.text.SimpleDateFormat instances. So, I have to get this out there: creating a new instance of SimpleDateFormat is incredibly expensive and should be minimized. In the case that prompted this post, I was using JProfiler to profile this code that parses a CSV file and discovered that 50% of the time it took to suck in the file and make 55,000 objects out of it was spent solely in the constructor of SimpleDateFormat. It created and then threw away a new one every time it had to parse a date. Whew!
“Great,” you think, “I’ll just create one, static instance, slap it in a field in a DateUtils helper class and life will be good.” Well, more precisely, life will be good about 97% of the time. A few days after you roll that code into production you’ll discover the second cool fact that’s good to know: SimpleDateFormat is not thread safe. Your code will work just fine most of the time and all of your regression tests will probably pass, but once your system gets under a production load you’ll see the occasional exception. “Fine,” you think, “I’ll just slap a ’synchronized’ around my use of that one, static instance.” Ok, fine, you could do that and you’d be more or less ok, but the problem is that you’ve now taken a very common operation (date formatting and parsing) and crammed all of your otherwise-lovely, super-parallel application through a single pipe to get it done.
大致意思:Tim Cull碰到一个SimpleDateFormat带来的严重的性能问题,该问题主要有SimpleDateFormat引发,创建一个SimpleDateFormat实例的开销比较昂贵,解析字符串时间时频繁创建生命周期短暂的实例导致性能低下。即使将SimpleDateFormat定义为静态类变量,貌似能解决这个问题,但是SimpleDateFormat是非线程安全的,同样存在问题,如果用‘synchronized’线程同步同样面临问题,同步导致性能下降(线程之间序列化的获取SimpleDateFormat实例)。 Tim Cull使用Threadlocal解决了此问题,对于每个线程SimpleDateFormat不存在影响他们之间协作的状态,为每个线程创建一个SimpleDateFormat变量的拷贝或者叫做副本,代码如下:
import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** * 使用ThreadLocal以空间换时间解决SimpleDateFormat线程安全问题。 * @author * */ public class DateUtil { private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; @SuppressWarnings("rawtypes") private static ThreadLocal threadLocal = new ThreadLocal() { protected synchronized Object initialValue() { return new SimpleDateFormat(DATE_FORMAT); } }; public static DateFormat getDateFormat() { return (DateFormat) threadLocal.get(); } public static Date parse(String textDate) throws ParseException { return getDateFormat().parse(textDate); } }
创建一个ThreadLocal类变量,这里创建时用了一个匿名类,覆盖了initialValue方法,主要作用是创建时初始化实例。也可以采用下面方式创建;
//第一次调用get将返回null private static ThreadLocal threadLocal = new ThreadLocal(); //获取线程的变量副本,如果不覆盖initialValue,第一次get返回null,故需要初始化一个SimpleDateFormat,并set到threadLocal中 public static DateFormat getDateFormat() { DateFormat df = (DateFormat) threadLocal.get(); if(df==null){ df = new SimpleDateFormat(DATE_FORMAT) threadLocal.set(df); } return df; }
我们看下我们覆盖的initialValue方法:
protected T initialValue() { return null;//直接返回null }
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-09-07
嗯,这也是一种直接的解决问题的思路
|
|
返回顶楼 | |
发表时间:2010-09-08
最后修改:2010-09-09
import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; public class DateUtil { private static final String DATE_FORMAT = "yyyy-MM-dd"; private static Map<String, DateFormat> map = new HashMap<String, DateFormat>(); private static String lock = "lock"; public static DateFormat getInstance(String pattern) { DateFormat instance = map.get(pattern); if (instance == null) { synchronized(lock) { // 线程同步,只让一个线程进来实例化 instance = map.get(pattern); // 如果两个线程同时取到的都为null时,其中一个线程实例化后,另一个线程就不需要实例化了 if (instance == null) { instance = new SimpleDateFormat(DATE_FORMAT); map.put(pattern, instance); } } } return instance; } public static DateFormat getDateFormat(String pattern) { return getInstance(pattern); } public static Date parse(String textDate, String pattern) throws ParseException { if (pattern == null || pattern.equals("")) { pattern = DATE_FORMAT; } if (textDate == null || textDate.equals("")) { return null; } return getDateFormat(pattern).parse(textDate); } } 对于各种不同的pattern,又保持多线程下同一个pattern共享同一个实例,以上代码可以实现,不妥之处,欢迎指正。 |
|
返回顶楼 | |
发表时间:2010-09-08
最后修改:2010-09-08
zhang34082 写道 有个问题一直没弄明白,问下:ThreadLocal 是线程的属性,假如有多个线程调用,是不是每个线程都有各自ThreadLocal的实例
static修饰ThreadLocal,说明这是类变量,既然是类变量,一个类只有一个,多个线程共享同一个类变量 |
|
返回顶楼 | |
发表时间:2010-09-08
真有想法(可以说是创意)。
|
|
返回顶楼 | |
发表时间:2010-09-08
支持one,
希望能经常看到这样的文章! |
|
返回顶楼 | |
发表时间:2010-09-08
没怎么明白
|
|
返回顶楼 | |
发表时间:2010-09-08
不明白 如果format 有2种或者更多 怎么处理
String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; String DATE_FORMAT = "yyyy/MM/dd HH:mm:ss"; ..... |
|
返回顶楼 | |
发表时间:2010-09-08
pengjunwu 写道 不明白 如果format 有2种或者更多 怎么处理
String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; String DATE_FORMAT = "yyyy/MM/dd HH:mm:ss"; ..... 如果你有两个format,那就建多一个ThreadLocal变量呀,楼主想要表达的意思是说不要每次都new一个SimpleFormat,同个线程可以共享一个SimpleFormat。 |
|
返回顶楼 | |
发表时间:2010-09-08
最后修改:2010-09-08
pengjunwu 写道 不明白 如果format 有2种或者更多 怎么处理
String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; String DATE_FORMAT = "yyyy/MM/dd HH:mm:ss"; ..... 这个示例代码针对多线程共享,循环创建SimpleDateFormat的解决方法; 您说的问题要具体问题具体分析;如果不涉及多线程大量创建对象这个方案基本用不着,所以还是直接实例化即可; 这篇文章重点是介绍ThreadLocal,所以当初没考虑你说的多种时间格式,应用场景不同,不同通用; |
|
返回顶楼 | |