'Spring Singleton beans thread safety

I'm new to Spring and have some basic question.In one of the Spring examples as given below, I noticed EmployeeManager is Autowired.

Question:

  1. 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?
  2. 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 -

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;
    }
}


Solution 1:[1]

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 :

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 before they 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.

Solution 2:[2]

Spring scope singleton has nothing to do with thread safety those are two different concepts.

The difference between singleton & prototype bean is how we ask spring container that manages beans life-cycle to return beans:

  • Singleton: Insure every time you call the bean it return the same instance.
  • Prototype: Return new instance when ever called.

Calling bean can be trigger by either @autowirde or 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.

To learn more Spring Bean Scopes

Solution 3:[3]

  1. 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.

  2. 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.

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.

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.

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 article regarding Spring singletons and thread safety that explains that and much more in great detail.

Hope this helps.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Pelit Mamani
Solution 2 Hussein Akar
Solution 3