郭霖

郭霖 查看完整档案

苏州编辑  |  填写毕业院校  |  填写所在公司/组织 guolin.tech 编辑
编辑

Android软件开发工程师。从事Android开发工作多年,有着丰富的项目实战经验,负责及参与开发过多款移动应用与游戏, 对Android系统架构及应用层开发有着深入的理解。

2014年, 创作了《第一行代码——Android》一书,目前《第一行代码 第3版》已出版。

个人动态

郭霖 发布了文章 · 10月21日

分享我成为GDE(Google开发者专家)的经历

本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭霖 即可关注,每个工作日都有文章更新。

经过一段漫长且复杂的申请过程,我的GDE申请总算是顺利通过了。

很荣幸现在我成为了国内第二位Android GDE(第一位是朱凯),而我想写一篇文章将整个过程分享出来,同时向国内的开发者们普及一下什么是GDE,以及如何去申请。

引子

今年4月,Android 11的Beta版即将上线之际。

鸿洋在微信上找我:老郭,最近有Google的那边的人联系你么?

我:没有啊,咋了?

鸿洋:有个说是和Google合作的传播伙伴,正在做Android 11面向开发者群体的传播规划,想要找国内影响力比较大的Android公众号来帮忙推广传播。

我:那可能是我的影响力还不够大吧

鸿洋:他们说在公众号找过你,你没有回复他们。

我:。。。。

鸿洋:那我把你的微信发给他们,让他们直接联系你。

一场奇妙的旅程就这么开始了。

与Google建立联系

话说我在国内的Android技术社区也算是活跃很多年了,写过百余篇博客,写过三本书,写过不少开源项目。但Google官方从来没有联系过我,我也没有主动联系过Google。当然,我不去主动联系Google是因为我不知道是否可以联系上Google,所以这次能够和Google建立联系我是很开心的。

至于去帮助Google做Android 11方面的推广,这点我当然是非常乐意的。毕竟从毕业以来我就一直在做Android方向的开发工作,既然是吃这碗饭的,帮助Google推广技术自然是义不容辞的事情。

本来我以为这件事情很简单,就是Google官方开发者公众号发布了一些Android 11的文章,我这边帮忙转发一下就可以了,然而事实并不是如此。

Google联系我之后表示,希望我可以参加7月4号在上海举办的Android 11 Meetup活动,并进行主题演讲。由于疫情的原因,这也是Google今年的第一场线下开发者活动。

这个邀请对于我来说是有点突然的,平时我都是以写博客、写书为主,最多是开几场Live Coding直播,几乎没有参加过任何线下主题演讲。但是换个角度想想,能受到Google官方的邀请,这也是对我的一种认可,如果拒绝的话就显得太不礼貌了,所以貌似我也就只剩一种选择了:好好准备!

由于演讲的内容要围绕Android 11展开,我大致翻阅了一下Android官网关于Android 11的新特性和行为变更,发现了一个比较有意思的点:AsyncTask在Android 11当中被废弃了。

AsyncTask可以说是陪伴了广大Android开发者许多年,一直以来都可以很好地帮助我们进行异步任务处理。那么为什么在Android 11当中这个类被废弃了呢?因为现在Google有了更加推荐的异步任务处理方式:协程。所以,我的演讲主题也就这么确定下来了。

首次线下演讲对于我来说还是相当紧张的,并且由于是Google官方的活动,我可不想在演讲中出现什么技术性的错误,要不然丢人就丢大了,因此必须进行非常全面的准备。

我上网参考了大量关于协程的文章,认真学习和总结,把之前没能掌握或者是有疑惑的知识点逐个击破。另外还编写了许多Demo程序,对这些知识点进行测试验证,以加深理解。

除了技术方面的准备,我还要思考演讲的内容划分,时间分配(事实证明我一直不擅长这个),甚至还学会了做PPT。

最终,Android 11 Meetup上海站的活动举办得相当成功,线下名额全部报满,线上一共11000人观看,并且普遍收到了大家的好评。想看这次活动回放的朋友可以 点击这里

