簡體   English   中英

多線程訪問Java中的同一對象

[英]Multiple threads accessing the same object in Java

我正在使用Java中的HttpSevlet構建Web服務器。 我創建了一個名為BaseApiABSTRACT類,該類擴展了HttpSevlet並將其用作父類。

每次doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException BaseApi的 doGet(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]是兩個不同的線程。 因為那里有一個詞。 而且我不知道是否可以有兩個主線程。 或者,如果這確實是主線程。

因此,基於日志。 我認為有創建兩個線程,創建DeviceListRequest @ 2f15bbceDeviceListRequest @ 5343acea DeviceListRequest的兩個不同的實例。

現在,隨着代碼繼續運行,我注意到線程開始可互換地訪問兩個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

同樣,只需查看Thread:和Instance:

看一下我顯示的這兩行日志

[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的線程是否被視為main線程? 如果是,那么為什么會有多個主線程? 3.如何避免有線程訪問未在其上創建的對象的問題?

謝謝!

這些是不同的線程。 Servlet容器具有一個線程池,該池中的線程將分配給http請求。 禁止servlet容器制作一個給定servlet的多個實例,但通常只有一個。 您應該期望從多個線程同時調用servlet。

不要讓Servlet保持可變狀態。 在Servlet中聲明的任何實例變量都應該是線程安全的。 使Servlet狀態與給定請求相關必然會帶來麻煩。 將與請求相關的狀態保留在方法的局部變量中。

  1. 線程[http-nio-8080-exec-8,5,main]和線程[http-nio-8080-exec-7,5,main]是兩個不同的線程嗎?

是的,每個請求都將在不同的線程中運行。

  1. 是否將名稱中包含main的線程視為主線程? 如果是,那么為什么會有多個主線程?

不,只有在JVM啟動時創建的初始線程才被視為主線程。 任何線程都可以重命名並在其名稱中包含“ main”。 您在日志中看到的主要名稱可能是ThreadGroup的名稱,實際的線程名稱是http-nio-8080-exec-7和http-nio-8080-exec-8。

  1. 如何避免有線程訪問未在其上創建的對象的問題?

這並不總是一個問題,在您的情況下,您會在DeviceListRequest中存儲一個DeviceListRequest ,並且通常只有一個Servlet用於多個請求。 相反,您應該使用局部變量,如果需要,或者如果您要將請求轉發到另一個Servlet,則將其傳遞給方法,然后將其保存在請求中。

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

然后,當您以后想要訪問它時,請執行以下操作:

req.getAttribute("someName");

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM