ThreadLocal有什么用+面试题_第1页
ThreadLocal有什么用+面试题_第2页
ThreadLocal有什么用+面试题_第3页
ThreadLocal有什么用+面试题_第4页
ThreadLocal有什么用+面试题_第5页
已阅读5页,还剩9页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

文档来源网络侵权联系删除PAGEPAGE1仅供参考ThreadLocal有什么用+面试题什么是ThreadLocal?

ThreadLocal诞生于JDK1.2,用于解决多线程间的数据隔离问题。也就是说ThreadLocal会为每一个线程创建一个单独的变量副本。ThreadLocal有什么用?ThreadLocal最典型的使用场景有两个:ThreadLocal可以用来管理Session,因为每个人的信息都是不一样的,所以就很适合用ThreadLocal来管理;数据库连接,为每一个线程分配一个独立的资源,也适合用ThreadLocal来实现。其中,ThreadLocal也被用在很多大型开源框架中,比如Spring的事务管理器,还有Hibernate的Session管理等,既然ThreadLocal用途如此广泛,那接下来就让我们共同看看ThreadLocal要怎么用?ThreadLocal使用中要注意什么?以及ThreadLocal的存储原理等,一起来看吧。ThreadLocal基础使用ThreadLocal常用方法有set(T)、get()、remove()等,具体使用请参考以下代码。ThreadLocalthreadLocal=newThreadLocal();

//存值

threadLocal.set(Arrays.asList("老王","Java面试题"));

//取值

Listlist=(List)threadLocal.get();

System.out.println(list.size());

System.out.println(threadLocal.get());

//删除值

threadLocal.remove();

System.out.println(threadLocal.get());以上程序执行结果如下:2[老王,Java面试题]nullThreadLocal所有方法,如下图所示:ThreadLocal数据共享既然ThreadLocal设计的初衷是解决线程间信息隔离的,那ThreadLocal能不能实现线程间信息共享呢?

答案是肯定的,只需要使用ThreadLocal的子类InheritableThreadLocal就可以轻松实现,来看具体实现代码:ThreadLocalinheritableThreadLocal=newInheritableThreadLocal();

inheritableThreadLocal.set("老王");

newThread(()->System.out.println(inheritableThreadLocal.get())).start();以上程序执行结果如下:老王从以上代码可以看出,主线程和新创建的线程之间实现了信息共享。ThreadLocal高级用法内存溢出代码演示下面我们用代码实现ThreadLocal内存溢出的情况,请参考以下代码。classThreadLocalTest{

staticThreadLocalthreadLocal=newThreadLocal();

staticIntegerMOCK_MAX=10000;

staticIntegerTHREAD_MAX=100;

publicstaticvoidmain(String[]args)throwsInterruptedException{

ExecutorServiceexecutorService=Executors.newFixedThreadPool(THREAD_MAX);

for(inti=0;i<THREAD_MAX;i++){

executorService.execute(()->{

threadLocal.set(newThreadLocalTest().getList());

System.out.println(Thread.currentThread().getName());

});

try{

Thread.sleep(1000);

}catch(InterruptedExceptione){

e.printStackTrace();

}

}

executorService.shutdown();

}

ListgetList(){

Listlist=newArrayList();

for(inti=0;i<MOCK_MAX;i++){

list.add("Version:JDK8");

list.add("ThreadLocal");

list.add("Author:老王");

list.add("DateTime:"+LocalDateTime.now());

list.add("Test:ThreadLocalOOM");

}

returnlist;

}

}设置JVM(Java虚拟机)启动参数-Xmx=100m(最大运行内存100M),运行程序不久后就会出现如下异常:此时我们用VisualVM观察到程序运行的内存使用情况,发现内存一直在缓慢地上升直到内存超出最大值,从而发生内存溢出的情况。内存使用情况,如下图所示:内存溢出原理分析在开始之前,先来看下ThreadLocal是如何存储数据的。

