java Spring Singleton bean 线程安全
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27098043/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring Singleton beans thread safety
提问by user1050619
I'm new to Spring and have some basic question.In one of the Spring examples as given below, I noticed EmployeeManager is Autowired.
我是 Spring 新手,有一些基本问题。在下面给出的 Spring 示例之一中,我注意到 EmployeeManager 是自动装配的。
Question:
问题:
- The EmployeeManager scope is not given, so I would assume the default scope is SINGLETON and Spring beans are not thread safe. Is this assumption, correct?
The EmployeeManager is defined part of the Servlet which can be accessed by multiple threads. Assume,"delete" method is called by multiple threads at the same time with values "1" "2" & "3" and same instance of EmployeeManager is generated for each thread(since its SINGLETON), which delete value will be executed.How Spring handles this condition?
@Controller public class EditEmployeeController { @Autowired private EmployeeManager employeeManager; @RequestMapping(value = "/", method = RequestMethod.GET) public String listEmployees(ModelMap map) { map.addAttribute("employee", new EmployeeEntity()); map.addAttribute("employeeList", employeeManager.getAllEmployees()); return "editEmployeeList"; } @RequestMapping(value = "/add", method = RequestMethod.POST) public String addEmployee(@ModelAttribute(value="employee") EmployeeEntity employee, BindingResult result) { employeeManager.addEmployee(employee); return "redirect:/"; } @RequestMapping("/delete/{employeeId}") public String deleteEmplyee(@PathVariable("employeeId") Integer employeeId) { employeeManager.deleteEmployee(employeeId); return "redirect:/"; } public void setEmployeeManager(EmployeeManager employeeManager) { this.employeeManager = employeeManager; } }
- EmployeeManager 范围没有给出,所以我假设默认范围是 SINGLETON 并且 Spring bean 不是线程安全的。这个假设正确吗?
EmployeeManager 被定义为 Servlet 的一部分,可以被多个线程访问。假设“delete”方法被多个线程同时调用,值为“1”、“2”和“3”,并且为每个线程生成相同的EmployeeManager实例(因为它是SINGLETON),删除值将被执行。 Spring 如何处理这种情况?
@Controller public class EditEmployeeController { @Autowired private EmployeeManager employeeManager; @RequestMapping(value = "/", method = RequestMethod.GET) public String listEmployees(ModelMap map) { map.addAttribute("employee", new EmployeeEntity()); map.addAttribute("employeeList", employeeManager.getAllEmployees()); return "editEmployeeList"; } @RequestMapping(value = "/add", method = RequestMethod.POST) public String addEmployee(@ModelAttribute(value="employee") EmployeeEntity employee, BindingResult result) { employeeManager.addEmployee(employee); return "redirect:/"; } @RequestMapping("/delete/{employeeId}") public String deleteEmplyee(@PathVariable("employeeId") Integer employeeId) { employeeManager.deleteEmployee(employeeId); return "redirect:/"; } public void setEmployeeManager(EmployeeManager employeeManager) { this.employeeManager = employeeManager; } }
EmployeeManager -
员工经理 -
public interface EmployeeManager {
public void addEmployee(EmployeeEntity employee);
public List<EmployeeEntity> getAllEmployees();
public void deleteEmployee(Integer employeeId);
}
@Service
public class EmployeeManagerImpl implements EmployeeManager
{
@Autowired
private EmployeeDAO employeeDAO;
@Override
@Transactional
public void addEmployee(EmployeeEntity employee) {
employeeDAO.addEmployee(employee);
}
@Override
@Transactional
public List<EmployeeEntity> getAllEmployees() {
return employeeDAO.getAllEmployees();
}
@Override
@Transactional
public void deleteEmployee(Integer employeeId) {
employeeDAO.deleteEmployee(employeeId);
}
public void setEmployeeDAO(EmployeeDAO employeeDAO) {
this.employeeDAO = employeeDAO;
}
}
回答by Pelit Mamani
I agree with the above answer. Just wanted to emphasize that The only protection from race-conditions is the "@Transactional" bit, which means spring replaces your EmployeeManagerImpl with an instance that consults the transactionManager at the start/end of each method. Roughtly speaking :
我同意上面的回答。只是想强调,竞争条件的唯一保护是“@Transactional”位,这意味着 spring 用一个实例替换您的 EmployeeManagerImpl ,该实例在每个方法的开始/结束时咨询 transactionManager。粗略地说:
public void addEmployee(EmployeeEntity employee) {
transactionManager.startTransaction();
employeeDAO.addEmployee(employee);
transactionManager.endTransaction();
// Just a rough outline; more accurately there should be 'finally' and rollback
}
...
Now if 2 threads access data at the same time, their behavior depends on your transactoinManager, transaction Isolation level, and how your DataSource interacts with it. In simple cases your thread will be forced to wait; on other cases the database could tolerate some concurrent access. That magic boils down to the transaction, not to spring. Also there is no control on threads beforethey reach the transaction. If 3 different threads ask to delete 1,2 and 3 you can't tell which one will get to 'startTransaction' first. But it shouldn't really matter - you might as well have a situation where someone asks to delete "2" on Sunday, someone else asks to delete "3" on Monday, and someone else asks to delete "1" on Tuesday. You just need a reasonable consistent end result.
现在,如果 2 个线程同时访问数据,它们的行为取决于您的 transactoinManager、事务隔离级别以及您的 DataSource 如何与其交互。在简单的情况下,您的线程将被迫等待;在其他情况下,数据库可以容忍一些并发访问。这种魔法归结为交易,而不是春天。在线程到达事务之前也没有对线程的控制。如果 3 个不同的线程要求删除 1,2 和 3,您无法判断哪个将首先进入“startTransaction”。但这并不重要-您可能会遇到这样的情况:周日有人要求删除“2”,周一有人要求删除“3”,而周二有人要求删除“1”。你只需要一个合理一致的最终结果。
回答by Hussein Akar
Spring scope singleton has nothing to do with thread safety those are two different concepts.
Spring 范围单例与线程安全无关,这是两个不同的概念。
The difference between singleton
& prototype
bean is how we ask spring container that manages beans life-cycle to return beans:
singleton
& prototype
bean的区别在于我们如何要求管理 bean 生命周期的 spring 容器返回 bean:
- Singleton: Insure every time you call the bean it return the same instance.
- Prototype: Return new instance when ever called.
- 单例:确保每次调用 bean 时它都返回相同的实例。
- 原型:调用时返回新实例。
Calling bean can be trigger by either @autowirde
or AppContext.getBean("beanName")
调用 bean 可以由@autowirde
或触发AppContext.getBean("beanName")
In summary it all depends on the objects injected the bean if it is not thread-safe then obviously it not gonna be thread-safe.
总之,这一切都取决于注入 bean 的对象,如果它不是线程安全的,那么显然它不会是线程安全的。
To learn more Spring Bean Scopes
了解更多Spring Bean 范围
回答by The Saint
Yes, your assumption is correct: if you do not declare a scope for your bean in Spring, it is a Singleton by default, which means it is not thread safe.
By virtue of the assumption above being true, the short answer to your question is that Spring does not do anything to handle multithreading of a singleton bean, so it is up to you to deal with thread safety and concurrency issues of that bean. The good news is that based on what your
EmployeeManager
bean does and the "1, 2, 3" scenario you outlined, it doesn't actually matter and you don't really have to do anything.
是的,你的假设是正确的:如果你没有在 Spring 中为你的 bean 声明一个作用域,它默认是一个Singleton,这意味着它不是线程安全的。
由于上述假设为真,对您的问题的简短回答是 Spring 没有做任何事情来处理单例 bean 的多线程,因此由您来处理该 bean 的线程安全和并发问题。好消息是,根据您的
EmployeeManager
bean 的作用以及您概述的“1、2、3”场景,这实际上并不重要,您实际上不必做任何事情。
Here is the long answer as to why that is the case. Contrary to the common misconception, there is no such thing as multiple threads being truly executed at the same time. Sure, they may appear to be executing at the same time, but what's really happening under the hood is that the JVM takes one of the threads, executes a portion of it, then in the most efficient way (you would hope) starts working on another thread, then another, then maybe the first again, etc.
关于为什么会这样,这是一个很长的答案。与常见的误解相反,不存在真正同时执行多个线程这样的事情。当然,它们可能看起来是在同时执行,但在幕后真正发生的是 JVM 获取其中一个线程,执行其中的一部分,然后以最有效的方式(您希望)开始工作另一个线程,然后另一个线程,然后可能又是第一个线程,等等。
The reason why this doesn't even matter to you is because your bean does not have any state. In other words, you are just passing customer IDs, and you don't care which gets deleted first.
这对您来说甚至无关紧要的原因是因为您的 bean没有任何 state。换句话说,您只是传递客户 ID,您并不关心哪个先被删除。
Now, if you were actually passing the SAME customer object in those threads, then you might have a problem. Bottom line is, the general rule of thumb is that any bean without state can be a singleton. You can read this articleregarding Spring singletons and thread safety that explains that and much more in great detail.
现在,如果您实际上是在这些线程中传递 SAME 客户对象,那么您可能会遇到问题。底线是,一般的经验法则是任何没有状态的 bean 都可以是单例。您可以阅读有关 Spring 单例和线程安全的这篇文章,其中详细解释了这一点。
Hope this helps.
希望这可以帮助。