【问题标题】:Are methods declared in servlets are thread-safe or the local variables in servlets are thread safeservlet 中声明的方法是线程安全的还是 servlet 中的局部变量是线程安全的
【发布时间】:2017-07-20 08:55:23
【问题描述】:

我必须知道在我的 servlet 中存在竞争条件,因为我有一些全局声明的变量在其中使用。所以我的问题是我是否在doGetdoPost 方法之外的其他方法中声明这些变量。它们不会在不同线程之间共享吗?

如果是,我们如何避免条件竞争。我想避免使用 ThreadLocalsingelThreadModel

这是一个例子

public class UserServlet extends HttpServlet {


    private String userString1 = "";
    private String userString2 = "";


    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        someTask(request, response);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        someTask(request, response);
    }

    public void someTask(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        userString1 = request.getParameter("someParameter");
        userString2 = request.getParameter("someParameter");

        }
}

注意:这只是一个示例,而不是实际代码。

【问题讨论】:

    标签: java multithreading servlets


    【解决方案1】:

    servlet 中声明的方法是线程安全的吗

    没有。

    或者 servlet 中的局部变量是线程安全的

    没有。

    我必须知道我的 servlet 中存在竞争条件

    有一个竞态条件。

    因为我有一些在其中使用的全局声明的变量。

    你有一些实例变量。

    所以我的问题是,如果我在 doGet()doPost() 方法之外的某个方法中声明这些变量,它们不会在不同线程之间共享吗?

    局部变量是声明它们的方法的局部变量。

    如果是,我们如何避免条件竞争。

    我无法遵循您的双重否定,但这仍然是一个种族条件。不要滥用标准术语。您需要避免 Servlet 的实例成员,除非它们是其方法的所有可能的并发调用所共有的和共享的。每个请求的变量必须是方法本地的,并且作为参数或返回值在方法之间传递。

    我想避免使用ThreadLocalSingleThreadModel

    你当然知道。 Servlet 中没有指定的线程语义可以使 ThreadLocal 工作,SingleThreadModel 已被弃用并将被删除。

    【讨论】:

    • 如果我在 someTask 方法中声明 private String userString1 = ""; private String userString2 = ""; 会怎样。它会解决竞争条件的问题吗?
    • 是的,这将解决它&你不能使用 private 在方法中声明的变量 String userString1 = request.getParameter("someParameter");
    • @SabirKhan 谢谢您的回复。是否有某种文档可供我参考,这些局部变量是线程安全的。另外,我从某人那里得知,将有一个公共方法的一个实例,它可以让多个线程共享本地声明的变量中包含的信息。这是真的吗?
    • @SabirKhan 不,声明private String 成员将不会解决此问题中提到的任何问题。
    • @SidhantBansal 方法局部变量根据定义是线程安全的,除非它们逃逸到另一个线程。
    【解决方案2】:

    Servlet 是一个 Java 类,因此如果它没有状态(即如果它没有任何状态变量),它就已经是线程安全的。您无需采取额外的行动。

    现在,您引入状态变量,例如 - userString1userString2,并且您的 servlet 仍然可以是线程安全的,因为 String 是不可变的,但是您正在为每个请求重新分配其引用,因此它不会保持线程安全,因为一个线程的分配可能会被其他线程更改为其他值(到那时,线程一会尝试使用它)。您可以拥有一个不可变的状态并保持线程安全,前提是您不要像在 someTask 方法中那样重新分配它的引用。

    所以使用那些字符串变量,前提是其目的是将一些常量字符串传递给所有请求线程。

    此外,在您的代码中,您通过someTask 方法中的赋值表达式将大量字符串放在堆上。

    最后,如果你必须在这些方法中使用可变状态对象,你必须使用 java 同步构造来确保你的类线程安全,但同样纯 Java 同步构造是每个 JVM 的,所以如果必须分布式部署,你将面临问题在一个集群上。在这种情况下,将需要一些分布式锁定机制。

    【讨论】:

      【解决方案3】:

      如果您在 doGet 或 doPost 方法中声明全局变量并分配值,它们肯定会被调用 servlet 的不同线程覆盖。您不应在 servlet 方法调用中为全局变量赋值。

      在您的代码中,是什么阻止您声明 userString1userString2 内部方法?为什么要在全球范围内声明它们?如果您希望这些变量在 servlet 的不同方法中使用,则只需将它们作为方法参数传递,而不是将它们声明为全局变量。

      例如

      public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          String userString1 = request.getParameter("someParameter");
          String userString2 = request.getParameter("someParameter");
          someTask(request, response, userString1, userString2);
      }
      
      public void someTask(HttpServletRequest request, HttpServletResponse response, String userString1, String userString2) throws ServletException, IOException {
          //do whatever you want with these variables.
      }
      

      public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          someTask(request, response);
      }
      
      public void someTask(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          String userString1 = request.getParameter("someParameter");
          String userString2 = request.getParameter("someParameter");
      
          //do whatever you want with these variables.
      }
      

      【讨论】:

      • 我想在 someTask 方法中声明所有这些变量。您对此有何看法?
      • 是的,您也可以在 someTask 方法中声明这些变量。它们是线程安全的,因为它们是方法本地的。
      • 我正在尝试在我的代码中做同样的事情。事情完成后会通知您。
      • 我对答案投了赞成票,但由于声誉较低,不会被记录。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-08-24
      • 2012-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-14
      相关资源
      最近更新 更多