Java线程(十二):线程本地变量ThreadLocal

小咪咪 2022-03-10 14:42 354阅读 0赞

https://blog.csdn.net/persistencegoing/article/details/84376427

首先说明ThreadLocal存放的值是线程内共享的,线程间互斥的,主要用于线程内共享一些数据,避免通过参数来传递,这样处理后,能够优雅的解决一些实际问题,比如Hibernate中的OpenSessionInView,就是使用ThreadLocal保存Session对象,还有我们经常用ThreadLocal存放Connection,代码如:

/**
* 数据库连接管理类
* @author 爽
*
*/
public class ConnectionManager {

  1. /\*\* 线程内共享Connection,ThreadLocal通常是全局的,支持泛型 \*/
  2. private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
  3. public static Connection getCurrConnection() \{
  4. // 获取当前线程内共享的Connection
  5. Connection conn = threadLocal.get();
  6. try \{
  7. // 判断连接是否可用
  8. if(conn == null || conn.isClosed()) \{
  9. // 创建新的Connection赋值给conn(略)
  10. // 保存Connection
  11. threadLocal.set(conn);
  12. \}
  13. \} catch (SQLException e) \{
  14. // 异常处理
  15. \}
  16. return conn;
  17. \}
  18. /\*\*
  19. \* 关闭当前数据库连接
  20. \*/
  21. public static void close() \{
  22. // 获取当前线程内共享的Connection
  23. Connection conn = threadLocal.get();
  24. try \{
  25. // 判断是否已经关闭
  26. if(conn != null && !conn.isClosed()) \{
  27. // 关闭资源
  28. conn.close();
  29. // 移除Connection
  30. threadLocal.remove();
  31. conn = null;
  32. \}
  33. \} catch (SQLException e) \{
  34. // 异常处理
  35. \}
  36. \}

}
这样处理的好处:

统一管理Connection;
不需要显示传参Connection,代码更优雅;
降低耦合性。
ThreadLocal有四个方法,分别为:

initialValue
protected T initialValue()
返回此线程局部变量的当前线程的初始值。最多在每次访问线程来获得每个线程局部变量时调用此方法一次,即线程第一次使用 get() 方法访问变量的时候。如果线程先于 get 方法调用 set(T) 方法,则不会在线程中再调用 initialValue 方法。
该实现只返回 null;如果程序员希望将线程局部变量初始化为 null 以外的某个值,则必须为 ThreadLocal 创建子类,并重写此方法。通常,将使用匿名内部类。initialValue 的典型实现将调用一个适当的构造方法,并返回新构造的对象。

返回:
返回此线程局部变量的初始值
get
public T get()
返回此线程局部变量的当前线程副本中的值。如果这是线程第一次调用该方法,则创建并初始化此副本。
返回:
此线程局部变量的当前线程的值
set
public void set(T value)
将此线程局部变量的当前线程副本中的值设置为指定值。许多应用程序不需要这项功能,它们只依赖于 initialValue() 方法来设置线程局部变量的值。
参数:
value - 存储在此线程局部变量的当前线程副本中的值。
remove
public void remove()
移除此线程局部变量的值。这可能有助于减少线程局部变量的存储需求。如果再次访问此线程局部变量,那么在默认情况下它将拥有其 initialValue。
很多人对ThreadLocal存在一定的误解,说ThreadLocal中有一个全局的Map,set时执行map.put(Thread.currentThread(), value),get和remove时也同理,但SUN的大师们是否是如此实现的,我们只能去看源码了。

  1. set方法:

/**
* Sets the current thread’s copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread’s copy of
* this thread-local.
*/
public void set(T value) {
// 获取当前线程对象
Thread t = Thread.currentThread();
// 获取当前线程本地变量Map
ThreadLocalMap map = getMap(t);
// map不为空
if (map != null)
// 存值
map.set(this, value);
else
// 创建一个当前线程本地变量Map
createMap(t, value);
}

/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
// 获取当前线程的本地变量Map
return t.threadLocals;
}
这里注意,ThreadLocal中是有一个Map,但这个Map不是我们平时使用的Map,而是ThreadLocalMap,ThreadLocalMap是ThreadLocal的一个内部类,不对外使用的。当使用ThreadLocal存值时,首先是获取到当前线程对象,然后获取到当前线程本地变量Map,最后将当前使用的ThreadLocal和传入的值放到Map中,也就是说ThreadLocalMap中存的值是[ThreadLocal对象, 存放的值],这样做的好处是,每个线程都对应一个本地变量的Map,所以一个线程可以存在多个线程本地变量。

  1. get方法:

/**
* Returns the value in the current thread’s copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread’s value of this thread-local
*/
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();
}
有了之前set方法的分析,get方法也同理,需要说明的是,如果没有进行过set操作,那从ThreadLocalMap中拿到的值就是null,这时get方法会返回初始值,也就是调用initialValue()方法,ThreadLocal中这个方法默认返回null。当我们有需要第一次get时就能得到一个值时,可以继承ThreadLocal,并且覆盖initialValue()方法。

  1. 本文来自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/15732053

发表评论

表情:
评论列表 (有 0 条评论,354人围观)

还没有评论,来说两句吧...

相关阅读

    相关 ThreadLocal本地线变量原理解析

    在Java多线程并发环境下,如果我们需要对某一个变量进行操作的话,很有可能将造成线程安全问题,为了解决这种线程安全问题,我们可以给操作这个变量的方法或代码块加各种锁,虽然可以实

    相关 JAVA线本地存储(ThreadLocal

    今天看书看到这么一句话,“防止任务在共享资源上产生冲突的第二种方式是根除对变量的共享。线程本地存储是一种自动化机制,可以为使用相同变量的每个不同线程都创建不同的存储”。(第一种