理解
ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。(最常见的ThreadLocal使用场景为 用来解决数据库连接、Session管理等。)
对比
当多个线程之间对一个变量的访问实际上是没有依赖关系的(比较多出现在需要连接其他资源信息上),即一个线程不需要关心其他线程是否对这个变量进行了修改的的时候我们可能会有2种选择:
- 每次都是在方法内部创建的连接,那么线程之间自然不存在线程安全问题。但是这样会有一个致命的影响:导致服务器压力非常大,并且严重影响程序执行性能。由于在方法中需要频繁地开启和关闭数据库连接,这样不尽严重影响程序执行效率,还可能导致服务器压力巨大。
- 使用ThreadLocal, 因为每个线程内部都会有一个该变量,且在线程内部任何地方都可以使用,线程之间互不影响,所以使用ThreadLocal就不存在线程安全的问题,也不会严重影响程序执行性能。但是由于在每个线程中都创建了副本,所以内存的占用会比不使用ThreadLocal要大,所以使用时,需要兼顾这方面的考量
ThreadLocal类的使用
ThreadLocal类提供的几个方法:
public T get() { }
public void set(T value) { }
public void remove() { }
protected T initialValue() { }
- ThreadLocal.get: 获取ThreadLocal中当前线程共享变量的值。
- ThreadLocal.set: 设置ThreadLocal中当前线程共享变量的值。
- ThreadLocal.remove: 移除ThreadLocal中当前线程共享变量的值。
- ThreadLocal.initialValue: ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值,默认情况下,initialValue方法返回的是null,如果没有调用set方法就直接调用get方法,会返回空指针,这个时候就需要重写此方法
ThreadLocal类的详解
get方法通过getMap(t)方法传入当前的线程获取到一个ThreadLocalMap,ThreadLocalMap也是个键值对key-value的形式存在的,传入ThreadLocal对象获取value值。set,remove方法就很容易理解了,也是对ThreadLocalMap进行操作
注意:
虽然ThreadLocal的get,set方法可以清除ThreadLocalMap中key为null的value,但是get,set方法在内存泄露后并不会必然调用,所以为了防止此类情况的出现,我们有两种手段:
- 使用完线程共享变量后,显示调用ThreadLocalMap.remove方法清除线程共享变量;
- JDK建议ThreadLocal定义为private static,这样ThreadLocal的弱引用问题则不存在了。