活动结束后,在Google一直负责和我联络的Tracy也在说:你讲得太好了,赶快去申请GDE吧!

恩?GDE?

什么是GDE

GDE的全称是Google Developer Expert,是Google在全球范围内开展的一个开发者专家认证项目。如果你对Google的某个技术领域非常擅长,同时在这个领域有比较高的影响力的话,那么就有可能成为Google官方认证的开发者专家。

由于我知道绝大部分的国内开发者对于GDE的了解可能都很少,因此这里我就给大家做一个比较详细的科普。

众所周知,Google是一家崇尚技术的科技公司,Google也经常会推出许多面向开发者的技术产品。开发者对于Google的整体生态来说是非常重要的一环。

在Google推出的这些技术产品中,某些影响力比较大且比较成功的技术,Google就会为其提供专家认证服务(GDE)。因此,GDE是有很多个领域的。当然,这些领域也会随着Google的技术迭代一直在变化。

目前Google一共提供了16个技术领域的GDE认证,如下图所示。

这些技术基本也代表着Google当下最热门的技术方向。

那么或许有的小伙伴会好奇,成为GDE具体有什么好处呢?

我感觉最主要的好处就是能够得到一个Google官方的认可,相当于官方承认你是这个领域的专家了。虽然Google不会直接发你钱,但是你完全可以借助这个Title去尝试获得更高的薪水,甚至是自主创业。

并且,成为GDE之后,你将可以和Google建立直接的联系,在技术方面有什么问题可以向Google的员工进行咨询,还能获得一些Google未发布产品的内部试用资格。

Google可能也会向你寻求一些技术建议,比如我最近就被问到,希望Android 12中可以增加哪些新功能?(当然我也没能给出什么有建设性的建议,如果你有什么功能是非常希望Android 12中加入的,可以告诉我,我再转告给Google。)

除了以上好处之外,直接经济上的好处也是有一些的。比如说,GDE将有很大的概率被邀请去参加每年的Google I/O大会(这也是我申请GDE的最主要原因),并且Google会帮你承担所有的门票、机票、酒店的费用。另外,JetBrains向所有GDE提供了免费的全家桶产品,原价大概200多美元一年吧,像我平时偶尔会用RubyMine写写服务器程序,现在这部分钱就能省下来了。

那么目前全球一共有多少位GDE呢?这个数字是一直在变化的。因为每天可能都会有新的GDE加入,但同时,GDE的身份并不是一直有效的,而是只有一年有效期,Google会在第二年重新评估你是否仍然具备GDE的资格,所以,每天可能又会有人失去GDE的身份。

截至我编写文章的时候,全球一共有843位GDE,分布于上图中的16个技术领域,其中Android GDE一共有109位。

Google在其开发者官网上有一个专门的页面,展示了所有的GDE,以及他们的详细信息,地址是:

https://developers.google.com/community/experts/directory

另外这个页面上还会使用Google地图来标注出每个GDE所在的位置,如下图所示。

从上图我们可以看出,中国其实是有很多位GDE的。

事实上,中国目前一共有30位GDE,但绝大多数的GDE都是Machine Learning这个领域的(24位)。而Android领域就比较少了,目前只有两位,并且我是最近才刚刚认证上的。

那么接下来,我就向大家详细介绍一下我的GDE申请过程。

如何申请GDE

受到了Google的邀请之后,我就开始了我的GDE申请之旅。

从开始申请到最终成为GDE,我经历了大概一个月左右的时间。据说这已经算是非常快的了,有些GDE甚至经历了半年之久的申请过程。

GDE的申请过程相当复杂,而且对申请人的要求很多。不过我最终总结下来,主要要求无非就是两点:技术和影响力。

技术自然不用多说,你既然申请成为这个领域的专家,没技术肯定是不行的。

影响力是Google非常看重的一点,就是你光有技术还不行,你还必须在这个领域上有比较大的影响力,Google才可能会授予你GDE的称号。

