首页 > 编程语言 >ThreadLocal源码分析-

ThreadLocal源码分析-

时间:2024-09-12 13:24:18浏览次数:8  
标签:分析 ThreadLocalMap value ThreadLocal 源码 线程 key null

ThreadLocal源码分析

ThreadLocal是解决线程安全问题的一种方法,它通过为每个线程提供一个独立的变量副本避免了变量并发访问的冲突问题。一个ThreadLocal变量只与当前自身线程相关,对其他线程是隔离的。下面这段代码展示了ThreadLocal的使用。

public class test {
    private static final ThreadLocal<Object> tl = new ThreadLocal<>();

    public static void main(String[] args) {
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5,10,1L, TimeUnit.MINUTES,new ArrayBlockingQueue<>(100));
        for (int i = 0; i < 5; i++) {
            MyRunnable runnable = new MyRunnable(i);
            poolExecutor.execute(runnable);
        }
        poolExecutor.shutdown();
        while (!poolExecutor.isTerminated()) {
        }
        System.out.println("Finished all threads");
    }

    public static class MyRunnable implements Runnable{
        private int id;
        public MyRunnable(int id){
            this.id=id;
        }
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"set()前:"+tl.get());
            tl.set(id);
            System.out.println(Thread.currentThread().getName()+"set()后:"+tl.get());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
执行结果:
pool-1-thread-2set()前:null
pool-1-thread-5set()前:null
pool-1-thread-5set()后:4
pool-1-thread-1set()前:null
pool-1-thread-4set()前:null
pool-1-thread-3set()前:null
pool-1-thread-4set()后:3
pool-1-thread-1set()后:0
pool-1-thread-2set()后:1
pool-1-thread-3set()后:2
Finished all threads

这段代码中定义了5个线程对同一个ThreadLocal进行get和set操作,但是每个线程get到的值都是线程set后的值。可见,ThreadLocal做到了多线程的数据隔离。下面看看ThreadLocal的源码。先看ThreadLocal的set()方法。

public class ThreadLocal<T> {
    
    public void set(T value) {
        //获取当前线程
        Thread t = Thread.currentThread();
        //获取当前线程的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            //将value添加到map,key是当前ThreadLocal的引用
            map.set(this, value);
        } else {
            createMap(t, value);
        }
    }
    
    ThreadLocalMap getMap(Thread t) {
        //每个线程都有一个名为threadLocals的ThreadLocalMap对象
        return t.threadLocals;
    }
    
    static class ThreadLocalMap {
        
        private void set(ThreadLocal<?> key, Object value) {
            Entry[] tab = table;
            int len = tab.length;
            //计算当前ThreadLocal在数组中的索引
            int i = key.threadLocalHashCode & (len-1);
			
            //找到一组具有相同hash的key
            //从这也能看出,ThreadLocalMap使用线性探测法解决哈希冲突
            for (Entry e = tab[i];
                e != null;
                e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();
				
                //如果是同一个ThreadLocal,就直接覆盖原来的值
                if (k == key) {
                    e.value = value;
                    return;
                }
                //如果key是null,说明key被回收了,替换掉它
                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }
			//如果key的hash地址是空的,那就直接插入
            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                //扩容
                rehash();
        }
	}  
}

根据ThreadLocal的源码,也能推断出ThreadLocal的数据结构了。

image-20240903162015072

接下来在看一下get()方法:

public class ThreadLocal<T> {
    public T get() {
    	//获取当前线程
        Thread t = Thread.currentThread();
        //获取当前线程的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        if (map != null) {
        	//取出当前ThreadLocal的键值对元素
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                //返回当前ThreadLocal变量值
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
    
    private Entry getEntry(ThreadLocal<?> key) {
        //计算hash地址
        int i = key.threadLocalHashCode & (table.length - 1);
        Entry e = table[i];
        //没有哈希冲突直接返回
        if (e != null && e.get() == key)
            return e;
        //发生了哈希冲突
        else
            return getEntryAfterMiss(key, i, e);
    }
    
    private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
        Entry[] tab = table;
        int len = tab.length;
        //线性探测依次遍历
        while (e != null) {
            ThreadLocal<?> k = e.get();
            if (k == key)
                return e;
            if (k == null)
                expungeStaleEntry(i);
            else
                //线性探测下一个索引地址
                i = nextIndex(i, len);
            e = tab[i];
        }
        return null;
    }
}

ThreadLocal的内存泄露问题:

ThreadLocalMap中的key是ThreadLocal的弱引用,弱引用不会阻止垃圾回收器回收ThreadLocal实例,当ThreadLocal被回收后,value仍然存在,他们会占用内存,但是却无法通过ThreadLocal来访问,这就造成了内存泄露。因此,在使用完ThreadLocal变量后,可以及时remove掉这个ThreadLocal关联的键值对。

public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null) {
        m.remove(this);
    }
}

标签:分析,ThreadLocalMap,value,ThreadLocal,源码,线程,key,null
From: https://www.cnblogs.com/Linwei33/p/18409993

相关文章

  • SpringBoot源码分析
    Springboot源码分析1、SpringApplication初始化从run()方法进入,可以看到Springboot首先创建了SpringApplication,然后调用SpringApplication的run()方法。publicstaticConfigurableApplicationContextrun(Class<?>[]primarySources,String[]args){return(newSprin......
  • 密码算法设计与分析 - 课程笔记
    基本概念安全威胁安全威胁被动攻击消息内容获取业务流分析主动攻击中断(可用性)篡改(完整性)伪造(真实性)人为威胁被动攻击被动攻击即窃听,是对系统的保密性进行攻击,如搭线窃听、非法拷贝等,以获取他人的信息。被动攻击分类:消息内容获取:直接对消息内容进行窃听,......
  • 收银系统源码、连锁店收银系统源码-收银台高频使用功能
    收银系统成为门店高频使用的软件工具,除了正常扫描商品、商品称重、收银结账、会员管理、处理订单以外,还有哪些功能也是门店日常经常会使用的功能呢?1.单品改价、单品打折门店可以给收银员开通权限,收银员在收银结算时可以给单个商品进行改价或者打折,最低优惠金额和最高优惠金额都是......
  • JAVA线程基础——ThreadLocal的使用和原理
    一、ThreadLocal        多线程访问同一个共享变量时特别容易出现并发问题,特别是在多个线程需要对一个共享变量进行写入时。为了保证线程安全,一般使用者在访问共享变量时需要进行适当的同步,如图1-3所示。        同步的措施一般是加锁,这就需要使用者对锁有......
  • NLP(文本处理技术)在数据分析中的应用实例
    在Python中,你可以实现多种自然语言处理(NLP)技术。Python拥有丰富的库和框架,使得NLP任务变得更加容易和高效。接下来将列举一些NLP(文本处理技术)具体功能的Python实现。一:文本预处理1:英文版#文本预处理#导入所需的库importrefromtextblobimportTextBlobfromgensim......
  • 农产品交易网站 毕业设计-附源码
    摘 要随着互联网大趋势的到来,社会的方方面面,各行各业都在考虑利用互联网作为媒介将自己的信息更及时有效地推广出去,而其中最好的方式就是建立网络管理系统,并对其进行信息管理。由于现在网络的发达,农产品交易的信息通过网络进行信息管理掀起了热潮,所以针对农产品交易信息管......
  • 微信阅读小程序设计与实现-计算机毕业设计源码+LW文档
    摘 要由于APP软件在开发以及运营上面所需成本较高,而用户手机需要安装各种APP软件,因此占用用户过多的手机存储空间,导致用户手机运行缓慢,体验度比较差,进而导致用户会卸载非必要的APP,倒逼管理者必须改变运营策略。随着微信小程序的出现,解决了用户非独立APP不可访问内容的痛点,所以很......
  • 毕业设计—基于SpringBoot的个人博客系统 (案例分析)
    摘 要随着科学技术的飞速发展,社会的方方面面、各行各业都在努力与现代的先进技术接轨,通过科技手段来提高自身的优势,个人博客系统当然也不能排除在外。个人博客系统是以实际运用为开发背景,运用软件工程开发方法,采用Java技术构建的一个管理系统。整个开发过程首先对软件系统......
  • 最新PHP在线客服系统I聊天源码网页端在线客服系统 带教程
    安装教程1.上传源码压缩包到网站目录并解压2.设置网站运行目录public(防跨站不要勾选)3.设置伪静态,选择thinkphp4.进入网站目录,打开终端 输入启动命令5.宝塔配置开启1238和2346端口后台登录地址:https://域名/admin详细教程查看压缩包中的安装说明.doc文档效果展示......
  • 使用cloc进行代码行数统计与分析
    cloc(CLOC全称:CountLinesofCode)是一个开源的命令行工具,用于计算项目代码中的实际代码行数,排除注释和空行。它支持多种编程语言,并且可以分析多种文件格式。cloc的主要功能:多语言支持:cloc支持超过300种编程语言,能有效识别不同语言的代码行、注释行和空行。多平台兼容:可以在Windows......