java面试之ThreadLocal问题

什么是ThreadLocal,它的基本用法是什么 

简单来说就是能在多线程中保持变量独立的线程对象

不用Threadlocal多线程访问同一个变量会出现的问题

package com.pxx;/*** Created by Administrator on 2023/9/3.*/
public class Demo1 {private String v1;public String getV1() {return v1;}public void setV1(String v1) {this.v1 = v1;}public static void main(String[] args) {//开启一个多线程去设置并且访问这个变量Demo1 d1 = new Demo1();//这里会循环五个线程for(int i = 0;i < 5;i++) {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {//设置并且打印一个变量的数据d1.setV1("data:" + Thread.currentThread().getName());System.out.println("-------------");//取出这个线程对应的名字和值System.out.println(Thread.currentThread().getName() + "=>" + d1.getV1());}});//设置一下每一个线程的名字t1.setName("线程" + i);t1.start();//开启直接进入到运行状态,然后当前线程开始运行,然后进行下一次循环,下一个线程进来,这个时候进程可能会仙湖干扰}}
}

下面直接已经线程混乱 

 一般来说我们可以用锁来解决,比如引入synchronized,这里我们先不用锁,我们用ThreadLocal这个类去解决

ThreadLocal类去解决线程不同步的问题

它的目的是保持变量独立

下面我们去看一下常见的方法

set():将变量绑定到当前线程中

get():获取当前线程绑定的变量

修改一下上面的代码

package com.pxx;/*** Created by Administrator on 2023/9/3.*/
public class Demo1 {//引入绑定变量的线程对象ThreadLocal<String>  tl1= new ThreadLocal();private String v1;public String getV1() {// return v1;return tl1.get();//得到通过set绑定的变量}public void setV1(String v1) {//this.v1 = v1;tl1.set(v1);//直接把这个v1绑定到对象里面}public static void main(String[] args) {Demo1 d1 = new Demo1();//这里会循环五个线程for(int i = 0;i < 5;i++) {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {//设置并且打印一个变量的数据d1.setV1("data:" + Thread.currentThread().getName());System.out.println("-------------");//取出这个线程对应的名字和值System.out.println(Thread.currentThread().getName() + "=>" + d1.getV1());}});//设置一下每一个线程的名字t1.setName("线程" + i);t1.start();//开启直接进入到运行状态,然后当前线程开始运行,然后进行下一次循环,下一个线程进来,这个时候进程可能会仙湖干扰}}
}

运行结果:

 ThreadLocal与synchronized的区别

先把上面的代码变成synchronized给锁住

package com.pxx;/*** Created by Administrator on 2023/9/3.*/
public class Demo3 {//引入绑定变量的线程对象ThreadLocal<String>  tl1 = new ThreadLocal();private String v1;public String getV1() {return v1;}public void setV1(String v1) {this.v1 = v1;}public static void main(String[] args) {Demo3 d1 = new Demo3();//这里会循环五个线程for(int i = 0;i < 5;i++) {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {synchronized (d1) {//这个加锁了//设置并且打印一个变量的数据d1.setV1("data:" + Thread.currentThread().getName());System.out.println("-------------");//取出这个线程对应的名字和值System.out.println(Thread.currentThread().getName() + "=>" + d1.getV1());}}});//设置一下每一个线程的名字t1.setName("线程" + i);t1.start();//开启直接进入到运行状态,然后当前线程开始运行,然后进行下一次循环,下一个线程进来,这个时候进程可能会仙湖干扰}}}

运行结果:

很明显是锁住了

先来说一下两者的共同点:都是处理多线程并发访问变量的问题

synchronized:它的效率会低一点,因为相当于就是说,线程是排队进行访问的,就像一个教室只有一个厕所,大家都要进去上,就要排队

ThreadLocal:效率高,相当于线程可以同时并发访问,效率高,就像一个教室多个厕所,彼此上,但是相互独立

ThreadLocal的内部结构

最早的一个设计原理

 JDK8的设计原理

关注一下JDK8,它是把Thread每一个线程作为了一个主线,然后Entry里面存放的是 ThreadLocal这样一个线程对象

这样的设计方案有两个好处:

1.每个map存储的entry变少,因为就是怎么说,Thread线程肯定比ThreadLocal这样一个线程多

2.那么主线Thread销毁掉,里面的map数据对象也就被销毁了

我分析一下源码

先去看set方法

再去看一下ThreadLocalMap这个类 

 

set就是添加到了这个map对象里面

这样不就说明一个问题:解决了线程并发访问,变量出错的问题

相当是什么,自己去上自己的厕所,彼此之间相互独立不影响

