关于threadLocal的使用中出现NullPointException的解决方案

在项目日志拦截器中使用了threadlocal,在记录日志时,

private static final ThreadLocal startTimeThreadLocal =
			new NamedThreadLocal("ThreadLocal StartTime");
	
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
			Object handler) throws Exception {
		if (logger.isDebugEnabled()){
			long beginTime = System.currentTimeMillis();//1、开始时间  
	        startTimeThreadLocal.set(beginTime);		//线程绑定变量(该数据只有当前请求的线程可见)  
	        logger.debug("开始计时: {}  URI: {}", new SimpleDateFormat("hh:mm:ss.SSS")
	        	.format(beginTime), request.getRequestURI());
		}
		return true;
	}
@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
			Object handler, Exception ex) throws Exception {
		
		// 打印JVM信息。
		if (logger.isDebugEnabled()){
			long beginTime = startTimeThreadLocal.get();//得到线程绑定的局部变量(开始时间)  
			long endTime = System.currentTimeMillis(); 	//2、结束时间  
	        logger.debug("计时结束:{}  耗时:{}  URI: {}  最大内存: {}m  已分配内存: {}m  已分配内存中的剩余空间: {}m  最大可用内存: {}m",
	        		new SimpleDateFormat("hh:mm:ss.SSS").format(endTime), DateUtil.formatDateTime(endTime - beginTime),
					request.getRequestURI(), Runtime.getRuntime().maxMemory()/1024/1024, Runtime.getRuntime().totalMemory()/1024/1024, Runtime.getRuntime().freeMemory()/1024/1024, 
					(Runtime.getRuntime().maxMemory()-Runtime.getRuntime().totalMemory()+Runtime.getRuntime().freeMemory())/1024/1024); 
	        //删除线程变量中的数据,防止内存泄漏
	        startTimeThreadLocal.remove();
		}
由于过滤器filter的拦截是的没有直接访问 preHandle方法,所以 ThreadLocal没有set值,就直接访问 afterCompletion从ThreadLocal中get值,导致ThreadLocal底层使用的map为空,执行initialValue方法并抛出NullPointException,根据jdk文档推荐做法

Method Detail

  • initialValue

    protected T initialValue()
    Returns the current thread's "initial value" for this thread-local variable. This method will be invoked the first time a thread accesses the variable with the  get()  method, unless the thread previously invoked the  set(T)  method, in which case the  initialValue  method will not be invoked for the thread. Normally, this method is invoked at most once per thread, but it may be invoked again in case of subsequent invocations of  remove()  followed by  get() .

    This implementation simply returns null; if the programmer desires thread-local variables to have an initial value other than nullThreadLocal must be subclassed, and this method overridden. Typically, an anonymous inner class will be used.

    Returns:
    the initial
    value for this thread-local
    采取如下解决方案,事实证明,该initialValue确实执行,并不在抛出NullPointException,但另外在项目中就存在为什么被过滤器拦截后执行发生变化,不是先执行prehandle再执行aftercompletion,而是出现反复执行 aftercompletion的问题待解决
    private static final ThreadLocal startTimeThreadLocal =
    			new NamedThreadLocal("ThreadLocal StartTime"){
    		protected Long initialValue() {
    			Logger logger = LoggerFactory.getLogger(getClass());
    			logger.info("ThreadLocal StartTime initialValue");
    			long beginTime = System.currentTimeMillis();
    			return beginTime;
    			
    		};
    	};




你可能感兴趣的:(java线程)