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()方法,并给出了处理线程退出的推荐做法。 阅读详情

相关推荐

自制JS某课脚本

根据实际需求自制网页讯网刷题脚本,绕过人脸,弹窗,答题,实现自动化运行

2301_81243054的博客 3458

ThreadLocal学习

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

飞雨飞羽飞鱼 1111

从零开始用飞牛fnas搭建集成DeepSeek-R1的远程AI工作台

从零开始用飞牛fnas搭建集成DeepSeek-R1的远程AI工作台

AI+全栈,AI+应用,实操落地,前沿技术 3807

多线程基础-ThreadLocal

讲解 threadLocal

weixin_43740223的博客 1005

ASME B31.3-2024工艺管道标准介绍

ASME B31.3-2024《过程管道》是ASME发布的最新版工业管道标准,适用于石油、化工、制药等行业的管道系统设计、建造和维护。该版标准进行了多项重要更新,包括软管管理、应力指数调整、新增管件类型、焊接检查和泄漏测试等条款修订,并新增了书面报告要求。标准共594页,为全球过程工业提供管道安全规范,对保障管道系统完整性、可靠性具有重要意义,被多国强制采用或推荐使用。

TXNMG的博客 4698

SpringBoot + MyBatis-Plus 高效实现数据变更记录:从注解到通用审计方案(附源码)

*** 数据变更审计插件* 拦截增删改操作,自动记录变更日志*/@Component@Resource/*** 拦截SQL执行*/@Override/*** 数据变更审计插件* 拦截增删改操作,自动记录变更日志/*** 拦截SQL执行。

2601_94871597的博客 558

ThreadLocal使用以及实现原理

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

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

10、进化动力学谱分析中的开放性问题

本文探讨了进化动力学谱分析中的若干开放性问题,重点围绕进化算法的性能与设计理论展开。文章首先介绍了规范模型及其线性动力学特性,随后深入分析了在实际应用中偏离理想情况的两个关键因素:有限种群和双亲重组。在此基础上提出了三个核心开放性问题:寻找最优传输矩阵以实现全局最优解的高效搜索、无限与有限种群模型谱之间的关系、以及卡林定理在多遗传算子下的推广可能性。通过谱条件、快速混合性与首次命中时间的关系分析,文章进一步讨论了全局吸引与快速收敛的理论基础,并提出了未来的研究方向,包括理论深化、实验拓展与跨学科融合,旨在为

cuda7parallel的博客 80

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

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

wzxue1984的博客 651

彻底解决Win11锁屏界面黑屏或者图片不变化

windows锁屏后黑屏

renkai721的专栏 1万+

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

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

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

2301_81173705的博客 511

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Java里的线程神器:ThreadLocal

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

编码人生 1044

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

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

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、付费专栏及课程。

余额充值