Home  >  Article  >  Java  >  Resolving the contradiction between singleton mode and thread safety under Spring

Resolving the contradiction between singleton mode and thread safety under Spring

不言
不言forward
2018-10-22 17:30:084455browse

The content of this article is about resolving the contradiction between singleton mode and thread safety under Spring. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

How many people often don’t know or ignore multi-threading issues when using the Spring framework?

Because when writing a program or doing unit testing, it is difficult to encounter multi-threading problems because it is not easy to simulate a multi-threading testing environment. Then when multiple threads call the same bean, there will be thread safety issues. If the bean creation mode in Spring is non-singleton, there will be no such problem.

But if you don’t consider potential vulnerabilities, they will become invisible killers of the program and break out when you don’t know it. Moreover, it is usually very troublesome to trigger in the production environment when the program is delivered for use.

Spring uses ThreadLocal to solve thread safety issues

We know that in general, only stateless beans can be shared in a multi-threaded environment. In Spring, most beans can Declared as singleton scope. It is because Spring uses ThreadLocal to process non-thread-safe states in some beans (such as RequestContextHolder, TransactionSynchronizationManager, LocaleContextHolder, etc.), making them thread-safe, because stateful beans can be shared among multiple threads.

General Web applications are divided into three levels: presentation layer, service layer and persistence layer. Corresponding logic is written in different layers, and the lower layer opens function calls to the upper layer through interfaces. Under normal circumstances, all program calls from receiving a request to returning a response belong to the same thread.

ThreadLocal is a good idea to solve thread safety problems. It solves the conflict of concurrent access to variables by providing an independent copy of the variable for each thread. In many cases, ThreadLocal is simpler and more convenient than directly using the synchronized synchronization mechanism to solve thread safety problems, and the resulting program has higher concurrency.

If there are multiple threads running at the same time in the process where your code is located, these threads may run this code at the same time. If the results of each run are the same as those of single-threaded runs, and the values ​​of other variables are the same as expected, it is thread-safe. In other words: the interface provided by a class or program is an atomic operation for threads or switching between multiple threads will not cause ambiguity in the execution results of the interface, which means that we do not need to consider synchronization issues. Thread safety issues are caused by global variables and static variables.

If there are only read operations and no write operations on global variables and static variables in each thread, generally speaking, this global variable is thread-safe; if multiple threads perform write operations at the same time , generally thread synchronization needs to be considered, otherwise thread safety may be affected.
1) Constants are always thread-safe because there are only read operations.
2) Creating a new instance before each method call is thread-safe because shared resources will not be accessed.
3) Local variables are thread-safe. Because every time a method is executed, a local variable will be created in a separate space, which is not a shared resource. Local variables include method parameter variables and method-internal variables.

Being stateful means having data storage function. Stateful objects (Stateful Beans) are objects with instance variables that can save data and are not thread-safe. No state is retained between method calls.

Stateless is an operation and cannot save data. A stateless object (Stateless Bean) is an object without instance variables. It cannot save data, it is an immutable class, and it is thread-safe.

Stateful objects:

Stateless beans are suitable for immutable mode. The technology is singleton mode, so that instances can be shared and performance improved. Stateful beans are not safe in a multi-threaded environment, so the Prototype prototype mode is suitable. Prototype: Each request for a bean creates a new bean instance.

The default implementation of Struts2 is Prototype mode. That is, a new Action instance is generated for each request, so there is no thread safety issue. It should be noted that if the life cycle of the action is managed by Spring, the scope must be configured as the prototype scope

Thread safety case

SimpleDateFormat (hereinafter referred to as sdf) class internal There is a Calendar object reference, which is used to store date information related to this sdf, such as sdf.parse(dateStr), sdf.format(date) and so on. The date-related String, Date, etc. passed in by method parameters are all friends. Calendar reference to store. This will cause a problem. If your sdf is static, then multiple threads will share this sdf, and also share this Calendar reference, and, observe the sdf.parse() method, You will find the following calls:

 Date parse() {
   calendar.clear(); // 清理calendar
   ... // 执行一些操作, 设置 calendar 的日期什么的
   calendar.getTime(); // 获取calendar的时间
 }

