金三银四跳槽季,BAT美团滴滴java面试大纲(带答案版)之二:ThreadLocal和Valotile

金三银四跳槽季,BAT美团滴滴java面试大纲(带答案版)之二:ThreadLocal和Valotile

继续面试大纲系列文章。

  ThreadLocal和Valotile是两个比较常见的知识点,虽然简单,但是能从一定程度上考察一个程序员,对多线程环境下,线程通信和数据安全的认知。闲话少说,进入正题:

一.ThreadLocal

  1. 问:请谈谈你对ThreadLocal的理解。
  2. 分析:在多线程环境下,我们经常遇到这样的场景:维护一个全局变量。如果要保证变量值的正确性(或者说变量值修改的原子性),    需用什么方式来实现呢?是的,对修改代码加锁可以实现,保证了在同一时刻只有一个线程来修改该变量值。办法当然不止一                种,并发包AtomicXXX一样能达到这个效果,原理,差不多,无非是通过锁来实现并发。那么还有没有其他思路呢?有,ThreadLocal,实现思路可谓是另辟蹊径。
  3. 答:每个线程,都会有一个Map(ThreadLocalMap),用来存储以我们定义的ThreadLocal对象为key,以我们自定义的值为value的  名值对。而这个Map,是来自于我们写的多线程程序继承的父线程Thread。以此机制,保证了多线程间该变量值的隔离。

   看下源码,以get()方法为切入口:

 1  public T get() {
 2         Thread t = Thread.currentThread();
 3         ThreadLocalMap map = getMap(t);
 4         if (map != null) {
 5             ThreadLocalMap.Entry e = map.getEntry(this);
 6             if (e != null) {
 7                 @SuppressWarnings("unchecked")
 8                 T result = (T)e.value;
 9                 return result;
10             }
11         }
12         return setInitialValue();
13     }

  重点是第三行,当前线程作为参数传入,我们来看下getMap(t)做了什么?

1 ThreadLocalMap getMap(Thread t) {
2         return t.threadLocals;
3     }

  是的,拿到当前线程对象的threadLocals对象,我们可以通过方法返回值推断,是一个ThreadLocalMap类型的对象。那么这个对象在哪定义的呢?继续看源码:

1 public class Thread implements Runnable {
2     ......
3     ThreadLocal.ThreadLocalMap threadLocals = null;
4     ......
5 }

  很明显,是在Thread类里定义。

  4.扩展:内存泄漏问题。

       ThreadLocal对象是弱引用。在GC时,会直接回收。这种情况下,Map中的key为null,value值还在,无法得到及时的释放。目前的策略是在调用get、set、remove等方法时,会启动回收这些值。但是如果一直没调用呢?嗯,很容易就导致内存泄漏了。当然,并不能因为此就认为是弱引用导致的内存泄露,而应该是,设计的这个变量存储机制,导致了泄露。所以在使用的时候,要及时释放(通过以上描述,你肯定已经想到怎么合理释放了吧?)

二.Valotile

    1.问:请你说下对Valotile的了解,以及使用场景。

  2.分析:多线程编程,我们要解决的问题集中在三个方面:

        a.原子性,最简单的例子就是,i++,在多线程环境下,最终的结果是不确定的,为什么?就是因为这么一个++操作,被编译为指令    后,是多个指令来完成的。那么遇到并发的情况,就会导致彼此“覆盖”的情况。

       b.可见性,通俗解释就是,在A线程对一个变量做了修改,在B线程中,能正确的读取到修改后的结果。究其原理,是cpu不是直        接 和系统内存通信,而是把变量读取到L1,L2等内部的缓存中,也叫作私有的数据工作栈。修改也是在内部缓存中,但是何时              同步到系统内存是不能确定的,有了这个时间差,在并发的时候,就可能会导致,读到的值,不是最新值。

       c.有序性:这里只说指令重排序,虚拟机在把代码编译为指令后执行,出于优化的目的,在保证结果不变的情况下,可能会调整指    令的执行顺序。

   3.答:valotile,能满足上述的可见性和有序性。但是无法保证原子性。

         可见性,是在修改后,强制把对变量的修改同步到系统内存。而其他cpu在读取自己的内部缓存中的值的时候,发现是valotile修饰  的,会把内部缓存中的值,置为无效,然后从系统内存读取。

         有序性,是通过内存屏障来实现的。所谓的内存屏障,可以理解为,在某些指令中,插入屏障指令,用以确保,在向屏障指令后面  继续执行的时候,其前面的所有指令已经执行完毕。

  4.扩展:在写单例模式时,我们通常会采用双层判断的方式,在最内层:

