Java中ThreadLocal对象的使用

news2024/9/25 11:15:36

目录

1、Threadlocal简介

2、ThreadLocal的主要方法:

2.1 initialValue():初始化ThreadLocal变量的值

2.2 set():为当前线程设置ThreadLocal变量的值

2.3 get():获取当前线程中ThreadLocal变量的值

2.4 remove():移除当前线程中ThreadLocal变量的值

3、ThreadLocal的优点

4、ThreadLocal的缺点

5、ThreadLocal常见应用场景

6、使用ThreadLocal对象

小结


1、Threadlocal简介

ThreadLocal是一个Java中的线程本地变量,它提供了一种在多线程环境下保证每个线程拥有自己独立的变量副本的方式。在多线程编程中,使用ThreadLocal可以避免线程安全问题,提高程序的性能和可靠性。

当多线程访问共享且可变的数据时,涉及到多线程间同步的问题,并不是所有时候,都要用到共享数据。使用ThreadLocal ,这样无需synchronized或ConcurrentHashMap,可以在不影响性能的情况下,也无需层层传递参数,达到保存业务内容信息的目的。

Threadlocal的结构:

2、ThreadLocal的主要方法:

2.1 initialValue():初始化ThreadLocal变量的值

当调用get()方法获取ThreadLocal变量时,如果该变量还没有被设置过值,则会自动调用initialValue()方法来获取初始值。

initialValue()方法是一个protected方法,需要在ThreadLocal的子类中进行重写。一般情况下,我们可以使用匿名内部类来重写initialValue()方法,如下所示:

ThreadLocal<String> threadLocal = new ThreadLocal<String>() {
    @Override
    protected String initialValue() {
        return "default";
    }
};

在上述代码中,我们创建了一个ThreadLocal对象,并重写了它的initialValue()方法,使其返回一个默认值"default"。当调用threadLocal.get()方法时,如果该ThreadLocal变量还没有被设置过值,则会返回默认值"default"。

需要注意的是,initialValue()方法只会在第一次调用get()方法时被调用,之后再调用get()方法时不会再次调用initialValue()方法。如果需要重新设置ThreadLocal变量的值,可以调用set()方法或者remove()方法。

它可以用来初始化ThreadLocal变量的值,避免出现空指针异常等问题。在使用ThreadLocal时,应该根据实际需要来重写initialValue()方法,以便为ThreadLocal变量设置合适的默认值。

2.2 set()为当前线程设置ThreadLocal变量的值

当调用set()方法设置ThreadLocal变量的值时,该值会被存储到当前线程的ThreadLocalMap中,并与当前ThreadLocal对象关联起来。set()方法的使用非常简单,只需要调用ThreadLocal对象的set()方法,并传入要设置的值即可,如下所示:

ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.set("value");

在上述代码中,我们创建了一个ThreadLocal对象,并调用它的set()方法,将字符串"value"设置为当前线程的ThreadLocal变量的值。当调用get()方法获取ThreadLocal变量的值时,就可以获取到该值了。

需要注意的是,在同一个线程中多次调用set()方法设置ThreadLocal变量的值,只会保留最后一次设置的值。另外,如果ThreadLocal变量的值与当前线程已经存在的值相同,则不会对ThreadLocal变量的值进行更新,而是直接返回原有的值。这也意味着,如果需要更新ThreadLocal变量的值,必须显式地调用set()方法。

它可以用于为当前线程设置ThreadLocal变量的值。在使用set()方法时,需要注意线程隔离的特性,以及多次设置ThreadLocal变量值时只保留最后一次设置的值的特点。

2.3 get()获取当前线程中ThreadLocal变量的值

当调用get()方法获取ThreadLocal变量的值时,会从当前线程的ThreadLocalMap中查找与当前ThreadLocal对象关联的变量值,并返回该值。用法如下:

ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.set("value");
String value = threadLocal.get();

需要注意的是,每个线程都有自己独立的ThreadLocal变量副本,因此不同线程之间获取的ThreadLocal变量的值互不干扰。而且,在同一个线程中多次调用get()方法获取ThreadLocal变量的值,会返回同一个值。另外,如果当前线程没有设置ThreadLocal变量的值,则会返回null。如果需要设置ThreadLocal变量的默认值,可以通过重写ThreadLocal的initialValue()方法来实现。

