Thread safety issues of Spring concurrent access

The following records summarize concurrency in spring. For theoretical analysis, refer to the thread safety of Singleton mode in Spring. It is recommended to read first

Concurrent access problems in spring:

We know that in general, only stateless beans can be shared in a multi-threaded environment. In Spring, most beans can be declared as singleton scope.
So what about stateful beans? Spring uses ThreadLocal to process beans that are not thread-safe (such as RequestContextHolder, TransactionSynchronizationManager, LocaleContextHolder, etc.), making them thread-safe, so stateful beans can be shared among multiple threads.

If you use stateful beans, you can also use the prototype mode to recreate a bean every time you inject, without affecting each other in multiple threads.



Stateful means having data storage capabilities. Stateful objects (Stateful Beans) are objects with instance variables that can store data and are not thread-safe. No state is preserved between method calls.
Stateless is an operation that cannot save data. Stateless object (Stateless Bean) is an object without instance variables. It cannot save data, it is an immutable class, and it is thread-safe.
Stateless beans are suitable for the immutable mode, and the technology is the singleton mode, which can share instances and improve performance.

Stateful Beans are not safe in a multi-threaded environment, so Prototype prototype mode is suitable. Prototype: Every request to the bean creates a new bean instance.

The servlet architecture is built on the Java multi-threading mechanism, and its life cycle is taken care of by the Web container. Only one instance of a Servlet class exists in the Application, that is, multiple threads are using this instance. This is an application of the singleton pattern. For example, the Service layer and the Dao layer can use the default singleton. Although the Service class also has attributes such as dao, these dao classes have no state information, that is, they are equivalent to immutable classes, so they do not affect them. Action in Struts2 has state information because there are instance objects such as User and BizEntity, and it is not safe in a multi-threaded environment, so the default implementation of Struts2 is Prototype mode. In Spring, in the Action of Struts2, the scope should be configured as the prototype scope. (Singleton pattern - singleton registry implementation and threadLocal - can handle the relationship between stateful beans)


There are also our entity beans, which are passed from the client to the controller-->service-->Dao in the background. In this process, these objects are singletons, so these singleton objects are processing our delivery to Wouldn't something go wrong with entity beans in the background?
Answer: [The entity bean is not a singleton], it is not handed over to spring to manage, we manually New each time [such as EMakeType et = new EMakeType();], so even those businesses that process the data we submit The processing class is shared by multiple threads, but the data they process is not shared. Each thread has its own copy of the data, so there will be no thread synchronization problems in terms of data.

(Here, I will add my own processing of entity beans in multi-threading in project development: 1. For entity beans, they are generally passed in the form of method parameters (parameters are local variables), so there will be no impact between multiple threads. 2. In some places, the prototype prototype mode is used directly for stateful beans. 3. For places where beans are used, they can be created by new method)

But those objects in xxxDao in Dao, or xxxService in controller, these objects are singletons, so there will be thread synchronization problems. But then again, although these objects will be accessed concurrently by multiple processes, we access the methods in them. These classes usually do not contain member variables . The ibatisDao in the Dao is encapsulated in the framework. It has been tested and there will be no thread synchronization problems. So the problem is the business objects in our own system, so we must pay attention to these business objects must not have independent member variables , otherwise there will be errors.


When spring uses ThreadLocal to maintain variables for those stateful beans [only variables, because the problem of thread synchronization is the problem of mutually exclusive access to member variables], ThreadLocal provides an independent copy of the variable for each thread that uses the variable, so each A thread can change its own copy independently without affecting the copies of other threads.


The above conclusion can be drawn from the summary of two blogs on the thread safety of concurrent access to spring. . . . .

Since Spring MVC is Singleton by default, there will be a potential security risk. The fundamental core is the problem of maintaining state of instance variables . This means that for each request, the system will use the original instance to process it, which leads to two results (the benefits of a singleton):

