Introduction to Java ThreadLocal

ThreadLocal plays an important role in the Spring, the management request scoped Bean, transaction management, task scheduling, AOP and other modules have emerged in their shadow, it plays an important role. To understand the underlying technology Spring transaction management, ThreadLocal must overcome a mountain fortress.

We know that spring to reduce the difficulty developers use a variety of data persistence technology through a variety of template class. These template classes are thread-safe, that is, more than DAO can reuse the same template instantiation without conflict. We use a template class access to the underlying data, depending on the persistence technology, the need to bind the template class resource data connection or session. But which is not thread safe these resources, which means that they can not be shared by multiple threads at the same time. Although the template class to obtain data connection or session, but the resource pool itself solve the data connection or session cache problem, not a security thread connection or session data through the resource pool.

According to conventional wisdom, if an object is not thread-safe in a multithreaded environment, access to the object must be synchronized thread synchronization. But the template class does not use thread synchronization mechanism, because the thread synchronization reduces concurrency, impact system performance. In addition, the synchronous address the challenges of large thread-safe through the code, you may difficult to achieve enhanced several times. So what exactly template class rely on magic magic, you can resolve the problem in a thread-safe without having to thread synchronization it? The answer is ThreadLocal!

ThreadLocal plays an important role in the Spring, the management request scoped Bean, transaction management, task scheduling, AOP and other modules have emerged in their shadow, it plays an important role. To understand the underlying technology Spring transaction management, ThreadLocal must overcome a mountain fortress.

What is ThreadLocal

As early version of the JDK 1.2 provides Java.lang.ThreadLocal, ThreadLocal provide a new way to solve the problem of concurrent multi-threaded programs. Using this tool can be very simple class to write a beautiful multi-threaded programs.
ThreadLocal, as the name suggests, it is not a thread but a thread of localized objects. When maintenance work using ThreadLocal variables in multi-threaded objects, ThreadLocal thread allocation of the variable using a separate copy for each variable. Therefore, each thread can independently change their copy without affecting other threads corresponding copy. From the point of view of threads, this variable is like a thread local variable, which is meant to express the class name "Local".

Java thread local variables are not new inventions, many languages ​​(such as IBM XL, FORTRAN) in the syntax level provides thread-local variables. It does not provide language-level support in Java, but with an alternative method, providing support through ThreadLocal class. Therefore, the code to write thread-local variables in Java to be relatively few clumsy, which is why the thread-local variables are not good reasons for the popularity of Java developers in.

ThreadLocal interface methods

ThreadLocal class interface is very simple, only four methods, let's find out.
void set (Object value)
Sets the current thread's value of the thread local variables;
public Object GET ()
This method returns the thread-local variable current thread corresponding to;
public void Remove ()
to delete the value of the current thread-local variables, the purpose of reduce memory usage, which is a new method for JDK 5.0. It should be noted that, when the end of the thread, the thread local variable should automatically be garbage, so explicitly call this method to clear the thread local variable is not required to operate, but it can speed up the recovery of memory speed;
protected Object the initialValue ()
returns to the initial value of the thread local variables, which is a method of a protected, apparently to allow subclasses designed cover. This method is a method to delay calling, call get () or set (Object) when the execution thread the first time, and only performed once. The default ThreadLocal in direct returns a null.

It is worth mentioning that in JDK5.0, ThreadLocal has support for generics, the class of the class name has become ThreadLocal. API methods been adjusted accordingly, the new version of the API methods are void set (T value), T get () and T initialValue ().

How do ThreadLocal is to maintain a copy of each thread variables it? In fact, the idea is very simple realization: There is a Map in ThreadLocal class, a thread for each copy of the variable storage, key elements in the Map is thread object, and the value of the variable corresponding to a copy of the thread. We ourselves can provide a simple implementation version:

Listing 9-3 SimpleThreadLocal

       
       
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
       
       
public class {
private Map valueMap = Collections.synchronizedMap( new HashMap());
public void set(Object newValue) {
valueMap.put(Thread.currentThread(), newValue);
}
public Object get() {
Thread currentThread = Thread.currentThread();
// ② return variables corresponding to this thread
Object o = valueMap.get(currentThread);
// ③ If there is no Map, save them into the Map
if (o == null && !valueMap.containsKey(currentThread)) {
o = initialValue();
valueMap.put(currentThread, o);
}
return o;
}
public void remove() {
valueMap.remove(Thread.currentThread());
}
public Object initialValue() {
return null;
}
}

虽然代码清单9 3中这个ThreadLocal实现版本显得比较幼稚,但它和JDK所提供的ThreadLocal类在实现思路上是非常相近的。

