2015-07-12 127 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")); 
}