我看到了三种工作方式。
使用'InheritableThreadLocal`
这是ThreadLocal 的子类和默认的JDK 功能。
可继承的线程局部变量会自动从其父线程复制到子线程中。
似乎是个不错的候选人。
已编辑:如果您的代码看起来像这样,那么它就是一个替代品。
public class Test {
// I guess this, in your actual code, is an injected Reference
InheritableThreadLocal<String> expensiveData = new InheritableThreadLocal<String>();
public void work(String userName){
expensiveData.set(computeExpensiveData(userName)); // whatever that is
Worker workUnit = new Worker();
workUnit.userData = expensiveData; // Spring does some variant of this (I guess)
Thread child = new Thread(workUnit); // This is the key line
child.start(); // As long as the parent thread has a clean "expensiveData"
// The child thread will have too.
}
protected static class Worker implements Runnable {
// This is injected too...
protected InheritableThreadLocal<String> userData = null;
@Override
public void run() {
userData.get(); // Returns spwaning thread's version
// Work ...!
return;
}
}
}
如果Thread 实例(此处为child 变量)未在此处创建或启动,而是在池中重用,则存在从Worker 的userData 中产生错误值的实际风险.
使用请求/会话范围的 bean
如果您访问数据的方式是通过某种 bean,也许您可以将此 bean 的生命周期与您的 webapp 的会话或请求联系起来。每次你都会自动获得一个新的 bean,但你会在你的范围内保留同一个 bean。
重构为缓存
Spring 具有@Cachable 注释,可以合理地轻松缓存给定的方法调用。再一次,您可以将对数据的访问设置为 @Cachable,并将缓存配置调整为“每个用户”。
编辑:Spring 缓存使用代理,所以它是一个正在工作的方法级拦截。
我称其为 reafactor,因为它需要您做一些工作,但在我看来这是一种更清洁、更像弹簧的处理方式。从与上面相同的示例开始,主要区别在于您必须创建一个新的 Bean,该 Bean 将实现:
public static interface ExpensiveUserDataCalculator {
@Cacheable // With the proper configuration tuning (eviction ? TTL ? size ?)
public String computeExpensiveData(String userName);
}
实现只是一个细节(您可能会从当前代码中复制/粘贴它)。
这个 bean 将被注入(用 Spring 的术语来说,它是 collaborator)。
这看起来像这样。请注意,您不必操作 ThreadLocal 或生成线程或其他任何东西。只需让每个协作者完成其工作,将它们与 Spring 连接在一起,然后使用 @Cacheable(或您认为合适的其他缓存方式)进行优化。缓存是您工作流程的“实施细节”。
// This is a thread pool, or whatever...
protected Object workDispatcher = null;
public void work(String userName){
Worker workUnit = new Worker(userName); // Setup
workDispatcher.dispatch(workUnit);
}
protected static class Worker implements Runnable {
// This is injected Injected in you situation (?)
protected ExpensiveUserDataCalculator userDataProvider = null;
protected String userName = null;
public Worker(String userName) { this.userName = userName; }
@Override
public void run() {
userDataProvider.computeExpensiveData(userName);
// Work ...!
return;
}
}
最后两个方法在 Spring 参考指南中有自己的部分,因此如果需要,请查看。