Spring定时任务中使用ThreadLocal的坑

  在项目中,发现Spring的定时任务中用ThreadLocal来保存上下文信息,且上下文信息中有一个属性在后面入库时是做为主键值。总觉得这里应该有问题,因为没有去看过Spring的定时任务的具体实现,也不知定时任务用没用线程池以及如何使用的,但如何用了线程池(且我觉得从性能和常理推测来看应该是要用的),用ThreadLocal保存上下文信息,并在后续使用(且使用完没有做remove),那么后面就一定会出现主键冲突呀。
  为了偷懒,也为了尽快验证我的推断,写了一个很简单的测试代码如下。

Context.java

package com.bijian.study.dto;

public class Context {

    private int seqNo;

    public int getSeqNo() {
        return seqNo;
    }

    public void setSeqNo(int seqNo) {
        this.seqNo = seqNo;
    }
}

HelloService.java

package com.bijian.study.service;

public interface HelloService {

    public String processService(String name) throws Exception;
    
    public String processService() throws Exception;
}

HelloServiceImpl.java

package com.bijian.study.service.impl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.bijian.study.dto.Context;
import com.bijian.study.service.HelloService;
import com.bijian.study.util.ContextThreadLocal;

@Service("helloService")
public class HelloServiceImpl implements HelloService {

    private static Logger logger = LoggerFactory.getLogger(HelloServiceImpl.class);
    
    private static int count;
    
    @Override
    public String processService(String name) throws Exception {
        
        logger.info("HelloService processService name:" + name);
        return "Hello " + name;
    }
    
    @Override
    public String processService() throws Exception {
        
        Context context = ContextThreadLocal.get();
        if(context == null) {
            count++;
            context = new Context();
            context.setSeqNo(count);
            ContextThreadLocal.set(context);
        }
        
        logger.info("HelloService processService seqNo:" + context.getSeqNo());
        return "Hello " + context.getSeqNo();
    }
}

ContextThreadLocal.java

package com.bijian.study.util;

import com.bijian.study.dto.Context;

public class ContextThreadLocal {

    public static final ThreadLocal userContextThreadLocal = new ThreadLocal();

    public static void set(Context userContext) {
        userContextThreadLocal.set(userContext);
    }

    public static void unset() {
        userContextThreadLocal.remove();
    }

    public static Context get() {
        return userContextThreadLocal.get();
    }
}

springMVC-servlet.xml



    
	   
	
	

    
    
       
           
        
           
    
    
	
		
	

  运行结果:

Spring定时任务中使用ThreadLocal的坑_第1张图片
  从运行结果来看,Spring定时任务肯定用到了线程,且在上面这种运用场景是肯定会出现主键冲突。当然,这里的解决办法也很简单,即在使用完ThreadLocal中的上下文信息后,做remove的清理动作。

  至于Spring Task的实现原理,还请看过这块代码或了解这块的读者指点一下,或者,有详细分析Spring Task实现原理的文章,也可以推荐一下,多谢。

你可能感兴趣的:(spring)