为啥Servlet中的实例变量不是线程安全的[重复]

Posted

技术标签:

【中文标题】为啥Servlet中的实例变量不是线程安全的[重复]【英文标题】:Why instance variable in Servlet is not thread-safe [duplicate]为什么Servlet中的实例变量不是线程安全的[重复] 【发布时间】:2012-05-02 22:41:01 【问题描述】:

当我阅读Head First Servlet and JSP 时,他们说实例变量是非线程安全的。

我不太明白这句话。例如:我有一个名为ActionServlet.java 的servlet。每次,每个用户的请求都会发送到服务器,容器会创建一个新线程并创建新的ActionServlet实例。

ActionServlet 可能有一个结构:

public class ActionServlet extends HttpServlet 
   // example of instance variable
   Instance variable;
   public void processRequest(HttpServletRequest request, HttpServletResponse response)    
       // process something relating to instance variable
   

所以,因为所有这些线程都为ActionServlet 创建了一个新的类实例,所以我在这里看不到任何问题。因为这些线程的实例是相互独立的。

请找出在多线程环境中使用实例变量时的问题。

谢谢:)

【问题讨论】:

想想从两个线程对同一个对象调用同一个方法。 @DanielFischer 我无法想象不同的线程如何使用相同的对象,因为:1)这些变量是私有的 2)这个线程的对象总是不同于其他线程(我认为)。我上面有一个示例代码。请告诉我更清楚。 @hqt:容器创建一个唯一的 servlet 实例,将其存储在某个全局数据结构(例如 map)中,每次请求进来时,它都会获取相应的 servlet(基于在请求的路径上)从地图,并调用其服务方法。 servlet 的字段是私有的这一事实没有任何影响。容器甚至不关心它们。 @JBNizet 哦,感谢您的明确解释。您在下面的评论对我来说仍然很棒:D 我想问最后一个问题:所以,当没有任何线程使用该 servlet 实例时,该实例将被删除内存并在需要时重新创建? servlet 未存储在线程池中。线程存储在线程池中。 servlet 在部署时或第一次需要时创建,然后保留在内存中,直到取消部署应用程序。如果它从内存中删除,容器将不得不在新请求进入时重新创建一个实例,因此会违反规范。而且无论如何都没有充分的理由将其从内存中删除,因为 servlet 通常是无状态的,因此根本不会消耗太多内存。 【参考方案1】:

你犯的错误在这里:

所以,因为所有这些线程都为 ActionServlet,所以我在这里看不到任何问题。因为实例 这些线程是相互独立的。

容器不会为每个请求创建 Servlet 类的新实例。它重用现有的。这就是为什么它们不是线程安全的。

Stripes Action Framework 确实为每个请求创建一个新实例,因此在该框架下这是一个好的假设。但是,例如,Struts 1 遵循 Servlet 模型,不会为每个请求创建新的操作。

这并不意味着容器仅限于单个实例,理论上它可以创建多个,但它不是指定的行为,因此不能依赖。大多数流行的都没有。

【讨论】:

事实上,容器必须只创建一个实例。规范是这样说的:“对于不在分布式环境中托管的 servlet(默认),servlet 容器必须在每个 servlet 声明中只使用一个实例” 哦。感谢@JBNizet 和 Will Hartung。这本书没有告诉我容器只是为一个 servlet 创建一个实例。【参考方案2】:

因为所有这些线程都创建了一个新的类实例(action.java),所以我看不出有什么问题

您假设每个线程都创建一个仅由该线程使用的类实例,这就是您没有任何问题的原因。

但是,请尝试用您的特定示例想象从两个线程访问同一个实例。如果两者同时使用您的requestresponse 成员会发生什么? 也许你会从无法识别的请求中读取数据,你会写出一个混合了两部分的不一致的响应。

所以在你的情况下,实例变量也不是线程安全的,因为如果两个线程访问同一个实例,它们可能会互相干扰。

【讨论】:

【参考方案3】:

问题是,您的 action.java 并不总是实例化,而是从实例池中获取,请求线程也是如此,它们是从线程池中获取的,因此可以共享一个 servlet 实例通过多个请求。

【讨论】:

servlet 没有实例池。每个 servlet 声明只有一个 servlet 实例。

以上是关于为啥Servlet中的实例变量不是线程安全的[重复]的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Servlet 不是线程安全的? [复制]

Servlet部分细节介绍

Servlet和Struts2的线程安全问题

Servlet的线程安全

为什么原生的servlet是线程不安全的而Struts2是线程安全的?

关于servlet的线程安全[重复]