instance = new Singleton()

其实这也有一个隐含的问题:这句赋值语句,其实是分三步来操作的:

    a.为instance分配内存

    b.调用Singleto构造函数来初始化变量

    c.instance指向上一步初始化的对象

        在jvm做了指令重排序优化后,上述步骤b和c不能保证,可能出现,c先执行,但是对象却没初始化,这时候其他线程判断的时候,发现是非null,但是使用的时候,却没有具体实例,导致报错。

所以,我们可以用valotile来修饰instance,避免该问题。

有了以上知识储备,相信可以应对80%的面试挑战了。如果还有兴趣深入了解,可以留言交流。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏安恒网络空间安全讲武堂

seacms修复历程总结

seacms修复历程总结 从6.45版本开始search.php就存在前台getshell的漏洞,到6.54官方对其进行修补,但修复方法是对用户输入的参数进行过...

6147
来自专栏犀利豆的技术空间

徒手撸框架--实现 RPC 远程调用

微服务已经是每个互联网开发者必须掌握的一项技术。而 RPC 框架,是构成微服务最重要的组成部分之一。趁最近有时间。又看了看 dubbo 的源码。dubbo 为了...

1492
来自专栏菩提树下的杨过

thrift 一个有意思的特性:Class名称无关性

最近开发的一个项目,后端采用thrift框架来提供rpc服务(java语言实现),然后前端采用php语言来生成thrift client调用后台RPC服务。由于...

2038
来自专栏大内老A

一个通过JSONP跨域调用WCF REST服务的例子(以jQuery为例)

JSONP(JSON with Padding)可以看成是JSON的一种“使用模式”,用以解决“跨域访问”的问题,这篇简单的文章给出一个简单的例子用于模拟如何通...

2087
来自专栏小灰灰

spring-boot & ffmpeg 搭建一个音频转码服务

利用FFMPEG实现一个音频转码服务 提供一个音频转码服务,主要是利用ffmpeg实现转码,利用java web对外提供http服务接口 背景 音频转码服务...

1.4K6
来自专栏有趣的django

python爬虫入门(七)Scrapy框架之Spider类

 Spider类 Spider类定义了如何爬取某个(或某些)网站。包括了爬取的动作(例如:是否跟进链接)以及如何从网页的内容中提取结构化数据(爬取item)。 ...

4237
来自专栏向治洪

android Handler机制之ThreadLocal详解

概述 我们在谈Handler机制的时候,其实也就是谈Handler、Message、Looper、MessageQueue之间的关系,对于其工作原理我们不做详解...

2079
来自专栏進无尽的文章

简述OC语言

对于一门语言的学习是需要时间领悟的,而对于一些原理性的问题,我们需要清楚其核心思想,知其然而知其所以然,这样才能有利于自己的后续发展。本文只是简述,没有面面具到...

2152
来自专栏微信公众号:Java团长

关于Java并发编程的总结和思考

编写优质的并发代码是一件难度极高的事情。Java语言从第一版本开始内置了对多线程的支持,这一点在当年是非常了不起的,但是当我们对并发编程有了更深刻的认识和更多的...

1252
来自专栏joycl

c#面试题汇总

下面的参考解答只是帮助大家理解,不用背,面试题、笔试题千变万化,不要梦想着把题覆盖了,下面的题是供大家查漏补缺用的,真正的把这些题搞懂了,才能“以不变应万变”。...

4831

扫码关注云+社区

领取腾讯云代金券