如果我在HttpServlet #init(ServletConfig)中分配实例字段,Servlet规范是否保证我可以在doGet()中读取它们?

时间:2013-10-03 11:17:08

标签: java servlets thread-safety specifications

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规范。 (任何版本,我很酷)。

2 个答案:

答案 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 CycleServlet 3.0 specification document.

的第2.3章中有所描述
  

实例化servlet对象后,容器必须   在处理来自客户端的请求之前初始化servlet。   提供初始化以便servlet可以读取持久性   配置数据,初始化昂贵的资源(如JDBC™)   基于API的连接),并执行其他一次性活动。的的   容器通过调用init方法初始化servlet实例   具有唯一的Servlet接口(每个servlet声明)   实现ServletConfig接口的对象。

粗体中的重要部分。