Spring集成的專案中,通過注解方式注入Bean的使用無處不在,在一次專案開發中,由voliate關鍵字思考到的Spring中單例Bean的執行緒安全問題,在此記錄采坑總結,
Spring的單例Bean(@Autowired等注解的Bean)默認是單例的,Spring并沒有對Bean有執行緒安全的控制策略,并發安全問題由開發決定,然而,執行緒安全與否取決于是否有共享的資源競爭關系存在,Spring中單例Bean是否是執行緒安全取決于Bean是有狀態的Bean還是無狀態的Bean,
何為有狀態Bean跟無狀態Bean?
- 有狀態的Bean:有資料存盤功能,eg:Bean中有User物件用于存盤用戶資訊;
- 無狀態的Bean:無資料存盤功能
下面是測驗的Demo:
(1)MainUser.java:
@Data
@Builder
public class MainUser {
private String id;
private String sex;
private String userName;
}
(2)SysMainController .java
@RequestMapping("/main")
@Controller
public class SysMainController {
@Autowired
private SysMainService mainService;
@RequestMapping(value = "/test")
public void mainMethod() {
// thread-1
new Thread(() -> {
MainUser user = MainUser.builder()
.id("1")
.sex("男")
.userName("張三")
.build();
mainService.testBean(user);
}).start();
// thread-2
new Thread(() -> {
MainUser user = MainUser.builder()
.id("2")
.sex("女")
.userName("小芳")
.build();
mainService.testBean(user);
}).start();
}
}
(3)SysMainServiceImpl.java
@Service
public class SysMainServiceImpl implements SysMainService {
/** 基本型別變數亦可 */
private MainUser user; // 非定義為static的變數
@Override
public void testBean(MainUser user) {
this.user = user;
print();
}
public void print() {
try {
Thread.sleep(2000);
System.out.println("ThreadName: " + Thread.currentThread().getName() + ", user: " + user);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
測驗多次的結果:
ThreadName: Thread-57, user: MainUser(id=2, sex=女, userName=小芳)
ThreadName: Thread-58, user: MainUser(id=2, sex=女, userName=小芳)
結論:
- 兩個執行緒進入print()方法都會創建自己的私有VM,但是user物件是共享的,這點容易在開發中被忽略;
- 故在Spring的單例模式的Bean中不應該在全域層面上定義有資料存盤功能的物件 / 基本型別變數 / static變數,會產生執行緒安全問題;若要這么做,可以通過宣告Bean的 Scope=‘prototype’ 或者使用本地變數 ThreadLocal 進行定義
以上為筆者遇到的問題進行的思考實驗,若有不正確的地方歡迎指出~~~
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/171606.html
標籤:其他
上一篇:攻防世界做題記錄(4)
