文章目录
- 前言
- 猜想来源
- 验证方法
- @Controller 的情况
- @ServerEndpoint 的情况
- 后记
前言
最近有 websocket 的需求。探索 @ServerEndpoint
的类成员变量特点。
这里类比 @Controller
讨论 @ServerEndpoint
类成员变量是否线程安全。
猜想来源
网上的教程大多数都这么展示程序:
@Component
@ServerEndpoint(value = "/ws")
public class MyWsEndpoint {
private final MyContext context = new MyContext();
@OnOpen
public void onOpen(Session session) {
}
// ... 省略
}
但是,同样的写法,类比 @Controller
,必然会产生线程安全问题。
@Controller
public class MyController {
private static final Logger logger = LoggerFactory.getLogger(MyController.class);
private final MyContext context = new MyContext();
@GetMapping("/normal")
@ResponseBody
public String normal() {
}
}
需要一种快速验证的方式, @ServerEndpoint
的类成员变量则线程安全
验证方法
- 最简单的用例,把读和写拆成两个步骤,如果读到了其他线程的值,则为不安全的情况
// 每个请求独立自增
int currentCount = ac.incrementAndGet();
// 多线程写
context.setCurrentCount(currentCount);
// 多线程读
if (context.getCurrentCount() != currentCount) {
logger.error("线程不安全!!!");
}
- 浏览器触发请求,多线程 debug 打断点 制造并发场景
@Controller 的情况
- 线程不安全
原因是@Controller
的类默认是单例的
@ServerEndpoint 的情况
贴一个前端测试代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ws client</title>
</head>
<body>
</body>
<script>
let ws = new WebSocket("ws://localhost:8080/ws")
ws.onopen = function () {
ws.send("hello")
}
ws.onmessage = function (message) {
console.log(message)
}
</script>
</html>
- 线程安全
后记
- 上文验证了
@ServerEndpoint
的类成员变量是线程安全的。 - 但是存在矛盾:
- 上面用例
@Component
和@ServerEndpoint
共同作用于一个类上 - Spring 容器管理
@Component
默认是单例的
- 上面用例
- 解释上文的矛盾:
- 【Spring】@ServerEndpoint 与 Spring 是如何集成的,
@Component
用于bean相关的api读取 @ServerEndpoint
使用的是servlet的模式,与Spring mvc 不同,在Filter里面做了new操作
- 【Spring】@ServerEndpoint 与 Spring 是如何集成的,