Spring Singleton豆线程安全

时间:2014-11-24 04:48:13

标签: java multithreading spring spring-mvc

我是Spring的新手并且有一些基本的问题。在下面给出的一个Spring示例中,我注意到EmployeeManager是Autowired。

问题:

  1. 未给出EmployeeManager范围,因此我假设默认范围是SINGLETON,而Spring bean不是线程安全的。这个假设是对的吗?
  2. EmployeeManager是Servlet的一部分,可以被多个线程访问。 假设,"删除"方法由多个线程同时使用值" 1" " 2" &安培; " 3"并且为每个线程生成了相同的EmployeeManager实例(因为它的SINGLETON),将执行删除值。春天如何处理这个条件?

    @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;
    }
    }
    
  3. 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;
        }
    }
    

3 个答案:

答案 0 :(得分:0)

我同意上述答案。只是想强调,对竞争条件的唯一保护是“@Transactional”位,这意味着spring用一个在每个方法的开头/结尾处查询transactionManager的实例替换你的EmployeeManagerImpl。滔滔不绝地说:

public void addEmployee(EmployeeEntity employee) {
    transactionManager.startTransaction();
    employeeDAO.addEmployee(employee);
    transactionManager.endTransaction();
    // Just a rough outline; more accurately there should be 'finally' and rollback
}
...

现在,如果2个线程同时访问数据,它们的行为取决于您的transactoinManager,事务隔离级别以及DataSource与之交互的方式。在简单的情况下,你的线程将被迫等待;在其他情况下,数据库可以容忍一些并发访问。这种魔力归结为交易,而不是春天。 在到达事务之前,线程也无法控制。如果3个不同的线程要求删除1,2和3,则无法确定哪个线程将首先进入“startTransaction”。但这应该不重要 - 你可能也有这样的情况,有人要求在星期日删除“2”,其他人要求在星期一删除“3”,而其他人要求在星期二删除“1”。你只需要一个合理的一致的最终结果。

答案 1 :(得分:0)

Spring范围单例与线程安全无关,这是两个不同的概念。

singletonprototype bean之间的区别在于,我们如何要求管理bean生命周期的spring容器返回bean:

  • Singleton:确保每次调用bean都返回相同的实例。
  • 原型:调用时返回新实例。

可以通过@autowirdeAppContext.getBean("beanName")来触发调用bean

总之,如果它不是线程安全的,那么一切都取决于注入bean的对象,显然,它不是线程安全的。

要了解更多Spring Bean Scopes

答案 2 :(得分:-1)

  1. 是的,你的假设是正确的:如果你没有在Spring中为你的bean声明一个范围,它就是Singleton by default,这意味着它不是线程安全的。

  2. 由于上面的假设是正确的,对你的问题的简短回答是Spring没有做任何事情来处理单例bean的多线程,因此由你来处理线程安全和并发问题那个豆子。好消息是基于你的EmployeeManager bean的作用以及&#34; 1,2,3&#34;你概述的情况,它实际上并不重要,你也不必做任何事情。

  3. 以下是为什么会出现这种情况的长期答案。与常见的误解相反,没有多个线程同时真正执行的事情。当然,它们似乎可能同时执行,但真正发生的事情是JVM采用其中一个线程,执行其中的一部分,然后以最有效的方式执行(您希望)开始处理另一个线程,然后另一个线程,然后可能是第一个,等等。

    这对你来说并不重要的原因是因为你的bean 没有任何状态。换句话说,您只是传递客户ID,而您并不关心哪些会被删除。

    现在,如果您实际在这些线程中传递了SAME客户对象,那么您可能会遇到问题。最重要的是,一般的经验法则是任何没有状态的bean都可以是单身。您可以阅读关于Spring单例和线程安全的this article,其中详细解释了这一点。

    希望这有帮助。