【问题标题】:Servlet that starts a thread only once for every visitor为每个访问者只启动一次线程的 Servlet
【发布时间】:2012-10-17 10:30:31
【问题描述】:

嘿,我想实现一个 Java Servlet,它只为每个用户启动一次线程。即使刷新它也不应该重新开始。我的最后一种方法给我带来了一些麻烦,所以没有代码^^。对servlet的布局有什么建议吗?

 public class LoaderServlet extends HttpServlet {
// The thread to load the needed information
private LoaderThread loader;
// The last.fm account
private String lfmaccount;

public LoaderServlet() {
    super();
    lfmaccount = "";
}

@Override
protected void doPost(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {
    if (loader != null) {
        response.setContentType("text/plain");
        response.setHeader("Cache-Control", "no-cache");
        PrintWriter out = response.getWriter();
        out.write(loader.getStatus());
        out.flush();
        out.close();
    } else {
        loader = new LoaderThread(lfmaccount);
        loader.start();
        request.getRequestDispatcher("WEB-INF/pages/loader.jsp").forward(
                request, response);
    }
}

@Override
protected void doGet(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {
    if (lfmaccount.isEmpty()) {
        lfmaccount = request.getSession().getAttribute("lfmUser")
                .toString();
    }
    request.getRequestDispatcher("WEB-INF/pages/loader.jsp").forward(
            request, response);
}
}

jsp 使用 ajax 定期发布到 servlet 并获取状态。线程只运行了 3 分钟,爬取了一些 last.fm 数据。

【问题讨论】:

  • 你能贴出你尝试过的代码吗?
  • 你最后的方法是什么?给出一些细节/代码sn-ps。只有这样我们才能告诉你如何改进它。
  • 为什么要为每个用户创建一个线程?我认为这种方法不可扩展,因为这意味着如果您有 1000 个用户,那么您需要 1000 个线程(每个线程都有自己的堆栈,这很可能会破坏您的内存)。
  • 如果您更准确地告诉我们目的,也许我们可以建议一种不同的、希望更好的方法来做您想做的事。在 Java EE 世界中,将线程锁定给用户的基本想法对我来说似乎很不寻常。通常最好让容器管理线程并避免处理它们。
  • 所以我有在我的网站上注册的用户。注册后,我必须从他们的帐户中抓取一些 lastfm 数据。所以没什么大不了的。我想避免的是他们在浏览器中按下刷新,然后触发新线程^^,当然让多个用户同时抓取他们的东西。

标签: java multithreading servlets multi-user


【解决方案1】:

您需要的是Session listener。对于每个浏览器会话,方法 sessionCreated() 只会被调用一次。因此,即使用户刷新页面,也不会有任何问题。 然后,您可以继续为每个 sessionCreated() 方法调用启动线程。

【讨论】:

    【解决方案2】:

    实现 javax.servlet.SingleThreadModel => 服务方法不会被并发执行。 请参阅 servlet 规范。

    【讨论】:

      【解决方案3】:

      假设它可以通过创建Map<String,Thread> 来实现,然后调用您的servlet,它会尝试使用sessionId 查找地图。

      只是一个草图:

      public class LoaderServlet extends HttpServlet {
      
        private Map<String,Thread> threadMap = new HashMap<>();
      
        protected void doPost(..) {
          String sessionId = request.getSesion().getId();
          Thread u = null;
          if(threadMap.containsKey()) {
              u = threadMap.get(sessionId);
          } else {
             u = new Thread(...);
             threadMap.put(sessionId, u);
          }
          // use thread 'u' as you wish
        } 
      
      }
      

      注意事项:

      • 这使用会话 ID,而不是用户来关联线程
      • 看看ThreadPools,他们很棒
      • 正如评论者指出的那样:同步问题未在此草图中考虑

      【讨论】:

      • 这个答案存在并发问题:如果同一会话中同时有 2 个帖子会发生什么?
      【解决方案4】:

      您的首要任务是弄清楚如何唯一地识别用户,例如,您将如何识别代理/SOHO 网关后面的不同用户?

      一旦你把它弄下来,它基本上只是有一个单例对象为用户提供服务线程映射到你的 servlet。

      然后我们进入@beny23 在上面的评论中提到的可扩展性问题......我完全同意所提出的观点 - 你的方法在可扩展性方面并不合理!

      干杯,

      【讨论】:

        【解决方案5】:

        据我了解,您希望避免并行处理来自同一用户的请求。我建议您使用其他方法:将锁与每个用户关联并将其存储在会话中。在开始处理用户请求之前 - 尝试获取该锁。因此,当前线程将等待来自该用户的其他请求正在处理。 (创建会话时使用会话监听器存储锁)

        【讨论】:

          猜你喜欢
          • 2019-06-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-09
          • 2012-05-06
          • 2020-12-13
          • 1970-01-01
          相关资源
          最近更新 更多