How Spring handles thread concurrency

How Spring handles thread concurrency

We know that Spring reduces the difficulty of developers using various data persistence technologies through various DAO template classes. These template classes are thread-safe, that is, multiple DAOs can reuse the same template instance without conflict.
We use the template class to access the underlying data. Depending on the persistence technology, the template class needs to bind data connection or session resources. But these resources themselves are not thread-safe, which means they cannot be shared by multiple threads at the same time.
Although the template class obtains the data connection or session through the resource pool, the resource pool itself solves the problem of caching the data connection or session, not the thread safety of the data connection or session.
According to traditional experience, if an object is not thread-safe, in a multi-threaded environment, the access to the object must be synchronized for thread synchronization. However, Spring's DAO template class does not use thread synchronization mechanism, because thread synchronization limits concurrent access, which will bring great performance loss.
In addition, addressing performance safety issues through code synchronization is challenging and may increase the implementation difficulty several times. So what kind of magic power does the template class rely on to solve the problem of thread safety without synchronization? The answer is ThreadLocal!
ThreadLocal plays an important role in Spring, and they appear in modules such as beans, transaction management, task scheduling, and AOP that manage the request scope, and play a pivotal role. To understand the underlying technology of Spring transaction management, ThreadLocal is a hilltop fortress that must be overcome.
What is ThreadLocal? java.lang.ThreadLocal was provided
as early as the JDK1.2 version. ThreadLocal provides a new idea for solving the concurrency problem of multi-threaded programs. Using this utility class, you can write beautiful multi-threaded programs very concisely.
ThreadLocal is easy to take for granted and take for granted that it is a "local thread". In fact, ThreadLocal is not a Thread, but a local variable of Thread. Maybe it is easier to understand it by naming it ThreadLocalVariable.
When using ThreadLocal to maintain variables, ThreadLocal provides an independent copy of the variable for each thread that uses the variable, so each thread can change its own copy independently without affecting the copies corresponding to other threads.
From the thread's point of view, the target variable is like the thread's local variable, which is what the "Local" in the class name means.
Thread local variables are not a new invention of Java, many languages ​​(such as IBM IBM XLFORTRAN) provide thread local variables at the syntax level. There is no language-level support in Java, but disguised support is provided through the ThreadLocal class.
So, coding thread-local variables in Java is relatively unwieldy, so thread-local variables are not very popular among Java developers.
The interface method of ThreadLocal The
ThreadLocal class interface is very simple. There are only 4 methods. Let's take a look at it first:
void set(Object value)

Sets the value of a thread-local variable for the current thread.
public Object get()

This method returns the thread local variable corresponding to the current thread.
public void remove()

The purpose of deleting the value of the current thread local variable is to reduce the memory usage. This method is a new method in JDK5.0. It should be pointed out that when the thread ends, the local variables corresponding to the thread will be automatically garbage collected, so it is not necessary to explicitly call this method to clear the local variables of the thread, but it can speed up the speed of memory reclamation.
protected Object initialValue()

Returns the initial value of the thread-local variable. This method is a protected method, apparently designed for subclasses to override. This method is a deferred call method that is executed when the thread calls get() or set(Object) for the first time, and only once. The default implementation in ThreadLocal simply returns a null.


It is worth mentioning that in JDK5.0, ThreadLocal already supports generics, and the class name of this class has been changed to ThreadLocal<T>. The API methods have also been adjusted accordingly. The API methods of the new version are voidset(T value), T get() and T initialValue().
How does ThreadLocal maintain a copy of the variable for each thread? In fact, the idea of ​​​​implementation is very simple: there is a Map in the ThreadLocal class, which is used to store the variable copy of each thread. The key of the element in the Map is the thread object, and the value corresponds to the variable copy of the thread. We can provide a simple implementation version ourselves:
01 // Code Listing 1 SimpleThreadLocal
02	class SimpleThreadLocal {
03	    private MapvalueMap = Collections.synchronizedMap(new HashMap());
04	    public voidset(Object newValue) {
05 valueMap.put(Thread.currentThread(), newValue);//①The key is the thread object, and the value is the variable copy of this thread
06	    }
07	    publicObject get() {
08	       Thread currentThread = Thread.currentThread();
09 Object o = valueMap.get(currentThread);// ② Return the variable corresponding to this thread
10 if (o == null &&!valueMap.containsKey(currentThread)) {// ③If it does not exist in the Map, put it in the Map
11 // saved in .
12	           o = initialValue();
13	           valueMap.put(currentThread, o);
14	       }
15	       return o;
16	    }
17	    public voidremove() {
18	       valueMap.remove(Thread.currentThread());
19	    }
20	    publicObject initialValue() {
21	       return null;
22	    }
23	}