一是我们不用每次创建Controller,
二是减少了对象创建和垃圾收集的时间;
由于只有一个Controller的instance,当多个线程同时调用它的时候,它里面的instance变量(可以理解为私有变量)就不是线程安全的了,会发生窜数据的问题。
当然大多数情况下,我们根本不需要考虑线程安全的问题,比如dao,service等,除非在bean中声明了实例变量。因此,我们在使用spring mvc 的contrller时,应避免在controller中定义实例变量(singleton唯一的不好是单例的变量容易出现问题,下面有解决的方案)
如:

public class Controller extends AbstractCommandController {
......
protected ModelAndView handle(HttpServletRequest request,HttpServletResponse response,
			Object command,BindException errors) throws Exception {
company = ................;
}
protected Company company;
}

在这里有声明一个变量company,这里就存在并发线程安全的问题。
如果控制器是使用单例形式,且controller中有一个私有的变量a,所有请求到同一个controller时,使用的a变量是共用的,即若是某个请求中修改了这个变量a,则,在别的请求中能够读到这个修改的内容。。

有几种解决方法:
1、在控制器中不使用实例变量(可以使用方法参数的形式解决,参考博文 Spring Bean Scope 有状态的Bean 无状态的Bean
2、将控制器的作用域从单例改为原型,即在spring配置文件Controller中声明 scope="prototype",每次都创建新的controller
3、在Controller中使用ThreadLocal变量

这几种做法有好有坏,第一种,需要开发人员拥有较高的编程水平与思想意识,在编码过程中力求避免出现这种BUG,

而第二种则是容器自动的对每个请求产生一个实例,由JVM进行垃圾回收,因此做到了线程安全。

使用第一种方式的好处是实例对象只有一个,所有的请求都调用该实例对象,速度和性能上要优于第二种,不好的地方,就是需要程序员自己去控制实例变量的状态保持问题。第二种由于每次请求都创建一个实例,所以会消耗较多的内存空间。

所以在使用spring开发web 时要注意,默认Controller、Dao、Service都是单例的。


【1】SpringMVC多线程环境中如何保证对象的安全性?

代码如下:
 
  

@RequestMapping("/user")@ControllerClass UserController{ @Resource UserService userService; @RequestMapping("/add") public void testA(User user){ userService.add(user); } @RequestMapping("/get") public void testA(int id){ userService.get(id); }}@Service("userService")Class UserService{ public static Map<Integer,User> usersCache = new HashMap<String,User>(); public void add(User user){ usersCache.put(user.getId(),user); } public void get(int id){ usersCache.get(id); }}

此段代码,usersCache对象就是线程不安全的。因为它是静态的全局共享对象。如果有多个线程同时调用add方法,可能会发生用户对象被覆盖的情况,也就是id对应对象不一致,这是多线程编程中最常发生的事情。
所以,可以使用 Collections 工具同步Map。
static Map<Integer, Users> usersCache = Collections.synchronizedMap(new HashMap<Integer, Users>());
研究一下,Spring中的源码,它对常用的开源框架做了大量封装,如, Hibernate中的sessionFactory,就使用的是 org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean,而在 AnnotationSessionFactoryBean的父类LocalSessionFactoryBean中,定义了大量的ThreadLocal来保证多线程的安全性。  
 
 

public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implements BeanClassLoaderAware { private static final ThreadLocal<DataSource> configTimeDataSourceHolder = new ThreadLocal<DataSource>(); private static final ThreadLocal<TransactionManager> configTimeTransactionManagerHolder = new ThreadLocal<TransactionManager>(); private static final ThreadLocal<Object> configTimeRegionFactoryHolder = new ThreadLocal<Object>(); private static final ThreadLocal<CacheProvider> configTimeCacheProviderHolder = new ThreadLocal<CacheProvider>(); private static final ThreadLocal<LobHandler> configTimeLobHandlerHolder = new ThreadLocal<LobHandler>();

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324756161&siteId=291194637