理解1
ThreadLocal,很多地方叫做线程本地变量,也有些地方叫做线程本地存储,其实意思差不多。可能很多朋友都知道ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
这句话从字面上看起来很容易理解,但是真正理解并不是那么容易。
我们还是先来看一个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class
ConnectionManager {
private
static
Connection connect =
null
;
public
static
Connection openConnection() {
if
(connect ==
null
){
connect = DriverManager.getConnection();
}
return
connect;
}
public
static
void
closeConnection() {
if
(connect!=
null
)
connect.close();
}
}
|
假设有这样一个数据库链接管理类,这段代码在单线程中使用是没有任何问题的,但是如果在多线程中使用呢?很显然,在多线程中使用会存在线程安全问题:第一,这里面的2个方法都没有进行同步,很可能在openConnection方法中会多次创建connect;第二,由于connect是共享变量,那么必然在调用connect的地方需要使用到同步来保障线程安全,因为很可能一个线程在使用connect进行数据库操作,而另外一个线程调用closeConnection关闭链接。
用ThreadLocal 写法 :
1
2
3
4
5
6
7
8
9
10
|
private
static
ThreadLocal<Connection> connectionHolder
=
new
ThreadLocal<Connection>() {
public
Connection initialValue() {
return
DriverManager.getConnection(DB_URL);
}
};
public
static
Connection getConnection() {
return
connectionHolder.get();
}
|
这样,如果多线程同时访问 ,着对 改线程来说,每个线程 都有自己的 Connection,这样 其他的connect 或者 Close, 操作的都是自己 Connection.
理解2
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。我们自己就可以提供一个简单的实现版本:
public class SimpleThreadLocal {
private Map valueMap = Collections.synchronizedMap(new HashMap());
public void set(Object newValue) {
valueMap.put(Thread.currentThread(), newValue); //①键为线程对象,值为本线程的变量副本
}
public Object get() {
Thread currentThread = Thread.currentThread();
Object o = valueMap.get(currentThread); //②返回本线程对应的变量
if ((o == null) && !valueMap.containsKey(currentThread)) { //③如果在Map中不存在,放到Map中保存起来。
o = initialValue();
valueMap.put(currentThread, o);
}
return o;
}
public void remove() {
valueMap.remove(Thread.currentThread());
}
public Object initialValue() {
return null;
}
}
下面的实例能够体现Spring对有状态Bean的改造思路:
代码清单3 TopicDao:非线程安全
public class TopicDao {
private Connection conn;①一个非线程安全的变量
public void addTopic(){
Statement stat = conn.createStatement();②引用非线程安全变量
…
}
}
由于①处的conn是成员变量,因为addTopic()方法是非线程安全的,必须在使用时创建一个新TopicDao实例(非singleton)。下面使用ThreadLocal对conn这个非线程安全的“状态”进行改造:
代码清单4 TopicDao:线程安全
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() {
④从ThreadLocal中获取线程对应的Connection
Statement stat = getConnection().createStatement();
}
}
参考文章
http://www.cnblogs.com/dolphin0520/p/3920407.html
http://justsee.iteye.com/blog/791919