 可能会造成的一个内存泄漏的问题

他分为两种情况来看:

第一种: 内存不够了,只有溢出memory overflow

第二种:内存泄漏memory leak,在堆上的空间得不到释放,会造成浪费,影响了程序的运行速度,这种浪费多了,就会造成内存的溢出 

我们这里再来引入两个概念

第一个:什么是强引用?我们正常的引用一个对象,没有指向的时候,就会被gc掉,也就是垃圾回收器给回收掉

第二个:什么是弱引用?一句来说,遇到gc就会被回收。只要垃圾回收机制一运行,不管jvm的内存空间时候是否足够,都会回收该对象占用的内存

下面用一张图展示一下引用关系

假如是key是强引用的情况

假设ThreadLocal用完了,引用被收回,又因为Map里面的ThreadLocal是一个强引用,所以ThreadLocal对象无法被回收掉

在没手动remove掉Entry类以及CurrentThread依然运行的情况下,Entry类根本不会被挥挥手,会造成内存泄漏

假设key是弱引用的情况下,ThreadLocal引用没了,map 里面是一个弱引用指向ThreadLocal,那么就表明ThreadLocal会马上被垃圾回收器给回收掉,他一回收掉,Key就为NULL,那么我们就再也无法访问到value ,value无法被回收,会导致内存泄漏

很明显源代码给出的是一个弱引用

上面也说明了强引用还是弱引用都会造成内存泄漏

那么造成内存泄漏的根本原因就是:

第一点:Entry类始终存在内存中,没有手动remove

第二点:CurrentThread线程依然在运行

好了,祝你早安午安晚安。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

详解IP协议

在介绍IP协议之前&#xff0c;先抛出一个概念&#xff1a;IP地址的作用——定位主机&#xff0c;具有将数据从主机A跨网络传输到主机B的能力&#xff0c;有了TCP提供的策略&#xff0c;例如滑动窗口、拥塞控制等&#xff0c;IP去执行它&#xff0c;所以我们通常叫TCP/IP协议&am…

降噪音频转录 Krisp: v1.40.7 Crack

主打人工智能降噪服务的初创公司「Krisp」近期宣布推出音频转录功能&#xff0c;能对电话和视频会议进行实时设备转录。该软件还整合的ChatGPT&#xff0c;以便快速总结内容&#xff0c;开放测试版于今天上线。 随着线上会议越来越频繁&#xff0c;会议转录已成为团队工作的重…

QT DAY7

主要完成多人聊天室&#xff0c;注册与登录使用sql3数据库进行对密码的保存&#xff0c;避免了用户重复登录、错误密码登录、重复注册的问题&#xff0c;之后使用TCP通信&#xff0c;连接上服务器后可在聊天室多人交流

十四、MySQL(DCL)如何实现用户的简单管理?配置用户?

1、DCL语句&#xff1a; 要学习DCL语言&#xff0c;就要清楚DCL语言是用来干什么的&#xff0c;首先DCL语言英文全称是Data Control Language&#xff0c;是数据控制语言&#xff0c;主要用来管理数据库用户、控制数据库的访问权限/ 2、DCL的基础语法&#xff1a; &#xff08;…

SpringBoot通过@Cacheable注解实现缓存功能

目录 一、Spring从3.1开始支持Cache二、Cacheable常用属性1、value/cacheNames2、key3、condition4、unless5、keyGenerator6、sync7、cacheManager 三、整合步骤1、加入pom2、启动类加EnableCaching注解3、controller或service加Cacheable注解即可 四、代码实例五、Spring Boo…

unity面试题(性能优化篇)

CPU 预处理、缓存数据 注释空的unity函数 运算cpu->gpu 减少昂贵计算(开方) 限制帧数 加载(预加载、分帧加载、异步加载、对象池) 慎用可空类型比较 避免频繁计算(分帧、隔帧) 算法优化 变体收集预热 使用clear操作代替容器的new操作 unity spine使用二进制格式…

Unity中Shader的变体shader_feature

文章目录 前言一、变体的类型1、multi_compile —— 无论如何都会被编译的变体2、shader_feature —— 通过材质的使用情况来决定是否编译的变体 二、使用 shader_feature 来控制 shader 效果的变化1、首先在属性面板暴露一个开关属性&#xff0c;用于配合shader_feature来控制…

Ubuntu 20.04.5 怎么安装微信

这是我的ubutun版本号 在这个系统装桌面版微信很多功能不健全。搜索了很多方法&#xff0c;这个算是不错的一个法子。 1.添加仓库 首次使用时&#xff0c;你需要运行如下一条命令将移植仓库添加到系统中。 wget -O- https://deepin-wine.i-m.dev/setup.sh | sh 2.应用安装 …

服务端请求伪造(SSRF)及漏洞复现

服务端请求伪造 1. 概述 服务器会根据用户提交的URL 发送一个HTTP 请求。使用用户指定的URL&#xff0c;Web 应用可以获取图片或者文件资源等。典型的例子是百度识图功能。 如果没有对用户提交URL 和远端服务器所返回的信息做合适的验证或过滤&#xff0c;就有可能存在“请求…

QT 初识多线程

1.QThread线程基础 QThread是Qt线程中有一个公共的抽象类&#xff0c;所有的线程类都是从QThread抽象类中派生的&#xff0c;需要实现QThread中的虚函数run(),通过start()函数来调用run函数。 void run&#xff08;&#xff09;函数是线程体函数&#xff0c;用于定义线程的功能…

ChatGPT:深度学习和机器学习的知识桥梁(文末送书)

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

【Unity3D】UI Toolkit样式选择器

1 前言 UI Toolkit简介 中介绍了样式属性&#xff0c;UI Toolkit容器 和 UI Toolkit元素 中介绍了容器和元素&#xff0c;本文将介绍样式选择器&#xff08;Selector&#xff09;&#xff0c;主要包含样式类选择器&#xff08;Class Selector&#xff09;、C# 类选择器&#xf…