Java中的ThreadLocal
Java中的ThreadLocal
变量值的共享可以使用public static变量的形式,所有线程可以共享一个变量。如果我们想实现一个线程都有自己的共享变量该如何解决呢,这里就要用到Java中的ThreadLocal类。
类ThreadLocal主要解决的就是每个线程绑定自己的值,可以将ThreadLocal类比喻成全局存放数据的盒子,盒子中可以存储每个线程的私有数据
我们先看下列代码
public static void main(String[] args) throws InterruptedException {
    ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };
    threadLocal.set(100);
    System.out.println("主线程中的值:"+threadLocal.get());
    Thread t = new Thread(){
        @Override
        public void run() {
            threadLocal.set(200);
            System.out.println("线程t中的值:"+threadLocal.get());
        }
    };
    t.start();
} 
代码定义了一个ThreadLocal对象,然后在主线程中,给ThreadLocal对象放入100,而线程t则放入200,并打印。结果如下
可见,每个线程都取到了自己存入的那个值。那么它到底是怎么实现的呢,让我们来分析分析吧
首先先介绍一下ThreadLocal的一些主要方法
1、initalValue方法
protected T initialValue() {
    return null;
} 
initalValue方法,表示默认值,可见是null,但为了安全起见,我们在初始化ThreadLocal对象时采用匿名内部类来重写这个方法,以防止空指针异常。也就是这样:
ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
    @Override
    protected Integer initialValue() {
        return 0;
    }
}; 
2、get方法
public T get() {
    //获取当前线程对象
    Thread t = Thread.currentThread();
    //取出线程类中的ThreadLocalMap
    ThreadLocal.ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocal.ThreadLocalMap.Entry e = map.getEntry(this);
        //如果Map不为空并且该线程确实存入了变量,则返回存入的变量
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    //Map为空,返回默认值
    return setInitialValue();
} 
这里要说到,每个线程中都维护了一个ThreadLocalMap对象,这个Map就存储着你所存入的值,下面会说到。
3、set方法
public void set(T value) {
    //获取当前线程对象
    Thread t = Thread.currentThread();
    //根据线程获取线程中的ThreadLocalMap
    ThreadLocal.ThreadLocalMap map = getMap(t);
    //如果map不为空则直接存入值
    if (map != null)
        map.set(this, value);
    //如果map为空,则创建Map并存入值
    else
        createMap(t, value);
} 
4、remove方法
public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        m.remove(this);
} 
remove是移除所存入的值
5、getMap方法
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
} 
根据线程去获得线程中的ThreadLocalMap对象
我们可以打开Thread类源码
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null;
这就是Thread类中的ThreadLocalMap对象,默认为空。只有你在使用ThreadLocal为线程保存私有属性时才会创建。
6、createMap方法
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
} 
将线程中的ThreadLocalMap对象实例化并写入存入值
接下来就要看存储数据的ThreadLocalMap
static class ThreadLocalMap {
    //Map节点,注意key是ThreadLocal而不是Thread
    static class Entry extends WeakReference<ThreadLocal<?>> {
        Object value;
        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
    //构造方法
    ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
        table = new ThreadLocal.ThreadLocalMap.Entry[INITIAL_CAPACITY];
        int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
        table[i] = new ThreadLocal.ThreadLocalMap.Entry(firstKey, firstValue);
        size = 1;
        setThreshold(INITIAL_CAPACITY);
    }
} 
Map很简单,只不过要注意的是Map的键是ThreadLocal而不是Thread。
总结
1、ThreadLocal是为了解决每个线程绑定自己的值
2、ThreadLocal默认值为null,所以有时候为了安全,初始化时使用匿名内部类来重写initalValue方法
3、每个线程对象内部都维护了一个ThreadLocalMao对象
4、ThreadLocalMap的键是ThreadLocal而不是Thread
5、ThreadlLocal可能会发生内存泄漏,所以在使用完ThreadLocal之后调用remove方法清除数据。
 关注公众号
关注公众号
					低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 
							
								
								    上一篇
								      编译型语言、解释型语言、静态类型语言、动态类型语言概念与区别今天看到篇对于静态、动态、强类型、弱类型总结的很到位的一篇博文,在此Mark下。 编译型语言和解释型语言 1、编译型语言 需通过编译器(compiler)将源代码编译成机器码,之后才能执行的语言。一般需经过编译(compile)、链接(linker)这两个步骤。编译是把源代码编译成机器码,链接是把各个模块的机器码和依赖库串连起来生成可执行文件。 优点:编译器一般会有预编译的过程对代码进行优化。因为编译只做一次,运行时不需要编译,所以编译型语言的程序执行效率高。可以脱离语言环境独立运行。 缺点:编译之后如果需要修改就需要整个模块重新编译。编译的时候根据对应的运行环境生成机器码,不同的操作系统之间移植就会有问题,需要根据运行的操作系统环境编译不同的可执行文件。 代表语言:C、C++、Pascal、Object-C以及最近很火的苹果新语言swift 2、解释型语言 解释性语言的程序不需要编译,相比编译型语言省了道工序,解释性语言在运行程序的时候才逐行翻译。 优点:有良好的平台兼容性,在任何环境中都可以运行,前提是安装了解释器(虚拟机)。灵活,修改代码的时候直接修改就可以,可以快速部署,不用停... 
- 
							
								
								    下一篇
								      php中Session使用方法详解php中Session使用方法详解 Session的声明与使用 Session的设置不同于Cookie,必须先启动,在PHP中必须调用session_start()。session_start()函数的语法格式如下: Bool session_start(void) //创建Session,开始一个会话,进行Session初始化 注意:session_start()函数之前不能有任何输出 当第一次访问网站时,Seesion_start()函数就会创建一个唯一的Session ID,并自动通过HTTP的响应头,将这个Session ID保存到客户端Cookie中。同时,也在服务器端创建一个以Session ID命名的文件,用于保存这个用户的会话信息。当同一个用户再次访问这个网站时,也会自动通过HTTP的请求头将Cookie中保存的Seesion ID再携带过来,这时Session_start()函数就不会再去分配一个新的Session ID,而是在服务器的硬盘中去寻找和这个Session ID同名的Session文件,将这之前为这个用户保存的会话信息读出,在当前脚本中应用,达到跟踪这个用... 
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS7,CentOS8安装Elasticsearch6.8.6
- MySQL数据库中FOR UPDATE的使用
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池

 
			
 
				 
				 
				 
				 
				 
				 
				



 微信收款码
微信收款码 支付宝收款码
支付宝收款码