首先,找到ThreadLocal.set()的源码,代码如下(此源码基于JDK8):publicvoidset(Tvalue){

Threadt=Thread.currentThread();

ThreadLocalMapmap=getMap(t);

if(map!=null)

map.set(this,value);

else

createMap(t,value);

}可以看出ThreadLocal首先获取到ThreadLocalMap对象,然后再执行ThreadLocalMap.set()方法,进而打开此方法的源码,代码如下:privatevoidset(ThreadLocal<?>key,Objectvalue){

Entry[]tab=table;

intlen=tab.length;

inti=key.threadLocalHashCode&(len-1);

for(Entrye=tab[i];

e!=null;

e=tab[i=nextIndex(i,len)]){

ThreadLocal<?>k=e.get();

if(k==key){

e.value=value;

return;

}

if(k==null){

replaceStaleEntry(key,value,i);

return;

}

}

tab[i]=newEntry(key,value);

intsz=++size;

if(!cleanSomeSlots(i,sz)&&sz>=threshold)

rehash();

}从整个代码可以看出,首先ThreadLocal并不存储数据,而是依靠ThreadLocalMap来存储数据,ThreadLocalMap中有一个Entry数组,每个Entry对象是以K/V的形式对数据进行存储的,其中K就是ThreadLocal本身,而V就是要存储的值,如下图所示:可以看出:一个Thread中只有一个ThreadLocalMap,每个ThreadLocalMap中存有多个ThreadLocal,ThreadLocal引用关系如下:enterimagedescriptionhere其中:实线代表强引用,虚线代表弱引用(弱引用具有更短暂的生命周期,在执行垃圾回收时,一旦发现只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存)。看到这里我们就理解了ThreadLocal造成内存溢出的原因:如果ThreadLocal没有被直接引用(外部强引用),在GC(垃圾回收)时,由于ThreadLocalMap中的key是弱引用,所以一定就会被回收,这样一来ThreadLocalMap中就会出现key为null的Entry,并且没有办法访问这些数据,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:ThreadRef->Thread->ThreaLocalMap->Entry->value并且永远无法回收,从而造成内存泄漏。ThreadLocal的正确使用方法既然已经知道了ThreadLocal内存溢出的原因,那解决办法就很清晰了,只需要在使用完ThreadLocal之后,调用remove()方法,清除掉ThreadLocalMap中的无用数据就可以了。

正确使用的完整示例代码如下:classThreadLocalTest{

staticThreadLocalthreadLocal=newThreadLocal();

staticIntegerMOCK_MAX=10000;

staticIntegerTHREAD_MAX=100;

publicstaticvoidmain(String[]args)throwsInterruptedException{

ExecutorServiceexecutorService=Executors.newFixedThreadPool(THREAD_MAX);

for(inti=0;i<THREAD_MAX;i++){

executorService.execute(()->{

threadLocal.set(newThreadLocalTest().getList());

System.out.println(Thread.currentThread().getName());

//移除对象

threadLocal.remove();

});

try{

Thread.sleep(1000);

}catch(InterruptedExceptione){

e.printStackTrace();

}

}

executorService.shutdown();

}

ListgetList(){

Listlist=newArrayList();

for(inti=0;i<MOCK_MAX;i++){

list.add("Version:JDK8");

list.add("ThreadLocal");

list.add("Author:老王");

list.add("DateTime:"+LocalDateTime.now());

list.add("Test:ThreadLocalOOM");

}

returnlist;

}

}可以看出核心代码,我们添加了一句threadLocal.remove()命令就解决了内存溢出的问题,这个时候运行代码观察,发现内存的值一直在一个固定的范围内,如下图所示:这样就解决了ThreadLocal内存溢出的问题了。相关面试题1.ThreadLocal为什么是线程安全的?答:ThreadLocal为每一个线程维护变量的副本,把共享数据的可见范围限制在同一个线程之内,因此ThreadLocal是线程安全的,每个线程都有属于自己的变量。2.ThreadLocal如何共享数据?答:通过ThreadLocal的子类InheritableThreadLocal可以天然的支持多线程间的信息共享。3.以下程序打印的结果是true还是false?ThreadLocalthreadLocal=newInheritableThreadLocal();

threadLocal.set("老王");

ThreadLocalthreadLocal2=newThreadLocal();

threadLocal2.set("老王");

newThread(()->{

System.out.println(threadLocal.get().equals(threadLocal2.get()));

}).start();答:false。