在使用get()方法时,需要注意线程隔离的特性,以及获取ThreadLocal变量的默认值的问题。

2.4 remove()移除当前线程中ThreadLocal变量的值

当调用remove()方法移除ThreadLocal变量的值时,会从当前线程的ThreadLocalMap中移除与当前ThreadLocal对象关联的变量值。

在同一个线程中多次调用remove()方法移除ThreadLocal变量的值,只会移除最后一次设置的值。另外,如果当前线程没有设置ThreadLocal变量的值,或者已经调用过remove()方法移除了变量的值,则再次调用remove()方法不会产生任何效果。

ThreadLocal的原理图:

图片来源:https://baijiahao.baidu.com/s?id=1773187827419181532&wfr=spider&for=pc

Thread,ThreadLocal 以及 ThreadLocalMap三者关系图示:

图片来源:ThreadLocal 详解 - 知乎

3、ThreadLocal的优点

(1)线程隔离:每个线程都有自己独立的变量副本,互不干扰,保证线程安全;

(2)高效性:相比于使用synchronized,ThreadLocal不需要加锁,能够提供更高的并发性能,有助于提高执行效率;

(3)简单易用:ThreadLocal的使用相对简单,只需创建一个ThreadLocal对象,并通过get()和set()方法进行读写操作;

(4)传参便利:在一些场景中,需要将某些数据在多个方法之间传递,但不希望通过方法参数传递或者全局变量来实现。ThreadLocal可以作为一种简洁的方式来传递上下文信息。

4、ThreadLocal的缺点

(1)内存泄漏风险:由于ThreadLocal的特性,每个线程持有一个变量副本,如果没有及时清理,可能会导致内存泄漏。在使用完ThreadLocal后,应该调用remove()方法来清除线程的变量副本;

(2)上下文切换问题:在使用ThreadLocal时,如果频繁地在线程间切换,可能会导致ThreadLocal中的数据无法被及时清理,从而影响程序的性能和内存消耗;

(3)难以调试:由于每个线程都有自己的变量副本,当出现问题时,调试起来可能会比较困难。需要仔细分析每个线程的状态和变量副本的值;

5、ThreadLocal常见应用场景

图片来源:https://baijiahao.baidu.com/s?id=1773187827419181532&wfr=spider&for=pc

6、使用ThreadLocal对象

使用ThreadLocal对象存放的变量能够达到线程安全的目的,它的一般用法如下:

public final static ThreadLocal<String> PARAMS = new ThreadLocal<String>();

下面是相关代码示例:

public class ThreadLocalExample {
    //可以将ThreadLocal对象声明为一个全局常量,所有的线程均使用这一常量
    //public final static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>()
    
    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };

    public static void main(String[] args) throws InterruptedException {
        // 创建多个线程并启动
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(new CounterRunnable());
            thread.start();
        }
        // 主线程等待所有子线程结束
        Thread.sleep(1000);
        // 输出结果
        System.out.println("Final result: " + threadLocal.get());
    }

    static class CounterRunnable implements Runnable {
        @Override
        public void run() {
            // 获取当前线程的变量副本,并进行自增操作
            int count = threadLocal.get();
            for (int i = 0; i < 10000; i++) {
                count++;
            }
            // 将修改后的值保存回变量副本中
            threadLocal.set(count);
            // 输出结果
            System.out.println("Thread " + Thread.currentThread().getId() + " finished. Count: " + count);
        }
    }
}

在上述代码中,我们创建了一个静态的ThreadLocal对象threadLocal,并重写了它的initialValue()方法,使其初始值为0。在主线程中创建多个子线程,并启动它们执行CounterRunnable任务。每个子线程在执行run()方法时,先通过threadLocal.get()获取当前线程的变量副本,进行自增操作,然后再通过threadLocal.set(count)将修改后的值保存回变量副本中

由于使用了ThreadLocal对象,每个线程都拥有自己独立的变量副本,并且不会相互干扰,从而避免了线程安全问题。在所有子线程执行完毕后,我们可以通过threadLocal.get()方法获取主线程的变量副本,得到最终的结果。

需要注意的是,在使用ThreadLocal时,一定要在每个线程结束时调用remove()方法来清除线程的变量副本,以防止出现内存泄漏的情况

