【问题标题】:Static Variables Behaviour in a Java ServletJava Servlet 中的静态变量行为
【发布时间】:2011-04-07 07:30:09
【问题描述】:

我正在开发一个 java servlet,它在运行时会在新线程中启动不同的对象方法。这些线程应该访问一个描述特定 servlet 实例的变量,比如 jobId。出于这个原因,我将 jobId 变量声明为静态的。 servlet 构造函数正在为每个 servlet 实例(调用)计算这个值。 如果同时调用 servlet 几次,我在徘徊,静态 jobId 变量在调用之间共享,这意味着某些线程会得到错误的 jobId,或者每次调用都计算一次 - 所以线程启动的特定 servlet 将使用为该特定 servlet 计算的 jobId(这是我希望它工作的方式)。 有任何想法吗? 非常感谢!

【问题讨论】:

  • 真的,避免使任何可变的static(包括单例)。

标签: java multithreading servlets static


【解决方案1】:

一个 servlet 只在 webapp 启动时创建一次,并在所有请求之间共享。无论是否静态,每个类/实例变量都将在所有请求/会话之间共享。您不想将请求/会话范围的数据分配给它们。而是将它们声明/分配为方法局部变量。例如

public class MyServlet extends HttpServlet {
    private static Object thisIsNotThreadsafe;
    private Object thisIsAlsoNotThreadsafe;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object thisIsThreadsafe;
        
        thisIsNotThreadsafe = request.getParameter("foo"); // BAD! Shared among all requests.
        thisIsAlsoNotThreadsafe = request.getParameter("foo"); // BAD! Shared among all requests.
        thisIsThreadsafe = request.getParameter("foo"); // Good.
    }
}

存在遗留和弃用 SingleThreadModel 接口,您可以让您的 servlet 实现以在每个请求期间强制创建。但这是一个糟糕的设计并且不必要地昂贵。这也是它被弃用的原因。

另见:

【讨论】:

  • 如果我使用私有静态最终变量怎么办?对于常数值。会好吗?
  • @Ommadawn:它们无论如何都不是可变的,所以没关系。仅当您将局部变量分配给实例变量声明时,才会出现此问题。使用已经不可能的常量。
【解决方案2】:

servlet 规范中没有定义 servlet 的实例化策略(据我所知,anywho),但通常的行为似乎是每个 servlet 配置只创建一个实例。因此,在您的情况下,每个请求都将使用相同的变量。

如果我是你,我会考虑将 jobId 作为参数发送给你正在运行线程的Runnables。例如,代替这个:

public class HelloWorld extends HttpServlet {
    private static long jobId;
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        jobId = Long.parseLong(request.getParameter("jobid");
        new Thread(new Worker()).start();
    }

    static class Worker implements Runnable {
        @Override
        public void run() {
            doSomethingWith(jobId);
        }
    }

}

像这样重构掉静态变量:

public class HelloWorld extends HttpServlet {
    // private static long jobId; -- delete, no longer needed
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        long jobId = Long.parseLong(request.getParameter("jobid"); // local variable
        new Thread(new Worker(jobId)).start(); // send jobId as parameter
    }

    static class Worker implements Runnable {
        private final long jobId; // non-static; every instance has one
        public Worker(long jobId) { // allow injection of jobId
            this.jobId = jobId;
        }
        @Override
        public void run() {
            doSomethingWith(jobId); // use instance variable instead of static
        }
    }

}

更容易阅读,没有并发问题 - 纯粹的胜利。

【讨论】:

  • servlet 规范中肯定有描述。阅读第 2.3 章。此外,必须非常小心地在 servlet 中生成线程。只要 OP 的功能要求不明确,并且目前显示的技能水平较低,我不建议这样做。这只会导致更多的混乱和麻烦。可能只是存储在会话范围内或作为请求参数传递/保留就绰绰有余了。
【解决方案3】:

静态变量是共享的。静态变量不属于任何一个实例,类的所有实例都可以访问它们。当您使用用于创建一个对象(类的一个实例)的构造函数时,在构造函数中设置静态变量通常没有意义,因为它超出了您正在创建的对象的范围.

至于什么可行,你可以把jobId放在HttpSession中,然后每个用户都有自己的副本。

【讨论】:

    【解决方案4】:

    static 表示每个实例都将访问相同的值。
    因此,连接到 servlet 的每个用户都将访问相同的值。当 2 个或更多用户连接在一起时,您的 jobId 可能会出错。

    您必须在每个连接中获取自己的值并将其存储在其他地方。


    资源:

    关于同一主题:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-02-19
      • 2010-12-23
      • 2012-04-10
      • 2012-09-12
      • 2016-02-07
      • 1970-01-01
      • 2013-04-05
      相关资源
      最近更新 更多