2014-11-24 75 views
0

我是Spring的新手,有一些基本的问题。在下面给出的Spring例子之一中,我注意到EmployeeManager是Autowired。Spring Singleton bean线程安全

问:

  1. 的EmployeeManager范围不给,所以我会假设默认范围是辛格尔顿和Spring bean是不是线程安全的。这个假设是正确的吗?
  2. EmployeeManager被定义为可以被多个线程访问的Servlet的一部分。 假设多个线程同时使用值“1”“2”&“3”调用“删除”方法,并为每个线程(自其SINGLETON以来)生成同一个EmployeeManager实例,该删除值将被执行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; 
    } 
} 
+0

也许阅读答案http://stackoverflow.com/questions/11508405/are-spring-mvc-controllers-singletons将有所帮助。 – Atul 2014-11-24 08:30:10

回答

0
  1. 是的,你的假设是正确的:如果你不声明范围在春天你的bean,它是一个Singleton by default,这意味着它不是线程安全的。

  2. 由于上述假设是真实的,所以您的问题的简短答案是Spring不会处理单线程bean的多线程问题,因此您需要处理线程安全性和并发问题那个豆子。好消息是,根据你的豆做什么和你所概述的“1,2,3”情景,这并不重要,你也不需要做任何事情。

下面是为什么情况如此漫长的答案。与普遍的误解相反,不存在多个线程同时真正执行的情况。当然,它们可能似乎是在同一时间执行的,但真正发生的是,JVM接受其中一个线程,执行其中的一部分,然后以最有效的方式(您希望)开始工作另一个线程,然后又一次,也许是第一次,等

之所以这样,甚至没有怎么了你是因为你的bean 没有任何状态。换句话说,您只是传递客户ID,而您并不在乎首先被删除。

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

希望这会有所帮助。

+1

“与普遍的误解相反,不存在多个线程同时真正执行的情况 - ”这听起来对我来说不对 - 您能否提供引用?另外,bean * does *有状态,它只是不可变...... – Gareth 2016-08-03 13:15:07

+0

@Gareth - 你不会得到一个体面的引用,因为该陈述是错误的。如果您的计算机具有多个内核(并且典型的现代PC或服务器硬件属于此类别),则JVM中的不同线程可以在不同核心上同时执行。 – 2018-02-25 07:26:18

0

我同意上面的答案。只是想强调一下,唯一的保护就是“@Transactional”位,这意味着Spring会用一个在每个方法的开始/结束处咨询transactionManager的实例替换EmployeeManagerImpl。Roughtly说:

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,事务隔离级别,和你的数据源如何与它进行交互。在简单情况下,你的线程将被迫等待;在其他情况下,数据库可以容忍一些并发访问。这种魔力归结为交易,而不是春天。 另外,在之前的线程上没有控制权,他们到达交易。如果3个不同的线程要求删除1,2和3,那么您无法确定哪个线程会首先进入'startTransaction'。但这并不重要 - 你可能会遇到这样的情况:有人要求在星期天删除“2”,其他人要求在星期一删除“3”,而其他人则要求在星期二删除“1”。你只需要一个合理的一致的最终结果。