2015-07-12 45 views
0

我已經回顧了一個帶有節點JS和套接字IO的聊天服務器示例,其示例代碼爲http://ahoj.io/nodejs-and-websocket-simple-chat-tutorial。在該示例中,服務器使用簡單的歷史變量來保存聊天記錄數據。由於Node Js是單線程,所以每件事情都可以正常工作。 (如果你對節點js不感興趣,你可以忽略上面的節點JS例子:)我將在下面的java中解釋它)帶靜態字符串的線程安全Servlet

考慮下面的servlet獲取message來自請求的字符串並將其添加到字符串中。該代碼可以是聊天服務器的一個例子。它從請求獲取用戶消息,並將其全部轉換爲history字符串,其他客戶端可以讀取它。

public class ChatServlet implements Servlet { 

    private static String history = "";  

    public void service(ServletRequest request, ServletResponse response) 
     history = history.concat(request.getParameter("message")); 
    } 

} 

從理論上講,這個代碼是不是線程安全的,因爲它使用global static變量(How do servlets work? Instantiation, sessions, shared variables and multithreading)。然而,我已經測試了上面的代碼與jMeter有很多併發請求和歷史字符串總是存儲所有消息(所以沒有客戶端消息丟失或覆蓋),並沒有出錯! 我沒有與線程一起工作,所以我想知道我是否在這裏失去了一些東西!上述代碼是線程安全的並且可以信任。


回答

1

不,不是。線程安全錯誤可能很難觸發 - 也許你的程序會錯過一個十億條消息,或者它可能會永遠錯過巧合。如果它是線程安全的,但是,它將保證永遠不會發生。

你可以簡單地用一個​​塊,以確保只有一個線程同時訪問history,像這樣:

synchronized(ChatServlet.class) { 
    history = history.concat(request.getParameter("message")); 
} 

這意味着:鎖ChatServlet.class,郵件添加到歷史記錄,然後解鎖ChatServlet.class

你永遠不能有兩個線程同時鎖定同一個對象 - 如果他們嘗試,其中一個將繼續,其餘的將等待第一個解鎖對象(然後另一個一個將繼續,其餘的將等待它解鎖對象,等等)。

還要確保只有history一個synchronized(ChatServlet.class)塊內 - 否則,它不能保證讀線程會看到最新的更新。

1

它不是線程安全的。不是線程安全的代碼不能保證失敗,但也不能保證可以正常工作。

2

正如其他人已經證實,這確實不是線程安全的,因爲它不可信。 JVM實現中的一些怪癖可能會使成爲一個可用的servlet,但不能保證它可以在另一個JVM上甚至在其他時間工作。

要添加到各種建議實現的,這裏有一個用的AtomicReference:

AtomicReference<String> history = new AtomicReference<>(""); 

public void service(ServletRequest request, ServletResponse response) 
    history.updateAndGet(h -> h.concat("123")); 
}