一个TheadLocal实例
下面,我们通过一个具体的实例了解一下ThreadLocal的具体使用方法。
代码清单9-4 SequenceNumber

       
       
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
       
       
package com.baobaotao.basic;
public class SequenceNumber {
//①通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值
private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>(){
public Integer initialValue(){
return 0;
}
};
//②获取下一个序列值
public int getNextNum(){
seqNum.set(seqNum.get()+ 1);
return seqNum.get();
}
public static void main(String[ ] args)
{
SequenceNumber sn = new SequenceNumber();
//③ 3个线程共享sn,各自产生序列号
TestClient t1 = new TestClient(sn);
TestClient t2 = new TestClient(sn);
TestClient t3 = new TestClient(sn);
t1.start();
t2.start();
t3.start();
}
private static class TestClient extends Thread
{
private SequenceNumber sn;
public TestClient(SequenceNumber sn) {
this.sn = sn;
}
public void run()
{
//④每个线程打出3个序列值
for ( int i = 0; i < 3; i++) {
System.out.println( "thread["+Thread.currentThread().getName()+
"] sn["+sn.getNextNum()+ "]");
}
}
}
}

通常我们通过匿名内部类的方式定义ThreadLocal的子类,提供初始的变量值,如①处所示。TestClient线程产生一组序列号,在③处,我们生成3个TestClient,它们共享同一个SequenceNumber实例。运行以上代码,在控制台上输出以下的结果:

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]

考查输出的结果信息,我们发现每个线程所产生的序号虽然都共享同一个Sequence Number实例,但它们并没有发生相互干扰的情况,而是各自产生独立的序列号,这是因为我们通过ThreadLocal为每一个线程提供了单独的副本。

与Thread同步机制的比较

ThreadLocal和线程同步机制相比有什么优势呢?ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。

在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序缜密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。

而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal为每一个线程提供一个独立的变量副本,从而隔离了多个线程对访问数据的冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的对象封装,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

由于ThreadLocal中可以持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,需要强制类型转换。但JDK 5.0通过泛型很好的解决了这个问题,在一定程度上简化ThreadLocal的使用,代码清单9-2就使用了JDK 5.0新的ThreadLocal版本。

概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式:访问串行化,对象共享化。而ThreadLocal采用了“以空间换时间”的方式:访问并行化,对象独享化。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

Spring使用ThreadLocal解决线程安全问题

我们知道在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全的“状态性对象”采用ThreadLocal进行封装,让它们也成为线程安全的“状态性对象”,因此有状态的Bean就能够以singleton的方式在多线程中正常工作了。

一般的Web应用划分为展现层、服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程,如图9-2所示。

这样用户就可以根据需要,将一些非线程安全的变量以ThreadLocal存放,在同一次请求响应的调用线程中,所有对象所访问的同一ThreadLocal变量都是当前线程所绑定的。
下面的实例能够体现Spring对有状态Bean的改造思路:

代码清单9-5 TopicDao:非线程安全

       
       
1
2
3
4
5
6
7
8
9
       
       
public class TopicDao {
//①一个非线程安全的变量
private Connection conn;
public void addTopic(){
//②引用非线程安全变量
Statement stat = conn.createStatement();
}
}

由于①处的conn是成员变量,因为addTopic()方法是非线程安全的,必须在使用时创建一个新TopicDao实例(非singleton)。下面使用ThreadLocal对conn这个非线程安全的“状态”进行改造:

代码清单9-6 TopicDao:线程安全

       
       
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
       
       
import java.sql.Connection;
import java.sql.Statement;
public class TopicDao {
//①使用ThreadLocal保存Connection变量
private static ThreadLocal<Connection> connThreadLocal = new ThreadLocal<Connection>();
public static Connection getConnection(){
//②如果connThreadLocal没有本线程对应的Connection创建一个新的Connection,
//并将其保存到线程本地变量中。
if (connThreadLocal.get() == null) {
Connection conn = ConnectionManager.getConnection();
connThreadLocal.set(conn);
return conn;
} else{
//③直接返回线程本地变量
return connThreadLocal.get();
}
}
public void addTopic () {
// ④ obtain the corresponding thread from a ThreadLocal
Statement stat = getConnection().createStatement();
}
}

When using different threads TopicDao, first determine connThreadLocal.get () whether null, if null, then the current thread has no corresponding Connection object, then creates a Connection object and thread to the local variables; if not is null, then the current thread already owns the Connection object to use it. Thus, to ensure that different threads use thread-related Connection, Connection without using other threads. Therefore, this can be done TopicDao singleton shared.

Of course, this example itself is very rough, the Connection's ThreadLocal directly on the Dao Dao can do more of this method of thread safety problem does not occur when the Connection Sharing, but can not share the same with other Dao Connection, to do the same transaction multi-Dao share the same Connection, you must use the ThreadLocal Connection saved in a common external class. But basically this example illustrates the Spring Solutions for thread-safe class of stateful. In this chapter the following pages, we will explain in detail how the Spring transaction management to solve the problem through ThreadLocal.

Original: Big Box  Java ThreadLocal Profile


Guess you like

Origin www.cnblogs.com/petewell/p/11615237.html