【问题标题】:Thread Safe Servlet线程安全的 Servlet
【发布时间】:2016-08-24 22:32:16
【问题描述】:

我正在开发一个 JSP MVC Web 应用程序。我对线程安全的 Servlet 概念感到困惑。以下是我的代码,请告诉我它是否线程安全。另外,告诉我为什么它是线程安全的或不是线程安全的。

JSP 代码;

 <%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Personal Profile</title>
</head>
<body>
    <form id="PersonalData" method="post" action="PersonalDataServlet">
        First Name:<input type="input" name="FirstNameField"><br>
        Last Name:<input type="input" name="LastNameField"><br>
        Email:<input type="input" name="EmailField">Without @ part<br>
        <input type="Submit" value="Submit Data">
    </form>
    <br>
    <br>
    <br>
    <%
        out.print(request.getAttribute("FullName") + "\n");
        out.print(request.getAttribute("EmailAddress"));
    %>
</body>
</html>

Servlet 代码;

package Servlets;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import Model.WelcomeName;

@WebServlet("/PersonalDataServlet")
public class PersonalDataServlet extends HttpServlet {

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String FirstNameServlet;
        String LastNameServlet;
        String EmailServlet;

        FirstNameServlet = request.getParameter("FirstNameField");
        LastNameServlet = request.getParameter("LastNameField");
        EmailServlet = request.getParameter("EmailField");

        System.out.println(FirstNameServlet + "\n" + LastNameServlet + "\n" + EmailServlet);

        WelcomeName WelcomeNameObject = new WelcomeName();
        WelcomeNameObject.Fullname(FirstNameServlet, LastNameServlet, EmailServlet);

        request.setAttribute("FullName", WelcomeNameObject.FullName);
        request.setAttribute("EmailAddress", WelcomeNameObject.EmailAddress);

        request.getRequestDispatcher("Profile.jsp").forward(request, response);

    }

}

用于实际计算或业务逻辑的其他类

package Model;

public class WelcomeName {

    public String FullName;
    public String EmailAddress;

    public void Fullname(String FirstName, String LastName, String Email) {
        FullName = (FirstName + " " + LastName);
        EmailAddress = (Email + "@gmail.com");
    }

}

【问题讨论】:

  • 您确实需要阅读一般的多线程和线程安全。参考文献Brian Goetz 的 Java 并发实践。回答此类问题的简单课程还不够。
  • 如果您没有更具体的问题;您的输入最好转到 codereview.stackexchange.com ...

标签: java multithreading jsp servlets thread-safety


【解决方案1】:

每个方法调用都有自己的堆栈帧分配给它,没有其他线程访问它。所有局部变量都分配在此堆栈帧上。对于 WelcomeNameObject,它是在堆上创建的,但对它的唯一引用是在创建它的方法调用的堆栈帧上,以及在请求对象上(即使请求对象改变了 WelcomeNameObject 上的某些内容,方法调用也完成了到那时,就没有冲突了)。其他线程没有机会获得对它的引用并对其进行更改,因此它是线程安全的。

如果您移动 welcomeName 变量的声明,使其成为 servlet 中的实例变量:

@WebServlet("/PersonalDataServlet")
public class PersonalDataServlet extends HttpServlet {

    private WelcomeName WelcomeNameObject;

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

那么结果将是不安全的。 servlet 容器保留一个线程池来执行请求,它将这些请求发送到 servlet 的同一个实例,因此并发请求可以修改 servlet 实例变量的内容。例如,一个线程可能进入方法,创建对象的新实例并将其分配给实例变量,然后填充它,然后另一个线程可以调用该方法并创建对象的新实例并分配 that 到同一个实例变量,这样第一次调用将返回一个与它刚刚创建的对象完全不同的对象。

【讨论】:

    【解决方案2】:

    您可以使用 SingleThreadModel。您的 servlet 类实现此接口,此后您的 servlet 是线程安全的。

    【讨论】:

    • 它提供了线程安全,但以牺牲并发为代价。从 servlet api 2.4 起,SingleThreadModel 已被弃用,这是有充分理由的。
    【解决方案3】:

    我在您提供的代码中没有发现线程安全问题。线程安全主要是代码如何使用共享数据的功能,因此我寻找

    • servlet 类的实例变量,如果 servlet 容器使用同一个 servlet 实例为多个线程中的请求提供服务,则这些变量在线程之间共享;

    • 通过 servlet 上下文或会话对象共享的对象;和

    • servlet 或 JSP 访问的任何类的静态变量。

    这些不是共享数据可以采用的唯一形式,但它们可能适用于您的 Web 应用程序,正如您所展示的那样。您没有通过任何这些机制共享任何数据,因此您的 servlet 和 JSP,正如所展示的,是线程安全的。

    如果您确实访问了共享对象,这不会自动使您的代码成为非线程安全的,但您需要通过适当的同步来保护您的访问,以确保它们的线程安全。作为一个功能问题,您还需要注意同步,以避免在任何相当长的时间内阻塞线程的可能性。

    【讨论】:

    • 非常感谢您的回复。请澄清一件事,包Model中的类WelcomeName与线程安全有关吗?因为这个类有实例变量。真正的工作是由 WelcomeName 类完成的,我在 servlet 类的 doPost 方法中创建了一个对象 WelcomeNameObject,并使用 Servlet 将数据输入/输出到 html 页面,真正的工作由 WelcomeName 完成。
    • @WaleedKhan,是的,WelcomeNameObject 类具有实例变量,但这对线程安全无关紧要,因为线程之间没有共享该类的实例(通过我在回答中描述的任何方式)。当我说“你的 servlet 类的实例变量”时,我的意思就是——换句话说,类 PersonalDataServlet 的实例变量。
    • 如果取 WelcomeName WelcomeNameObject = new WelcomeName();在servlet类的doPost方法之外,它仍然是线程安全的吗?
    • @WaleedKhan,方法的局部变量对线程安全没有直接影响。它们不是线程之间共享数据的工具。
    猜你喜欢
    • 2012-02-10
    • 1970-01-01
    • 2010-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-14
    相关资源
    最近更新 更多