mirror of
https://github.com/CyC2018/Interview-Notebook.git
synced 2026-10-30 18:23:52 +00:00
Update Spring.md
补充 Bean 生命周期的完整输出,增加单例 Bean 并发计数的实测附录。
This commit is contained in:
+27
-2
@@ -22,6 +22,7 @@
|
||||
* [动态 SQL](#动态-sql)
|
||||
* [缓存](#缓存)
|
||||
* [七、Spring 中的设计模式](#七spring-中的设计模式)
|
||||
* [附录:实测](#附录实测)
|
||||
* [参考资料](#参考资料)
|
||||
<!-- GFM-TOC -->
|
||||
|
||||
@@ -77,7 +78,7 @@ public class OrderService {
|
||||
6. BeanPostProcessor 的 postProcessAfterInitialization(),AOP 就是在这一步把 Bean 替换为代理对象的;
|
||||
7. 容器关闭时销毁:依次调用 @PreDestroy 方法、DisposableBean 的 destroy() 和配置的 destroy-method。
|
||||
|
||||
让一个 Bean 实现上面所有的回调,实际运行打印的顺序如下:
|
||||
让一个 Bean 实现上面所有的回调,实际运行打印的顺序如下(Spring Boot 3.3):
|
||||
|
||||
```text
|
||||
1 构造函数
|
||||
@@ -90,6 +91,7 @@ public class OrderService {
|
||||
8 BeanPostProcessor after
|
||||
9 @PreDestroy
|
||||
10 DisposableBean.destroy
|
||||
11 destroy-method
|
||||
```
|
||||
|
||||
### Bean 的作用域与线程安全
|
||||
@@ -111,7 +113,7 @@ public class CounterController {
|
||||
}
|
||||
```
|
||||
|
||||
在本机用 64 个线程并发请求 1 万次,计数只增加了 9975,丢失了 25 次更新。解决方法是不在单例中保存状态,把数据放在方法的局部变量中;确实需要共享时使用 AtomicInteger 等线程安全的类,每个线程各自的数据放在 ThreadLocal 中。
|
||||
例如用 64 个线程并发请求 1 万次,多次运行中每轮丢失的更新从 0 次到 67 次不等(Spring Boot 3.3 实测,见[附录:实测](#附录实测))。是否丢失、丢失多少取决于线程执行的时序,这类问题在测试中很难稳定复现。解决方法是不在单例中保存状态,把数据放在方法的局部变量中;确实需要共享时使用 AtomicInteger 等线程安全的类,每个线程各自的数据放在 ThreadLocal 中。
|
||||
|
||||
需要注意的是,把 prototype 的 Bean 注入到单例中,只会在创建单例时注入一次,之后每次使用的都是同一个对象。需要每次得到新对象时,注入 ObjectProvider 并在使用时调用 getObject()。
|
||||
|
||||
@@ -349,6 +351,29 @@ public void on(OrderPaidEvent e) { ... } // 积分服务、短信
|
||||
|
||||
两个监听方法都在 main 线程中执行;短信服务先执行并抛出异常后,积分服务的监听方法没有被调用,异常还传回了订单服务。监听方法之间的顺序默认不确定,可以用 @Order 指定;不希望互相影响时,加上 @Async 让监听方法在线程池中异步执行,或者用 @TransactionalEventListener 在订单的事务提交之后再处理。
|
||||
|
||||
## 附录:实测
|
||||
|
||||
Spring Boot 3.3.5、JDK 21,Apple M1 Pro。CounterController 与正文相同,用 Python 的线程池以 64 个线程请求 /count,每轮 1 万次,用请求前后 /count 的返回值计算增加了多少,应用启动后连续运行 3 轮:
|
||||
|
||||
```text
|
||||
第 1 轮:64 个线程并发请求 10000 次,计数增加 9987,丢失 13 次更新
|
||||
第 2 轮:64 个线程并发请求 10000 次,计数增加 10000,丢失 0 次更新
|
||||
第 3 轮:64 个线程并发请求 10000 次,计数增加 10000,丢失 0 次更新
|
||||
```
|
||||
|
||||
2026-10-28 重新启动应用后再运行两组:
|
||||
|
||||
```text
|
||||
== 启动后第 1 组
|
||||
第 1 轮:64 个线程并发请求 10000 次,计数增加 9933,丢失 67 次更新
|
||||
第 2 轮:64 个线程并发请求 10000 次,计数增加 9986,丢失 14 次更新
|
||||
第 3 轮:64 个线程并发请求 10000 次,计数增加 9997,丢失 3 次更新
|
||||
== 启动后第 2 组
|
||||
第 1 轮:64 个线程并发请求 10000 次,计数增加 9999,丢失 1 次更新
|
||||
第 2 轮:64 个线程并发请求 10000 次,计数增加 9997,丢失 3 次更新
|
||||
第 3 轮:64 个线程并发请求 10000 次,计数增加 9998,丢失 2 次更新
|
||||
```
|
||||
|
||||
## 参考资料
|
||||
|
||||
- [Spring Framework: Dependency Injection](https://docs.spring.io/spring-framework/reference/core/beans/dependencies/factory-collaborators.html)
|
||||
|
||||
Reference in New Issue
Block a user