而影响力又可以再具体划分成以下几点:公开演讲(尤其重要),博客,书,视频教程,开源项目。

其中,公开演讲是最最重要的一环,因为Google特别看重你在线下技术社区的参与度。另外其他几个部分都是加分项,越多越好,上不封顶。

当你认为你具备了所有成为一名GDE的条件之后,就可以去尝试申请GDE了。不过,申请GDE还需要一位引荐人,并且引荐人必须是Google员工才行,这里我要特别感谢Google的钟辉老师愿意帮我引荐。

那么你可能会说,我上哪有什么机会去认识Google员工帮我引荐啊?没错,所以首先你自身还是要有比较大的影响力才行,有了影响力自然就会有机会认识Google员工(主动或被动都有可能)。或者你也可以联系其他GDE帮助你引荐,比如说我。

当你获得了引荐资格之后,会有专门负责GDE项目的Google员工与你进行对接。首先他会发你一个链接,让你在这个网页上填写申请资料,注意必须全部都用英文填写。

填写申请资料大概是我申请GDE过程中最痛苦的一个部分,因为要填的内容实在是太多了。

我记得有两个部分是需要你非常详细地去填写的:个人介绍和申请原因。

个人介绍是让Google快速了解你的最佳途径,因此你需要将自己最有优势的一面展现出来,让Google知道你有多出色。另外,假如你能顺利成为一位GDE的话,在这里填写的内容,最终也会成为你的GDE专属页面上的个人介绍。

下图是我的GDE专属页面。

而申请原因要如何填写就不太好说了,我不清楚Google会如何评估这部分资料,甚至不清楚Google想要从申请原因中获取怎样的信息。但根据我的大体猜测,不应该在申请原因中填写太过功利性的目的,因为成为GDE本身就是一个无经济收益的事情,Google更希望看到的是你愿意在开发者社区中无偿做出贡献的态度。

总之,关于申请原因这块,我相信1000个人就会有1000种写法,只要你的原因是充分合理的即可,并没有什么所谓的标准答案,因此这里我就不把当初我写的申请原因分享出来了。

将上述两大块内容填写完成之后,接下来就到了要你使劲吹牛的时间:证明你的影响力。注意这里我并没有开玩笑的意思,因为Google想要确切地知道你的影响力到底有多大,所以你有任何值得吹嘘的地方,都要尽可能地写上。

关于影响力这块的资料填写,主要分为线下影响力、内容创作、项目贡献这3个块面。

线下影响力就是指你参加过哪些线下开发者活动,发表过多少次演讲,总共影响到了多少人,Google和非Google的活动都可以。当然,由于今年疫情的原因,许多开发者活动变成了线上举行,所以这部分内容的填写今年变得相对灵活了一点,一些线上演讲也可以算到里面。

内容创作是指你创作过哪些与Google技术相关的内容,这些内容影响到了多少开发者,主要包括博客、书、视频教程等等。这部分内容的填写对于我来说就非常有优势了,因为我的博客访问量以及书的销量都是相当可观的,所以可以在这个地方好好吹一波。

最后项目贡献这部分我的理解是开源项目的贡献,不知道在公司开发的商业项目能不能算到里面。总之你需要把你做过哪些拿得出手的项目都填写上去,然后这些项目在开发者群体中有多大的影响力(如star数量)也要告诉Google,好让Google对你可以有一个更加综合的评估。

我印象中要填写的申请资料主要就是这些了,由于全部都要用英文来填写,所以还是挺花时间的,我大概用了一周左右的时间才全部填写完成。

申请资料填写完成之后,点击提交审核,你的GDE申请之旅就正式起飞了。

面试

不过填写申请资料仅仅只是GDE申请的开始,接下来还有重重考验在等着你。

在你提交完申请资料之后,将会立即收到一封邮件,告诉你成为一名GDE需要经历哪些步骤。

一共是五步,详情见下图:

