commit
3aefab4a4d
139
2018 这一年.md
Normal file
139
2018 这一年.md
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
作者:CyC2018
|
||||||
|
|
||||||
|
链接:https://www.nowcoder.com/discuss/137593
|
||||||
|
|
||||||
|
来源:牛客网
|
||||||
|
|
||||||
|
## 前言
|
||||||
|
|
||||||
|
2018,有过迷茫,有过努力,也有很多收获。为了记录这一年以来的感受,于是有了这篇文章。
|
||||||
|
|
||||||
|
## Offer 情况
|
||||||
|
|
||||||
|
经过了长达一年左右的复习,秋招也收到了几个比较满意的 Offer,参加面试的都通过了。
|
||||||
|
|
||||||
|
- 百度,企业智能平台;
|
||||||
|
- 阿里,高德地图,部门已联系,目前还在申报 Offer 中;
|
||||||
|
- 腾讯,IEG 游戏平台,后台研发,SP;
|
||||||
|
- 字节跳动,头条后台研发,SSP;
|
||||||
|
- 华为,Cloud Bu;
|
||||||
|
- 网易游戏,梦幻事业部;
|
||||||
|
- 顺丰科技。
|
||||||
|
|
||||||
|
## 前期准备
|
||||||
|
|
||||||
|
也是在去年十一月份左右,看着身边两年制的同学经历了长时间而又艰难的秋招,我开始意识到自己应该提前准备了,否则自己的秋招会很惨。
|
||||||
|
|
||||||
|
本科的时候,虽然学过计算机网络、操作系统和数据结构等课程,而且 Leetcode 也刷了一两百题,但是离招聘要求还差的很远,学的都很浅只够应付考试,也没有实际的项目经验。
|
||||||
|
|
||||||
|
我的研究生方向是计算机图形学,研究生期间主要做一些科研项目。在选择招聘方向的时候,我也纠结了是不是找图形学相关方向的,但是考虑到图形学的选择不是很多,所以还是决定投后台研发相关的岗位。
|
||||||
|
|
||||||
|
于是开始收集各种学习资料,也买了很多纸质书。最开始的学习效率并不是很高,很迷茫,觉得要学的内容很多无从下手。那时候看别人的面经,感觉自己太弱了,很多内容都没接触过,于是更加迷茫。迷茫的时候总想着逃避,要是不复习多好,玩玩游戏每天多简单。但是游戏玩的越多,那种焦虑感越是强烈。解决焦虑的唯一办法就是想办法解决当前问题。当慢慢地从消极的学习态度中调整过来,掌握的知识越多,那种焦虑感也随之消失。当然这个过程并不容易,不仅需要很好的毅力,也要根据自身情况找到问题的有效解决方法。
|
||||||
|
|
||||||
|
## 春招开始
|
||||||
|
|
||||||
|
三月份各个公司就开始春招了,那时候刚把一些基础知识简单地复习了一下,Leetcode 刷到了三四百题。但是没有后台研发相关的项目,于是花了一个星期左右用 PHP 做了一个微博系统。当时做简历特别痛苦,没内容可以写,看着其他人简历各种新技术,自己都没掌握,所以很虚。
|
||||||
|
|
||||||
|
## 阿里一轮游
|
||||||
|
|
||||||
|
最开始投的阿里,实验室大几届有个师兄在天猫精灵团队,所以给我内推了。于是我人生中第一场面试就是阿里,很自然地被虐了一遍。记得当时约好下午两点电话面试,午饭都没吃,怕吃完之后犯困影响状态,然后找了一个很安静又没人的地方呆到了两点,调整自己的状态。可是面试官突然打电话来说有个会议要开,所以推迟了大概一个小时。苦苦等到三点左右,面试正式开始,不出所料面得非常糟糕。首先自己表述的很有问题,很多内容没回答到关键点上,自己会的内容也不怎么继续扩展回答。其次知识掌握得确实不够,连线程安全、ThreadLocal、函数式编程都不会。虽然被虐的很惨,但是也有好处,知道了面试到底是怎样的,自己还有哪方面的不足,该怎么准备。
|
||||||
|
|
||||||
|
## 腾讯被鞭尸
|
||||||
|
|
||||||
|
第二场面试是腾讯,在经历了阿里的面试之后,并且又继续复习了一段时间,我对面试就比较有信心了。一面其实回答的挺理想的,虽然很多问题没有立马回答出来,但是经过面试官的耐心提示之后都能回答一些内容。当时面了一个半小时,面试体验特别好。印象比较深刻的题目有,阅读一个 Redis 源码,分析存在哪些问题。其实就是一个计数器实现的限流算法,会有临界值的问题,但是当时没回答出来,只能听面试官给我解释。还有一个微信扫二维码,这个过程发生了什么,也没回答得很好,不过面试官也很耐心地纠正我回答上的错误。一面顺利通过了,但是总监面挂了。总监面没有问什么技术问题,就是问了问项目和职业规划。自己的项目确实比较 Low,我自己在介绍的时候也说得很不堪。职业规划我说自己希望在一些方面深入学习,因为自己现在在这些方面还很薄弱... 面完之后我就知道挂了,因为整个面试过程我都特别虚,还主动说自己技术能力不行。不出所料,面完的当天晚上,状态变成了不合适。
|
||||||
|
|
||||||
|
但是过了几天,突然收到腾讯的电话,问我是否愿意去深圳参加面试(笔者学校在广州)。当然我毫不犹豫地答应了,很开心腾讯还能给我机会。经过了上一场面试的启示,这次面试我表现地非常自信,自己知道的知识都很有信心地表达出来,被问到不会的内容也不会那么慌张,和面试官探讨一些细节,然后说说自己的想法,还有自己看过相关的内容。由于这是腾讯云部门,对 Linux 内核和 C++ 有很高的要求,问了几个相关的问题我都没回答出来,比如如何实现守护进程,Linux 信号机制,Linux 线程的不可中断阻塞状态如何进入等等。除了这些问题,其它地回答的都还行。遗憾的是,当天晚上面试官打电话告知我面试没通过。但是他说我其它方面都很不错,所以问我愿不愿意参加腾讯云 Java 部门的招聘,于是第二天我又去了一个新的部门面试。
|
||||||
|
|
||||||
|
这次面试是在部门的会议室进行的,进到公司之后说实话没有自己想象中那么好,工位很挤环境一般。一开始就先随便聊聊,学校的研究工作,学习之类的。然后看了看项目,看完之后我就知道凉了一半,这个项目确实太水了,面试官看了之后没有接着问,也能感受到面试官有点嫌弃。然后他就问了一些基础知识,问到进程调度算法,面试官让我实现一个任务调度系统。因为是第一次手写代码,而且之前确实没考虑过这个问题,然后就胡乱写了一堆代码,特别乱,而且到处涂改。显然面试官是不满意的,写了也有十几分钟之后,我自己都知道已经凉了,然后面试官没让我接着写,也没给我任何提示,说就到这里,面试结束了,还有没有什么问题想问的。当然看过任务调度系统相关的文章会觉得挺容易的,比如使用时间轮实现等等。我依然记得面试官送我出门时候的热情,送我坐电梯的时候还很热情地和我说,非常感谢参加本次面试,辛苦了。
|
||||||
|
|
||||||
|
## 虎牙过于自信
|
||||||
|
|
||||||
|
经过了阿里和腾讯的面试之后,我觉得自己大概已经知道该怎么面试了,面试时候该注意什么,该怎么表达等等。而且腾讯面试表现也不差,虽然最后没通过。所以在虎牙面试的时候特别放松,觉得应该能通过。前面面的也都还行,虽然有几个问题没回答好,比如分析一下微博的时间线。通过了第一轮面试直接等第二轮,等到了晚上七点多才等到我。虎牙面试还是很注重技术的,虽然问的都不是很深入,只要简单回答到点上就不会接着问下去。二面也有一些问题没回答好,比如 ConcurrentHashMap 的并发机制,问 Spring 直接说不会。也有一些问题回答得比较乱,没有条理。但是我觉得大部分问题都回答的不错,应该能通过。可是面试完之后,面试官问有没有什么问题要问他,由于太过放松,我就问你们都加班到这么晚不吃饭吗,好饿啊,周六周日还加班吗... 问完之后面试官就很严肃了,说平常不加班的,我突然意识到了问题的严重性... 最后还是凉了。
|
||||||
|
|
||||||
|
## 百度第一个 Offer
|
||||||
|
|
||||||
|
被三家连续拒了之后,都开始怀疑自己了,不过还是提醒自己要保持信心。
|
||||||
|
|
||||||
|
幸运的是,百度的面试非常适合我,三轮都是技术面,而且手写算法题目居多,而我准备最多的是算法,所以很顺利通过了面试。但是面试表现并没有特别好,过了比较长的时间才被捞,而且是工程效率部门,做内部工具的,对个人成长并不好,所以不是特别满意。
|
||||||
|
|
||||||
|
## 网易游戏最好的面试体验
|
||||||
|
|
||||||
|
其实最开始没有打算投网易游戏的,因为被脉脉洗脑,已经放弃了做游戏。但是因为前面面试基本被拒了,担心没有实习 Offer,因此就试试看。
|
||||||
|
|
||||||
|
因为没有特别想去网易游戏,所以面试过程也比较放松,就当去聊聊天。面试官非常 nice,那天下午挤了很久地铁,比较口渴,然后面试官看我说得沙哑了,到门口帮我买了一瓶可乐,非常感激。面试之前我就提出我对 C++ 不熟悉,最近主要看 Java 的内容。面试官还是说没关系,尽量回答就好。当然最后我都把问题往 Java 那里回答了,比如 Map 的实现,内存管理等等。最后聊了一些玩过的游戏,就让我回去等消息。网易游戏就一轮面试,确实就一轮。周五参加的面试,下周一就给 Offer 了,效率特别高。
|
||||||
|
|
||||||
|
## 微众玄学面试
|
||||||
|
|
||||||
|
通过微众面试我自己都非常吃惊,一面的时候就简单自我介绍了一下,然后面试官开始介绍他自己的工作经历,以及现在部门在做的内容。之后问了我一个场景分析问题,我想了一会儿没想出来,于是面试官拿起草稿纸把各种需求详细说了一遍,然后把系统架构图也画了出来... 最后他问还有什么我优势的地方他没问到的,我问他怎么不问问算法题,他说笔试都通过了没必要再问。面完之后我觉得聊得很开心,但是技术问题没回答好,出乎意料收到了二面通知。二面没问技术,就让介绍了项目,再问问家住哪之类的问题,也顺利通过了。HR 面就不用介绍。收到了微众的 Offer,得知了部门是贷款科技部,非常核心,很吃香,近几年也在扩展一些业务,还是有点小心动的。虽然最后没选择去微众实习,但是一面面试官加了我微信,我很感谢他一面非常耐心给我讲解,并让我通过。他说我是他面试的第一顺位,也就是第一个面试者,所以会放宽很多,也希望我秋招能加入他们。
|
||||||
|
|
||||||
|
## 实习选择
|
||||||
|
|
||||||
|
其实最理想的是去百度实习,秋招也会容易很多。但是考虑到百度是在北京,部门很边缘,而且需要实习很长时间也不一定能转正,所以还是放弃了。最后只能在网易游戏和微众选,虽然自己不想做游戏,但是考虑到网易游戏的平台认可程度比微众好,秋招肯定会更容易一些。而且秋招如果还想进微众的话也会比较容易,因为面试官和 HR 都说秋招的时候会优先考虑我,所以最后还是去了网易游戏实习。
|
||||||
|
|
||||||
|
## 实习之前的快速学习期
|
||||||
|
|
||||||
|
经历了春招之后,认识到了自己身上的不足,比如交流表达能力的欠缺,知识积累得不够,项目深度不够。因此在实习之前的两三个月,开始针对这些问题逐个解决。
|
||||||
|
|
||||||
|
- 交流表达能力欠缺,就提前准备好各种非技术问题,然后对着镜子回答,把自己当成听众,并且也用录音机录下来。
|
||||||
|
- 知识积累不够,采取的策略是保证广度优先,并且在重要的内容上保证深度。其实之前基础知识已经掌握的比较好了,再学其它技术的时候都有很多相同的地方,所以学起来很快。
|
||||||
|
- 项目深度不够,就把那个微博系统做了一点改进,学了 Spring 之后改用 Java 实现。
|
||||||
|
|
||||||
|
## 不那么安心的实习
|
||||||
|
|
||||||
|
去实习的时候还是挺惊喜的,因为我被安排的工作是游戏引擎相关的,和自己的研究生方向紧密相关,我觉得做完实习项目之后自己的毕业论文也会比较有灵感。
|
||||||
|
|
||||||
|
但不幸的是,在去的第一天部门接待聚餐上,服务端主程就说,我们部门工作制是九九六,现在互联网都是九九六。在实习之前我了解的是实习生六点就可以走,而且只用上五天班,听到他这么一说心都凉透了,因为已经想好了晚上和周末时间用来复习。如果知道是九九六,我会选择去百度。
|
||||||
|
|
||||||
|
其实网易游戏部门氛围还是不错的,对员工很好,而且我的实习导师人也很好,在我生病的那几天很关心我。但是九九六的工作制对秋招复习还是有很大影响的,而且每天上下班花在路上的时间超过了两个小时,下班回寝室之后总想着看会儿视频休息一下,然后又要早早睡觉赶着第二天上班。没办法只能在上下班地铁上复习,还有就是午休时间接着复习。
|
||||||
|
|
||||||
|
## 秋招开始
|
||||||
|
|
||||||
|
实习之后已经是九月份了,那时候已经错过了所有提前批。而且实习的时候没怎么复习,九月初还是感觉没怎么准备充分,所以就又等了半个月才开始投简历。
|
||||||
|
|
||||||
|
但是这个时候和春招相比,已经把大部分后台研发相关的知识点过了一遍,很多重要的内容前前后后也看了十几遍,没有春招时候那么迷茫和焦虑。即使被问到没有掌握的知识,我也有把握通过讨论的方式,给出大概的思路,因为很多技术确实是相通的。
|
||||||
|
|
||||||
|
## 阿里看不懂的内部流程
|
||||||
|
|
||||||
|
秋招第一个投递的依然是阿里,最开始系统自动发起了一个新的流程,然后过了几天自动回绝了... 八月末的时候也找人内推了,但是又被阿里直接回绝了... 那时候已经觉得可能是春招面试表现太差,此生无缘阿里了。可是过了一段时间,正式校招的时候,阿里又发起了一个新的流程戏弄我,收到笔试通知的时候,我还犹豫了到底参不参加,因为那时候已经九月中旬,听说阿里已经没有 HC 了。而且按前面回绝我的态度,感觉即使笔试通过面试也通过不了。笔试那天晚上,本来准备看个电影放松一下,后来想了想还是参加了笔试,笔试各种机器学习和数学题,感觉拿错了试卷,笔试完我已经把阿里从我的公司进度列表中删除了,不再纠结阿里。可是过了一段时间收到阿里的面试通知,我以为是走走形式,可能参加笔试的人很少了,所以才选中我参加面试。那时候阿里招聘官网状态一排的已回绝,让我对阿里有一种恐惧感,觉得面试肯定挂。但是真正面试的时候却意外的顺利,收到二面通知的时候特别激动,然后面完二面又让直接等 HR 面,HR 面虽然不是很理想,但是没有很大的问题。又过了很长一段时间,在我去深圳参加腾讯招聘的高铁上,收到了高德地图 HR 的电话,问是否愿意去。虽然得知部门在北京有点小失落,但是还是很开心终于被阿里认可了,摆脱了对阿里的恐惧。
|
||||||
|
|
||||||
|
实验室上届毕业在阿里云的大佬某天突然和我说,他们部门有新的 HC,让我把简历发给他,他要帮我内推,会帮我安排一场线下面试,如果通过的话,到时候和高德的 HR 沟通一下,直接把我从高德捞过来。很感谢大佬向他老大极力推荐我,给我了这次面试机会。线下面试也很顺利,聊聊实习项目,问问我的开源博客,然后问些 Paxos 等分布式的问题,还有就是手写代码,信号量实现生产者消费者,以及一个位运算的问题。其实位运算的问题面试的时候写的不完善,面试官让我之后完善了再发给他,因为面试一个多小时有点长了。过后我写了详细文档讲解了思路,以及使用 JUnit 进行了详细的单元测试,把文档和代码都发给了他。现在面试已经通过了,但是最近阿里集团 HC 比较紧张,也不知道能不能批到 HC。
|
||||||
|
|
||||||
|
## 百度又是不那么满意的部门
|
||||||
|
|
||||||
|
虽然阿里是最先开始流程,但是第一个参加面试的是百度。因为实习的时候通过了百度的面试,所以这次面试还是比较有信心的。百度面试连续三天,都在同一个地方,最后签约也在同一个地方。还记得每次都坐一个小时左右的地铁去那里,路线已经非常熟悉了,和每天去实习的感觉类似。百度面试比较注重技术,三轮面试基本都是问技术问题,而且问的也比较深入,内容也非常广。但是面的不是那么理想,有两点原因,首先是因为确实有些知识点还没掌握好,比如 AC 自动机,系统故障分析等等;其次是对实习项目的描述上还不够好,没有把实习内容的闪光点描述出来,也没有讲清楚为什么做这个项目,自己通过什么方法去做,以及最后的结果。
|
||||||
|
|
||||||
|
最后百度给了白菜价,部门是企业智能平台,主要是内部系统,虽然会接触到机器学习和大数据。
|
||||||
|
|
||||||
|
## 腾讯虐我千百遍
|
||||||
|
|
||||||
|
秋招腾讯第一场面试和实习参加腾讯面试的感觉非常像,第一轮技术面感觉很好,手写堆排序算法,二部图分析等等。面完之后通知待会儿二面,听到之后还是很激动的,觉得这次应该没问题了。我在等二面的时候,碰到了室友(他经常不住宿舍,所以不清楚他也去面试),聊着聊着居然发现我两是同一个面试官,而且他是来二面的,也就是等一下我两就要一前一后进去面试。二面的感觉和实习二面非常像,非技术问题回答的支支吾吾,然后面试官开始质疑我说的内容,给我压力,我没有当场反驳,就说了哦,好像是这样的。因为面试官全程都绷着脸,所以我也比较紧张,很多问题没回答好。过了几天,室友和我说收到 HR 面试通知了,我去官网看了一下状态,已经变成了熟悉的不合适。这次面试失败的主要原因是自己在应对这种压力时处理地不是很好,主要体现在失去信心以及紧张。解决方法也简单,做好充分准备来保持信心,受到质疑的时候积极反驳,紧张的时候计时调整心态,可以试试深呼吸或者喝水。
|
||||||
|
|
||||||
|
因为实习有被捞起来的经历,所以被拒之后我特别希望能继续被捞起来,然后把简历上的面试城市改成了深圳。苦苦等到深圳场面试的前几天,在不经意的一个下午手机突然响了,我记得是短信邮件同时收到面试通知。于是又开始了新一轮被腾讯虐的面试之旅。
|
||||||
|
|
||||||
|
一面和之前一样也是意外地顺利,虽然问了一些 C++ 的问题,但是我都说到 Java 相关的实现上。在一些问题上确实回答的深度不够,比如网络编程里面的水平触发和边缘触发等问题。然后问了几个算法,本来要求手写,我说我实现过,所以就讲了讲思路。面试和腾讯第一场面试一样持续了一个半小时,面试官也很好,很多问题都会给提示,即使最开始回答的有问题。二面面试官也很好,问了问实习项目,然后再聊一聊一些技术,经过了之前的面试,到这次面试真的就像在聊天一样而不是面试,我们都会说一些对技术上的理解。HR 面其实面得很差,对于非技术问题的吹水能力我还是不太行。最终和我预期的一样,给了我 SP 的 Offer,因为觉得自己面得还可以,但是也不够好到给 SSP,有些 C++ 问题还是没回答的特别好。
|
||||||
|
|
||||||
|
## 头条意外的惊喜
|
||||||
|
|
||||||
|
之前看到学弟收到头条的 Offer,薪资非常诱人,所以也想去试试。也听说头条面试难度非常大,主要考察手写算法,因为自己算法方面准备得比较充分,所以觉得会比较顺利,但是也没有特别高的预期。前两面中规中矩,算法题和其它问题我都回答的比较好,到三面的时候,问了一个错排问题,其实最开始我给了正确的递推公式,但不是面试官想要的答案,所以让我再想想。我想了十几分钟还是觉得没问题,那时候觉得自己已经凉了,因为面试官一直不满意。后面的几个问题也没回答的很好,分析一个 SQL 语句的具体执行过程,比如会怎么利用索引,怎么优化之类的,虽然在他的提示下还是回答了,但是感觉并不好。面完之后我立马查了一下那个错排问题,证实了我的答案是正确的,于是写了一个详细的文档,联系 HR 让她发给面试官。出乎意料的是,HR 让我不用担心,他说面试官对我的评价很好... 不过最后还是让她把文档发给了面试官。之后收到了加面通知,头条加面有两种情况,一是三轮评级都是 4 可以评 SSP,二是面试官评价差别很大,再面一轮决定是否录用。收到加面的时候完全不知道自己属于哪一种,感觉两种情况都有可能。加面回答的也不好,主要是问项目,面了 25 分钟就草草结束,最后面试官说有些内容需要找一些文献参考参考。面完之后我觉得,即使我属于第一种要评 SSP 的情况,加面面的那么差应该也没希望了。苦苦等了好多天之后,最后确定是 SSP 之后,还是很惊喜的,感觉是对自己这么长时间复习的一个认可。
|
||||||
|
|
||||||
|
## 顺丰最后的保底
|
||||||
|
|
||||||
|
投顺丰是因为九月中旬很多公司都结束了招聘,所以那时候比较慌,就投了顺丰当做保底,顺便也练练手。最开始还担心顺丰笔试没通过,因为编程题最后一题没做出来,那题的题目都出错了,而且题目是网上直接 copy 过来的,网上的源码都不能通过,更别说我自己的实现了。顺丰面试主要问了数据库的内容,而且问的特别深,几乎把每种日志的实现和作用都问了一遍。面顺丰的时候也比较早,那时候有些问题的回答上没有组织好,回答得比较凌乱,虽然最后也算给了一个小 SP。
|
||||||
|
|
||||||
|
## 华为特别纠结的部门
|
||||||
|
|
||||||
|
去华为面试确实是没有压力的,因为都知道华为面试不怎么问技术,虽然还是问了我一些技术问题,不过不是问的很深。面试主要介绍项目,我对自己的实习项目还是比较有信心的,因为觉得做的确实不错,而且面了很多场了,知道该怎么介绍项目。面试官问我个人意愿,我说自己对分布式中间件等比较感兴趣,于是面试官把我推荐到了 Cloud Bu。本来没打算签华为的,现场签约也就去看看到底给我开多少。最开始其实给我开了十四级最高的薪资,我本来不是很想去,虽然对这个部门感兴趣,但是薪资确实比不上头条。然后随口问了一句可不可以给十五级,本来 HR 说是可以试着申请一下,不过最后没申请成功。
|
||||||
|
|
||||||
|
## 技术博客
|
||||||
|
|
||||||
|
最后安利一下自己的技术博客:[CS-Notes](https://github.com/CyC2018/CS-Notes),虽然现在还有很多不完善的地方,但以后会不断改进。
|
||||||
|
|
||||||
|
## 小结
|
||||||
|
|
||||||
|
很多人都说,面试和考试一样,要背很多没用的东西。最开始我也认同这种看法,可是参加了几场面试之后,我就不这么认为了。因为面试出的问题,有很多是实际开发中碰到的,所以准备面试相当于提前做入职准备。而且面试中考察的思维能力、交流表达能力、应对压力能力,都是真正工作中所需要的。
|
||||||
|
|
||||||
|
我觉得自己比别人做的好的地方是,有很强烈的想找到好工作的意愿,才驱使我不断学习,所以态度很重要。
|
||||||
|
|
||||||
|
信心源自于充分准备,有了信心,面试的时候才能游刃有余。而毫无依据的自我感觉良好,在每次失败之后都看不到自身的不足,而是怪罪于外界因素。
|
||||||
|
|
||||||
|
做好自己的简历,我在简历上花了很长时间,只要允许,我都会用这个简历给面试官演示:[个人简历](https://cyc2018.github.io)。
|
46
Additional.md
Normal file
46
Additional.md
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# 前言
|
||||||
|
|
||||||
|
本文档提供博客内容的补充资料,记录一些还没写又很重要的内容。欢迎补充!
|
||||||
|
|
||||||
|
格式:\[资料名称\]\[标签1\]\[标签2\]...
|
||||||
|
|
||||||
|
|
||||||
|
# 系统设计
|
||||||
|
|
||||||
|
- [[system-design-primer][Github]](https://github.com/donnemartin/system-design-primer)
|
||||||
|
- [[Leetcode / Interview Questions][题集]](https://leetcode.com/discuss/interview-question/?orderBy=most_votes)
|
||||||
|
- [[系统设计面试题精选][Gitbook]](https://soulmachine.gitbooks.io/system-design/content/cn/)
|
||||||
|
- [[海量数据面试题]](https://samanthachen.github.io/2016/08/01/%E6%B5%B7%E9%87%8F%E6%95%B0%E6%8D%AE%E9%9D%A2%E8%AF%95%E9%A2%98/)
|
||||||
|
- [[前端经典面试题: 从输入 URL 到页面加载发生了什么?][具体问题]](https://segmentfault.com/a/1190000006879700)
|
||||||
|
- [[秒杀系统架构分析与实战][具体问题]](https://my.oschina.net/xianggao/blog/524943)
|
||||||
|
- [[微信二维码登录原理][具体问题]](https://zhuanlan.zhihu.com/p/22325152?refer=bittiger)
|
||||||
|
- [[Create a TinyURL System][具体问题]](https://github.com/CyC2018/CS-Notes)
|
||||||
|
- [[Design a Key-Value Store (Part I)][具体问题]](http://blog.gainlo.co/index.php/2016/06/14/design-a-key-value-store-part-i/)
|
||||||
|
- [[坦率地讲 服务熔断 & 服务降级][知识点]](http://lexuslee.me/2018/02/01/2018-01-18-Service-fallback/)
|
||||||
|
- [[理解 HTTP 幂等性][知识点]](https://www.cnblogs.com/weidagang2046/archive/2011/06/04/idempotence.html)
|
||||||
|
- [[接口限流算法][知识点]](https://blog.csdn.net/ljj821061514/article/details/52512943)
|
||||||
|
- [[微服务学习资料汇总][知识点]](https://www.infoq.cn/article/2014%2F07%2Fmicroservice-learning-resources)
|
||||||
|
- [[理解 RESTful 架构][知识点]](http://www.ruanyifeng.com/blog/2011/09/restful.html)
|
||||||
|
- [[MapReduce 算法][知识点]](https://github.com/xuelangZF/CS_Offer/blob/master/Others/Hadoop_Spark.md)
|
||||||
|
|
||||||
|
# Spring
|
||||||
|
|
||||||
|
- [[Spring 揭秘][书籍]](https://book.douban.com/subject/3897837/)
|
||||||
|
- [[69 道 Spring 面试题和答案][面试题集锦]](http://ifeve.com/spring-interview-questions-and-answers/)
|
||||||
|
- [[Spring 面试题][面试题集锦]](https://github.com/Homiss/Java-interview-questions/blob/master/%E6%A1%86%E6%9E%B6/Spring%20%E9%9D%A2%E8%AF%95%E9%A2%98.md)
|
||||||
|
|
||||||
|
# 中间件
|
||||||
|
|
||||||
|
- [[RabbitMQ 实战][书籍]](https://book.douban.com/subject/26649178/)
|
||||||
|
- [[从 Paxos 到 Zookeeper][书籍]](https://book.douban.com/subject/26292004/)]
|
||||||
|
- [[Apache Dubbo][文档]](http://dubbo.apache.org/zh-cn/)
|
||||||
|
- [[nginx 快速入门之基本原理篇][入门]](https://zhuanlan.zhihu.com/p/31196264)
|
||||||
|
- [[深入理解 Nginx][书籍]](https://book.douban.com/subject/22793675/)
|
||||||
|
|
||||||
|
# 编程语言思想
|
||||||
|
|
||||||
|
- [[函数式编程初探][入门]](http://www.ruanyifeng.com/blog/2012/04/functional_programming.html)]
|
||||||
|
- [[函数式编程][全面]](https://coolshell.cn/articles/10822.html)
|
||||||
|
- [[闭包][文档]](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures)
|
||||||
|
- [[尾调用优化]](http://www.ruanyifeng.com/blog/2015/04/tail-call.html)
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
# 数据结构与算法
|
# 数据结构与算法
|
||||||
|
|
||||||
- [算法](https://book.douban.com/subject/19952400/)
|
- [算法](https://book.douban.com/subject/19952400/)
|
||||||
- [数据结构与算法分析](https://book.douban.com/subject/3351237/)
|
- [数据结构与算法分析](https://book.douban.com/subject/3351237/)
|
||||||
- [编程珠玑](https://book.douban.com/subject/3227098/)
|
- [编程珠玑](https://book.douban.com/subject/3227098/)
|
||||||
@ -41,6 +41,7 @@
|
|||||||
|
|
||||||
- [Java 编程思想](https://book.douban.com/subject/2130190/)
|
- [Java 编程思想](https://book.douban.com/subject/2130190/)
|
||||||
- [Effective java 中文版](https://book.douban.com/subject/3360807/)
|
- [Effective java 中文版](https://book.douban.com/subject/3360807/)
|
||||||
|
- [深入Java虚拟机(原书第2版)](https://book.douban.com/subject/1138768/)
|
||||||
- [深入理解 Java 虚拟机](https://book.douban.com/subject/24722612/)
|
- [深入理解 Java 虚拟机](https://book.douban.com/subject/24722612/)
|
||||||
- [Java 并发编程实战](https://book.douban.com/subject/10484692/)
|
- [Java 并发编程实战](https://book.douban.com/subject/10484692/)
|
||||||
- [精通 Spring 4.x](https://book.douban.com/subject/26952826/)
|
- [精通 Spring 4.x](https://book.douban.com/subject/26952826/)
|
||||||
|
14
README.md
14
README.md
@ -8,7 +8,7 @@
|
|||||||
<img src="other/LogoMakr_0zpEzN.png" width="150px">
|
<img src="other/LogoMakr_0zpEzN.png" width="150px">
|
||||||
<br>
|
<br>
|
||||||
<a href="other/Group.md"> <img src="https://img.shields.io/badge/>-group-4ab8a1.svg"></a> <a href="https://legacy.gitbook.com/book/cyc2018/interview-notebook/details"> <img src="https://img.shields.io/badge/_-gitbook-4ab8a1.svg"></a>
|
<a href="other/Group.md"> <img src="https://img.shields.io/badge/>-group-4ab8a1.svg"></a> <a href="https://legacy.gitbook.com/book/cyc2018/interview-notebook/details"> <img src="https://img.shields.io/badge/_-gitbook-4ab8a1.svg"></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
### :pencil2: 算法
|
### :pencil2: 算法
|
||||||
|
|
||||||
@ -88,7 +88,7 @@
|
|||||||
|
|
||||||
- [Java 容器](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Java%20容器.md)
|
- [Java 容器](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Java%20容器.md)
|
||||||
|
|
||||||
源码分析:ArrayList、Vector、CopyOnWriteArrayList、LinkedList、HashMap、ConcurrentHashMap、LinkedHashMap、WeekHashMap。
|
源码分析:ArrayList、Vector、CopyOnWriteArrayList、LinkedList、HashMap、ConcurrentHashMap、LinkedHashMap、WeakHashMap。
|
||||||
|
|
||||||
- [Java 并发](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Java%20并发.md)
|
- [Java 并发](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Java%20并发.md)
|
||||||
|
|
||||||
@ -168,10 +168,6 @@
|
|||||||
|
|
||||||
学习笔记不是从网上到处拼凑而来,除了少部分引用书上和技术文档的原文,其余都是笔者的原创。在您引用本仓库内容或者对内容进行修改演绎时,请遵循文末的开源协议,谢谢。
|
学习笔记不是从网上到处拼凑而来,除了少部分引用书上和技术文档的原文,其余都是笔者的原创。在您引用本仓库内容或者对内容进行修改演绎时,请遵循文末的开源协议,谢谢。
|
||||||
|
|
||||||
#### BookList
|
|
||||||
|
|
||||||
本仓库参考的书目:[BOOKLIST](https://github.com/CyC2018/Interview-Notebook/blob/master/BOOKLIST.md)。
|
|
||||||
|
|
||||||
#### How To Contribute
|
#### How To Contribute
|
||||||
|
|
||||||
笔记内容是笔者一个字一个字打上去的,难免会有一些笔误,如果发现笔误可直接对相应文档进行编辑修改。
|
笔记内容是笔者一个字一个字打上去的,难免会有一些笔误,如果发现笔误可直接对相应文档进行编辑修改。
|
||||||
@ -180,9 +176,13 @@
|
|||||||
|
|
||||||
欢迎在 Issue 中提交对本仓库的改进建议~
|
欢迎在 Issue 中提交对本仓库的改进建议~
|
||||||
|
|
||||||
|
#### BookList
|
||||||
|
|
||||||
|
本仓库参考的书目:[BOOKLIST](https://github.com/CyC2018/Interview-Notebook/blob/master/BOOKLIST.md)。
|
||||||
|
|
||||||
#### Typesetting
|
#### Typesetting
|
||||||
|
|
||||||
笔记内容按照 [中文文案排版指北](http://mazhuang.org/wiki/chinese-copywriting-guidelines/) 进行排版,以保证内容的可读性。
|
笔记内容按照 [中文文案排版指北](https://mazhuang.org/wiki/chinese-copywriting-guidelines/) 进行排版,以保证内容的可读性。
|
||||||
|
|
||||||
笔记不使用 `![]()` 这种方式来引用图片,而是用 `<img>` 标签。一方面是为了能够控制图片以合适的大小显示,另一方面是因为 GFM 不支持 `<center> ![]() </center>` 让图片居中显示,只能使用 `<div align="center"> <img src=""/> </div>` 达到居中的效果。
|
笔记不使用 `![]()` 这种方式来引用图片,而是用 `<img>` 标签。一方面是为了能够控制图片以合适的大小显示,另一方面是因为 GFM 不支持 `<center> ![]() </center>` 让图片居中显示,只能使用 `<div align="center"> <img src=""/> </div>` 达到居中的效果。
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
This file used to generate gitbook catalogue.
|
This file used to generate gitbook catalogue.
|
||||||
|
|
||||||
# Summary
|
# Summary
|
||||||
|
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
<!-- GFM-TOC -->
|
|
||||||
* [0. 进程内存空间中,堆和栈的区别](#0-进程内存空间中,堆和栈的区别)
|
|
||||||
<!-- GFM-TOC -->
|
|
||||||
|
|
||||||
|
|
||||||
# 0. 进程内存空间中,堆和栈的区别
|
|
||||||
|
|
||||||
> C++
|
|
||||||
|
|
||||||
堆:动态、malloc()、new、链式分配、向上生长;栈:函数调用、编译器分配回收、向下生长。
|
|
||||||
|
|
||||||
https://www.cnblogs.com/sunziying/p/6510030.html
|
|
||||||
|
|
||||||
By @CyC
|
|
||||||
|
|
||||||
---
|
|
@ -44,14 +44,14 @@ Git 版本库有一个称为 stage 的暂存区,还有自动创建的 master
|
|||||||
|
|
||||||
- git add files 把文件的修改添加到暂存区
|
- git add files 把文件的修改添加到暂存区
|
||||||
- git commit 把暂存区的修改提交到当前分支,提交之后暂存区就被清空了
|
- git commit 把暂存区的修改提交到当前分支,提交之后暂存区就被清空了
|
||||||
- git reset -- files 使用当前分支上的修改覆盖暂缓区,用来撤销最后一次 git add files
|
- git reset -- files 使用当前分支上的修改覆盖暂存区,用来撤销最后一次 git add files
|
||||||
- git checkout -- files 使用暂存区的修改覆盖工作目录,用来撤销本地修改
|
- git checkout -- files 使用暂存区的修改覆盖工作目录,用来撤销本地修改
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//17976404-95f5-480e-9cb4-250e6aa1d55f.png"/> </div><br>
|
<div align="center"> <img src="../pics//17976404-95f5-480e-9cb4-250e6aa1d55f.png"/> </div><br>
|
||||||
|
|
||||||
可以跳过暂存区域直接从分支中取出修改,或者直接提交修改到分支中。
|
可以跳过暂存区域直接从分支中取出修改,或者直接提交修改到分支中。
|
||||||
|
|
||||||
- git commit -a 直接把所有文件的修改添加到暂缓区然后执行提交
|
- git commit -a 直接把所有文件的修改添加到暂存区然后执行提交
|
||||||
- git checkout HEAD -- files 取出最后一次修改,可以用来进行回滚操作
|
- git checkout HEAD -- files 取出最后一次修改,可以用来进行回滚操作
|
||||||
|
|
||||||
# 分支实现
|
# 分支实现
|
||||||
|
@ -63,6 +63,8 @@ public static void listAllFiles(File dir) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
从 Java7 开始,可以使用 Paths 和 Files 代替 File。
|
||||||
|
|
||||||
# 三、字节操作
|
# 三、字节操作
|
||||||
|
|
||||||
## 实现文件复制
|
## 实现文件复制
|
||||||
@ -120,7 +122,7 @@ DataInputStream 装饰者提供了对更多数据类型进行输入的操作,
|
|||||||
|
|
||||||
UTF-16be 中的 be 指的是 Big Endian,也就是大端。相应地也有 UTF-16le,le 指的是 Little Endian,也就是小端。
|
UTF-16be 中的 be 指的是 Big Endian,也就是大端。相应地也有 UTF-16le,le 指的是 Little Endian,也就是小端。
|
||||||
|
|
||||||
Java 使用双字节编码 UTF-16be,这不是指 Java 只支持这一种编码方式,而是说 char 这种类型使用 UTF-16be 进行编码。char 类型占 16 位,也就是两个字节,Java 使用这种双字节编码是为了让一个中文或者一个英文都能使用一个 char 来存储。
|
Java 的内存编码使用双字节编码 UTF-16be,这不是指 Java 只支持这一种编码方式,而是说 char 这种类型使用 UTF-16be 进行编码。char 类型占 16 位,也就是两个字节,Java 使用这种双字节编码是为了让一个中文或者一个英文都能使用一个 char 来存储。
|
||||||
|
|
||||||
## String 的编码方式
|
## String 的编码方式
|
||||||
|
|
||||||
@ -611,7 +613,7 @@ NIO 与普通 I/O 的区别主要有以下两点:
|
|||||||
- [Java NIO Tutorial](http://tutorials.jenkov.com/java-nio/index.html)
|
- [Java NIO Tutorial](http://tutorials.jenkov.com/java-nio/index.html)
|
||||||
- [Java NIO 浅析](https://tech.meituan.com/nio.html)
|
- [Java NIO 浅析](https://tech.meituan.com/nio.html)
|
||||||
- [IBM: 深入分析 Java I/O 的工作机制](https://www.ibm.com/developerworks/cn/java/j-lo-javaio/index.html)
|
- [IBM: 深入分析 Java I/O 的工作机制](https://www.ibm.com/developerworks/cn/java/j-lo-javaio/index.html)
|
||||||
- [IBM: 深入分析 Java 中的中文编码问题](https://www.ibm.com/developerworks/cn/java/j-lo-chinesecoding/index.htm)
|
- [IBM: 深入分析 Java 中的中文编码问题](https://www.ibm.com/developerworks/cn/java/j-lo-chinesecoding/index.html)
|
||||||
- [IBM: Java 序列化的高级认识](https://www.ibm.com/developerworks/cn/java/j-lo-serial/index.html)
|
- [IBM: Java 序列化的高级认识](https://www.ibm.com/developerworks/cn/java/j-lo-serial/index.html)
|
||||||
- [NIO 与传统 IO 的区别](http://blog.csdn.net/shimiso/article/details/24990499)
|
- [NIO 与传统 IO 的区别](http://blog.csdn.net/shimiso/article/details/24990499)
|
||||||
- [Decorator Design Pattern](http://stg-tud.github.io/sedc/Lecture/ws13-14/5.3-Decorator.html#mode=document)
|
- [Decorator Design Pattern](http://stg-tud.github.io/sedc/Lecture/ws13-14/5.3-Decorator.html#mode=document)
|
||||||
|
@ -1209,19 +1209,21 @@ Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect
|
|||||||
- **Method** :可以使用 invoke() 方法调用与 Method 对象关联的方法;
|
- **Method** :可以使用 invoke() 方法调用与 Method 对象关联的方法;
|
||||||
- **Constructor** :可以用 Constructor 创建新的对象。
|
- **Constructor** :可以用 Constructor 创建新的对象。
|
||||||
|
|
||||||
**Advantages of Using Reflection:**
|
**反射的优点:**
|
||||||
|
|
||||||
- **Extensibility Features** : An application may make use of external, user-defined classes by creating instances of extensibility objects using their fully-qualified names.
|
* **可扩展性** :应用程序可以利用全限定名创建可扩展对象的实例,来使用来自外部的用户自定义类。
|
||||||
- **Class Browsers and Visual Development Environments** : A class browser needs to be able to enumerate the members of classes. Visual development environments can benefit from making use of type information available in reflection to aid the developer in writing correct code.
|
* **类浏览器和可视化开发环境** :一个类浏览器需要可以枚举类的成员。可视化开发环境(如 IDE)可以从利用反射中可用的类型信息中受益,以帮助程序员编写正确的代码。
|
||||||
- **Debuggers and Test Tools** : Debuggers need to be able to examine private members on classes. Test harnesses can make use of reflection to systematically call a discoverable set APIs defined on a class, to insure a high level of code coverage in a test suite.
|
* **调试器和测试工具** : 调试器需要能够检查一个类里的私有成员。测试工具可以利用反射来自动地调用类里定义的可被发现的 API 定义,以确保一组测试中有较高的代码覆盖率。
|
||||||
|
|
||||||
**Drawbacks of Reflection:**
|
**反射的缺点:**
|
||||||
|
|
||||||
Reflection is powerful, but should not be used indiscriminately. If it is possible to perform an operation without using reflection, then it is preferable to avoid using it. The following concerns should be kept in mind when accessing code via reflection.
|
尽管反射非常强大,但也不能滥用。如果一个功能可以不用反射完成,那么最好就不用。在我们使用反射技术时,下面几条内容应该牢记于心。
|
||||||
|
|
||||||
- **Performance Overhead** : Because reflection involves types that are dynamically resolved, certain Java virtual machine optimizations can not be performed. Consequently, reflective operations have slower performance than their non-reflective counterparts, and should be avoided in sections of code which are called frequently in performance-sensitive applications.
|
* **性能开销** :反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。
|
||||||
- **Security Restrictions** : Reflection requires a runtime permission which may not be present when running under a security manager. This is in an important consideration for code which has to run in a restricted security context, such as in an Applet.
|
|
||||||
- **Exposure of Internals** :Since reflection allows code to perform operations that would be illegal in non-reflective code, such as accessing private fields and methods, the use of reflection can result in unexpected side-effects, which may render code dysfunctional and may destroy portability. Reflective code breaks abstractions and therefore may change behavior with upgrades of the platform.
|
* **安全限制** :使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如 Applet,那么这就是个问题了。
|
||||||
|
|
||||||
|
* **内部暴露** :由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用,这可能导致代码功能失调并破坏可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。
|
||||||
|
|
||||||
|
|
||||||
- [Trail: The Reflection API](https://docs.oracle.com/javase/tutorial/reflect/index.html)
|
- [Trail: The Reflection API](https://docs.oracle.com/javase/tutorial/reflect/index.html)
|
||||||
|
@ -68,7 +68,7 @@
|
|||||||
|
|
||||||
<div align="center"> <img src="../pics//SoWkIImgAStDuUBAp2j9BKfBJ4vLy0G.png"/> </div><br>
|
<div align="center"> <img src="../pics//SoWkIImgAStDuUBAp2j9BKfBJ4vLy0G.png"/> </div><br>
|
||||||
|
|
||||||
Collection 实现了 Iterable 接口,其中的 iterator() 方法能够产生一个 Iterator 对象,通过这个对象就可以迭代遍历 Collection 中的元素。
|
Collection 继承了 Iterable 接口,其中的 iterator() 方法能够产生一个 Iterator 对象,通过这个对象就可以迭代遍历 Collection 中的元素。
|
||||||
|
|
||||||
从 JDK 1.5 之后可以使用 foreach 方法来遍历实现了 Iterable 接口的聚合对象。
|
从 JDK 1.5 之后可以使用 foreach 方法来遍历实现了 Iterable 接口的聚合对象。
|
||||||
|
|
||||||
@ -646,8 +646,8 @@ static int indexFor(int h, int length) {
|
|||||||
| 参数 | 含义 |
|
| 参数 | 含义 |
|
||||||
| :--: | :-- |
|
| :--: | :-- |
|
||||||
| capacity | table 的容量大小,默认为 16。需要注意的是 capacity 必须保证为 2 的 n 次方。|
|
| capacity | table 的容量大小,默认为 16。需要注意的是 capacity 必须保证为 2 的 n 次方。|
|
||||||
| size | table 的实际使用量。 |
|
| size | 键值对数量。 |
|
||||||
| threshold | size 的临界值,size 必须小于 threshold,如果大于等于,就必须进行扩容操作。 |
|
| threshold | size 的临界值,当 size 大于等于 threshold 就必须进行扩容操作。 |
|
||||||
| loadFactor | 装载因子,table 能够使用的比例,threshold = capacity * loadFactor。|
|
| loadFactor | 装载因子,table 能够使用的比例,threshold = capacity * loadFactor。|
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@ -727,7 +727,7 @@ new capacity : 00100000
|
|||||||
|
|
||||||
对于一个 Key,
|
对于一个 Key,
|
||||||
|
|
||||||
- 它的哈希值如果在第 6 位上为 0,那么取模得到的结果和之前一样;
|
- 它的哈希值如果在第 5 位上为 0,那么取模得到的结果和之前一样;
|
||||||
- 如果为 1,那么得到的结果为原来的结果 +16。
|
- 如果为 1,那么得到的结果为原来的结果 +16。
|
||||||
|
|
||||||
### 7. 计算数组容量
|
### 7. 计算数组容量
|
||||||
|
@ -632,7 +632,7 @@ B
|
|||||||
|
|
||||||
它们都属于 Object 的一部分,而不属于 Thread。
|
它们都属于 Object 的一部分,而不属于 Thread。
|
||||||
|
|
||||||
只能用在同步方法或者同步控制块中使用,否则会在运行时抛出 IllegalMonitorStateExeception。
|
只能用在同步方法或者同步控制块中使用,否则会在运行时抛出 IllegalMonitorStateException。
|
||||||
|
|
||||||
使用 wait() 挂起期间,线程会释放锁。这是因为,如果没有释放锁,那么其它线程就无法进入对象的同步方法或者同步控制块中,那么就无法执行 notify() 或者 notifyAll() 来唤醒挂起的线程,造成死锁。
|
使用 wait() 挂起期间,线程会释放锁。这是因为,如果没有释放锁,那么其它线程就无法进入对象的同步方法或者同步控制块中,那么就无法执行 notify() 或者 notifyAll() 来唤醒挂起的线程,造成死锁。
|
||||||
|
|
||||||
@ -766,7 +766,7 @@ run..run..run..run..run..run..run..run..run..run..end
|
|||||||
|
|
||||||
用来控制多个线程互相等待,只有当多个线程都到达时,这些线程才会继续执行。
|
用来控制多个线程互相等待,只有当多个线程都到达时,这些线程才会继续执行。
|
||||||
|
|
||||||
和 CountdownLatch 相似,都是通过维护计数器来实现的。线程执行 await() 方法之后计数器会减 1,并进行等待,直到计数器为 0,所有调用 awati() 方法而在等待的线程才能继续执行。
|
和 CountdownLatch 相似,都是通过维护计数器来实现的。线程执行 await() 方法之后计数器会减 1,并进行等待,直到计数器为 0,所有调用 await() 方法而在等待的线程才能继续执行。
|
||||||
|
|
||||||
CyclicBarrier 和 CountdownLatch 的一个区别是,CyclicBarrier 的计数器通过调用 reset() 方法可以循环使用,所以它才叫做循环屏障。
|
CyclicBarrier 和 CountdownLatch 的一个区别是,CyclicBarrier 的计数器通过调用 reset() 方法可以循环使用,所以它才叫做循环屏障。
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
# 一、运行时数据区域
|
# 一、运行时数据区域
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//c9ad2bf4-5580-4018-bce4-1b9a71804d9c.png" width="450"/> </div><br>
|
<div align="center"> <img src="../pics//85370d54-40d1-4912-bcbe-37a2481c861d.png" width="450"/> </div><br>
|
||||||
|
|
||||||
## 程序计数器
|
## 程序计数器
|
||||||
|
|
||||||
@ -40,7 +40,7 @@
|
|||||||
|
|
||||||
每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息。从方法调用直至执行完成的过程,就对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。
|
每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息。从方法调用直至执行完成的过程,就对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//926c7438-c5e1-4b94-840a-dcb24ff1dafe.png" width="500"/> </div><br>
|
<div align="center"> <img src="../pics//28ab96b4-82ea-4d99-99fb-b320f60d0a58.png" width="500"/> </div><br>
|
||||||
|
|
||||||
可以通过 -Xss 这个虚拟机参数来指定每个线程的 Java 虚拟机栈内存大小:
|
可以通过 -Xss 这个虚拟机参数来指定每个线程的 Java 虚拟机栈内存大小:
|
||||||
|
|
||||||
@ -227,7 +227,7 @@ obj = null;
|
|||||||
|
|
||||||
<div align="center"> <img src="../pics//a4248c4b-6c1d-4fb8-a557-86da92d3a294.jpg" width=""/> </div><br>
|
<div align="center"> <img src="../pics//a4248c4b-6c1d-4fb8-a557-86da92d3a294.jpg" width=""/> </div><br>
|
||||||
|
|
||||||
将存活的对象进行标记,然后清理掉未被标记的对象。
|
标记要回收的对象,然后清除。
|
||||||
|
|
||||||
不足:
|
不足:
|
||||||
|
|
||||||
|
@ -178,12 +178,12 @@ else if ( ret == 0 )
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If we detect the event, zero it out so we can reuse the structure
|
// If we detect the event, zero it out so we can reuse the structure
|
||||||
if ( pfd[0].revents & POLLIN )
|
if ( fds[0].revents & POLLIN )
|
||||||
pfd[0].revents = 0;
|
fds[0].revents = 0;
|
||||||
// input event on sock1
|
// input event on sock1
|
||||||
|
|
||||||
if ( pfd[1].revents & POLLOUT )
|
if ( fds[1].revents & POLLOUT )
|
||||||
pfd[1].revents = 0;
|
fds[1].revents = 0;
|
||||||
// output event on sock2
|
// output event on sock2
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -4,3 +4,4 @@
|
|||||||
|
|
||||||
- [Twitter Java Style Guide](https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/styleguide.md)
|
- [Twitter Java Style Guide](https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/styleguide.md)
|
||||||
- [Google Java Style Guide](http://google.github.io/styleguide/javaguide.html)
|
- [Google Java Style Guide](http://google.github.io/styleguide/javaguide.html)
|
||||||
|
- [阿里巴巴Java开发手册](https://github.com/alibaba/p3c/blob/master/%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4Java%E5%BC%80%E5%8F%91%E6%89%8B%E5%86%8C%EF%BC%88%E8%AF%A6%E5%B0%BD%E7%89%88%EF%BC%89.pdf)
|
||||||
|
28
notes/分布式.md
28
notes/分布式.md
@ -181,8 +181,8 @@ Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点的父
|
|||||||
|
|
||||||
可用性和一致性往往是冲突的,很难使它们同时满足。在多个节点之间进行数据同步时,
|
可用性和一致性往往是冲突的,很难使它们同时满足。在多个节点之间进行数据同步时,
|
||||||
|
|
||||||
- 为了保证一致性(CP),就需要让所有节点下线成为不可用的状态,等待同步完成;
|
- 为了保证一致性(CP),不能访问未同步完成的节点,也就失去了部分可用性;
|
||||||
- 为了保证可用性(AP),在同步过程中允许读取所有节点的数据,但是数据可能不一致。
|
- 为了保证可用性(AP),允许读取所有节点的数据,但是数据可能不一致。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//0b587744-c0a8-46f2-8d72-e8f070d67b4b.jpg"/> </div><br>
|
<div align="center"> <img src="../pics//0b587744-c0a8-46f2-8d72-e8f070d67b4b.jpg"/> </div><br>
|
||||||
|
|
||||||
@ -228,31 +228,37 @@ ACID 要求强一致性,通常运用在传统的数据库系统上。而 BASE
|
|||||||
|
|
||||||
规定一个提议包含两个字段:[n, v],其中 n 为序号(具有唯一性),v 为提议值。
|
规定一个提议包含两个字段:[n, v],其中 n 为序号(具有唯一性),v 为提议值。
|
||||||
|
|
||||||
下图演示了两个 Proposer 和三个 Acceptor 的系统中运行该算法的初始过程,每个 Proposer 都会向所有 Acceptor 发送提议请求。
|
### 1. Prepare 阶段
|
||||||
|
|
||||||
|
下图演示了两个 Proposer 和三个 Acceptor 的系统中运行该算法的初始过程,每个 Proposer 都会向所有 Acceptor 发送 Prepare 请求。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//1a9977e4-2f5c-49a6-aec9-f3027c9f46a7.png"/> </div><br>
|
<div align="center"> <img src="../pics//1a9977e4-2f5c-49a6-aec9-f3027c9f46a7.png"/> </div><br>
|
||||||
|
|
||||||
当 Acceptor 接收到一个提议请求,包含的提议为 [n1, v1],并且之前还未接收过提议请求,那么发送一个提议响应,设置当前接收到的提议为 [n1, v1],并且保证以后不会再接受序号小于 n1 的提议。
|
当 Acceptor 接收到一个 Prepare 请求,包含的提议为 [n1, v1],并且之前还未接收过 Prepare 请求,那么发送一个 Prepare 响应,设置当前接收到的提议为 [n1, v1],并且保证以后不会再接受序号小于 n1 的提议。
|
||||||
|
|
||||||
如下图,Acceptor X 在收到 [n=2, v=8] 的提议请求时,由于之前没有接收过提议,因此就发送一个 [no previous] 的提议响应,设置当前接收到的提议为 [n=2, v=8],并且保证以后不会再接受序号小于 2 的提议。其它的 Acceptor 类似。
|
如下图,Acceptor X 在收到 [n=2, v=8] 的 Prepare 请求时,由于之前没有接收过提议,因此就发送一个 [no previous] 的 Prepare 响应,设置当前接收到的提议为 [n=2, v=8],并且保证以后不会再接受序号小于 2 的提议。其它的 Acceptor 类似。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//fb44307f-8e98-4ff7-a918-31dacfa564b4.jpg"/> </div><br>
|
<div align="center"> <img src="../pics//fb44307f-8e98-4ff7-a918-31dacfa564b4.jpg"/> </div><br>
|
||||||
|
|
||||||
如果 Acceptor 接收到一个提议请求,包含的提议为 [n2, v2],并且之前已经接收过提议 [n1, v1]。如果 n1 > n2,那么就丢弃该提议请求;否则,发送提议响应,该提议响应包含之前已经接收过的提议 [n1, v1],设置当前接收到的提议为 [n2, v2],并且保证以后不会再接受序号小于 n2 的提议。
|
如果 Acceptor 接收到一个 Prepare 请求,包含的提议为 [n2, v2],并且之前已经接收过提议 [n1, v1]。如果 n1 > n2,那么就丢弃该提议请求;否则,发送 Prepare 响应,该 Prepare 响应包含之前已经接收过的提议 [n1, v1],设置当前接收到的提议为 [n2, v2],并且保证以后不会再接受序号小于 n2 的提议。
|
||||||
|
|
||||||
如下图,Acceptor Z 收到 Proposer A 发来的 [n=2, v=8] 的提议请求,由于之前已经接收过 [n=4, v=5] 的提议,并且 n > 2,因此就抛弃该提议请求;Acceptor X 收到 Proposer B 发来的 [n=4, v=5] 的提议请求,因为之前接收到的提议为 [n=2, v=8],并且 2 <= 4,因此就发送 [n=2, v=8] 的提议响应,设置当前接收到的提议为 [n=4, v=5],并且保证以后不会再接受序号小于 4 的提议。Acceptor Y 类似。
|
如下图,Acceptor Z 收到 Proposer A 发来的 [n=2, v=8] 的 Prepare 请求,由于之前已经接收过 [n=4, v=5] 的提议,并且 n > 2,因此就抛弃该提议请求;Acceptor X 收到 Proposer B 发来的 [n=4, v=5] 的 Prepare 请求,因为之前接收到的提议为 [n=2, v=8],并且 2 <= 4,因此就发送 [n=2, v=8] 的 Prepare 响应,设置当前接收到的提议为 [n=4, v=5],并且保证以后不会再接受序号小于 4 的提议。Acceptor Y 类似。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//2bcc58ad-bf7f-485c-89b5-e7cafc211ce2.jpg"/> </div><br>
|
<div align="center"> <img src="../pics//2bcc58ad-bf7f-485c-89b5-e7cafc211ce2.jpg"/> </div><br>
|
||||||
|
|
||||||
当一个 Proposer 接收到超过一半 Acceptor 的提议响应时,就可以发送接受请求。
|
### 2. Accept 阶段
|
||||||
|
|
||||||
Proposer A 接收到两个提议响应之后,就发送 [n=2, v=8] 接受请求。该接受请求会被所有 Acceptor 丢弃,因为此时所有 Acceptor 都保证不接受序号小于 4 的提议。
|
当一个 Proposer 接收到超过一半 Acceptor 的 Prepare 响应时,就可以发送 Accept 请求。
|
||||||
|
|
||||||
Proposer B 过后也收到了两个提议响应,因此也开始发送接受请求。需要注意的是,接受请求的 v 需要取它收到的最大 v 值,也就是 8。因此它发送 [n=4, v=8] 的接受请求。
|
Proposer A 接收到两个 Prepare 响应之后,就发送 [n=2, v=8] Accept 请求。该 Accept 请求会被所有 Acceptor 丢弃,因为此时所有 Acceptor 都保证不接受序号小于 4 的提议。
|
||||||
|
|
||||||
|
Proposer B 过后也收到了两个 Prepare 响应,因此也开始发送 Accept 请求。需要注意的是,Accept 请求的 v 需要取它收到的最大提议编号对应的 v 值,也就是 8。因此它发送 [n=4, v=8] 的 Accept 请求。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//9b838aee-0996-44a5-9b0f-3d1e3e2f5100.png"/> </div><br>
|
<div align="center"> <img src="../pics//9b838aee-0996-44a5-9b0f-3d1e3e2f5100.png"/> </div><br>
|
||||||
|
|
||||||
Acceptor 接收到接受请求时,如果序号大于等于该 Acceptor 承诺的最小序号,那么就发送通知给所有的 Learner。当 Learner 发现有大多数的 Acceptor 接收了某个提议,那么该提议的提议值就被 Paxos 选择出来。
|
### 3. Learn 阶段
|
||||||
|
|
||||||
|
Acceptor 接收到 Accept 请求时,如果序号大于等于该 Acceptor 承诺的最小序号,那么就发送 Learn 提议给所有的 Learner。当 Learner 发现有大多数的 Acceptor 接收了某个提议,那么该提议的提议值就被 Paxos 选择出来。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//bf667594-bb4b-4634-bf9b-0596a45415ba.jpg"/> </div><br>
|
<div align="center"> <img src="../pics//bf667594-bb4b-4634-bf9b-0596a45415ba.jpg"/> </div><br>
|
||||||
|
|
||||||
|
@ -724,7 +724,7 @@ private char[][] buildMatrix(char[] array) {
|
|||||||
|
|
||||||
地上有一个 m 行和 n 列的方格。一个机器人从坐标 (0, 0) 的格子开始移动,每一次只能向左右上下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于 k 的格子。
|
地上有一个 m 行和 n 列的方格。一个机器人从坐标 (0, 0) 的格子开始移动,每一次只能向左右上下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于 k 的格子。
|
||||||
|
|
||||||
例如,当 k 为 18 时,机器人能够进入方格 (35,37),因为 3+5+3+7=18。但是,它不能进入方格 (35,37),因为 3+5+3+8=19。请问该机器人能够达到多少个格子?
|
例如,当 k 为 18 时,机器人能够进入方格 (35,37),因为 3+5+3+7=18。但是,它不能进入方格 (35,38),因为 3+5+3+8=19。请问该机器人能够达到多少个格子?
|
||||||
|
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
@ -1160,7 +1160,7 @@ public ListNode FindKthToTail(ListNode head, int k) {
|
|||||||
|
|
||||||
在相遇点,slow 要到环的入口点还需要移动 z 个节点,如果让 fast 重新从头开始移动,并且速度变为每次移动一个节点,那么它到环入口点还需要移动 x 个节点。在上面已经推导出 x=z,因此 fast 和 slow 将在环入口点相遇。
|
在相遇点,slow 要到环的入口点还需要移动 z 个节点,如果让 fast 重新从头开始移动,并且速度变为每次移动一个节点,那么它到环入口点还需要移动 x 个节点。在上面已经推导出 x=z,因此 fast 和 slow 将在环入口点相遇。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//70fa1f83-dae7-456d-b94b-ce28963b2ba1.png"/> </div><br>
|
<div align="center"> <img src="../pics//70fa1f83-dae7-456d-b94b-ce28963b2ba1.png" width="500"/> </div><br>
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public ListNode EntryNodeOfLoop(ListNode pHead) {
|
public ListNode EntryNodeOfLoop(ListNode pHead) {
|
||||||
|
@ -221,8 +221,8 @@ MySQL 中提供了两种封锁粒度:行级锁以及表级锁。
|
|||||||
| unlock-x(A) |. |
|
| unlock-x(A) |. |
|
||||||
| | obtain |
|
| | obtain |
|
||||||
| | read A=20 |
|
| | read A=20 |
|
||||||
| | commit |
|
|
||||||
| | unlock-s(A)|
|
| | unlock-s(A)|
|
||||||
|
| | commit |
|
||||||
|
|
||||||
**三级封锁协议**
|
**三级封锁协议**
|
||||||
|
|
||||||
|
@ -96,7 +96,8 @@ POM 代表项目对象模型,它是一个 XML 文件,保存在项目根目
|
|||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
[groupId, artifactId, version, packaging, classfier] 称为一个项目的坐标,其中 groupId、artifactId、version 必须定义,packaging 可选(默认为 Jar),classfier 不能直接定义的,需要结合插件使用。
|
[groupId, artifactId, version, packaging, classifier] 称为一个项目的坐标,其中 groupId、artifactId、version 必须定义,packaging 可选(默认为 Jar),classifier 不能直接定义的,需要结合插件使用。
|
||||||
|
|
||||||
|
|
||||||
- groupId:项目组 Id,必须全球唯一;
|
- groupId:项目组 Id,必须全球唯一;
|
||||||
- artifactId:项目 Id,即项目名;
|
- artifactId:项目 Id,即项目名;
|
||||||
|
@ -541,7 +541,7 @@ public class QuickSort<T extends Comparable<T>> extends Sort<T> {
|
|||||||
|
|
||||||
### 2. 切分
|
### 2. 切分
|
||||||
|
|
||||||
取 a[l] 作为切分元素,然后从数组的左端向右扫描直到找到第一个大于等于它的元素,再从数组的右端向左扫描找到第一个小于等于它的元素,交换这两个元素。不断进行这个过程,就可以保证左指针 i 的左侧元素都不大于切分元素,右指针 j 的右侧元素都不小于切分元素。当两个指针相遇时,将切分元素 a[l] 和 a[j] 交换位置。
|
取 a[l] 作为切分元素,然后从数组的左端向右扫描直到找到第一个大于等于它的元素,再从数组的右端向左扫描找到第一个小于它的元素,交换这两个元素。不断进行这个过程,就可以保证左指针 i 的左侧元素都不大于切分元素,右指针 j 的右侧元素都不小于切分元素。当两个指针相遇时,将切分元素 a[l] 和 a[j] 交换位置。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//766aedd0-1b00-4065-aa2b-7d31138df84f.png" width="400"/> </div><br>
|
<div align="center"> <img src="../pics//766aedd0-1b00-4065-aa2b-7d31138df84f.png" width="400"/> </div><br>
|
||||||
|
|
||||||
|
@ -613,7 +613,7 @@ void philosopher(int i) {
|
|||||||
think();
|
think();
|
||||||
take_two(i);
|
take_two(i);
|
||||||
eat();
|
eat();
|
||||||
put_tow(i);
|
put_two(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -625,7 +625,7 @@ void take_two(int i) {
|
|||||||
down(&s[i]);
|
down(&s[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void put_tow(i) {
|
void put_two(i) {
|
||||||
down(&mutex);
|
down(&mutex);
|
||||||
state[i] = THINKING;
|
state[i] = THINKING;
|
||||||
test(LEFT);
|
test(LEFT);
|
||||||
@ -861,7 +861,7 @@ FIFO 常用于客户-服务器应用程序中,FIFO 用作汇聚点,在客户
|
|||||||
|
|
||||||
### 1. 最佳
|
### 1. 最佳
|
||||||
|
|
||||||
> Optimal
|
> OPT, Optimal replacement algorithm
|
||||||
|
|
||||||
所选择的被换出的页面将是最长时间内不再被访问,通常可以保证获得最低的缺页率。
|
所选择的被换出的页面将是最长时间内不再被访问,通常可以保证获得最低的缺页率。
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ uniqueInstance 采用 volatile 关键字修饰也是很有必要的, `uniqueIn
|
|||||||
2. 初始化 uniqueInstance
|
2. 初始化 uniqueInstance
|
||||||
3. 将 uniqueInstance 指向分配的内存地址
|
3. 将 uniqueInstance 指向分配的内存地址
|
||||||
|
|
||||||
但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1>3>2。指令重排在单线程环境下不会出先问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T<sub>1</sub> 执行了 1 和 3,此时 T<sub>2</sub> 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。
|
但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1>3>2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T<sub>1</sub> 执行了 1 和 3,此时 T<sub>2</sub> 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。
|
||||||
|
|
||||||
使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。
|
使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。
|
||||||
|
|
||||||
@ -2557,6 +2557,10 @@ public class Client {
|
|||||||
remoteControl1.on();
|
remoteControl1.on();
|
||||||
remoteControl1.off();
|
remoteControl1.off();
|
||||||
remoteControl1.tuneChannel();
|
remoteControl1.tuneChannel();
|
||||||
|
RemoteControl remoteControl2 = new ConcreteRemoteControl2(new Sony());
|
||||||
|
remoteControl2.on();
|
||||||
|
remoteControl2.off();
|
||||||
|
remoteControl2.tuneChannel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -2578,7 +2582,7 @@ public class Client {
|
|||||||
|
|
||||||
组合对象拥有一个或者多个组件对象,因此组合对象的操作可以委托给组件对象去处理,而组件对象可以是另一个组合对象或者叶子对象。
|
组合对象拥有一个或者多个组件对象,因此组合对象的操作可以委托给组件对象去处理,而组件对象可以是另一个组合对象或者叶子对象。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//3fb5b255-b791-45b6-8754-325c8741855a.png"/> </div><br>
|
<div align="center"> <img src="../pics//77931a4b-72ba-4016-827d-84b9a6845a51.png"/> </div><br>
|
||||||
|
|
||||||
### Implementation
|
### Implementation
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ Vihical <|-- Trunck
|
|||||||
|
|
||||||
## 实现关系 (Realization)
|
## 实现关系 (Realization)
|
||||||
|
|
||||||
用来实现一个接口,在 Java 中使用 implement 关键字。
|
用来实现一个接口,在 Java 中使用 implements 关键字。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//SoWkIImgAStDuU8goIp9ILK8IatCoQn.png"/> </div><br>
|
<div align="center"> <img src="../pics//SoWkIImgAStDuU8goIp9ILK8IatCoQn.png"/> </div><br>
|
||||||
|
|
||||||
|
@ -11,3 +11,5 @@
|
|||||||
交流群不讨论政治,不讨论有争议性的话题,不发表仇视言论,不传播谣言,不发布广告(招聘信息之类的可以)。
|
交流群不讨论政治,不讨论有争议性的话题,不发表仇视言论,不传播谣言,不发布广告(招聘信息之类的可以)。
|
||||||
|
|
||||||
</br> <div align="center"><img src="group.png" width="300px"></div> </br>
|
</br> <div align="center"><img src="group.png" width="300px"></div> </br>
|
||||||
|
|
||||||
|
|
||||||
|
BIN
pics/28ab96b4-82ea-4d99-99fb-b320f60d0a58.png
Normal file
BIN
pics/28ab96b4-82ea-4d99-99fb-b320f60d0a58.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
BIN
pics/77931a4b-72ba-4016-827d-84b9a6845a51.png
Normal file
BIN
pics/77931a4b-72ba-4016-827d-84b9a6845a51.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
BIN
pics/85370d54-40d1-4912-bcbe-37a2481c861d.png
Normal file
BIN
pics/85370d54-40d1-4912-bcbe-37a2481c861d.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
Loading…
x
Reference in New Issue
Block a user