Servlet规范规定容器将实例化我的java.servlet.HttpServlet
的单个实例,并从多个工作线程调用服务方法(doGet()
/ doPost()
)。
根据正常的线程规则,无法保证init(ServeltConfig)
'中实例级字段的分配发生在'执行doGet()
的其他线程从那些相同字段读取之前,除非某人在某些时候安排同步。
事实上,容器确实可以进行某种外部同步,以确保init()
中完成的工作对“后续”线程可见。
但是,Servlet规范是否明确保证我是线程安全的?我现在无法找到这样的保证,虽然我必须承认,自从Servlet 2.4以来我没有从头到尾阅读规范。
修改
例如,由于一些回答者混淆了我的问题,我的问题是:Servlet规范是什么说下面的类是线程安全的?
@WebServlet (initParams = {@WebInitParam(name="b", value="true")})
public Decider extends HttpServlet {
private boolean b = false;
public void init(ServletConfig config) {
this.b = Boolean.parseBoolean(config.getAttribute("b"));
}
public void doGet(HttpServletRequest req, HttpServletResponse res) {
res.sendRedirect(b ? "/true" ? "/false");
}
}
当然,如果我这样做的话:
public static void main(String[] argv) {
HttpServlet s = new Decider();
Thread t1 = new Thread(new Runnable() {
public void run() {
s.init(...);
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
s.doGet(...);
}
});
t1.start();
t2.start();
}
...然后我有一个线程错误。什么使容器必然不同?
编辑2
所有声明“容器处理它”的答案当然是受欢迎的,但我的问题是关于 Servlet规范是否保证这种行为。要充分回答这个问题,你必须参考Servlet规范。 (任何版本,我很酷)。
答案 0 :(得分:7)
init
javadoc中明确说明了这一点:
servlet容器之后只调用一次init方法 实例化servlet。 init方法必须成功完成 在servlet可以接收任何请求之前。
如果你遵循servlet生命周期,它会说servlet应该init
- 来自多个线程的service
请求之前。
答案 1 :(得分:2)
...然后我有一个线程错误。什么使容器成为必然 不同?
在您的示例中,init()
和doGet()
方法可能会重叠。这在Servlet
容器中是不可能的。容器在开始处理请求之前需要注意执行所有init()
调用和其他初始化。跨这些方法边界没有多线程问题。
您仍然会遇到doXXX()
方法中使用共享日期的问题。
如果没有查看每个容器的源代码,最好的办法是查看javadoc for Servlet
(和Filter
):
此接口定义了初始化servlet以进行服务的方法 请求,以及从服务器中删除servlet。这些被称为 生命周期方法按以下顺序调用:
- 构造servlet,然后使用init方法初始化。
- 处理客户对服务方法的任何调用。
- servlet停止服务,然后使用destroy方法销毁 收集并最终确定垃圾。
要真正支持Servlet
规范,容器必须遵循这些规则。
此Servlet Life Cycle
在Servlet 3.0 specification document.
实例化servlet对象后,容器必须 在处理来自客户端的请求之前初始化servlet。 提供初始化以便servlet可以读取持久性 配置数据,初始化昂贵的资源(如JDBC™) 基于API的连接),并执行其他一次性活动。的的 容器通过调用
init
方法初始化servlet实例 具有唯一的Servlet接口(每个servlet声明) 实现ServletConfig接口的对象。
粗体中的重要部分。