1. 什么是ThreadLocal?

线程局部变量(通常,ThreadLocal变量是private static修饰的,此时ThreadLocal变量相当于成为了线程内部的全局变量)

2. 使用场景

变量在线程内部共享,线程间无关

再具体点,可以分为两类:

  • 单例的对象中static属性,线程内共享,线程间无关;
  • 工具类属性,线程内共享,线程间无关。

为什么这么说呢?下面看4个问题:

(1)对象为什么要是单例的?

如果对象不是单例的,那么大可以每次都new一个对象,然后对用到属性赋值就行,代码如下:

public class Service {

    private String key;

    void A() {
// 代码实现,中间用到key
} void B() {
// 代码实现,中间用到key
} // 省略get和set方法 }

在使用时,每个线程都new Service(),并对key赋值,然后调用其中的方法就行了,保证方法A和B用的key都是一个值。

(2)单例对象的属性共享

如果希望单例对象中的某个属性可以被共享,那么将属性声明为static就行了:

public class Service {

    private static String key;

    // 省略其他方法

}

上面的实现确实保证了所有方法都能使用key,然而,在多线程环境下,key是不安全的。

(3)单例对象在线程内属性共享,不同线程间相互不影响

这就轮到ThreadLocal上场了:

public class Service {

    private static ThreadLocal<String> key = new ThreadLocal<String>() {
protected String initialValue() {
return Thread.currentThread().getName();
}
}; public void A() {
System.out.println("methodA: " + key.get());
key.set("methodA: " + key.get());
} public void B() {
System.out.println("methodB: " + key.get());
}
}

使用方式:

public class ThreadLocalTest {

    public static void main(String[] args) {

        final Service service = new Service(); //模拟单例对象的使用

        new Thread(new Runnable() {
@Override
public void run() {
service.A();
service.B();
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
service.A();
service.B();
}
}).start(); } }

运行结果:

methodA: Thread-0
methodB: methodA: Thread-0
methodA: Thread-1
methodB: methodA: Thread-1

(4)工具类中线程共享,线程间无关

工具类的代码:

public final class XUtil {

    private static ThreadLocal<String> key = new ThreadLocal<String>();

    private XUtil() {
} public static void A() {
// 实现
} public static void B() {
// 实现
} }

在使用XUtil时,每个线程中key可以使用,不同线程间不受影响。

3. ThreadLocal的实现原理

为什么一个static的变量(即:类变量)可以做到:线程内共享,线程间无关?

看下ThreadLocal中的get源码:

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();
}

关键就在getMap()方法:

ThreadLocalMap getMap(Thread t) {
return t.threadlocals;
}

取的是当前线程内部的threadLocals属性。

查看Thread类:

ThreadLocal.ThreadLocalMap threadLocals = null;

threadLocals是ThreadLocal类中自定义的一个HashMap类。

原来数据就存在当前线程内部,自然就能做到线程内共享,线程间无关了。

接着看下set的源码:

public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap();
if(map != null)
map.set(this, value);
else
createMap(t,value);
}

无论是map.set(this, value)还是createMap(t, value),最后都是将数据保存到当前线程中的那个HashMap中:将ThreadLocal变量作为key,value就是要保存的数据。

4. ThreadLocal的内存泄露

在前面看到数据最终是存在线程内部的一个Map中的:

ThreadLocal.ThreadLocalMap threadLocals = null;

且key是ThreadLocal变量的引用,在get方法:

ThreadLocalMap.Entry e = map.getEntry(this); // this为当前对象的引用

当ThreadLocal变量被销毁时,而当前线程又持有ThreadLocal的引用,那么ThreadLocal就不会被回收,导致内存泄露。

然而,编写JDK的大牛们考虑到了这个问题,因此将ThreadLocalMap的key设置为弱引用

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

对ThreadLocal变量的弱引用,在GC时,ThreadLocal变量就会被回收。

于是,在当前线程的本地变量HashMap中,原来ThreadLocal作为key的,现在变成null作为key了,该key-value变得不可访问了,如果当前线程一直不结束,那么value对应的对象就无法释放,也就是发生内存泄露了。

参考文献:

《深入分析 ThreadLocal 内存泄漏问题》

