繁体   English   中英

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

[英]Thread Safe Servlet With a Static String

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

考虑下面的servlet,它从请求中获取message String并将其添加到字符串中。 此代码可能是聊天服务器的示例。 它从请求中获取用户消息,并将所有消息发送到history String,其他客户端可以读取它。

public class ChatServlet implements Servlet {

    private static String history = "";    

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

}

从理论上讲,此代码不是线程安全的,因为它使用了global static变量( servlet如何工作?实例化,会话,共享变量和多线程 )。

但是,我已经使用jMeter使用大量并发请求测试了上面的代码,并且历史记录字符串始终存储所有消息(因此,不会丢失或覆盖任何客户端消息),并且没有出错! 我没有使用线程,所以我想知道我是否在这里缺少什么! 上面的代码是线程安全的并且可以信任吗。


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

为了增加各种提议的实现,这里是一个带有AtomicReference的实现:

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

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

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

不,这不对。 线程安全性错误可能很难触发-也许您的程序将错过十亿条消息,或者永远不会巧合地错过一条消息。 但是,如果它是线程安全的,则将永远不会发生。

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

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

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

你永远不能拥有两个线程同时锁定同一目标-如果他们尝试,其中一人将进行,其余的将在附近等的第一个解锁对象(然后又是一个将继续,剩下的将等待它解锁对象,依此类推)。

此外,请确保仅读取synchronized(ChatServlet.class)块中的history -否则,不能保证读取线程将看到最新更新。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM