Java线程:深入ThreadLocal

Java 并发编程:线程变量 ThreadLocal 即线程变量,是 Java 提供的用于实现线程本地变量的工具类。每个线程可以通过对象访问其专属的变量,避免了多线程环境下变量共享导致的数据不一致问题。通常情况下,我们创建的成员变量都是线程不安全的。因为他可能被多个线程同时修改,此变量对于多个线程之间彼此并不独立,是共享变量。而中填充的变量属于当前线程,该变量对其他线程而言是隔离的。Ps:ThreadLocal 很容易让人望文生义,想当然地认为是一个 “本地线程”。其实, 阅读详情
Java线程:深入ThreadLocal
 
ThreadLocal与线程成员变量还有区别,ThreadLocal该类提供了线程局部变量。这个局部变量与一般的成员变量不一样,ThreadLocal的变量在被多个线程使用时候,每个线程只能拿到该变量的一个副本,这是Java API中的描述,通过阅读API源码,发现并非副本,副本什么概念?克隆品? 或者是别的样子,太模糊。
 
准确的说,应该是ThreadLocal类型的变量内部的注册表(Map<Thread,T>)发生了变化,但ThreadLocal类型的变量本身的确是一个,这才是本质!
 
下面就做个例子:
 
一、标准例子
 
定义了MyThreadLocal类,创建它的一个对象tlt,分别给四个线程使用,结果四个线程tlt变量并没有出现共用现象,二是各用各的,这说明,四个线程使用的是tlt的副本(克隆品)。
 
/**
* 使用了ThreadLocal的类
*
* @author leizhimin 2010-1-5 10:35:27
*/

public class MyThreadLocal {
         //定义了一个ThreadLocal变量,用来保存int或Integer数据
         private ThreadLocal<Integer> tl = new ThreadLocal<Integer>() {
                @Override
                 protected Integer initialValue() {
                         return 0;
                }
        };

         public Integer getNextNum() {
                 //将tl的值获取后加1,并更新设置t1的值
                tl.set(tl.get() + 1);
                 return tl.get();
        }
}
 
/**
* 测试线程
*
* @author leizhimin 2010-1-5 10:39:18
*/

public class TestThread extends Thread {
         private MyThreadLocal tlt = new MyThreadLocal();

         public TestThread(MyThreadLocal tlt) {
                 this.tlt = tlt;
        }

        @Override
         public void run() {
                 for ( int i = 0; i < 3; i++) {
                        System.out.println(Thread.currentThread().getName() + "\t" + tlt.getNextNum());
                }
        }
}
 
/**
* ThreadLocal测试
*
* @author leizhimin 2010-1-5 10:43:48
*/

public class Test {
         public static void main(String[] args) {
                MyThreadLocal tlt = new MyThreadLocal();
                Thread t1 = new TestThread(tlt);
                Thread t2 = new TestThread(tlt);
                Thread t3 = new TestThread(tlt);
                Thread t4 = new TestThread(tlt);
                t1.start();
                t2.start();
                t3.start();
                t4.start();

        }
}
 
可以看出,三个线程各自独立编号,互不影响:
Thread-0  1
Thread-1  1
Thread-0  2
Thread-1  2
Thread-0  3
Thread-1  3
Thread-2  1
Thread-3  1
Thread-2  2
Thread-3  2
Thread-2  3
Thread-3  3

Process finished with exit code 0
 
tlt对象是一个,废话tl对象也是一个,因为组合关系是一对一的。但是tl对象内部的Map随着线程的增多,会创建很多Integer对象。只是Integer和int已经通用了。所以感觉不到Integer的对象属性。
 
二、不用ThreadLocal
 
假如不用ThreadLocal,只需要将MyThreadLocal类重新定义为:
/**
* 使用了ThreadLocal的类
*
* @author leizhimin 2010-1-5 10:35:27
*/

public class MyThreadLocal {
         private Integer t1 = 0;
         public Integer getNextNum(){
                 return t1=t1+1;
        }



//        //定义了一个ThreadLocal变量,用来保存int或Integer数据
//        private ThreadLocal<Integer> tl = new ThreadLocal<Integer>() {
//                @Override
//                protected Integer initialValue() {
//                        return 0;
//                }
//        };
//
//        public Integer getNextNum() {
//                //将tl的值获取后加1,并更新设置t1的值
//                tl.set(tl.get() + 1);
//                return tl.get();
//        }
}
 
然后运行测试:
Thread-2  1
Thread-2  2
Thread-1  4
Thread-1  6
Thread-3  3
Thread-3  9
Thread-3  10
Thread-1  8
Thread-0  7
Thread-0  11
Thread-0  12
Thread-2  5

Process finished with exit code 0
 
从这里可以看出,四个线程共享了tlt变量,结果每个线程都直接修改tlt的属性。
 
三、自己实现个ThreadLocal
 
package com.lavasoft.test2;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
* 使用了ThreadLocal的类
*
* @author leizhimin 2010-1-5 10:35:27
*/

public class MyThreadLocal {

         //定义了一个ThreadLocal变量,用来保存int或Integer数据
         private com.lavasoft.test2.ThreadLocal<Integer> tl = new com.lavasoft.test2.ThreadLocal<Integer>() {
                @Override
                 protected Integer initialValue() {
                         return 0;
                }
        };

         public Integer getNextNum() {
                 //将tl的值获取后加1,并更新设置t1的值
                tl.set(tl.get() + 1);
                 return tl.get();
        }
}

class ThreadLocal<T> {
         private Map<Thread, T> map = Collections.synchronizedMap( new HashMap<Thread, T>());

         public ThreadLocal() {
        }

         protected T initialValue() {
                 return null;
        }

         public T get() {
                Thread t = Thread.currentThread();
                T obj = map.get(t);
                 if (obj == null && !map.containsKey(t)) {
                        obj = initialValue();
                        map.put(t, obj);
                }
                 return obj;
        }

         public void set(T value) {
                map.put(Thread.currentThread(), value);
        }

         public void remove() {
                map.remove(Thread.currentThread());
        }
}
 
运行测试:
Thread-0  1
Thread-0  2
Thread-0  3
Thread-2  1
Thread-2  2
Thread-3  1
Thread-2  3
Thread-3  2
Thread-1  1
Thread-3  3
Thread-1  2
Thread-1  3

Process finished with exit code 0
 
很意外,这个山寨版的ThreadLocal也同样运行很好,实现了JavaAPI中ThreadLocal的功能。
 
四、透过现象看本质
 
其实从程序角度看,tlt变量的确是一个,毫无疑问的。但是为什么打印出来的数字就互不影响呢?
是因为使用了Integer吗?-----不是。
原因是:protected T initialValue()和get(),因为每个线程在调用get()时候,发现Map中不存在就创建。调用它的时候,就创建了一个新变量,类型为T。每次都新建,当然各用个的互不影响了。
为了看清本质,将Integer换掉,重写部分类:
 
package com.lavasoft.test2;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
* 使用了ThreadLocal的类
*
* @author leizhimin 2010-1-5 10:35:27
*/

public class MyThreadLocal {

         //定义了一个ThreadLocal变量,用来保存int或Integer数据
         //        private ThreadLocal<Bean> tl = new ThreadLocal<Bean>() {
         private com.lavasoft.test2.ThreadLocal<Bean> tl = new com.lavasoft.test2.ThreadLocal<Bean>() {
                @Override
                 protected Bean initialValue() {
                         return new Bean();
                }
        };

        @Override
         public String toString() {
                 return "MyThreadLocal{" +
                                 "tl=" + tl +
                                '}';
        }

         public Bean getBean() {
                 return tl.get();
        }

}

class ThreadLocal<T> {
         private Map<Thread, T> map = Collections.synchronizedMap( new HashMap<Thread, T>());

         public ThreadLocal() {
        }

         protected T initialValue() {
                 return null;
        }

         public T get() {
                Thread t = Thread.currentThread();
                T obj = map.get(t);
                 if (obj == null && !map.containsKey(t)) {
                        obj = initialValue();
                        map.put(t, obj);
                }
                 return obj;
        }

         public void set(T value) {
                map.put(Thread.currentThread(), value);
        }

         public void remove() {
                map.remove(Thread.currentThread());
        }
}
 
package com.lavasoft.test2;

/**
* 测试Bean
*
* @author leizhimin 2010-1-5 14:18:26
*/

public class Bean {
         private String id = "0";
         private String name = "none";

         public Bean() {
        }

         public Bean(String id, String name) {
                 this.id = id;
                 this.name = name;
        }

         public String getId() {
                 return id;
        }

         public void setId(String id) {
                 this.id = id;
        }

         public String getName() {
                 return name;
        }

         public void setName(String name) {
                 this.name = name;
        }

         public String showinfo() {
                 return "Bean{" +
                                 "id='" + id + '\'' +
                                 ", name='" + name + '\'' +
                                '}';
        }
}
 
package com.lavasoft.test2;

/**
* 测试线程
*
* @author leizhimin 2010-1-5 10:39:18
*/

public class TestThread extends Thread {
         private MyThreadLocal tlt = new MyThreadLocal();

         public TestThread(MyThreadLocal tlt) {
                 this.tlt = tlt;
        }

        @Override
         public void run() {
                System.out.println( ">>>>>:" + tlt);
                 for ( int i = 0; i < 3; i++) {
                        System.out.println(Thread.currentThread().getName() + "\t" +tlt.getBean()+ "\t"+tlt.getBean().showinfo());
                }
        }
}
 
然后运行测试:
>>>>>:MyThreadLocal{tl=com.lavasoft.test2.MyThreadLocal$1@1de3f2d}
>>>>>:MyThreadLocal{tl=com.lavasoft.test2.MyThreadLocal$1@1de3f2d}
>>>>>:MyThreadLocal{tl=com.lavasoft.test2.MyThreadLocal$1@1de3f2d}
>>>>>:MyThreadLocal{tl=com.lavasoft.test2.MyThreadLocal$1@1de3f2d}
Thread-1  com.lavasoft.test2.Bean@291aff  Bean{id='0', name='none'}
Thread-2  com.lavasoft.test2.Bean@fe64b9  Bean{id='0', name='none'}
Thread-3  com.lavasoft.test2.Bean@186db54  Bean{id='0', name='none'}
Thread-2  com.lavasoft.test2.Bean@fe64b9  Bean{id='0', name='none'}
Thread-2  com.lavasoft.test2.Bean@fe64b9  Bean{id='0', name='none'}
Thread-0  com.lavasoft.test2.Bean@291aff  Bean{id='0', name='none'}
Thread-3  com.lavasoft.test2.Bean@186db54  Bean{id='0', name='none'}
Thread-3  com.lavasoft.test2.Bean@186db54  Bean{id='0', name='none'}
Thread-1  com.lavasoft.test2.Bean@291aff  Bean{id='0', name='none'}
Thread-0  com.lavasoft.test2.Bean@291aff  Bean{id='0', name='none'}
Thread-0  com.lavasoft.test2.Bean@291aff  Bean{id='0', name='none'}
Thread-1  com.lavasoft.test2.Bean@291aff  Bean{id='0', name='none'}

Process finished with exit code 0

本文出自 “ 熔 岩” 博客,请务必保留此出处 http://lavasoft.blog.51cto.com/62575/258459
 
从打印结果很清楚的看到,MyThreadLocal的tlt对象的确是一个,tlt对象里的ThreadLocal的tl对象也是一个,但是,将t1t给每个线程用的时候,线程会重新创建Bean对象加入到ThreadLocal的Map中去使用。
Java线程管理:ThreadLocal与线程取消实践 本文深入探讨了Java中的ThreadLocal和线程取消机制,通过实际代码示例展示如何使用ThreadLocal进行线程本地数据存储,以及如何正确管理线程的生命周期和取消线程执行。同时,文章分析了ThreadLocal的性能影响和线程取消的不同策略,提出了在Java中应该避免使用过时的thread.stop()方法,并给出了处理线程退出的推荐做法。 阅读详情

相关推荐

Maven

是一款自动化构建工具,专注于Java项目的构建、依赖的管理。

weixin_58738870的博客 2163

ThreadLocal学习

首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。  另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作

飞雨飞羽飞鱼 1111

告别瞎猜!用通达信/同花顺这些资金流向函数,一眼看穿主力动向(附实战公式)

本文详细解析了通达信/同花顺资金流向函数的实战应用,帮助投资者精准识别主力动向。通过12个核心资金函数的组合逻辑,构建主力行为监控体系,避免未来函数陷阱,识别主力对倒行为,并提前捕捉异动。适合股票投资者提升自选股分析能力。

weixin_42522964的博客 1199

多线程基础-ThreadLocal

讲解 threadLocal

weixin_43740223的博客 1005

Python逆向爬取Tik Tok,MsToken,X-Bogus以及signature

tiktok作为字节海外的自媒体平台,具有很非常大的价值,本文旨在教会大家实现Tik Tok的签名获取以及数据的最终爬取。

weixin_74305707的博客 3270

C#开发者必备:VSPD虚拟串口快速配置指南(附汉化教程)

本文为C#开发者提供了VSPD虚拟串口的快速配置与汉化教程。详细介绍了如何安装、汉化VSPD,并创建稳定的虚拟串口对,重点讲解了如何将其集成到C#开发工作流中,使用SerialPort类进行数据通信测试、协议模拟及故障排查,有效提升硬件通信开发的效率。

weixin_29224529的博客 258

ThreadLocal使用以及实现原理

ThreadLocal使用 2、ThreadLocal使用场景 3、ThreadLocal实现原理

IT从业者,生活精致一点,工作认真一点! 619

从稀疏表示到低秩表示(一)

确定研究方向后一直在狂补理论,最近看了一些文章,有了些想法,顺便也总结了representation系列的文章,由于我刚接触,可能会有些不足,愿大家共同指正。 从稀疏表示到低秩表示系列文章包括如下内容: 一、 sparse representation 二、 一、 sparse representation 1  sparsity  一个线性表示解决的问题如下图所示:

彼岸花 3万+

什么是线程局部变量?如何在Java中使用线程局部变量?

线程局部变量(Thread Local Variable)是指每个线程都有自己的值副本,互不干扰,线程之间互不可见。它可以理解为一种特殊的变量,每个线程对应一个变量副本,不同线程之间访问的是各自的副本。线程局部变量适用于在多线程环境下需要保持独立状态的变量,例如记录用户信息或跟踪线程执行过程等。使用线程局部变量时,要注意及时清理或移除变量的值,以免造成内存泄露。是Java提供的一个用于创建线程局部变量的类,它通过为每个线程创建一个独立的变量副本来实现。方法为当前线程的局部变量设置值。类来实现线程局部变量。

wzxue1984的博客 651

surfer10与Arcgis10.3 完美生成等值线和等值面方法

surfer10 与Arcgis10.3生成等值线和等值面的步骤如下: 1.准备surfer用的Book1.dat文件,格式如下: "x" "y" "z" 116.471992 40.809975 232 116.335556 40.700542 1235 116.541626 40.713333 214 116.73349 40.798607 566 2.在surfer中,选择Gr

哈里·雪利博客 1万+

重塑计算的五层蛋糕:解读黄仁勋CES2026演讲背后的AI新秩序与物理世界

2026年CES演讲揭示AI新纪元:从语言模型到物理智能 NVIDIA CEO黄仁勋在2026年CES演讲中描绘了计算产业的根本性变革。核心突破包括: 计算范式转移:从指令执行转向意图理解,智能体系统崛起 物理AI突破:发布Cosmos世界模型,通过合成数据训练AI理解物理定律 全新VeraRubin计算平台:6芯片协同设计,解决摩尔定律放缓与AI需求增长的矛盾 自动驾驶革命:推出具备推理能力的Alpamayo模型,实现L4级自动驾驶 演讲标志着AI正从数字世界迈向物理世界,NVIDIA通过全栈布局,正在重

老孙 | Ethan Sun 的博客:AI 智能体与 AI 原生软件的工程实践 1273

Java多线程入门:三种线程创建方式 + ThreadLocal实战

本文介绍了Java多线程开发的三个核心知识点:1)创建线程的三种方式(继承Thread类、实现Runnable接口、实现Callable接口),分析各自的优缺点和适用场景;2)ThreadLocal的原理与使用,通过数据库连接管理、用户会话信息等典型场景说明其线程隔离特性;3)综合示例展示线程池与ThreadLocal的配合使用。文章强调ThreadLocal使用后必须调用remove()避免内存泄漏,并指出这些基础知识对后续学习线程池、锁机制等高级内容的重要性。

2301_81173705的博客 511

【AI编程】Codex 进阶与高阶技巧:从熟练到精通——2026 最新教程

Skill 是 Codex 的「插件」—— 一段 Markdown 定义触发条件、输入参数和执行流程。比自定义命令更强大,支持参数化、多步骤、条件判断。

十年老程序猿,AI 疯狂研究专家,前沿硬核知识分享 192

Java里的线程神器:ThreadLocal

今天我们要学习一种在JAVA线程中至关重要的类——ThreadLocal。ThreadLocal是一个强大的JAVA类,它能实现线程局部变量的功能。通过ThreadLocal,每一个线程都可以拥有自己的一份变量副本,互相之间不会影响操作,真正做到数据隔离。尤其在多线程环境下,避免了线程间混乱的数据共享、避免出现竞争条件,使得编程更加优雅。

编码人生 1044

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

java threadlocal_Java线程:深入ThreadLocal

Java线程:深入ThreadLocalThreadLocal与线程成员变量还有区别,ThreadLocal该类提供了线程局部变量。这个局部变量与一般的成员变量不一样,ThreadLocal的变量在被多个线程使用时候,每个线程只能拿到该变量的一个副本,这是Java API中的描述,通过阅读API源码,发现并非副本,副本什么概念?克隆品? 或者是别的样子,太模糊。准确的说,应该是ThreadLoca...

weixin_39785723的博客 135

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Java基础:多线程之线程范围内的数据共享ThreadLocal

问题:实现线程范围内的数据共享,即每个线程只能访问它自己的数据,不能访问其他线程的数据。具体逻辑如下图所示: 1.方法一:使用Map<线程Thread,数据类型DataType> 使用Map<线程Thread,数据类型DataType>,其中key为Thread,即线程;value为DataType,即要线程范围内共享的数据的数据类型。这样就可以通过线程来获取其相应的数据。

静墨的博客 528

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

Java ThreadLocal完全指南:为什么需要线程局部变量?

ThreadLocal的核心价值在于:为每个线程提供独立的变量副本,从根本上避免了多线程竞争,同时提供了跨层传递数据的能力。为什么需要使用ThreadLocal?维度说明线程隔离每个线程拥有独立副本,无需加锁,性能更高简化传参避免在调用链中层层传递参数,降低耦合度资源管理确保同一线程使用同一资源(如数据库连接)⚠️ 使用前提:必须正确清理(remove()),否则在线程池场景会导致内存泄漏。

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 362

java线程池threadlocal_Java多线程:ThreadLocal

一、ThreadLocal基础知识ThreadLocal是线程的一个本地化对象,或者说是局部变量。当工作于多线程中的对象使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程分配一个独立的变量副本。所以每一个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。ThreadLocal不是用来解决对象共享访问问题的,而主要是提供了线程保持对象的方法和避免参数传递...

weixin_31289551的博客 429

JAVA多线程:ThreadLocal