ThreadLocal的使用场景及实现原理的更多相关文章

  1. etcd:从应用场景到实现原理的全方位解读 转自infoq

    转自 infoq etcd:从应用场景到实现原理的全方位解读 http://www.infoq.com/cn/articles/etcd-interpretation-application-scen ...

  2. [转帖]kafka入门:简介、使用场景、设计原理、主要配置及集群搭建

    kafka入门:简介.使用场景.设计原理.主要配置及集群搭建 http://www.aboutyun.com/thread-9341-1-1.html 还没看完 感觉挺好的. 问题导读: 1.zook ...

  3. ThreadLocal的使用场景分析

    目录 一.ThreadLocal介绍 二.使用场景1——数据库事务问题 2.1 问题背景 2.2 方案1-修改接口传参 2.3 方案2-使用ThreadLocal 三.使用场景2——日志追踪问题 四. ...

  4. volatile、ThreadLocal的使用场景和原理

    并发编程中的三个概念 原子性 一个或多个操作.要么全部执行完成并且执行过程不会被打断,要么不执行.最常见的例子:i++/i--操作.不是原子性操作,如果不做好同步性就容易造成线程安全问题. 可见性 多 ...

  5. Java并发编程原理与实战二十五:ThreadLocal线程局部变量的使用和原理

    1.什么是ThreadLocal ThreadLocal顾名思义是线程局部变量.这种变量和普通的变量不同,这种变量在每个线程中通过get和set方法访问, 每个线程有自己独立的变量副本.线程局部变量不 ...

  6. 面试官:ThreadLocal的应用场景和注意事项有哪些?

    前言 ThreadLocal主要有如下2个作用 保证线程安全 在线程级别传递变量 保证线程安全 最近一个小伙伴把项目中封装的日期工具类用在多线程环境下居然出了问题,来看看怎么回事吧 日期转换的一个工具 ...

  7. kafka入门:简介、使用场景、设计原理、主要配置及集群搭建(转)

    问题导读: 1.zookeeper在kafka的作用是什么? 2.kafka中几乎不允许对消息进行"随机读写"的原因是什么? 3.kafka集群consumer和producer状 ...

  8. ThreadLocal的设计与使用(原理篇)

    在jdk1.2推出时开始支持java.lang.ThreadLocal.在J2SE5.0中的声明为:            public class ThreadLocal<T> exte ...

  9. memcache的应用场景和实现原理

    面临的问题 对于高并发高访问的 Web应用程序来说,数据库存取瓶颈一直是个令人头疼的问题.特别当你的程序架构还是建立在单数据库模式,而一个数据池连接数峰 值已经达到500的时候,那你的程序运行离崩溃的 ...

随机推荐

  1. 素数判断-----埃氏筛法&欧拉筛法

    埃氏筛法 /* |埃式筛法| |快速筛选素数| |15-7-26| */ #include <iostream> #include <cstdio> using namespa ...

  2. Linux加密、安全版块、root密码破解

    当一个入侵者进入了你的系统并且种植了木马,通常会想办法来隐蔽这个木马(除了木马自身的一些隐蔽特性外,他会尽量给你检查系统的过程设置障碍),通常入侵者会修改一些文件,比如管理员通常用ps -aux来查看 ...

  3. 【ZOJ 3200】Police and Thief

    ZOJ 3200 首先我写了个高斯消元,但是消出来了一些奇怪的东西,我就放弃了... 然后只好考虑dp:\(dp[i][j][k]\)表示走到了第i步,到了\((j,k)\)这个节点的概率. 那么答案 ...

  4. Omi框架学习之旅 - 组件通讯(data-*通讯) 及原理说明

    上一篇文章说了omi中的组件,以及组件如何使用及嵌套. 那omi中的组件是怎么通讯的呢? 其实omi提供的通讯方式比较丰富,各有千秋,各有各的场景用途.所以按需使用即可. 老规矩:先上demo代码, ...

  5. [07] 使用注解完成IOC配置

    1.扫描配置 之前使用的Spring的Bean管理都是通过xml的配置文件来操作的,在Spring3.0之后已经引入了注解形式,Spring可以在指定路径下进行扫描,寻找标注了@Component.@ ...

  6. Python文学家为Python写的一首词?(附中英文版)

    The Zen of Python, by Tim Peters (Python之禅 by Tim Peters) Beautiful is better than ugly. (优美胜于丑陋(Pyt ...

  7. identity一些接口

    ASP.NET平台通过HttpContext对象提供一些关于用户的有用信息,该对象由Authorize注解属性使用 的,以检查当前请求的状态,考察用户是否已被认证.HttpContext.User属性 ...

  8. Luogu4137 Rmq problem/mex 主席树

    传送门 用主席树水莫队题…… 我们对于前缀和建立主席树,对于主席树中的每一个叶子节点表示它对应的数字最后出现的位置的编号,非叶子节点求左右节点的最小值,那么对于每一次询问$l,r$就是在第$r$棵主席 ...

  9. Ionic 动态配置url路由的设置

    随着Ionic App功能的不断增加,需要路由的url设置就越来越多,不喜欢在config函数中写一堆硬代码,一则不美,二则维护起来也麻烦,能不能把这些数据独立出来呢? 经过查找资料与各种实验,最终找 ...

  10. 如何用区块链技术解决信任问题?Fabric 架构深度解读

    阿里妹导读:区块链技术,随着比特币的兴起而为大家所知.但是具体到技术本身,大家相对熟悉的几个词可能是“数据不可篡改”.“公开链”.“分布式数据”.“共识机制”等. 这篇文章将抛砖引玉,通过深度解读Hy ...