带有静态字符串的线程安全Servlet

时间:2015-07-12 10:07:08

标签: java node.js multithreading servlets thread-safety

我在http://ahoj.io/nodejs-and-websocket-simple-chat-tutorial审核了一个带有Node JS和套接字IO的聊天服务器示例。在该示例中,在服务器上使用简单的历史变量来保存聊天历史数据。由于Node Js是单线程,所以每件事情都可以。 (如果您对节点js不感兴趣,可以忽略上面的节点JS示例:)我将在下面的java中解释它)

考虑下面的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测试了上面的代码,并且有很多并发请求,历史字符串总是存储所有消息(所以没有客户端消息丢失或覆盖),没有出错! 我没有使用线程,所以我想知道我在这里错过了什么!以上代码是否是线程安全的,可以信任。

3 个答案:

答案 0 :(得分:2)

正如其他人已经证实的那样,这确实不是线程安全的,因为它不可信任。 JVM实现中的一些怪癖可能使它成为一个可行的servlet,但不能保证它可以在另一个JVM上工作,甚至不能在其他时间工作。

要添加各种建议的实现,这里有一个AtomicReference:

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

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

答案 1 :(得分:1)

它不是线程安全的。不保证线程安全的代码不会失败,但也不保证能够正常工作。

答案 2 :(得分:1)

不,不是。线程安全漏洞可能很难触发 - 也许你的程序会丢失十亿分之一的消息,或者它可能永远不会错过消息巧合。但是,如果它是线程安全的,那么它将永远不会发生。

您可以简单地使用synchronized块来确保一次只有一个线程访问history,如下所示:

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

这意味着:锁定ChatServlet.class,将消息添加到历史记录中,然后解锁ChatServlet.class

你永远不会有两个线程同时锁定同一个对象 - 如果他们尝试,其中一个将继续,其余的将等待第一个解锁对象(然后另一个一个) 将继续,其余的将等待它解锁对象,依此类推。)

另外,请确保只在<{1}}块中读取 history - 否则,不能保证阅读线程会看到最新的更新。< / p>