百木园-与人分享,
就是让自己快乐。

ThreadLocal 详解

一、ThreadLocal 简介

ThreadLocal实例通常作为静态的私有的(private static)字段出现在一个类中,这个类用来关联一个线程。ThreadLocal是一个线程级别的局部变量,下面是线程局部变量(ThreadLocal variables)的关键点:

  A、当使用ThreadLocal维护变量时,若多个线程访问ThreadLocal实例,ThreadLocal为每个使用该变量的线程提供了一个独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。

  B、从线程的角度看,目标变量就像是线程的本地变量,这也是类名中Local所要表达的意思。

二、ThreadLocal 特点及用途:

1.ThreadLocal是单线程内共享资源,多线程间无法共享(即线程A访问不了线程B中ThreadLocal存放的值);

2.ThreadLocal是本地变量,无法跨jvm传递;

3.ThreadLocal的出现可以减少通过参数来传递(使代码更加简洁,降低耦合性),Hibernate中的OpenSessionInView,就始终保证当前线程只有一个在使用中的Connection(或Hibernate Session),代码如下:

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

三、ThreadLocal 实现原理

定义了一个Map(非普通map) 结构,

clipboard

例如:

定义一个 ThreadLocal 对象

public static ThreadLocal<Integer> integerThreadLocal = new ThreadLocal<>();

key 就是 integerThreadLocal

value 就是 Integer 类型的值

在 Thread 里面持有该对象的引用,也就是说,不同的线程,有各自的 ThreadLocalMap

clipboard

例如:A 线程,有一个 ThreadLocalMap,key 是一系列 ThreadLocal 对象,value 是 A 线程使用这一系列 ThreadLocal 对应的值;同理,B 线程,也有一个 ThreadLocalMap,,key 是一系列 ThreadLocal 对象,value 是 B 线程使用这一系列 ThreadLocal 对应的值。

所以,现在有两个 ThreadLocal 对象,被3个线程 TA TB TC 使用:

public static ThreadLocal<Integer> integerThreadLocal = new ThreadLocal<>();

public static ThreadLocal<String> stringThreadLocal = new ThreadLocal<>();

TA:integerThreadLocal.set(1);stringThreadLocal.set(\"A\");

TB:integerThreadLocal.set(2);stringThreadLocal.set(\"B\");

TC:integerThreadLocal.set(3);stringThreadLocal.set(\"C\");

最终存储结构是:

{{\"thread\":TA,\"threadLocalMap\":{\"integerThreadLocal\":1,\"stringThreadLocal\":\"A\"}},

{\"thread\":TB,\"threadLocalMap\":{\"integerThreadLocal\":2,\"stringThreadLocal\":\"B\"}},

{\"thread\":TC,\"threadLocalMap\":{\"integerThreadLocal\":3,\"stringThreadLocal\":\"C\"}}}

格式化后,如下:

{

	\"TA\": {

		\"integerThreadLocal\": 1,

		\"stringThreadLocal\": \"A\"

	 },

	\"TB\": {

		\"integerThreadLocal\": 2,

		\"stringThreadLocal\": \"B\"

	 },

	\"TC\": {

		\"integerThreadLocal\": 3,

		\"stringThreadLocal\": \"C\"

	 }
}

使用时:

stringThreadLocal.get();

clipboard

四、ThreadLocal 的弱引用

强引用:

Object obj = new Object();

只要 obj 不为空,gc 的时候 就不会回收 obj,此为强引用。

弱引用:

WeakReference<Object> weakRef = new WeakReference<>(new Object());

发生 gc 时,尽管 括号里面的对象 不为空,也会被回收,但是 weakRef 是强引用,不会被回收。

ThreadLocalMap 的 key 是 ThreadLocal 对象的弱引用,即 gc 以后,ThreadLocalMap 的 key 将指向 null,而 value 依旧是强引用,不会被回收,会造成内存泄露。

所以,ThreadLocal 的弱引用,解决了内存泄露吗?

解决了一部分,key 确实被回收了,但是 value 不是弱引用,没有被回收,还是有可能造成内存泄露。

五、ThreadLocal 解决内存泄露

两种内存泄露情况:

1.发生了 gc,弱引用被回收了,ThreadLocalMap 的 key 指向了 null,而 value 是强引用,不会被回收,会造成内存泄露

2.线程被线程池管理,不会销毁,其对应的 ThreadLocalMap 也不会被回收,会造成内存泄露

解决方案:

1.主动调用 ThreadLocal 的 remove 方法,

clipboard

2.ThreadLocal 在 set、get、remove 时,会自动移除 key 为 null 的 Entry。由于,ThreadLocalMap 的 key 是弱引用,gc 后 key 会指向 null,所以,这部分会被置为 null,方便下次 gc 时回收

clipboard


来源:https://www.cnblogs.com/god-smile996/p/16333784.html
本站部分图文来源于网络,如有侵权请联系删除。

未经允许不得转载:百木园 » ThreadLocal 详解

相关推荐

  • 暂无文章