转:ThreadLocal示例

 本文借花献佛,引用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变量的拷贝或者叫做副本,代码如下:

 

Java代码 复制代码
  1. import java.text.DateFormat;   
  2. import java.text.ParseException;   
  3. import java.text.SimpleDateFormat;   
  4. import java.util.Date;   
  5. /**  
  6.  * 使用ThreadLocal以空间换时间解决SimpleDateFormat线程安全问题。  
  7.  * @author   
  8.  *  
  9.  */  
  10. public class DateUtil {   
  11.        
  12.     private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";   
  13.        
  14.     @SuppressWarnings("rawtypes")   
  15.     private static ThreadLocal threadLocal = new ThreadLocal() {   
  16.         protected synchronized Object initialValue() {   
  17.             return new SimpleDateFormat(DATE_FORMAT);   
  18.         }   
  19.     };   
  20.   
  21.     public static DateFormat getDateFormat() {   
  22.         return (DateFormat) threadLocal.get();   
  23.     }   
  24.   
  25.     public static Date parse(String textDate) throws ParseException {   
  26.         return getDateFormat().parse(textDate);   
  27.     }   
  28. }  
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方法,主要作用是创建时初始化实例。也可以采用下面方式创建;

 

Java代码 复制代码
  1. //第一次调用get将返回null   
  2. private static ThreadLocal threadLocal = new ThreadLocal();   
  3. //获取线程的变量副本,如果不覆盖initialValue,第一次get返回null,故需要初始化一个SimpleDateFormat,并set到threadLocal中   
  4. public static DateFormat getDateFormat()    
  5. {   
  6.     DateFormat df = (DateFormat) threadLocal.get();   
  7.     if(df==null){   
  8.         df = new SimpleDateFormat(DATE_FORMAT)   
  9.         threadLocal.set(df);   
  10.     }   
  11.     return df;   
  12. }  
//第一次调用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方法:

 

Java代码 复制代码
  1. protected T initialValue() {   
  2.         return null;//直接返回null   
  3.     }  

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值