2014-11-24 102 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」。你只需要一個合理的一致的最終結果。