第一步是资格审查。Google会先对你的申请资料进行评估,确保你的资历足以担当得起GDE这个名号,不然可能在资格审查这一轮就会被刷掉。当然我认为这个概率很小,因为申请GDE都是需要Google员工引荐的,如果资历不够的话,首先他就不会引荐你。

过了资格审查这一关,接下来就会进入第一轮面试。第一轮Google会安排一位与你申请领域相同的GDE作为你的面试官,这位面试官可能来自于世界上任何一个国家,所以你要做好他的英语口音不标准的心理准备。不过在英语方面也不需要太过担心,毕竟你是在申请GDE而不是在做英语考试。只要你能听得懂对面在问什么,并且能用英语把自己想说的话表达出来就可以了,听不懂的地方可以多问几遍Pardon?面试官是不会介意的。

我的一轮面试官是一位来自印尼雅加达的GDE:Andrew Kurniadi。

Google会通过邮件让我们俩建立会话,然后我们自行沟通面试时间就可以了。以下是部分沟通细节:

面试的具体内容我就不能跟大家透漏了,其实无非就是我前面跟大家总结的两个点:技术和影响力,一切都是围绕这两个点展开的。

Andrew是一位相当友好的GDE,在开始面试前我一直比较担心我的英语口语到底行不行,面试结束后他告诉我完全不需要担心英语的问题,因为他觉得我的英语非常棒。一位好的GDE果然非常善于鼓励人。

首轮面试结束之后,面试官应该会根据面试的结果填写总结报告并提交给Google,具体是怎么操作的我就不清楚了,Andrew在面试的时候有跟我解释,但其实我并没有怎么听懂。

总之,我大概是在首轮面试两天之后收到了面试通过的邮件,与此同时Google会帮你安排第二轮面试。

第二轮面试的面试官将会是一名Google员工,这次我的运气比较好,Google帮我安排了一名中国区的Google员工来帮我面试,就是我们国内Android圈非常知名的陈卓老师。

由陈卓老师来帮我面试算是有利有弊吧,好处就是我最担心的语言障碍没有了,总算可以比较舒适地问答了。坏处就是,由于没有了语言障碍,面试官可以向你问更多更复杂的问题,并且你不能再以听不懂当作借口了。

我的一轮面试只花了30分钟左右的时间,而二轮面试足足花了一个小时,可能也是和陈卓老师聊得比较投缘吧

同样,我不能将二轮面试的具体内容分享出来,但大体无非还是围绕着我前面提到的那两点展开的。

两轮面试都通过了之后,你离GDE就只差一步之遥了:签署保密协议和服务与条款。

这两项虽然已经不是什么考核内容了,但却是你成为GDE的必备前提条件。我当时就因为服务与条款邮件莫名其妙进入了垃圾邮箱,导致我没看到这封邮件,然后GDE的申请进度就一直卡在那里,白白多等了一个多星期。

关于保密协议这块,因为GDE是有可能获取到一些Google的内部信息的,另外还能得到一些Google未发布产品的试用资格,为了防止这些机密信息被泄漏出去,所有GDE都必须签署保密协议才行。由于签署了保密协议,我在写本文时也比较谨慎,不过以上所有信息和截图都是我在签署保密之前就可以获取到的,所以应该不会触犯保密协议的规则。

而服务与条款这块,就是Google要和每一位GDE进行的一系列约定,哪些事情你可以做,哪些事情你不可以做。比如你不可以代表Google的立场去发表任何声明,还有你不可以向Google索要薪水等等。

Welcome On Board

以上所有环节全部通过之后,恭喜,你就正式成为一名GDE了。如果你还有点太敢相信的话,检查一下你的邮箱,将会看到这样一封邮件:

成为GDE之后,你将会收到一大堆Google发来的资料,包括GDE的Guide Line,GDE的专属联络通道,GDE的专属差旅网站资源等等等等。我大概花了一个晚上的时间才将这些资料全部梳理清楚。

每一个GDE的领域,在Google都会有一个全球范围的负责人,这个负责人会很快与你取得联系,并要求与你进行一次视频会面。这次视频会面的主要目的是为了欢迎你加入GDE的行列,向你介绍一些GDE的知识,并回答你的各种关于GDE的问题。

