ThreadLocal是什么?
早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。
java.lang.ThreadLocal提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。
每个线程都保持对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被GC回收(除非存在对这些副本的其他引用)。
ThreadLocal接口方法
- ThreadLocal对外提供了四个方法:
- void set(T value) 设置当前局部变量的值
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
- product T initialValue() 返回该线程局部变量的初始值,该方法是一个protected的方法,方法内部并没有具体实现代码,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。但是如果在调用 get() 后又调用了 remove(),则可能再次调用此方法。
protected T initialValue() {
return null;
}
- public T get() 该方法返回当前线程所对应的线程局部变量。
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
- public void remove() 将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。其实在线程结束后对应的线程变量会自动被GC,主动调用这个方法目的是尽早回收,释放内存.
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
源码浅析
也许我们分析JDK中的源码有些难以理解,我们可以将ThreadLocal类简化一下,简化后的代码如下
class MyThreadLocal<T>{
private Map<Thread,T> map = new HashMap<Thread,T>();
protected T initialValue() {
return null;
}
public void set(T value) {
map.put(Thread.currentThread(), value);
}
public void remove() {
map.remove(Thread.currentThread());
}
public T get() {
return map.get(Thread.currentThread());
}
}
其实在ThreadLocal内部是维护了一个Map来保存数据的,虽然使用的时候获取的是value值,但是其每一个value对应的key都是当前线程,因此相当于将变量给每一个线程复制了一份,各个线程对数据的操作不会互相影响.
实用案例
我们在对数据库进行操作的时候有时候为了数据安全必须加事务操作,但是一般事务管理在Service层,而数据操作在DAO层,一般的做法就是在Service层建立连接然后传参的形式传递给DAO层,这无疑增加了程序的侵入性,其实在Spring的事务管理底层就使用了ThreadLocal来解决这种情况,我们也可以模拟使用ThreadLocal编写一个带有事务操作工具类.
public class JdbcUtils {
private static DataSource dataSource = new ComboPooledDataSource();
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
//获取数据源
public static DataSource getDataSource() {
return dataSource;
}
//获取连接
public static Connection getConnection() throws SQLException {
Connection con = tl.get();
if(con == null) {
return dataSource.getConnection();
}
return con;
}
//开始事务
public static void beginTranscation() throws SQLException {
Connection con = tl.get();
if(con != null ) {
throw new SQLException("事务已经开启,在没有结束当前事务时,不能再开启事务!");
}
con = dataSource.getConnection();
con.setAutoCommit(false);
tl.set(con);
}
//提交事务
public static void commitTransaction() throws SQLException {
Connection con = tl.get();
if(con == null ) {
throw new SQLException("当前没有事务,所以不能提交事务!");
}
con.commit();
con.close();
tl.remove();
}
// 回滚事务
public static void rollbackTransaction() throws SQLException {
Connection con = tl.get();
if(con == null) {
throw new SQLException("当前没有事务,所以不能回滚事务!");
}
con.rollback();
con.close();
tl.remove();
}
}
网友评论