The problem that will arise here is that if thread A calls sdf.parse(), and before calendar.getTime() is executed after calendar.clear(), thread B calls sdf.parse again. (), at this time thread B also executed the sdf.clear() method, which caused thread A's calendar data to be cleared (actually A and B were cleared at the same time). Or when A executed calendar. After clear(), it is suspended. At this time, B starts to call sdf.parse() and ends smoothly. In this way, the date stored in A's calendar becomes the date of the calendar set by B later

This problem There is a more important issue hidden behind it - statelessness: One of the benefits of the stateless method is that it can be safely called in various environments. To measure whether a method is stateful, it depends on whether it changes other things, such as global variables and instance fields. The format method changes the calendar field of SimpleDateFormat during the running process, so it is stateful.

This also reminds us to pay attention to the following three points when developing and designing the system:

When writing public classes by yourself, you must comment on the consequences of multi-threaded calls. Clearly explain

In a thread environment, we must pay attention to the thread safety of each shared variable variable

When designing our classes and methods, we must try our best to design them as seamless Status

Solution

1. Create a new instance when needed:

Instructions: Use SimpleDateFormat when needed Create a new instance in the place where you want to create a new instance. Whenever you change an object with thread safety issues from shared to locally private, you can avoid multi-threading problems, but it also increases the burden of creating objects. Under normal circumstances, the impact on performance is not very obvious.

2. Use synchronization: Synchronize SimpleDateFormat object

public class DateSyncUtil {
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
      
    public static String formatDate(Date date)throws ParseException{
        synchronized(sdf){
            return sdf.format(date);
        }  
    }
    
    public static Date parse(String strDate) throws ParseException{
        synchronized(sdf){
            return sdf.parse(strDate);
        }
    } 
}

Description: When there are many threads, when one thread calls this method, other threads that want to call this method It is necessary to block. When the amount of multi-thread concurrency is large, it will have a certain impact on performance.

3. Use ThreadLocal:

public class ConcurrentDateUtil {
    private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() {
        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    };
    public static Date parse(String dateStr) throws ParseException {
        return threadLocal.get().parse(dateStr);
    }
    public static String format(Date date) {
        return threadLocal.get().format(date);
    }
}

or

ThreadLocal<DateFormat>(); 
 
    public static DateFormat getDateFormat()   
    {  
        DateFormat df = threadLocal.get();  
        if(df==null){  
            df = new SimpleDateFormat(date_format);  
            threadLocal.set(df);  
        }  
        return df;  
    }  
    public static String formatDate(Date date) throws ParseException {
        return getDateFormat().format(date);
    }
    public static Date parse(String strDate) throws ParseException {
        return getDateFormat().parse(strDate);
    }   
}

Note: Using ThreadLocal also makes shared variables exclusive, and thread exclusiveness is definitely Compared with method exclusivity, it can reduce a lot of the overhead of creating objects in a concurrent environment. If the performance requirements are relatively high, this method is generally recommended.

4. Abandon JDK and use time formatting classes in other libraries:

Use FastDateFormat in Apache commons, claiming to be fast and thread-safe SimpleDateFormat , Unfortunately it can only format dates and cannot parse date strings.

Use the Joda-Time class library to handle time-related issues

Do a simple stress test. Method one is the slowest and method three is the fastest, but even the slowest method has poor performance. Not bad, the general system method 1 and method 2 can be satisfied, so it is difficult to become the bottleneck of your system at this point. From a simple perspective, it is recommended to use method one or method two. If you are pursuing a little performance improvement when necessary, you can consider using method three, using ThreadLocal for caching.

Joda-Time class library is perfect for time processing and is recommended to be used.

Summary

Back to the question at the beginning of the article: "How many people often don't know or ignore the issue of multi-threading when using the Spring framework?" 》

In fact, anyone can write code. Why is the effect of the code written by the architect so different from yours? It should be this kind of small problem that you didn't consider but the architect took it into consideration.

Architects have broader knowledge, have seen more specific situations, and have richer experience in solving various problems. As long as you develop the thinking and habits of an architect, will you still be far away from being an architect?


The above is the detailed content of Resolving the contradiction between singleton mode and thread safety under Spring. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:segmentfault.com. If there is any infringement, please contact admin@php.cn delete