但是这对于我来说,又像是经历了一次面试,因为整个视频会面过程又是全英文进行的。

如果你的英文水平并不是非常好的话,这里我可以教你一个小窍门。就是你先提前跟他打一剂预防针,告诉他:I'm sorry, my English is not very good, so I need to make a apology in advance. 然后对面出于客气就会说:That's fine. Don't worry about it. 最后结束的时候他还会再补充一句:I think your English is perfect!

我屡试不爽。

GDE的责任

很明显,成为GDE只是一个开始。如果你想把成为GDE当成一个终点的话,那么你可能并不适合去申请这个头衔,因为GDE是要承担很多责任的。

Google非常乐于和愿意分享并传播Google技术的人一起合作,所以才有了GDE这个项目。能够成为GDE,说明Google对你的技术水平,以及你的技术影响力都表示了足够的认可。但如果你就此躺在功劳簿上,不再持续分享和传播你所擅长的技术,那么很遗憾,Google将会在下一年移除你的GDE身份。

所以,在申请GDE之前,一定要先想清楚这一点。

我在申请之前就进行了一下自我评估,我认为无论我是不是GDE,常年以来我都一直在分享Android相关的开发技术,我非常乐于做这件事,并且也愿意持续做下去,所以才决定提交了申请。

事实证明,这可能是我今年最正确的决定之一。这场奇妙的旅程让我结识了许多优秀的Googler,包括钟辉老师、陈卓老师、Tracy、Ben Weiss等等。甚至我竟然还能跟我的偶像Yigit Boyar(Jetpack负责人,RecyclerView作者)进行视频连线,共同参加一场圆桌会议,这实在是太不可思议了。

Tracy在刚刚加上我微信的时候就告诉我,Google一直在招募优秀的GDE候选人,同时希望进一步扩大国内Android GDE的人数。

而现在,我已经成为国内第二位Android GDE了。

如果你也具备成为GDE的资质,同时有兴趣申请的话,请与我联系。

关注我的技术公众号,每天都有优质技术文章推送。

微信扫一扫下方二维码即可关注:

查看原文

赞 11 收藏 2 评论 2

郭霖 发布了文章 · 10月12日

volatile关键字在Android中到底有什么用?

本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭霖 即可关注,每个工作日都有文章更新。

上周六在公众号分享了一篇关于Java volatile关键字的文章,发布之后有朋友在留言里指出,说这个关键字没啥用啊,Android开发又不像服务器那样有那么高的并发,老分享这种知识干啥?

让我意识到有些朋友对于volatile这个关键字的理解还是有误区的。

另外也有朋友留言说,虽然知道volatile关键字的作用,但是想不出在Android开发中具体有什么用途。

所以我准备写篇文章来剖析一下这个关键字,顺便回答一下这些朋友的疑问。

由于这篇文章是我用周日一天时间赶出来的,所以可能不会像平时的文章那样充实,但是对于上述问题我相信还是可以解释清楚的。

对volatile关键字的作用有疑问的同学,可能都不太了解CPU高速缓存这个概念,所以我们先从这个概念讲起。

CPU高速缓存和可见性问题

当一个程序运行的时候,数据是保存在内存当中的,但是执行程序这个工作却是由CPU完成的。那么当CPU正在执行着任务呢,突然需要用到某个数据,它就会从内存中去读取这个数据,得到了数据之后再继续向下执行任务。

这是理论上理想的工作方式,但是却存在着一个问题。我们知道,CPU的发展是遵循摩尔定律的,每18个月左右集成电路上晶体管的数量就可以翻一倍,因此CPU的速度只会变得越来越快。

但是光CPU快没有用呀,因为CPU再快还是要从内存去读取数据,而这个过程是非常缓慢的,所以就大大限制了CPU的发展。

为了解决这个问题,CPU厂商引入了高速缓存功能。内存里存储的数据,CPU高速缓存里也可以存一份,这样当频繁需要去访问某个数据时就不需要重复从内存中去获取了,CPU高速缓存里有,那么直接拿缓存中的数据即可,这样就可以大大提升CPU的工作效率。

而当程序要对某个数据进行修改时,也可以先修改高速缓存中的数据,因为这样会非常快,等运算结束之后,再将缓存中的数据写回到内存当中即可。

这种工作方式在单线程的场景下是没问题的,准确来讲,在单核多线程的场景下也是没问题的。但如果到了多核多线程的场景下,可能就会出现问题。

我们都知道,现在不管是手机还是电脑,动不动就声称是多核的,多核就是CPU中有多个运算单元的意思。因为一个运算单元在同一时间其实只能处理一个任务,即使我们开了多个线程,对于单核CPU而言,它只能先处理这个线程中的一些任务,然后暂停下来转去处理另外一个线程中的任务,以此交替。而多核CPU的话,则可以允许在同一时间处理多个任务,这样效率当然就更高了。

但是多核CPU又带来了一个新的挑战,那就是在多线程的场景下,CPU高速缓存中的数据可能不准确了。原因也很简单,我们通过下面这张图来理解一下。

可以看到,这里有两个线程,分别通过两个CPU的运算单元来执行程序,但它们是共享同一个内存的。现在CPU1从内存中读取数据A,并写入高速缓存,CPU2也从内存中读取数据A,并写入高速缓存。

到目前为止还是没有问题的,但是如果线程2修改了数据A的值,首先CPU2会更新高速缓存中A的值,然后再将它写回到内存当中。这个时候,线程1再访问数据A,CPU1发现高速缓存当中有A的值啊,那么直接返回缓存中的值不就行了。此时你会发现,线程1和线程2访问同一个数据A,得到的值却不一样了。

这就是多核多线程场景下遇到的可见性问题,因为当一个线程去修改某个变量的值时,该变量对于另外一个线程并不是立即可见的。

为了让以上理论知识更具有说服力,这里我编写了一个小Demo来验证上述说法,代码如下所示:

public class Main {

    static boolean flag;

    public static void main(String... args) {
        new Thread1().start();
        new Thread2().start();
    }

    static class Thread1 extends Thread {
        @Override
        public void run() {
            while (true) {
                if (flag) {
                    flag = false;
                    System.out.println("Thread1 set flag to false");
                }
            }
        }
    }

    static class Thread2 extends Thread {
        @Override
        public void run() {
            while (true) {
                if (!flag) {
                    flag = true;
                    System.out.println("Thread2 set flag to true");
                }
            }
        }
    }

}

这段代码真的非常简单,我们开启了两个线程来对同一个变量flag进行修改。Thread1使用一个while(true)循环,发现flag是true时就把它改为false。Thread2也使用一个while(true)循环,发现flag是false时就把它改为true。

理论上来说,这两个线程同时运行,那么就应该一直交替打印,你改我的值,我再给你改回去。

实际上真的会是这样吗?我们来运行一下就知道了。

可以看到,打印过程只持续了一小会就停止打印了,但是程序却没有结束,依然显示在运行中。

这怎么可能呢?理论上来说,flag要么为true,要么为false。true的时候Thread1应该打印,false的时候Thread2应该打印,两边都不打印是为什么呢?

我们用刚才所学的知识就可以解释这个原本解释不了的问题,因为Thread1和Thread2的CPU高速缓存中各有一份flag值,其中Thread1中缓存的flag值是false,Thread2中缓存的flag值是true,所以两边就都不会打印了。

这样我们就通过一个实际的例子演示了刚才所说的可见性问题。那么该如何解决呢?

答案很明显,volatile。

volatile这个关键字的其中一个重要作用就是解决可见性问题,即保证当一个线程修改了某个变量之后,该变量对于另外一个线程是立即可见的。