前言 说起ThreadLocal即便你没有直接用到过,它也间接的出现在你使用过的框架里,比如Spring的事物管理,Hibernate的Session管理、logback(和log4j)中的MDC功能实现等。而在项目开发中,比如用到的一些分页功能的实现往往也会借助于ThreadLocal。 正是因为ThreadLocal的无处不在,所以在面试的时候也经常会被问到它的实现原理、核心API使用以及内存泄露的问题。 而且基于这些问题还可以拓展到线程安全方面、JVM内存管理与分析、Hash算法等等知识点。可见

SeanSunnn 2278

Java 中的 ThreadLocal:每个线程都有自己的小秘密

就像是给每个线程配了一个私密小保险箱,确保了多线程环境下数据的独立性和安全性。它提供了简便的方式来实现线程隔离,避免了多线程对共享数据的竞争问题。不过,也要小心内存泄漏和性能开销等问题,避免陷入“贪大求全”的陷阱。

萌萌白白的博客 733

ThreadLocal:Java 多线程环境下的“局部变量”神器

ThreadLocal 是 Java 中用于实现线程局部变量的类,它提供了一种机制,让每个线程可以独立地访问自己的变量副本,避免了线程间的数据竞争问题。ThreadLocal 通常用于存储线程私有的数据,例如数据库连接、用户会话信息等。

千淘万漉虽辛苦,吹尽狂沙始到金 2万+

java多线程:ThreadLocal详解

场景: 在MVC模型中,我们要拿到登陆用户的session信息来获取用户名userName。 提出问题: ThreadLocal是什么?用来解决什么问题? ThreadLocal的优缺点 ThreadLocal的使用 ThreadLocal的内存泄漏问题 ThreadLocal是什么 ThreadLocal是一个保存线程局部变量的工具,每一个线程都可以独立地在ThreadLocal中保存与获取自己的变量,而该变量不会受其它线程的影响。 ThreadLocal的使用

qq_41257358的博客 6446

Java多线程学习三十:ThreadLocal 适合用在哪些实际生产的场景中

我们在学习一个工具之前,首先应该知道这个工具的作用,能带来哪些好处,而不是一上来就闷头进入工具的 API、用法等,否则就算我们把某个工具的用法学会了,也不知道应该在什么场景下使用。所以,我们先来看看究竟哪些场景下需要用到 ThreadLocal。 在通常的业务开发中,ThreadLocal 有两种典型的使用场景。 场景1,ThreadLocal 用作保存每个线程独享的对象,为每个线程都创建一个副本,这样每个线程都可以修改自己所拥有的副本, 而不会影响其他线程的副本,确保了线程安全。 场景2,Threa

徘徊在牛A与牛C之间的码农 296

java线程池threadlocal_Java多线程10:ThreadLocal的作用及使用

ThreadLocal的作用从上一篇对于ThreadLocal的分析来看,可以得出结论:ThreadLocal不是用来解决共享对象的多线程访问问题的, 通过ThreadLocal的set()方法设置到线程的ThreadLocal.ThreadLocalMap里的是是线程自己要存储的对象,其他线程 不需要去访问,也是访问不到的。各个线程中的ThreadLocal.ThreadLocalMap以及Th...

weixin_34856035的博客 220

java多线程threadlocal_Java进阶:Java多线程之ThreadLocal

ThreadLocal的核心思想很简单:为每个独立的线程提供一个变量的副本。Java提供的synchronized关键字使用了“同步锁”的机制来阻止线程的竞争访问,即“以时间换空间”。: " 10pt; FONT-SIZE:> ThreadLocal则使用了“拷贝副本”的方式,人人有份,你用你的,我用我的,大家互不影响,是“以空间换时间”。每个线程修改变量时,实际上修改的是变量的副本,...

weixin_42500789的博客 141
上一篇: 深入研究java.lang.ThreadLocal类
下一篇: ThreadLocal的正确用法
ichsonx
博客等级 码龄19年 202粉丝 74原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值