【问题标题】:Multiple threads accessing the same object in JavaJava中多个线程访问同一个对象
【发布时间】:2017-10-18 02:42:41
【问题描述】:

我正在使用 Java 中的 HttpSevlet 构建一个 Web 服务器。我创建了一个名为 BaseApiABSTRACT 类,它扩展了 HttpSevlet 并用作父类。

每次调用 BaseApidoGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException。一个新的 BaseRequest 被实例化。 BaseRequest 只是我创建的另一个类,它是 BaseApi 的成员。

public abstract class BaseApi extends HttpServlet
{
    private static final long serialVersionUID = 6333682258528494467L;

    protected BaseRequest request;
}

然后我有一个子类,DeviceList 扩展了 BaseApi,DeviceListRequest 扩展了 BaseRequest

public class DeviceList extends BaseApi

public class DeviceListRequest extends BaseRequest

问题是这样的。每次调用 DeviceList 的 doGet 方法时,我认为会创建一个新线程。嗯,我发出了 2 个并发请求,日志是这样的。

[2017-10-18 02:06:39,760] INFO  Thread:Thread[http-nio-8080-exec-8,5,main] Instance:server.request.DeviceListRequest@2f15bbce retCode:-3
[2017-10-18 02:06:39,761] INFO  Thread:Thread[http-nio-8080-exec-7,5,main] Instance:server.request.DeviceListRequest@5343acea retCode:-3

好吧,不要介意 retCode 的事情,只关注 Thread:Instance:。我不确定Thread[http-nio-8080-exec-8,5,main]Thread[http-nio-8080-exec-7,5,main] 是否是两个不同的线程。因为那里有一个词MAIN。而且我不知道是否可以有两个主线程。或者,如果这确实是主线程。

所以,根据日志。我认为创建了两个线程和 DeviceListRequest 的两个不同实例创建了 DeviceListRequest@2f15bbceDeviceListRequest@5343acea

现在,随着代码继续运行,我注意到线程开始交替访问两个 DeviceListRequest。这是其余的日志

[2017-10-18 02:06:39,760] INFO  Thread:Thread[http-nio-8080-exec-8,5,main] Instance:server.request.DeviceListRequest@2f15bbce retCode:-3
[2017-10-18 02:06:39,761] INFO  Thread:Thread[http-nio-8080-exec-7,5,main] Instance:server.request.DeviceListRequest@5343acea retCode:-3
[2017-10-18 02:06:39,765] INFO  Thread:Thread[http-nio-8080-exec-8,5,main] Instance:server.request.DeviceListRequest@2f15bbce retCode:0
[2017-10-18 02:06:39,765] INFO  Thread:Thread[http-nio-8080-exec-8,5,main] Instance:server.request.DeviceListRequest@2f15bbce setResponseObjectWithKey->serverResponse:{"devices":[]} serverResponse_id:636342462
[2017-10-18 02:06:39,765] INFO  Thread:Thread[http-nio-8080-exec-8,5,main] Instance:server.request.DeviceListRequest@5343acea serverResponse:{"ret_msg":"unknown","ret_code":-3} serverResponse_id:1626294887
[2017-10-18 02:06:39,766] INFO  Thread:Thread[http-nio-8080-exec-7,5,main] Instance:server.request.DeviceListRequest@5343acea retCode:0
[2017-10-18 02:06:39,766] INFO  Thread:Thread[http-nio-8080-exec-7,5,main] Instance:server.request.DeviceListRequest@5343acea setResponseObjectWithKey->serverResponse:{"devices":[],"ret_msg":"unknown","ret_code":-3} serverResponse_id:1626294887
[2017-10-18 02:06:39,766] INFO  Thread:Thread[http-nio-8080-exec-7,5,main] Instance:server.request.DeviceListRequest@5343acea serverResponse:{"devices":[],"ret_msg":"OK","ret_code":0} serverResponse_id:1626294887

再次,只看线程:和实例:

看看我展示的这两行日志

[2017-10-18 02:06:39,765] INFO  Thread:Thread[http-nio-8080-exec-8,5,main] Instance:server.request.DeviceListRequest@2f15bbce setResponseObjectWithKey->serverResponse:{"devices":[]} serverResponse_id:636342462
[2017-10-18 02:06:39,765] INFO  Thread:Thread[http-nio-8080-exec-8,5,main] Instance:server.request.DeviceListRequest@5343acea serverResponse:{"ret_msg":"unknown","ret_code":-3} serverResponse_id:1626294887

线程 Thread:Thread[http-nio-8080-exec-8,5,main] 访问对象 DeviceListRequest@2f15bbceDeviceListRequest@5343acea强>

注意:顺便说一下,我使用System.identityHashCode(yourObject) 来获取对象的ID。我正在使用Thread.currentThread() 来获取线程标识符。

我的问题如下: 1.是Thread[http-nio-8080-exec-8,5,main]Thread[http-nio-8080-exec-7,5,main]两个不同的线程? 2. 名称中包含 main 的线程是否被视为主线程?如果是,那为什么有不止一个主线程? 3. 如何避免有线程访问不是在其上创建的对象的问题?

谢谢!

【问题讨论】:

    标签: java multithreading servlets


    【解决方案1】:
    1. Thread[http-nio-8080-exec-8,5,main] 和 Thread[http-nio-8080-exec-7,5,main] 是两个不同的线程吗?

    是的,每个请求都将在不同的线程中运行。

    1. 名称中包含 main 的线程是否被视为主线程?如果是,那为什么有不止一个主线程?

    不,只有在 JVM 启动时创建的初始线程才被视为主线程。任何线程都可以重命名并在其名称中包含“main”。您在日志中看到的主要可能是 ThreadGroup 的名称,线程的实际名称是 http-nio-8080-exec-7 和 http-nio-8080-exec-8。

    1. 如何避免有线程访问不是在其上创建的对象的问题?

    这并不总是一个问题,在您的情况下发生的情况是您在 Servlet 中存储了一个DeviceListRequest,并且通常只有一个 Servlet 用于多个请求。相反,您应该使用局部变量,如果需要将其传递给方法,或者如果您要将请求转发给另一个 Servlet,请将其保存在请求中。

    req.setAttribute("someName", new DeviceListRequest());
    

    然后当你想稍后访问它时:

    req.getAttribute("someName");
    

    【讨论】:

      【解决方案2】:

      这些是不同的线程。 servlet 容器有一个线程池,池中的线程将被分配给 http 请求。不禁止 servlet 容器创建给定 servlet 的多个实例,但通常只有一个。您应该期望从多个线程同时调用 servlet。

      不要让 servlet 保持可变状态。在 servlet 中声明的任何实例变量都应该是线程安全的。使 servlet 状态与给定请求相关联肯定会很麻烦。将与请求相关的状态保存在方法的局部变量中。

      【讨论】:

      • 加一!感谢您的信息!
      猜你喜欢
      • 1970-01-01
      • 2016-05-03
      • 2012-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多