Java Servlet 3规范和ThreadLocal
据我所知,Servlet3规范引入了异步处理特性。除此之外,这将意味着同一线程可以并且将被重新用于处理另一个并发HTTP请求。这不是革命性的,至少对以前与NIO合作过的人来说是这样 无论如何,这导致了另一件重要的事情:noJava Servlet 3规范和ThreadLocal,java,multithreading,servlets,thread-local,servlet-3.0,Java,Multithreading,Servlets,Thread Local,Servlet 3.0,据我所知,Servlet3规范引入了异步处理特性。除此之外,这将意味着同一线程可以并且将被重新用于处理另一个并发HTTP请求。这不是革命性的,至少对以前与NIO合作过的人来说是这样 无论如何,这导致了另一件重要的事情:noThreadLocalvariables作为请求数据的临时存储。因为如果同一个线程突然成为不同HTTP请求的载体线程,请求本地数据将暴露给另一个请求 所有这些都是我基于阅读文章的纯推测,我没有时间玩任何Servlet3实现(Tomcat7、GlassFish 3.0.X等) 因
ThreadLocal
variables作为请求数据的临时存储。因为如果同一个线程突然成为不同HTTP请求的载体线程,请求本地数据将暴露给另一个请求
所有这些都是我基于阅读文章的纯推测,我没有时间玩任何Servlet3实现(Tomcat7、GlassFish 3.0.X等)
因此,问题是:
- 我假设
将不再是保存请求数据的方便黑客,对吗ThreadLocal
- 有没有人玩过Servlet 3实现中的任何一个并尝试使用
s来证明上述内容ThreadLocal
- 除了在HTTP会话中存储数据外,还有其他类似的易于接触的黑客吗
ThreadLocal
。事实上,我总是建议不要在类似的情况下使用它。然而,信不信由你,线程上下文的使用频率远远超过了你的想象。Spring的OpenSessionInViewFilter
就是一个很好的例子,根据其Javadoc:
此筛选器生成Hibernate会话
可通过当前线程访问,
它将被自动检测到
事务管理器
这不是严格意义上的ThreadLocal
(尚未检查源代码),但听起来已经很令人担忧了。我可以想到更多类似的场景,而丰富的web框架使得这种情况更可能发生
简单地说,许多人已经在这个黑客之上建造了他们的沙堡,不管有没有意识到。因此,斯蒂芬的回答是可以理解的,但不完全是我想要的。我想确认是否有人真正尝试过,并且能够重现失败的行为,因此这个问题可以作为被同样问题困住的其他人的参考点。(注意:我还没有详细阅读Servlet 3规范,所以我不能肯定该规范说明了您认为它的功能。我只是假设它确实如此……)
我认为ThreadLocal将不再是保存请求数据的方便黑客,对吗
使用ThreadLocal
始终是一种糟糕的方法,因为当工作线程完成一个请求并启动另一个请求时,您总是有信息泄漏的风险。将内容作为属性存储在ServletRequest
对象中总是一个更好的主意
现在你有了另一个理由,可以用“正确”的方式来做
有没有人玩过Servlet3实现中的任何一个,并尝试使用ThreadLocals来证明上述内容
这不是正确的方法。它只告诉您在测试的特定环境下特定实现的特定行为。您不能概括
正确的方法是假设它有时会发生,如果规范说它可以……并设计你的webapp来考虑它
(不用担心!显然,在这种情况下,默认情况下不会发生这种情况。您的webapp必须显式启用异步处理功能。如果您的代码中充斥着线程局部变量,建议您不要这样做…)
除了在HTTP会话中存储数据外,还有其他类似的易于接触的黑客吗
不正确。唯一正确的答案是将特定于请求的数据存储在ServletRequest或ServletResponse对象中。即使将其存储在HTTP会话中也可能是错误的,因为对于给定会话,可能会有多个请求同时处于活动状态。除非您明确要求异步处理,否则异步处理不会打扰您 例如,如果servlet或请求的筛选器链中的任何筛选器未标记为
true
,则无法使请求异步。因此,您仍然可以对常规请求使用常规做法
当然,如果您确实需要异步处理,那么您需要使用适当的实践。基本上,当异步处理请求时,其处理被分为多个部分。这些部分不共享线程本地状态,但是,您仍然可以在每个部分内使用线程本地状态,尽管您必须在这两个部分之间手动管理状态n零件。注意:黑客攻击随之而来。小心使用,或者干脆不要使用 只要您继续了解代码在哪个线程中执行,就没有理由不能安全地使用ThreadLocal
try {
tl.set(value);
doStuffUsingThreadLocal();
} finally {
tl.remove();
}
这并不是说你的调用堆栈是随机切换的。见鬼,如果你想在调用堆栈的深处设置ThreadLocal值,然后再进一步使用,你也可以破解它:
public class Nasty {
static ThreadLocal<Set<ThreadLocal<?>>> cleanMe =
new ThreadLocal<Set<ThreadLocal<?>>>() {
protected Set<ThreadLocal<?>> initialValue() {
return new HashSet<ThreadLocal<?>>();
}
};
static void register(ThreadLocal<?> toClean) {
cleanMe.get().add(toClean);
}
static void cleanup() {
for(ThreadLocal<?> tl : toClean)
tl.remove();
toClean.clear();
}
}
公共类{
静态线程本地>>(){
受保护集>();
}
};
静态无效寄存器(ThreadLocal toClean){
cleanMe.get().add(toClean);
}
静态空洞清理(){
用于(ThreadLocal tl:toClean)
tl.删除();
toClean.clear();
}
}
然后在设置ThreadLocals时注册ThreadLocals,并在某个地方的finally子句中进行清理。这都是可耻的恶意行为,您可能不应该这么做。很抱歉,我写了它,但为时已晚://我仍然想知道为什么人们会使用腐烂的javax.servlet API来实际实现他们的servlet。我所做的是:
- 我有一个基类
,它有请求、响应的私有字段,还有一个HttpRequestHandler
方法,可以向ge抛出异常和一些实用方法handle()