先看这么一段代码:
@Service public class AccountService { private String message; public void foo1() { if (true) { this.message = "a"; } else { this.message = "b"; } } public void foo2() { // 修改this.message的代码... // ... ... } }
如果你打算在@Controller里这么调用AccountService :
accountService.foo1(); model.addAttribute(accountService.getMessage());
那么就有线程安全的危险了。
问题原因
在Spring中,bean的默认scope是singleton,也就是说容器中只有一个bean的实例。而在Java Web环境中,web服务器会为每一个请求创建一个线程来处理它。这样一来,在@Controller中调用@Service bean的方法就会导致有多个线程在执行@Service方法,例如线程A在执行foo1()方法,线程B在执行foo2()方法。那么问题来了,多个线程同时读写message成员变量,就可能让getMessage()方法返回错误的值
解决方法
1. 将@Service bean的scope改为 "request",即:
@Service @Scope("request") public class AccountService { private String message;
这样Spring会为每一个请求分别创建一个AccoutService对象,每个线程都有自己的message变量,就不会出错了。但坏处是创建@Service bean的开销往往比较大,会导致程序性能下降。
2. 使用不可变对象(Immuable Object)封装message变量
定义如下类:
class MessageWrapper { private String message; public MessageWrapper(String msg) { this.message = msg; } // 只提供get方法 public String getMessage() { return this.message; } }
AccountService的foo1()方法修改如下:
@Service public class AccountService { public MessageWrapper foo1() { if (true) { return new MessageWrapper("a"); } else { return new MessageWrapper("b"); } // ... ... }
这样便可以完美避免线程安全问题,又不会带来过多的额外开销。
时间: 2024-10-03 13:44:22