小结

总而言之,ThreadLocal对象是一种非常实用的多线程编程工具,可以有效地避免线程安全问题,提高程序的性能和可靠性。它的使用场景包括但不限于线程池、Web应用程序等。

参考:

https://baijiahao.baidu.com/s?id=1773187827419181532&wfr=spider&for=pc

ThreadLocal 详解 - 知乎

ThreadLocal 使用手册 | 建议收藏 - 简书

Java 超详细讲解ThreadLocal类的使用_java_脚本之家


感谢阅读,码字不易,多谢点赞!如有不当之处,欢迎反馈指出,感谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1133864.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

精简版STC单片机串口程序(只有初始化和sendbyte)

摘要&#xff1a;本文分享两个函数构成的STC单片机串口发送程序&#xff0c;代码占用空间极小。不想调用stdio.h和printf但是还想用串口发送简单的调试信息&#xff1f;那就试试它吧&#xff01; 直接上代码 &#xff0c;核心函数只有2个&#xff0c;如下所示 void UartInit(v…

newstar week3 pwn

newstar week3 pwn 巩固知识&#xff0c;如有错误记得纠正&#xff0c;感谢师傅们的评阅 puts or system? Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)int __cdecl main(int argc, const…

springboot-scanBasePackages包扫描

目录 原因&#xff1a; 方式一&#xff1a; 方式二&#xff1a; 原因&#xff1a; 由于对rocketMq进行了一次封装&#xff0c;mq模块里面引用了RocketMQTemplate的bean&#xff0c;如果只引入jar包的依赖&#xff0c;启动的时候不会报错&#xff0c;但是在调用到 RocketMQT…

【OpenCV实现图像阈值处理】

文章目录 概要简单阈值调整自适应阈值调整大津(Otsus)阈值法Otsus 二值化是如何工作的 概要 OpenCV库中的图像处理技术&#xff0c;主要分为几何变换、图像阈值调整和平滑处理三个部分。 在几何变换方面&#xff0c;OpenCV提供了cv.warpAffine和cv.warpPerspective函数&#…

4、让电机转起来【51单片机控制步进电机-TB6600系列】

摘要&#xff1a;本节介绍用简单的方式&#xff0c;让步进电机转起来。其目的之一是对电机转动有直观的感受&#xff0c;二是熟悉整个开发流程。 本系列教程必要的51单片机基础包括IO口操作、中断、定时器三个部分&#xff0c;相关基础教程网上很多&#xff0c;可以自行学习 一…

VMware Ubuntu 关闭自动更新

##1. VMware 17Pro&#xff0c;ubuntu16.04 关闭自动更新 1.1 编辑–》 首选项–》更新–》启动时检查产品更新 2. 这里关了还不够&#xff0c;第二天打开的时候还是提醒系统更新&#xff0c;需要关闭另外的地方 3. 关闭更新检查&#xff0c;默认的是隔天检查一次&#xff0c;…

怎么修复vcomp140.dll丢失问题?5个详细的修复方法分享

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“vcomp140.dll丢失”。那么&#xff0c;vcomp140.dll是什么&#xff1f;它丢失会造成哪些问题呢&#xff1f;小编将从以下几个方面进行详细阐述。 一、vcomp140.dll是什么&#xff1f; vco…

如何使用透明贴图实现火焰效果

1、透明贴图的原理 透明贴图是一种纹理贴图&#xff0c;用于模拟物体部分或全部的透明效果。其原理基于透明度和混合技术。 在计算机图形中&#xff0c;如何显示透明的物体是一个具有挑战性的问题。这是因为透明物体不会像不透明物体那样完全遮挡后面的物体&#xff0c;而是允…

【REDIS】redis-命令大全

【REDIS】redis-命令大全 redis-命令的官方文档 键命令 序号命令及描述1DEL key 该命令用于在 key 存在时删除 key。2DUMP key 序列化给定 key &#xff0c;并返回被序列化的值。3EXISTS key 检查给定 key 是否存在。4EXPIRE key seconds 为给定 key 设置过期时间&#xf…

Python爬虫核心模块urllib的学习