题目分析:因为threadLocal使用的是InheritableThreadLocal(共享本地线程),所以threadLocal.get()结果为老王,而threadLocal2使用的是ThreadLocal,因此在新线程中threadLocal2.get()的结果为null,因而它们比较的最终结果为false。4.ThreadLocal为什么会发生内存溢出?答:ThreadLocal造成内存溢出的原因:如果ThreadLocal没有被直接引用(外部强引用),在GC(垃圾回收)时,由于ThreadLocalMap中的key是弱引用,所以一定就会被回收,这样一来ThreadLocalMap中就会出现key为null的Entry,并且没有办法访问这些数据,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:ThreadRef->Thread->ThreaLocalMap->Entry->value并且永远无法回收,从而造成内存泄漏。5.解决ThreadLocal内存溢出的关键代码是什么?答:关键代码为threadLocal.remove(),使用完ThreadLocal之后,调用remove()方法,清除掉ThreadLocalMap中的无用数据就可以避免内存溢出了。6.ThreadLocal和Synchonized有什么区别?答:ThreadLocal和Synchonized都用于解决多线程并发访问,防止任务在共享资源上产生冲突,但是ThreadLocal与Synchronized有本质的区别,Synchronized用于实现同步机制,是利用锁的机制使变量或代码块在某一时刻只能被一个线程访问,是一种“以时间换空间”的方式;而ThreadLocal为每一个线程提供了独立的变量副本,这样每个线程的(变量)操作都是相互隔离的,这是一种“以空间换时间”的方式。总结ThreadLocal的主要方法是set(T)和get(),用于多线程间的数据隔离,ThreadLocal也提供了InheritableThreadLocal子类,用于实现多线程间的数据共享。但使用ThreadLocal一定要注意用完之后使用remove()清空ThreadLocal,不然会操作内存溢出的问题。 如何轻松获得Offer你好,我是王磊,某上市公司技术研发经理,前奇虎360员工,有着10余年的编程工作经验,目前主要负责新员工技术面试和构建企业技术架构的相关事宜。随着面试过的人数增加,我发现面试者们暴露出了技术方面的很多问题,为了让更多面试者少走一些弯路,也为了让企业能招到合适的技术人才,于是就诞生了这个专栏。为了写好这个专栏内容,我先后拜访了一二十家互联网公司,与不同的面试官和面试者进行面对面探讨,深入了解了企业对于面试者的要求和常见的Java面试题型。之后我花了大半年的时间,结合自己4年多作为面试官的经历,把这些内容整理成文,用大约10万字的内容对Java的核心知识点和常见的500多道面试题,做了详细的介绍,也就是本专栏中你所看到的全部内容,希望对你能有所帮助。为什么要学这个专栏内容?「因为它能为你赢得面试的主动权,让你获得更多的Offer。」从业十多年,我从面试者变成面试官,在Java面试上积累了比较丰富的经验。其实,很多面试者在搜集面试资料的时候都踩过一些“坑”,你是不是也遇到过:免费搜索的面试题,内容不全面,这就算了,有时候答案都不准确;很多培训机构提供的面试宝典内容虽然不少,但深度不够,且面试题过于老旧脱离了企业实际需要;还有很多付费的面试题存在滥竽充数,提供了很多没有价值的面试题,钱花了,干货没学到;市面上大部分面试题只讲了基础概念,没有提供题目解析和示例代码,不利于读者真正的掌握背后的原理,只能死记硬背,且容易忘记。为了规避这些“坑”,我跑了很多家互联网公司,来确认Java面试中实际考察的高频知识点和常见题型。可是有了第一手素材后,我要如何让大家真正从我的讲解中学到干货、用到实处呢?经过反复验证,我才设计了如下的内容讲述模式。第一,500+面试题详解。如果你是还没走入职场的新人,我会为你提供完整的Java技术栈讲解,以及最新、最全、最实用的500多道Java面试题详解。第二,10万字Java核心知识点梳理。本专栏的每一篇内容,都采用的是「核心知识点+N道相关面试题」的模式,让你不单能应付面试,还能学到更多的Java核心知识。第三,技术、面试搭配平衡,不但让你学到心里,还助你展示出来。面对目前技术市场的相对冷淡和一个职位多个应聘者竞争的现状,面试者们只有掌握更多Java核心技能和面试理论知识,才能在众多面试者中脱颖而出。本专栏每篇文章大致分为两个部分:Java核心点介绍+相关面试题详解,这两部分内容相辅相成,前面的核心知识点介绍让后面的面试题更容易理解,后面的面试题加深了读者对于Java核心点的掌握。如此一来,让你所学及所用,不仅能够应付面试,更能学习到更多有价值的Java技术点,让你在面试中和工作中都能展示的更加出色。专栏大纲本专栏分为七大部分,共计37讲,约10万字。第一部分:Java基础强化这部分包含7篇文章,我会从Java最基础的内容讲起。有最常见的String面试题从表象到原理的深入讲解;还有Java8中新特性的介绍,比如时间和日期模块,让你使用更简洁和优化的方式写出更完美的代码;还有我们日常用的很多包装类不为人知的有趣现象和知识盲点介绍;还有数组以及算法的介绍,虽然基础但容易被面试者忽略和容易出错的问题……第二部分:各种类和克隆这部分包含4篇文章,除了会深入讲解Java中的各种类和接口的相关内容,还会深入讲解浅克隆和深克隆的各种实现方式,以及配合各种图片让你更形象地理解深/浅克隆的本质。第三部分:数据结构和队列这部分包含4篇文章,对面试中必考的集合,除了相关的面试题讲解,更要理清各种集合之间的关

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论