欢迎点赞阅读,一同学习交流,有疑问请留言 。

GitHub上也有开源 JavaHouse 欢迎star

1 引入

在Java8里面,ThreadLocal 是一个泛型类。这个类可以提供线程变量。每个线程都有自己的变量。这意味着什么?每一个线程都有自己的资源,就像在现实生活中,每一个程序员都有自己的一个对象,不用去竞争,绝对的线程安全啊。那么 ThreadLocal 究竟怎么用呢?

2 类的说明

* This class provides thread-local variables.  These variables differ from
* their normal counterparts in that each thread that accesses one (via its
* {@code get} or {@code set} method) has its own, independently initialized
* copy of the variable. {@code ThreadLocal} instances are typically private
* static fields in classes that wish to associate state with a thread (e.g.,
* a user ID or Transaction ID).

这是 ThreadLocal 类上面的说明。大概意思是提供线程变量,通常是被 static fields 修饰。

3 创建他

创建 ThreadLocal 有两种方法,一种通过原始的无参构造器, 另一种是使用Java8的 lamaba 表达式。

3.1 无参构造器

源码

/**
* Creates a thread local variable.
* @see #withInitial(java.util.function.Supplier)
*/
public ThreadLocal() {
}

使用并初始化

private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue(){
return 1;
}
};

3.2 lamaba 表达式


/**
* Creates a thread local variable. The initial value of the variable is
* determined by invoking the {@code get} method on the {@code Supplier}.
*
* @param <S> the type of the thread local's value
* @param supplier the supplier to be used to determine the initial value
* @return a new thread local variable
* @throws NullPointerException if the specified supplier is null
* @since 1.8
*/
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
return new SuppliedThreadLocal<>(supplier);
}

使用并初始化

private static final ThreadLocal<Integer> threadId = ThreadLocal.withInitial(() -> 1);

其实使用如果使用 IDEA 的话, 编译器也会提示可以有 lamaba。 但是看看里面的源码还是挺有意思的。

4 getter() 方法

/**
* 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) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}

这里可以看到 Thread.currentThread,获取了当前线程,还有 ThreadLocalMap 这个类,他是一个哈希结构(key-value)。getMap() 方法通过当前线程去获取他。 然后从再通过 this 关键字作为key,得到相应的值value。当然如果为空,就会返回初始化的值。

5 setter() 方法

什么情况下不会返回初始化的默认值呢,答案就是调用了setter() 方法。先看源码

/**
* 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();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

大概意思就是将当前线程作为key, 要设的值作为 value 放在 ThreadLocalMap 这个哈希结构中。看到这里,就知道为什么说 ThreadLocal 可以提供线程变量了。他讲每个线程都分开存储,每个线程都有自己的独立资源,不存在资源共享的情况,所以线程安全。

6 内存泄漏

每个线程变量都放进一个 ThreadLocalMap 里面,不会有内存问题吗。我截取一部分源码

static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value; Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
} }

这里可以看到了 WeakReference 这个类,可以知道 ThreadLocalMap 类是一个弱引用,按道理来说,一般执行完线程,就会被虚拟机的垃圾回收机制回收了。但是,真的是这样的吗。如果是线程池环境里面,线程一直存在,那么 ThreadLocal 就会变成起强引用了,不能被回收了。所以有内存泄漏问题。

那现在就是 remove() 方法出场的时候了。按照字面意思,可以知道,该方法可以清掉线程变量资源,事实也是这样。所以,在程序的最后,最好调用一下 emove() 方法,防止内存泄漏。

参考

《实战Java高并发程序设计》

ThreadLocal 源码代

关注微信公众号,随时移动端阅读

最新文章

  1. java中的成员变量和局部变量区别
  2. 新篇章,Golang 和 beego 初识
  3. JQuery easyui Datagrid 分页事件
  4. Windows出现带空格文件名无法删除
  5. AngularJs $rootScope.Scope 作用域操作
  6. Rank List
  7. 使用自定义 URL 实现控制器之间的跳转-b
  8. CSS3新增UI样式
  9. Ubuntu 14.04 下使用IDEA开发Spark应用 入门
  10. [STM32F429-DISCO-uCosiii]3.uCOSIII 移植
  11. python学习之路网络编程篇(第五篇)-续篇
  12. buffer格式的转换
  13. Linux系统安装 OpenSSL两种方法
  14. bind配置文件
  15. FFmpeg Basic学习笔记(4)
  16. jdbc连接sqlserver报错java.lang.ClassNotFoundException: com.microsoft.jdbc.sqlserver.SQLServerDriver
  17. [kafka] 001_kafka起步
  18. Hive 和 HBase区别
  19. ADB安装
  20. PHP session 写入数据库中的方法

热门文章

  1. Numpy 排序和使用索引
  2. [考试反思]0817NOIP模拟测试24:冲淡
  3. JS中获取元素属性的逆天大法
  4. P3097 [USACO13DEC]最优挤奶(线段树优化dp)
  5. day2 上午 游戏 对应关系---&gt;判断素数----&gt;多重背包 神题
  6. VM小技巧——虚拟机解决vm窗口太小的办法
  7. 更新linux时候提示“由于没有公钥,无法验证下列签名&quot;.
  8. ARP协议字段解读
  9. Feign 调用丢失Header的解决方案
  10. 张孝祥java高新技术 --- jkd1.5 新特性