Although the ThreadLocal implementation version in Listing 9-3 is relatively naive, it is similar to the ThreadLocal class provided by the JDK in terms of implementation ideas.
An instance of TheadLocal
Next , let's take a look at the specific usage of ThreadLocal through a specific instance
01	package threadLocalDemo;
02	public class SequenceNumber {
03 //①Override the initialValue() method of ThreadLocal through the anonymous inner class to specify the initial value
04	    privatestatic ThreadLocal<Integer> seqNum =new ThreadLocal<Integer>() {
05	       public Integer initialValue() {
06	           return 0;
07	       }
08	    };
09 //②Get the next sequence value
10	    public intgetNextNum() {
11	       seqNum.set(seqNum.get() + 1);
12	       return seqNum.get();
13	    }
14	    publicstatic void main(String[] args)
15	    {
16	       SequenceNumber sn = new SequenceNumber();
17 // ③ 3 threads share sn, each generating serial numbers
18	       TestClient t1 = new TestClient(sn);
19	       TestClient t2 = new TestClient(sn);
20	       TestClient t3 = new TestClient(sn);
21	       t1.start();
22	       t2.start();
23	       t3.start();
24	    }
25	    privatestatic class TestClient extends Thread
26	    {
27	       private SequenceNumber sn;
28	       public TestClient(SequenceNumber sn) {
29	           this.sn = sn;
30	       }
31	       public void run()
32	       {
33	           for (int i = 0; i < 3; i++) {
34 // ④ Each thread prints 3 sequence values
35	               System.out.println("thread[" + Thread.currentThread().getName()+"]sn[" + sn.getNextNum() + "]");
36	           }
37	       }
38	    }
39	}

Usually, we define a subclass of ThreadLocal by way of an anonymous inner class, and provide the initial variable value, as shown in ① in the example. The TestClient thread generates a set of sequence numbers. At ③, we generate 3 TestClients that share the same SequenceNumber instance. Running the above code will output the following results on the console:
thread[Thread-2] sn[1]
thread[Thread-0] sn[1]
thread[Thread-1] sn[1]
thread[Thread-2] sn[2]
thread[Thread-0] sn[2]
thread[Thread-1] sn[2]
thread[Thread-2] sn[3]
thread[Thread-0] sn[3]
thread[Thread-1] sn[3]

Looking at the output result information, we found that although the sequence numbers generated by each thread share the same SequenceNumber instance, they do not interfere with each other, but each generate independent sequence numbers. This is because we pass ThreadLocal for each thread. A thread provides a separate copy.
Comparison
of Thread Synchronization Mechanisms What are the advantages of ThreadLocal and thread synchronization mechanisms? Both ThreadLocal and thread synchronization mechanisms are designed to solve the access conflict problem of the same variable in multiple threads.
In the synchronization mechanism, the lock mechanism of the object ensures that only one thread accesses the variable at the same time. At this time, the variable is shared by multiple threads. Using the synchronization mechanism requires the program to carefully analyze when to read and write the variable, when to lock an object, and when to release the object lock and other complicated issues. Program design and writing relatively difficult.
ThreadLocal solves the concurrent access of multiple threads from another perspective. ThreadLocal will provide each thread with an independent copy of the variable, thereby isolating the access conflict of multiple threads to the data. Since each thread has its own copy of the variable, there is no need to synchronize the variable. ThreadLocal provides thread-safe shared objects. When writing multi-threaded code, unsafe variables can be encapsulated into ThreadLocal.
Since any type of object can be held in ThreadLocal, the get() provided by the lower version of JDK returns an Object object, which requires type conversion. But JDK5.0 solves this problem very well through generics, which simplifies the use of ThreadLocal to a certain extent. Code Listing 92 uses the new ThreadLocal<T> version of JDK5.0.
To sum up, for the problem of multi-threaded resource sharing, the synchronization mechanism adopts the method of "exchanging time for space", while ThreadLocal adopts the method of "exchanging space for time". The former only provides a variable for different threads to queue for access, while the latter provides a variable for each thread, so it can be accessed at the same time without affecting each other.
Spring uses ThreadLocal to solve thread safety problems.
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 scopes. 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 layers: presentation layer, service layer and persistence layer. Corresponding logic is written in different layers, and the lower layer opens functions to the upper layer through interfaces. In general, all program calls from receiving a request to returning a response belong to the same thread, as shown in Figure 9-2:

Figure 1
The The variables of ThreadLocal are stored in ThreadLocal. In the calling thread of the same request response, all associated objects refer to the same variable.
The following example can reflect Spring's transformation of stateful beans:
Code Listing 3 TopicDao: Non-thread-safe
1	public class TopicDao {
2 private Connection conn; ① a non-thread-safe variable
3	public void addTopic(){
4 Statement stat = conn.createStatement(); ② Refers to non-thread-safe variables
5	…
6	}
7	}

Since the conn at ① is a member variable, because the addTopic() method is not thread-safe, a new TopicDao instance (non-singleton) must be created when it is used. The following uses ThreadLocal to transform the non-thread-safe "state" of conn:
Code Listing 4 TopicDao: Thread Safety
01	package threadLocalDemo;
02	import java.sql.Connection;
03	import java.sql.SQLException;
04	import java.sql.Statement;
05	public class SqlConnection {
06 //①Use ThreadLocal to save the Connection variable
07	    privatestatic ThreadLocal<Connection>connThreadLocal = newThreadLocal<Connection>();
08	    publicstatic Connection getConnection() {
09 // ② If connThreadLocal does not have a connection corresponding to this thread, create a new Connection,
10 // and save it to a thread local variable.
11	       if (connThreadLocal.get() == null) {
12	           Connection conn = getConnection();
13	           connThreadLocal.set(conn);
14	           return conn;
15	       } else {
16	           return connThreadLocal.get();
17 // ③ Return directly to thread local variables
18	       }
19	    }
20	    public voidaddTopic() {
21 // ④ Get the Connection corresponding to the thread from ThreadLocal
22	       try {
23	           Statement stat = getConnection().createStatement();
24	       } catch (SQLException e) {
25 e.printStackTrace ();
26	       }
27	    }
28	}

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326958753&siteId=291194637