SegmentFault 最新的文章 |
- 成为第一没有捷径:AI新势力MindSpore成长秘籍 | 源创者说 专访开源专家胡晓曼
- 给面试加点硬菜:延迟任务场景,该如何提高吞吐量和时效性!
- 当Synchronized遇到这玩意儿,有个大坑,要注意!
- 精读《vue-lit 源码》
成为第一没有捷径:AI新势力MindSpore成长秘籍 | 源创者说 专访开源专家胡晓曼 Posted: 10 Feb 2022 10:42 PM PST 访谈者:马玮,SegmentFault 思否技术编辑 受访者:胡晓曼,华为MindSpore运营总监,LF AI & DATA Outreach Committee Chair 胡晓曼,华为昇思MindSpore运营专家,LF AI & DATA基金会Outreach委员会主席,中国电子学会专家库成员。TinyMS项目技术负责人,MSG·Women In Tech创始人。曾任汽车之家、百度等公司算法和运营团队负责人,负责多个深度学习落地项目及开源项目社区运营,入选「2021中国开源先锋 33 人」年度榜单。 https://www.bilibili.com/vide... 一个不懂技术的产品经理不是一个好的开源运营2015年,刚刚从数学系毕业的胡晓曼成为了一名算法工程师。一边写代码,一边在博客网站上写机器学习算法原理,通俗易懂的方式很快就让她在技术圈里有了知名度。 2017年开始,胡晓曼撰写了"三个月从零入门深度学习"的技术教程,迅速在博客园成为了知名技术博主,点击量超500w,同时也受邀开始不定期的各类线下分享。 2019年,胡晓曼在技术分享道路上再进一步,成为了一名"深度学习布道师",帮助更多开发者迅速踏入深度学习世界的大门。在这一过程中胡晓曼与华为结缘,在2020年加入华为负责华为全场景AI框架MindSpore的开源社区运作与管理。 "在华为,我可以从全盘的视角思考一个开源社区如何完成从0到1的成长过程。" 与技术布道师的角色不同,在华为的职业生涯最吸引胡晓曼的是她能够从一开始就参与到一个新兴开源社区的成长当中。对于胡晓曼自己来说,从程序员的岗位跨界到社区运营也是一种新的突破。一位跨界人与一个年轻的社区共同褪去青涩,走向成熟,的确称得上难得而宝贵的经历。 懂技术,更要懂用户 如今,MindSpore社区已经拥有超过80万用户,4000名贡献者,社区下载量超过100万。从0到1带领开源社区迎来这样的繁荣景象,胡晓曼总结了如下几点经验: 1. 扎实的技术功底算法专家出身的胡晓曼具备扎实的技术功底。MindSpore框架的上手和使用对胡晓曼来说颇为轻松,她也能深度参与和理解开发者讨论的各类技术主题。在她看来,开源技术社区的运营至少应该能独立使用运营的产品,最好能深入理解代码和原理相关的内容。具备这样的技术背景,运营才能更好地与社区用户打成一片,亲身感受社区氛围,了解用户的需求与痛点。 2. 多样化的运营方式胡晓曼认为,开源社区的运营需要与该项目的发展周期相结合,在开源初期,她把目标定为"迅速提升技术影响力"。MindSpore是一个迭代速度非常快的AI框架,在开源初期,每个月月末都会发布新版本,让开发者体验新的版本特性,如何让开发者们更快了解这些新特性的优势,是她最先解决的问题之一。 在不断尝试的过程中,胡晓曼将各类互联网社区的成功经验融入进来,于是开始利用视频、趣味课程等方式来吸引用户,获得了相当好的效果。技术视频往往较为枯燥乏味,胡晓曼将开发者的痛点和新版本的特性结合在一块,拍摄了1分钟新版本特性短视频,发布在抖音、B站等各个网站。在没有任何渠道推广的情况下,全网播放量突破百万,迅速打出了MindSpore的技术品牌。算法专家背景出身的她,将以往模型训练的经验,运用到开源运营上,首次提出了"用模型的思维做运营"的方法论,把每一次运营的方法作为一次小的模型,按照整个"模型开发"流程一样不断地迭代优化,上线后进行效果评估、收集社区反馈,在下一次改进时去芜存菁,这样的进化方式成为了MindSpore社区快速成长的主要驱动力之一。 3. 平等友好的社区氛围除了技术背景和多样化的手段以外,胡晓曼提到了开源社区建设工作中最关键的价值点。 "真正的开源社区中,运营方与开发者应该处于互相平等的地位。" 很多开源技术的社区管理方会有一种居高临下的心理认知,认为开发者只是"贡献者"的角色,将自身置于合作的主导地位上,甚至会有大家长的心态。但胡晓曼认为,社区运营与用户的地位一定应该是平等开放的,这样才能让用户体会到自己的参与和贡献具有价值感。 这种平等开放的理念贯穿在了MindSpore社区的发展过程中,并得到了广大社区成员的认可与支持。不少开发者会持续提出自己的建议,运营方会很快给出相应的反馈,这样的交流推动了整个社区的成长。这样的社区氛围也让胡晓曼获得了更多成就感,让她体会到自己的工作为成千上万的开发者、社区成员确确实实地带来了收益。 一个开源社区是否成功,是很难去量化的,上游社区的开源社区健康度指标并没有一个标准的评价体系,即使有也很难适用于所有的开源社区。其他的平台性质指标,譬如GitHub的star、fork、watch等,更不是衡量一个开源社区成功的唯一标准,那么选择什么样的目标作为考量指标,决定了这个开源社区未来的走向和价值定位。在这个过程中,需要反复地去验证、磨合,证明,确保MindSpore开源社区的定位,是完全以开发者为本的社区,而不是唯指标论的社区。指标是用来衡量自己的工作的,不是来单一衡量开发者社区的唯一目标。 产品思维做社区,成!1. ToB新范式:MSG·企业行为什么比传统模式具有更高的转化率开源产品对于企业是否能带来商业价值一直是业界的思考问题。传统的ToB模式需要投入大量的人力和时间,周期长见效慢,如何更有效更迅速地了解企业的痛点和诉求,是胡晓曼想突破的核心点所在。2021年下半年,她设计了一套完整的MindSpore企业拓展方案,面对不同类型、行业、所处阶段的企业均有针对性方案。今年7月开启了第一场MSG企业行·南京场,联合江苏昇腾创新中心,覆盖了96%南京本地AI科技企业,针对优势行业进行了企业赋能,同时让他们了解MindSpore的技术优势、计算中心和创新中心的扶持力度,给当地的中小型AI企业搭建了合作的桥梁。后续陆续将企业行的成功模式带入了武汉、西安、成都。为500多家高意向企业建立了合作的渠道,大力节省了企业本身需要在AI人才、算力等方面需要耗费的资金,具有显著的商业价值。 2. 项目交付制:TinyMS如何在开源半年内助力团队获得比赛金奖?去年3月末,胡晓曼带领团队开发了新型AI工具——基于MindSpore开发的高阶API工具TinyMS。在项目启动之初,胡晓曼就立志想要为AI初学者设计一个完全零基础也能上手使用的高阶AI框架工具,并配备保姆级的教程,帮助小白从最基础的shell脚本、python学习、必备的数学知识等开始学,直到能自己上手写模型。10月,第七届中国国际"互联网+"大学生创新创业大赛总决赛在南昌开幕,来自华南理工大学的团队选择了TinyMS赛题——『使用MindSpore高阶工具TinyMS支持的网络CycleGAN训练图像风格迁移模型』,在6100个项目中、12000余名学生的激烈角逐下获得了产业赛道唯一金奖。事后采访获奖的两位同学代表,均认为正是因为TinyMS的极致易用,才让他们在如此短的时间内完成开发和调试,最终摘得桂冠。胡晓曼始终认为,在开源社区的运作和管理中,对于任何项目,都要有清晰明确的定位和规划,并采取项目交付制,满足最小MVP原则先上线,再不断优化迭代,才能尽可能地让产品更加完善,满足用户需求。 3. 生态永传承:两大生态案例落地的背后故事2021年4月,一个下午的时间,山水自然保护中心和MindSpore社区一拍即合,决定为国家保护动物做件大事,合作三江源野外红外相机识别项目。历经半年反复调优及野外实地实验,2021年底终于在山水自然保护生态系统上线。这是首个基于国产框架的三江源红外影像自然保护预训练模型,并且开源所有相关工具。 除了生态保护,文化传承也少不了昇思MindSpore的助力。在胡晓曼开展的MSG·Women In Tech活动中,AI和加密艺术家宋婷女士想借助AI为文化传承尽一份力,宋婷团队基于MindSpore的GAN网络,对世界保护非物质文化遗产扎染的图案进行训练,生成新的扎染图案来保护非传承工艺,最终在央视作为开源文化特色案例播出。 很多非AI领域的人往往不知道AI能为人们的生活带来什么改变,能够为大自然做出什么贡献,这两个案例生动形象展示了昇思MindSpore AI框架为自然生态和文化领域带来的贡献,这也是胡晓曼认为投入开源的最大价值。 想入行?这么做如何将自身打造成一名优秀的社区运营专家呢?胡晓曼回顾自身经验,提出了三点供大家参考。 1. 保持初心,与开发者感同身受当你在进入一个新的领域时,一定会有手足无措的感觉,但是熟悉了以后,就很难体会到当初作为小白的心境了。所以要时刻保持自己的新鲜感,与开发者感同身受,去回顾当时的心境。除了日常工作外,胡晓曼会经常学习不同的技术框架,如Julia等,了解其他不同类别的技术社区的发展历史,技术特性的更新,和社区氛围的建设,这样能帮助她跳出原本的AI领域,从另一个角度去看待原本的工作是否有值得学习的点。此外,为了保持与开发者同频率的心境,胡晓曼还会定期和开发者聊天,了解他们在使用产品时的体验,真正做到零距离面对开发者。 2. 广泛涉猎,迅速转化灵感并执行胡晓曼阅读的范围非常广泛,除了早期做程序员的时候经常刷的工具书,哲学、自然科学、社会心理学等各种类型都有涉猎,并在自己的读书笔记中记录值得思考的点。在阅读不同行业、不同领域的书籍时,她经常会和工作中的应用场景结合在一起,引出一些新的思考,然后把思考转化为可操作的执行方案,再来快速迭代,最后抽象成一条可行的方法论,在以后类似的场景进行复用。除此之外,她还会体验各类不同的互联网社区,包括小红书、抖音等各类内容平台,了解各平台是如何吸引用户自发产生内容,进行流量分发、设立激励机制等等。虽然是不同的产品,但是成功的产品一定会有共性,能够总结出通用的一些经验,尤其在把握人性方面,这些全品类的内容平台把这一点运用到了极致。对开源社区,除了技术产品本身的实力需要足够过硬,好的内容吸引机制也是社区运营必不可少的因素之一。 3. 量化指标,不断提升工作效果如果让你尝试一项新事物,不定任何目标和截止时间,很容易随着时间的推移拖延,或者因为长期看不到进步而放弃。所以胡晓曼提出,对待工作,或者新事物,要学会量化学习,具化指标,定量完成,阶段更新。为什么会得出这个结论呢?其实在日常生活中,胡晓曼热爱健身,积极尝试各种运动,包括羽毛球、普拉提、滑雪等,在认真学习过多种运动后,她发现健身,或者说不同的运动是可以量化学习的。譬如健身,很多人在没有系统学习和科学的方法之前直接上大重量就很容易受伤,而不定目标,每次泛泛的学习,长期下来也会有挫败感。同理,当开发者接触一个新的领域,新的框架,如果没有阶段性的反馈,很难去发现自己存在的问题,而人往往都具有惰性,所以胡晓曼在社区中设计了一个开发者进阶体系,让所有进入到MindSpore开源社区的开发者都能清楚的找到自己的定位,引导激励开发者不断再向前一步,这样能让每一位开发者在社区中不断成长。在个人工作中,胡晓曼也经常会量化自己的工作指标,用数据定量反馈成果,而不是用体验去描述结果的好坏。 主流之外,更要关注1. 关注女性开发者:来自女性的视角与见解策划女性开发者主题的线下沙龙活动是胡晓曼运营工作中的一项独到成果,她是国内第一个在开源社区中格外关注女性开发者的人。女性程序员出身的胡晓曼更容易理解IT技术行业中女性作为少数力量所普遍存在的心理认知与担忧。例如很多女性会对自身的职业前景、职业发展有种种疑虑,于是胡晓曼专门策划了MSG·Women In Tech的女性专场活动,针对科技领域的女性从业者,为她们组建一个独有的空间,让行业中的女性前辈分享自己的案例,解决职场上发展的困惑。 常见的技术沙龙往往是布道式的,几位专家发表演讲,与会者认真听讲,互动部分只占很小的比例。但MSG·Women in Tech不再是单一的听,而是与嘉宾完全互动,和参会的人共同探讨职业问题。目前为止,共举办了4期活动,分别在北京、上海、深圳邀请了二十多位优秀女性行业前辈分享经验,吸引了300+位女性科技从业者报名,100+位女性来到现场互相交流。分享如何成为一个合格的技术leader、如何向上管理、如何争取自己想做的项目等等一系列在职场中的常见问题,与会者和嘉宾围着桌子坐成一圈,大家一同去探索解决方案,分享经验,在去年9月作为华为Women In Tech官方代表,拍摄视频发布于华为海内外官方账号,传播量超千万,受到广泛的认可。 2. 开源运营也开源:能开源的绝不止代码在开源社区里,常见的开源项目通常以代码为主,但胡晓曼想,为什么我们不能把开源运营也开源出来呢?不是只有代码经验才可以复用,开源社区运营本身也可以总结成经验形成方法论再开源出来,造福新入行的同学。去年三月末,MindSpore开源社区联合开放原子基金会,共同发起了一个『把社区运营也开源』的开源社区0xCommops,希望大家共同贡献,持续完善开源社区运营体系,打造一个开源社区运营的知识库、工具库。 一些小建议很多年轻的技术从业者甚至非技术背景的年轻人都对开源社区运营这样的岗位感兴趣,胡晓曼也为这些小伙伴提出了宝贵的建议。
总而言之,热爱自己的工作,把它作为事业而不是职业,坚定的走下去,会让你走的更远,带来意想不到的价值! 关于华为开源作为可信赖的开源公民,华为通过持续贡献,携手伙伴,提倡包容、公平、开放和更团结的协作,共建世界级基础软件开源社区,加速行业数字化进程。 · 主流开源组织的积极参与者和支持者。目前华为已是数十个国际开源基金会的顶级/初创会员。 ·规模贡献开源基础软件,夯实数字基础设施生态底座,携手伙伴、开发者共建开源生态。近两年来,面向云原生、自动化和智能化,华为先后开源了KubeEdge 、MindSpore 、openEuler、openGauss、OpenHarmony等多个平台级基础软件开源项目,成为被全球开发者所接受的开源社区,并在各行业商用落地。 · 积极建设可持续发展、有生命力的可信开源社区。华为致力于完善社区生态治理架构,确保社区持续演进。 关注华为开源公众号,了解更多! 点击此处,进入华为开源官网了解更多 |
Posted: 14 Feb 2022 06:00 PM PST 作者:小傅哥 沉淀、分享、成长,让自己和他人都能有所收获!😄 一、前言
哈哈哈,说好的不卷了,能凑活用就行了。但每次接到新需求时都手痒,想结合着上一次的架构设计和落地经验,在这一次需求上在迭代更新,或者找到完全颠覆之前的更优方案。卷完代码的那一刻总是神清气爽 其实大部分喜欢写代码的一类纯粹码农,都是比较卷的,就比如一个需求在实现上是能用大概 二、延迟任务场景
当我们的实际业务需求场景中,有一些活动开始前的状态变更、订单结算后的T+1对账、贷款单息费的产生,都是需要使用到延迟任务来进行触达。实际的操作一般会有 Quartz、Schedule 来对你的库表数据进行定时扫描和处理,当条件满足后做数据状态的变更或者产生新的数据插入到表中。 这样一个简单的需求就是延迟任务最初需求,如果需求前期内容较少、使用方不多,可能在实际开发中就只是一个单台机器直接对着表一顿轮训就完事了。但随着业务需求的发展和功能的复杂度提升,往往反馈到研发设计和实现,就不那么简单了,比如:你需要保障尽可能低延迟完成较大规模的数据量扫描处理,否则就像贷款单息费的产生,已经到了第二天用户还没看到自己的息费信息或者是还款后的重新对账,可能就这个时候就要产生客诉了。 那么,类似这样的场景该如何设计呢? 三、延迟任务设计通常的任务中心处理流程主要,主要是由定时任务扫描任务库表,把即将达到超时时间的任务信息扫描到处理队列( 问题:
1. 任务表方式除了一些较小的状态变更场景,例如在各自业务的库表中,就包含了一个状态字段,这个字段一方面有程序逻辑处理变更的状态,也有到达 那么还有一些较大也较为频繁使用的场景,如果都是在每个系统的各自所需的N多个表中,都添加这样的字段进行维护,就显得非常冗余了,也不那么易于维护。所以针对这样的场景就很适合做一个通用的任务延时系统,各业务系统把需要被延时执行的动作提交到延时系统中,再有延时系统在指定时间下进行回调,回调的动作可以是接口或者MQ消息进行触达。例如可以设计这样一个任务调度表:
2. 低延迟方式低延迟处理方案,是在任务表方式的基础上,新增加的时间把控处理。它可以把即将到期的前一段时间的任务,放置到 Redis 集群队里中,在消费的时候再从队列中 pop 出来,这样可以更快的接近任务的处理时效,避免因为扫库间隔较大延迟任务执行。
Redis 消费队列
简单案例
测试数据
四、总结
五、系列推荐 |
Posted: 13 Feb 2022 08:37 PM PST 你好呀,我是歪歪。 前几天在思否上看到别人提的关于 Synchronized 的一个用法问题,我觉得挺有意思的,这个问题其实也是我三年前面试某公司的时候遇到的一个真题,当时不知道面试官想要考什么,没有回答的特别好,后来研究了一下就记住了。 所以看到这个问题的时候觉得特别亲切,准备分享给你一起看看: 首先为了方便你看文章的时候复现问题,我给你一份直接拿出来就能跑的代码,希望你有时间的话也把代码拿出来跑一下:
程序逻辑也很简单,是一个模拟抢票的过程,一共 10 张票,开启两个线程去抢票。 票是共享资源,且有两个线程来消费,所以为了保证线程安全,TicketConsumer 的逻辑里面用了 synchronized 关键字。 这是应该是大家在初学 synchronized 的时候都会写到的例子,期望的结果是 10 张票,两个人抢,每张票只有一个人能抢到。 但是实际运行结果是这样的,我只截取开始部分的日志: 截图里面有三个框起来的部分。 最上面的部分,就是两个人都在抢第 10 张票,从日志输出上看也完全没有任何毛病,最终只有一个人抢到了票,然后进入到第 9 张票的争夺过程。 但是下面被框起来的第 9 张票的争夺部分就有点让人懵逼了:
为什么两个人都抢到了第 9 张票,且成功锁到的对象都一样的? 这玩意,超出认知了啊。 这两个线程怎么可能拿到同一把锁,然后去执行业务逻辑呢? 所以,提问者的问题就浮现出来了。
为什么没有生效?我们先来看一个问题。 首先,我们从日志的输出上已经非常明确的知道,synchronized 在第二轮抢第 9 张票的时候失效了。 经过理论知识支撑,我们知道 synchronized 失效,肯定是锁出问题了。 如果只有一把锁,多个线程来竞争同一把锁,synchronized 绝对是不会有任何毛病的。 但是这里两个线程并没有达成互斥的条件,也就是说这里绝对存在的不止一把锁。 这是我们可以通过理论知识推导出来的结论。 先得出结论了,那么我怎么去证明"锁不止一把"呢? 能进入 synchronized 说明肯定获得了锁,所以我只要看各个线程持有的锁是什么就知道了。 那么怎么去看线程持有什么锁呢? jstack 命令,打印线程堆栈功能,了解一下? 这些信息都藏在线程堆栈里面,我们拿出来一看便知。 在 idea 里面怎么拿到线程堆栈呢? 这就是一个在 idea 里面调试的小技巧了,我之前的文章里面应该也出现过多次。 首先为了方便获取线程堆栈信息,我把这里的睡眠时间调整到 10s: 跑起来之后点击这里的"照相机"图标: 点击几次就会有对应点击时间点的几个 Dump 信息: 由于我需要观察前两次锁的情况,而每次线程进入锁之后都会等待 10s 时间,所以我就在项目启动的第一个 10s 和第二个 10s 之间各点击一次就行。 为了更直观的观察数据,我选择点击下面这个图标,把 Dump 信息复制下来: 复制下来的信息很多,但是我们只需要关心 why 和 mx 这两个线程即可。 这是第一次 Dump 中的相关信息: mx 线程是 BLOCKED 状态,它在等待地址为 0x000000076c07b058 的锁。 why 线程是 TIMED_WAITING 状态,它在 sleeping,说明它抢到了锁,在执行业务逻辑。而它抢到的锁,你说巧不巧,正是 mx 线程等待的 0x000000076c07b058。 从输出日志上来看,第一次抢票确实是 why 线程抢到了: 从 Dump 信息看,两个线程竞争的是同一把锁,所以第一次没毛病。 好,我们接着看第二次的 Dump 信息: 这一次,两个线程都在 TIMED_WAITING,都在 sleeping,说明都拿到了锁,进入了业务逻辑。 但是仔细一看,两个线程拿的锁是不相同的锁。 mx 锁的是 0x000000076c07b058。 why 锁的是 0x000000076c07b048。 由于不是同一把锁,所以并不存在竞争关系,因此都可以进入 synchronized 执行业务逻辑,所以两个线程都在 sleeping,也没毛病。 然后,我再把两次 Dump 的信息放在一起给你看一下,这样就更直观了: 如果我用"锁一"来代替 0x000000076c07b058,"锁二"来代替 0x000000076c07b048。 那么流程是这样的: why 加锁一成功,执行业务逻辑,mx 进入锁一等待状态。 why 释放锁一,等待锁一的 mx 被唤醒,持有锁一,继续执行业务。 同时 why 加锁二成功,执行业务逻辑。 从线程堆栈中,我们确实证明了 synchronized 没有生效的原因是锁发生了变化。 同时,从线程堆栈中我们也能看出来为什么锁对象 System.identityHashCode 的输出是一样的。 第一次 Dump 的时候,ticket 都是 10,其中 mx 没有抢到锁,被 synchronized 锁住。 why 线程执行了 所以,当 why 线程释放锁之后,mx 线程拿到锁继续执行,发现 ticket=9。 而 why 也搞到一把新锁,也可以进入 synchronized 的逻辑,也发现 ticket=9。 好家伙,ticket 都是 9, System.identityHashCode 能不一样吗? 按理来说,why 释放锁一后应该继续和 mx 竞争锁一,但是却不知道它在哪搞到一把新锁。 那么问题就来了:锁为什么发生了变化呢? 谁动了我的锁?经过前面一顿分析,我们坐实了锁确实发生了变化,当你分析出这一点的时候勃然大怒,拍案而起,大喊一声:是哪个瓜娃子动了我的锁?这不是坑爹吗? 按照我的经验,这个时候不要急着甩锅,继续往下看,你会发现小丑竟是自己: 抢完票之后,执行了 这个时候你把大腿一拍,恍然大悟,对着围观群众说:问题不大,手抖而已。 于是大手一挥,把加锁的地方改成这样:
利用 class 对象来作为锁对象,保证了锁的唯一性。 经过验证也确实没毛病,非常完美,打完收工。 但是,真的就收工了吗? 其实关于锁对象为什么发生了变化,还隔了一点点东西没有说出来。 它就藏在字节码里面。 我们通过 javap 命令,反查字节码,可以看到这样的信息:
让人熟悉的 Integer 从 -128 到 127 的缓存。 也就是说我们的程序里面,会涉及到拆箱和装箱的过程,这个过程中会调用到 对于 Integer,当值在缓存范围内的时候,会返回同一个对象。当超过缓存范围,每次都会 new 一个新对象出来。 这应该是一个必备的八股文知识点,我在这里给你强调这个是想表达什么意思呢? 很简单,改动一下代码就明白了。 我把初始化票数从 10 修改为 200,超过缓存范围,程序运行结果是这样的: 很明显,从第一次的日志输出来看,锁都不是同一把锁了。 这就是我前面说的:因为超过缓存范围,执行了两次 new Integer(200) 的操作,这是两个不同的对象,拿来作为锁,就是两把不一样的锁。 再修改回 10,运行一次,你感受一下: 从日志输出来看,这个时候只有一把锁,所以只有一个线程抢到了票。 因为 10 是在缓存范围内的数字,所以每次是从缓存中获取出来,是同一个对象。 我写这一小段的目的是为了体现 Integer 有缓存这个知识点,大家都知道。但是当它和其他东西揉在一起的时候因为这个缓存会带来什么问题,你得分析出来,这比直接记住干瘪的知识点有效一点。 但是... 我们的初始票是 10, 如果你有这个疑问的话,那么我劝你再好好想想。 10 是 10,9 是 9。 虽然它们都在缓存范围内,但是本来就是两个不同的对象,构建缓存的时候也是 new 出来的: 为什么我要补充这一段看起来很傻的说明呢? 因为我在网上看到其他写类似问题的时候,有的文章写的不清楚,会让读者误认为"缓存范围内的值都是同一个对象",这样会误导初学者。 总之一句话:请别用 Integer 作为锁对象,你把握不住。 但是... stackoverflow但是,我写文章的时候在 stackoverflow 上也看到了一个类似的问题。 这个哥们的问题在于:他知道 Integer 不能做为锁对象,但是他的需求又似乎必须把 Integer 作为锁对象。 https://stackoverflow.com/que... 我给你描述一下他的问题。 首先看标号为 ① 的地方,他的程序其实就是先从缓存中获取,如果缓存中没有则从数据库获取,然后在放到缓存里面去。 非常简单清晰的逻辑。 但是他考虑到并发的场景下,如果有多个线程同一时刻都来获取同一个 id,但是这个 id 对应的数据并没有在缓存里面,那么这些线程都会去执行查询数据库并维护缓存的动作。 对应查询和存储的动作,他用的是 fairly expensive 来形容。 就是"相当昂贵"的意思,说白了就是这个动作非常的"重",最好不要重复去做。 所以只需要让某一个线程来执行这个 fairly expensive 的操作就好了。 于是他想到了标号为 ② 的地方的代码。 用 synchronized 来把 id 锁一下,不幸的是,id 是 Integer 类型的。 在标号为 ③ 的地方他自己也说了:不同的 Integer 对象,它们并不会共享锁,那么 synchronized 也没啥卵用。 其实他这句话也不严谨,经过前面的分析,我们知道在缓存范围内的 Integer 对象,它们还是会共享同一把锁的,这里说的"共享"就是竞争的意思。 但是很明显,他的 id 范围肯定比 Integer 缓存范围大。 那么问题就来了:这玩意该咋搞啊? 我看到这个问题的时候想到的第一个问题是:上面这个需求我好像也经常做啊,我是怎么做的来着? 想了几秒恍然大悟,哦,现在都是分布式应用了,我特么直接用的是 Redis 做锁呀。 根本就没有考虑过这个问题。 如果现在不让用 Redis,就是单体应用,那么怎么解决呢? 在看高赞回答之前,我们先看看这个问题下面的一个评论: 开头三个字母:FYI。 看不懂没关系,因为这个不是重点。 但是你知道的,我的英语水平 very high,所以我也顺便教点英文。 FYI,是一个常用的英文缩写,全称是 for your information,供参考的意思。 所以你就知道,他后面肯定是给你附上一个资料了,翻译过来就是: Brian Goetz 在他的 Devoxx 2018 演讲中提到,我们不应该把 Integer 作为锁。 你可以通过这个链接直达这一部分的讲解,只有不到 30s秒的时间,随便练练听力:https://www.youtube.com/watch... 那么问题又来了? Brian Goetz 是谁,凭什么他说的话看起来就很权威的样子? Java Language Architect at Oracle,开发 Java 语言的,就问你怕不怕。 同时,他还是我多次推荐过的《Java并发编程实践》这本书的作者。 好了,现在也找到大佬背书了,接下来带你看看高赞回答是怎么说的。 前部分就不详说了,其实就是我们前面提到的那一些点,不能用 Integer ,涉及到缓存内、缓存外巴拉巴拉的... 关注划线的部分,我加上自己的理解给你翻译一下: 如果你真的必须用 Integer 作为锁,那么你需要搞一个 Map 或 Integer 的 Set,通过集合类做映射,你就可以保证映射出来的是你想要的明确的一个实例。而这个实例,就那可以拿来做锁。 然后他给出了这样的代码片段: 就是用 ConcurrentHashMap 然后用 putIfAbsent 方法来做一个映射。 比如多次调用 locks.putIfAbsent(200, 200),在 map 里面也只有一个值为 200 的 Integer 对象,这是 map 的特性保证的,无需过多解释。 但是这个哥们很好,为了防止有人转不过这个弯,他又给大家解释了一下。 首先,他说你也可以这样的写: 但这样一来,你就会多产生一个很小成本,就是每次访问的时候,如果这个值没有被映射,你都会创建一个 Object 对象。 为了避免这一点,他只是把整数本身保存在 Map 中。这样做的目的是什么?这与直接使用整数本身有什么不同呢? 他是这样解释的,其实就是我前面说的"这是 map 的特性保证的": 当你从 Map 中执行 get() 时,会用到 equals() 方法比较键值。 两个相同值的不同 Integer 实例,调用 equals() 方法是会判定为相同的 。 因此,你可以传递任何数量的 "new Integer(5)" 的不同 Integer 实例作为 getCacheSyncObject 的参数,但是你将永远只能得到传递进来的包含该值的第一个实例。 就是这个意思: 汇总一句话:就是通过 Map 做了映射,不管你 new 多少个 Integer 出来,这多个 Integer 都会被映射为同一个 Integer,从而保证即使超出 Integer 缓存范围时,也只有一把锁。 除了高赞回答之外,还有两个回答我也想说一下。 第一个是这个: 不用关心他说的内容是什么,只是我看到这句话翻译的时候虎躯一震: skin this cat ??? 太残忍了吧。 我当时就觉得这个翻译肯定不太对,这肯定是一个小俚语。于是考证了一下,原来是这个意思: 免费送你一个英语小知识,不用客气。 第二个应该关注的回答排在最后: 这个哥们叫你看看《Java并发编程实战》的第 5.6 节的内容,里面有你要寻找的答案。 巧了,我手边就有这本书,于是我翻开看了一眼。 第 5.6 节的名称叫做"构建高效且可伸缩的结果缓存": 好家伙,我仔细一看这一节,发现这是宝贝呀。 你看书里面的示例代码: .png) 不就和提问题的这个哥们的代码如出一辙吗? 都是从缓存中获取,拿不到再去构建。 不同的地方在于书上把 synchronize 加在了方法上。但是书上也说了,这是最差的解决方案,只是为了引出问题。 随后他借助了 ConcurrentHashMap、putIfAbsent 和 FutureTask 给出了一个相对较好的解决方案。 你可以看到完全是从另外一个角度去解决问题的,根本就没有在 synchronize 上纠缠,直接第二个方法就拿掉了 synchronize。 看完书上的方案后我才恍然大悟:好家伙,虽然前面给出的方案可以解决这个问题,但是总感觉怪怪的,又说不出来哪里怪。原来是死盯着 synchronize 不放,思路一开始就没打开啊。 书里面一共给出了四段代码,解决方案层层递进,具体是怎么写的,由于书上已经写的很清楚了,我就不赘述了,大家去翻翻书就行了。 没有书的直接在网上搜"构建高效且可伸缩的结果缓存"也能搜出原文。 我就指个路,看去吧。 本文已收录至个人博客,欢迎来玩: https://www.whywhy.vip/ |
Posted: 13 Feb 2022 05:09 PM PST vue-lit 基于 lit-html + @vue/reactivity 仅用 70 行代码就给模版引擎实现了 Vue Composition API,用来开发 web component。 概述
上面定义了
接下来看
借助模版引擎 lit-html 的能力,可以同时在模版中传递变量与函数,再借助 @vue/reactivity 能力,让变量变化时生成新的模版,更新组件 dom。 精读阅读源码可以发现,vue-lit 巧妙的融合了三种技术方案,它们配合方式是:
其中响应式能力与模版能力分别是 @vue/reactivity、lit-html 这两个包提供的,我们只需要从源码中寻找剩下的两个功能:如何在修改值后触发模版刷新,以及如何构造生命周期函数的。 首先看如何在值修改后触发模版刷新。以下我把与重渲染相关代码摘出来了:
可以清晰的看到,首先 然后在 然后看生命周期是如何实现的,由于生命周期贯穿整个实现流程,因此必须结合全量源码看,下面贴出全量核心代码,上面介绍过的部分可以忽略不看,只看生命周期的实现:
生命周期实现形如 而生命周期函数还有一个特点,即并不分组件实例,因此必须有一个
这样,我们就将 接下来为了方便,封装了 接下来就是在对应位置调用对应函数了: 首先在 然后在
这样就很容易看懂了,只有初始化渲染过后,从第二次渲染开始,在执行 由于 最后几个生命周期函数都是利用 web component 原生 API 实现的:
分别实现 最后的最后,还利用
看下面的代码片段就知道原因了:
早在初始化时,就将 总结vue-lit 实现非常巧妙,学习他的源码可以同时了解一下几种概念:
以及如何将它们串起来,利用 70 行代码实现一个优雅的渲染引擎。 最后,用这种模式创建的 web component 引入的 runtime lib 在 gzip 后只有 6kb,但却能享受到现代化框架的响应式开发体验,如果你觉得这个 runtime 大小可以忽略不计,那这就是一个非常理想的创建可维护 web component 的 lib。 讨论地址是:精读《vue-lit 源码》· Issue #396 · dt-fe/weekly 如果你想参与讨论,请 点击这里,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。 关注 前端精读微信公众号 <img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg"> 版权声明:自由转载-非商用-非衍生-保持署名(创意共享 3.0 许可证) |
You are subscribed to email updates from SegmentFault 最新的文章. To stop receiving these emails, you may unsubscribe now. | Email delivery powered by Google |
Google, 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States |
No comments:
Post a Comment