​ 因为在玩Python challenge的时候&#xff0c;有用过这个模块&#xff0c;而且学习这个模块之后也对系统学习网络爬虫有用。 ​ 当时查了各种资料学习&#xff0c;没有碰官网文档&#xff08;因为还是对英语有抗拒性&#xff09;&#xff0c;但是还是官方的文档最具权威和学…

使用Java做业务开发,如何做好一个定时任务的技术选型?

1. 轻量级任务调度 Quartz Scheduler 适用场景: 单机或简单的分布式任务调度特点: 提供丰富的调度选项&#xff0c;如Cron表达式、固定间隔等&#xff1b;支持持久化&#xff0c;能够在应用重启后恢复任务&#xff1b;支持任务监听和触发器监听。建议: 如果你的应用是基于Spr…

搭建docker本地仓库

1.拉取私有仓库镜像 [rootmaster1 ~]# docker pull registry [rootmaster1 ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE nginx v1 546db553f62a About an hour ago …

ftp远程连接传输的常见问题有哪些?如何一站式解决传输问题?

众多传统老行业很多已经部署了FTP传输相关系统&#xff0c;随着数据量和文件量的增加&#xff0c;一些相应的问题也出现了&#xff0c;些问题可能会影响传输的效率和安全性。本文将介绍FTP的常见问题和解决方法&#xff0c;并说明为什么大文件传输平台可以帮助企业实现更快更安…

安卓主板_MTK联发科4G低功耗安卓主板开发板方案

ZM358-DP安卓主板是一款性能功能强大的4G安卓平台。它采用了联发科MTK6737、MTK8735、MTK6753、MTK6735等芯片平台&#xff0c;64位四核Cortex-A53架构&#xff0c;主频高达1.3GHz&#xff0c;搭载ARM Mail-T450 MP2 GPU。 安卓主板具备多路显示屏接口&#xff0c;包括双LVDS、…

强劲升级,太极2.x你值得拥有!

嗨&#xff0c;大家好&#xff0c;最近桃桃没顾得上给大家分享好用好玩的软件。 还记得前段时间给大家分享的太极1.0软件&#xff1f; 最近大佬对软件进行了全新升级&#xff0c;升级后的功能更强更稳定&#xff0c;轻度用户使用基本功能就已经足够了&#xff0c;壕无人性的同学…

广州华锐互动:VR技术应用到工程项目施工安全培训的好处

随着科技的飞速发展&#xff0c;虚拟现实(VR)技术已经深入到各个领域。在建筑施工领域&#xff0c;VR技术的应用为工程项目施工安全培训带来了许多好处。本文将探讨VR技术在这方面的优势和应用。 首先&#xff0c;VR技术能够提供沉浸式的安全培训体验。通过VR设备&#xff0c;学…

cuDNN安装成功

验证方法&#xff1a;winR cmd进入安装目录下&#xff0c;再进入到 extras\demo_suite下&#xff0c;执行.\bandwidthTest.exe和.\deviceQuery.exe&#xff0c;得到下图。

蓝桥算法赛(摆玩具)

问题描述 小蓝是一个热爱收集玩具的小伙子&#xff0c;他拥有 n 个不同的玩具。 这天&#xff0c;他把 n 个玩具按照高度顺序从矮到高摆放在了窗台上&#xff0c;然后&#xff0c;他希望将这些玩具分成 k 个段&#xff0c;使得所有分段的极差之和尽可能小。 具体来说&…

Vue props实现父组件给子组件传递数据

Vue中的配置项Props能让组件接收外部传递过来的数据。 一、传递数据 在要传递的组件标签中配置传递信息&#xff1a; 属性名 "属性值" 注意&#xff1a;如果传递的属性值是一个表达式&#xff0c;要使用&#xff1a;属性名"属性值" 的形式。 二、接收数…

竞赛选题 深度学习动物识别 - 卷积神经网络 机器视觉 图像识别

文章目录 0 前言1 背景2 算法原理2.1 动物识别方法概况2.2 常用的网络模型2.2.1 B-CNN2.2.2 SSD 3 SSD动物目标检测流程4 实现效果5 部分相关代码5.1 数据预处理5.2 构建卷积神经网络5.3 tensorflow计算图可视化5.4 网络模型训练5.5 对猫狗图像进行2分类 6 最后 0 前言 &#…