至于volatile的工作原理,太底层方面的内容我也说不上来,大概原理就是当一个变量被声明成volatile之后,任何一个线程对它进行修改,都会让所有其他CPU高速缓存中的值过期,这样其他线程就必须去内存中重新获取最新的值,也就解决了可见性的问题。

我们可以将刚才的代码进行如下修改:

public class Main {

    volatile static boolean flag;
    ...

}

没错,就是这么简单,在flag变量的前面加上volatile关键字即可。然后重新运行程序,效果如下图所示。

一切如我们所预期的那样运行了。

指令重排问题

volatile关键字还有另外一个重要的作用,就是禁止指令重排,这又是一个非常有趣的问题。

我们先来看两段代码:

// 第一段代码
int a = 10;
int b = 5;
a = 20;
System.out.println(a + b);

// 第二段代码
int a = 10;
a = 20;
int b = 5;
System.out.println(a + b);

第一段代码,我们声明了一个a变量等于10,又声明了一个b变量等于5,然后将a变量的值改成了20,最后打印a + b的值。

第二段代码,我们声明了一个a变量等于10,然后将a变量的值改成了20,又声明了一个b变量等于5,最后打印a + b的值。

这两段代码有区别吗?

不用瞎猜了,这两段代码没有任何区别,声明变量b和修改变量a之间的顺序是随意的,它们之间谁也不碍着谁。

也正是因为这个原因,CPU在执行代码时,其实并不一定会严格按照我们编写的顺序去执行,而是可能会考虑一些效率方面的原因,对那些先后顺序无关紧要的代码进行重新排序,这个操作就被称为指令重排。

这么看来,指令重排这个操作没毛病啊。确实,但只限在单线程环境下。

很多问题一旦进入了多线程环境,就会变得更加复杂,我们来看如下代码:

public class Main {

    static boolean init;
    static String value;

    static class Thread1 extends Thread {
        @Override
        public void run() {
            value = "hello world";
            init = true;
        }
    }

    static class Thread2 extends Thread {
        @Override
        public void run() {
            while (!init) {
                // 等待初始化完成
            }
            value.toUpperCase();
        }
    }

}

这段代码的思路仍然很简单,Thread1用于对value数据进行初始化,初始化完成之后会将init设置成true。Thread2则会先通过while循环等待初始化完成,完成之后再对value数据进行操作。

那么这段代码可以正常工作吗?未必,因为根据刚才的指令重排理论,Thread1中value和init这两个变量之间是没有先后顺序的。如果CPU将这两条指令进行了重排,那么就可能出现初始化已完成,但是value还没有赋值的情况。这样Thread2的while循环就会跳出,然后在操作value的时候出现空指针异常。

所以说,指令重排功能一旦进入了多线程环境,也是可能会出现问题的。

而至于解决方案嘛,当然还是volatile了。

对某个变量声明了volatile关键字之后,同时也就意味着禁止对该变量进行指令重排。所以我们只需要这样修改代码就能够保证程序的安全性了。

public class Main {

    volatile static boolean init;
    ...

}

volatile在Android上的应用

现在我们已经了解了volatile关键字的主要作用,但是就像开篇时那位朋友提到的一样,很多人想不出来这个关键字在Android上有什么用途。

其实我觉得任何一个技术点都不应该去生搬硬套,你只要掌握了它,该用到时能想到它就可以了,而不是绞尽脑汁去想我到底要在哪里使用它。

我在看一些Google库的源码时,其实时不时就能看到这个关键字,只要是涉及多线程编程的时候,volatile的出场率还是不低的。

这里我给大家举一个常见的示例吧,在Android上我们应该都编写过文件下载这个功能。在执行下载任务时,我们需要开启一个线程,然后从网络上读取流数据,并写入到本地,重复执行这个过程,直到所有数据都读取完毕。

那么这个过程我可以用如下简易代码进行表示:

public class DownloadTask {

    public void download() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    byte[] bytes = readBytesFromNetwork(); // 从网络上读取数据
                    if (bytes.length == 0) {
                        break; // 下载完毕,跳出循环
                    }
                    writeBytesToDisk(bytes); // 将数据写入到本地
                }
            }
        }).start();
    }

}

到此为止没什么问题。

不过现在又来了一个新的需求,要求允许用户取消下载。我们都知道,Java的线程是不可以中断的,所以如果想要做取消下载的功能,一般都是通过标记位来实现的,代码如下所示:

public class DownloadTask {

    boolean isCanceled = false;

    public void download() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (!isCanceled) {
                    byte[] bytes = readBytesFromNetwork();
                    if (bytes.length == 0) {
                        break;
                    }
                    writeBytesToDisk(bytes);
                }
            }
        }).start();
    }

    public void cancel() {
        isCanceled = true;
    }

}

这里我们增加了一个isCanceled变量和一个cancel()方法,调用cancel()方法时将isCanceled变量设置为true,表示下载已取消。

然后在download()方法当中,如果发现isCanceled变量为true,就跳出循环不再继续执行下载任务,这样也就实现了取消下载的功能。

这种写法能够正常工作吗?根据我的实际测试,确实基本上都是可以正常工作的。

但是这种写法真的安全吗?不,因为你会发现download()方法和cancel()方法是运行在两个线程当中的,因此cancel()方法对于isCanceled变量的修改,未必对download()方法就立即可见。

所以,存在着这样一种可能,就是我们明明已经将isCanceled变量设置成了true,但是download()方法所使用的CPU高速缓存中记录的isCanceled变量还是false,从而导致下载无法被取消的情况出现。

因此,最安全的写法就是对isCanceled变量声明volatile关键字:

public class DownloadTask {

    volatile boolean isCanceled = false;
    ...

}

这样就可以保证你的取消下载功能始终是安全的了。

好了,关于volatile关键字的作用,以及它在Android开发中具体有哪些用途,相信到这里就解释的差不多了。

本来是想用周日一天时间写篇小短文的,写着写着好像最后又写出了不少内容,不过只要对大家有帮助就好。


如果想要学习Kotlin和最新的Android知识,可以参考我的新书 《第一行代码 第3版》点击此处查看详情


关注我的技术公众号,每个工作日都有优质技术文章推送。

微信扫一扫下方二维码即可关注:

查看原文

赞 6 收藏 3 评论 0

郭霖 关注了用户 · 9月29日

凹凸实验室 @o2team

凹凸实验室(Aotu.io,英文简称O2) 始建于2015年10月,是一个年轻基情的技术团队。

O2面向多终端技术体系,致力于构建沉淀与分享包括但不限于交互、页面制作技巧、前端开发、原生APP开发等方面的专业知识及案例。

求简历:aotu@jd.com

关注 764

郭霖 关注了专栏 · 9月29日

前端食堂

个人公众号:前端食堂 你的前端食堂,记得按时吃饭~

关注 2558

郭霖 关注了专栏 · 9月29日

前端下午茶公众号

你不能把这个世界,让给你鄙视的人

关注 1515

郭霖 关注了专栏 · 9月29日

阿里技术

阿里巴巴官方技术号,关于阿里巴巴经济体的技术创新、实战经验、技术人的成长心得均呈现于此。

关注 1700

郭霖 关注了专栏 · 9月29日

springboot 最佳实践

SpringBoot技术的势、道、术分析与实践。

关注 1208

郭霖 关注了用户 · 9月29日

不一样的科技宅 @huangxunhui

每天进步一点点,体验不一样的生活。

关注 453

郭霖 关注了专栏 · 9月29日

K8S生态

Container, Docker, Go, Kubernetes, Python, Vim; 微信公众号: MoeLove

关注 609

郭霖 关注了用户 · 9月29日

码哥字节 @magebyte

深入探讨 Java 相关技术,包括行业动态,架构设计,设计模式,框架使用,源码分析,数据结构等。

关注 2282

认证与成就

  • 认证信息 Android 技术专家,《第一行代码-Android》作者
  • 获得 33 次点赞
  • 获得 1 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 1 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 9月22日
个人主页被 2k 人浏览