摘要
老J是中国IT的见证者与参与者,他的技术体系虽然不是很适合如今的情况,但他对于数据库的设计、Java底层等有很多独到的见解和丰富的实践经验,让我深受启发。
正文
老J的技术分享之总结
老J做IT这块有二十多个年头了,算是中国IT的见证者与参与者。
那个时候刚开始接触和了解时,对于他的一些建议,我不是很乐于去接受,因为我觉得他的那一套技术体系不是很适合如今的情况,当时间久了后发现,他对于数据库的设计、Java底层等有很多独到的见解和丰富的实践经验等,还是很值得学习和借鉴的。以下是老J的技术分享(其中也有我自己的一些分享进行补充完善),由于时间比较长,我只能记的个大概,不过好在当初做了一些笔记。
一、线程急需避免的两类情况
- 死锁;
- 死循环。
1.死锁
(1)什么是死锁?
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程成为死锁进程。
(2)产生死锁的原因主要包括哪些?
- 系统资源不足;
- 程序执行的顺序问题;
- 资源分配不当。
(3)产生死锁的四个必要条件有哪些?
- 互斥条件:一个资源每次只能被一个进程使用。
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:进程已获得资源,在未使用完之前,不能强行剥离。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发现死锁。
(4)如何预防死锁?
由于互斥条件是非共享设备所必须的,不仅不能改变,还应该加以保证,因此,主要是破坏产生死锁的后三个条件:
- 破坏请求与保持条件:对一个进程在请求资源时,它不能持有不可剥夺资源。
- 破坏不可剥夺条件:对一个已经保持某些不可剥夺资源的进程,提出新的资源请求而不能得到满足时,它必须释放已经保持的所有资源,待以后需要时再重新申请。
- 破坏循环等待条件:对系统所有资源类型进行线性排列,并赋予不同序号。规定每个进程必须按序号递增的顺序请求资源。
(5)避免死锁的方法有哪些?
- 一次封锁法:每个进程(事务)将所要使用的数据全部加锁,否则,就不能继续执行。
- 顺序封锁法:预先对数据对象规定一个封锁顺序,所有进程(事务)都按这个顺序枷锁。
- 银行家算法:保证进程处于安全进程序列。
(6)哪些方法有助于最大限度地降低死锁的概率?
- 按同一顺序访问对象;
- 避免事务中的用户交互;
- 保持事务简短并在一个批处理中;
- 使用低隔离级别。
2.死循环
(1)什么是死循环?
在编程中,一个靠自身控制无法终止的程序称为“死循环”。
(2)死循环的危害有哪些?
- 程序进入假死状态(当某个请求导致的死循环,该请求将会在很大的一段时间内,都无法获取接口的返回,程序好像进入假死状态一样);
- CPU使用率飙升(代码出现死循环后,由于没有休眠,一直不断抢占cpu资源,导致cpu长时间处于繁忙状态,必定会使cpu使用率飙升);
- 内存使用率飙升(如果代码出现死循环时,循环体内有大量创建对象的逻辑,垃圾回收器无法及时回收,会导致内存使用率飙升。同时,如果垃圾回收器频繁回收对象,也会造成cpu使用率飙升问题);
- StackOverFlowError栈溢出(在一些递归调用的场景,如果出现无限递归,最终会报StackOverflowError栈溢出,导致程序直接挂掉)。
(3)哪些场景会造成死循环?
- 一般循环遍历(for、foreach、while);
- Iterator遍历;
- 类中使用自己的对象;
- 无限递归;
- HashMap使用不当;
- 动态代理使用不当。
(4)写代码如何避免死循环?
对于Java程序员而言,需要严谨地对待自己写的Code(不可太过随意,代码逻辑严谨性体现编写者的思维逻辑),同时要深入地熟悉Java,注重基本功,这样一来就能避免写死循环代码。
二、缓存
1.缓存是什么?
缓存就是数据交换的缓冲区(称作Cache),当某一硬件要读取数据时,会首先从缓存中查找需要的数据,如果找到了则直接执行,找不到的话则从内存中找。由于缓存的运行速度比内存快得多,故缓存的作用就是帮助硬件更快地运行。
2.为什么要使用缓存?
缓存的本质就是用空间换时间,牺牲数据的实时性,以服务器内存中的数据暂时代替从数据库读取最新的数据,减少数据库IO,减轻服务器压力,减少网络延迟,加快页面打开速度。
3.在Java中对于缓存有哪些现成的解决方案?
- EhCache(Java分布式缓存框架);
- Cacheonix(高性能Java分布式缓存系统);
- ASimpleCache(轻量级Android缓存框架);
- JBoss Cache(基于事务的Java缓存框架);
- Voldemort(基于Java开发的分布式键-值缓存系统,像JBoss Cache一样,Voldemort同样支持多台服务器之间的缓存同步,以增强系统的可靠性和读取性能)。
其中我仅仅只使用过Ehcache,关于Ehcache的使用,感兴趣的朋友可以参考我的这篇博客:
SSM框架之整合EhCache
4.Ehcache和Redis的区别又什么呢?
- 概念不同(Redis是一个基于内存的高性能key-value数据库,而EhCache是纯Java进程内缓存框架);
- 运行程序不同(Redis独立运行程序需单独安装与Java程序间接关联(如SpringBoot需要操作Redis等),而Ehcache与Java程序直接绑定在一起,Java程序挂掉,Ehcache也会随之挂掉,而Redis却能保持完好);
- 场景不同(Redis适合分布式缓存,而EhCahce仅适合单个应用缓存)。
5.现成解决方案的利与弊有哪些?分情况来看。
(1)EhCache
a.EhCache的优点有哪些?
- 快速;
- 简单;
- 缓存数据有两级:内存和磁盘,因此无需担心容量问题;
- 缓存数据会在虚拟机重启的过程中写入磁盘;
- 可以通过RMI、可插入API等方式进行分布式缓存;
- 具有缓存和缓存管理器的侦听接口;
- 支持多缓存管理器实例,以及一个实例的多个缓存区域;
- 提供Hibernate的缓存实现;
- 多种缓存策略,Ehcache提供了对大数据的内存和硬盘的存储,最近版本允许多实例、保存对象高灵活性、提供LRU、LFU、FIFO淘汰算法,基础属性支持热配置、支持的插件多。
b.EhCache的缺点有哪些?
- 使用磁盘Cache的时候非常占用磁盘空间:这是因为DiskCache的算法简单,该算法简单也导致Cache的效率非常高。它只是对元素直接追加存储。因此搜索元素的时候非常的快。如果使用DiskCache的,在很频繁的应用中,很快磁盘会满。
- 不能保证数据的安全:当突然kill掉java程序的时候,可能会产生冲突,EhCache的解决方法是如果文件冲突了,则重建cache。这对于Cache数据需要保存的时候可能不利。当然,Cache只是简单的加速,而不能保证数据的安全。如果想保证数据的存储安全,可以使用Bekeley DB Java Edition版本。这是个嵌入式数据库。可以确保存储安全和空间的利用率。
(2)Cacheonix
a.Cacheonix的优点有哪些?
- 可靠的分布式Java 缓存;
- 通过复制实现高可用性;
- 支持泛型的缓存API;
- 可与ORM框架集成;
- 使用数据分区实现负载均衡;
- 支持非多播网络;
- 高性能计算;
- 快速的本地Java缓存;
- 分布式锁机制。
b.Cacheonix的缺点有哪些?
我觉得最大的缺点是网上相关的资源太少,哪怕我用Google搜索也如此,同时该项目start或fork实在是太少,间接说明应用的不是很多。
这里就讲上面两个,另外三个就不讲了,意义不大。
6.缓存的优缺点有哪些?
前面提到过缓存的优点,如减少数据库IO、减轻服务器压力、减少网络延迟、加快页面响应速度等。
那么缺点有哪些呢?凡是有利也有弊,如下:
- 存放内存的数据可能丢失(内存一断电就会清空数据);
- 缓存的数据可能与数据库中的不一致;
- 内存的成本高;
- 内存容量相对硬盘而言要少。
7.Java中常用缓存机制有哪些?
- 内部(如HashMap以及前面提到的缓存框架等);
- 外部(如Redis、Memcache等)。
8.缓存实际的应用场景分为哪几类?
- CPU缓存(是位于CPU与内存之间的临时存储器,它的容量比内存小的多但是交换速度却比内存要快得多);
- 浏览器缓存(浏览器可以缓存一些静态资源,比如图片、js、css等,这些都是不常变化的内容,所以没有必要每次都去请求);
- CDN缓存(客户端会先检查浏览器的缓存,若缓存过期则会像CDN发送请求,CDN检查缓存数据还未过期,那么直接返回响应,只需两步搞定。但是,CDN缓存过期,那么需要向应用服务器发起请求,获得新的数据响应,这部分新的数据按一定的缓存策略会选择是否缓存在CDN中);
- 数据库缓存(存入数据库的数据具有持久化特点,所谓持久化就是将对象保存到可永久保存的存储设备中(通常是磁盘));
- 业务层缓存(针对具体的业务做数据缓存,如博客的排行榜、点赞功能、队列(排队处理)、分布式锁或单线程机制等)。
三、客户端与服务端数据交互
1.数据传输的格式有哪些?
二进制、文本、json、xml等。
2.数据传输格式的效率比较,哪个效率最高?
二进制>文本>json>xml(从左往右,效率逐渐降低)。
3.聚焦常用-JSON
JSON对于前后端开发人员来说,是每天都要提到的。
(1)JSON为什么这么流行?应用场景有哪些?
a.JSON为什么这么流行?如下原因:
- 简洁、简单、体积小(相对XML而言);
- 上手容易,高效;
- 跨语言(与具体的编程语言无关)。
b.应用场景有哪些?这里只列举主要的,即接口返回数据:
- Ajax异步交互;
- RPC远程调用;
- 前后端分离-后端响应数据;
- API开放;
- 企业间的数据合作。
(2)Java中的JSON解析库有哪些?
- Json-Simple;
- Gson;
- FastJson;
- Jackson。
(3)从适用场景出发,我们应该如何使用合适的JSON解析库?
首先选择一个合适的JSON库要从多个方面考虑,例如:
- 字符串解析成JSON性能;
- 字符串解析成JavaBean性能;
- JavaBean构造JSON性能;
- 集合构造JSON性能;
- 易用性。
网上关于Json-lib、Gson、FastJson、Jackson的效率比较有很多,这里不再赘述。
a.为什么要使用Gson,它有哪些优点?
- gson支持更深层次关联查询;
- gson比json更简单;
- 异步传值非常方便;
- 页面上eval之后可以直接用 xx.xx 的方式取值;
- 减少格式错误导致的程序异常。
b.为什么使用FastJson,它有哪些优点?
- 速度快;
- 使用广泛;
- 测试完备;
- 使用简单;
- 功能完备。
b.为什么使用Jackson,它有哪些优点?
- 解析大文件的速度比较快;
- 运行时占用的内存比较少,性能更佳;
- API很灵活,容易进行扩展和定制。
d.某位支付宝技术专家这样概括Json-Simple、Gson、Jackson的适用场景?
- 如果你的应用经常会处理大的JSON文件,那么Jackson应该是你的菜。GSON在大文件上表现得相当吃力。
- 如果你主要是处理小文件请求,比如某个微服务或者分布式架构的初始化,那么GSON当是首选。Jackson在小文件上的表现则不如人意。
- 如果这两种文件你都经常会处理到,那么JSON.Simple对此类场景则更为适合。在不同的文件大小上Jackson和GSON的表现都不太好。
四、框架
1.框架是什么?
这里引用维基百科的概述:
软件框架,通常指的是为了实现某个业界标准或完成特定基本任务的软件组件规范,也指为了实现某个软件组件规范时,提供规范所要求之基础功能的软件产品。
框架的功能类似于基础设施,与具体的软件应用无关,但是提供并实现最为基础的软件架构和体系。软件开发者通常依据特定的框架实现更为复杂的商业运用和业务逻辑。这样的软件应用可以在支持同一种框架的软件系统中运行。
简而言之,框架就是制定一套规范或者规则(思想),大家(程序员)在该规范或者规则(思想)下工作。或者说使用别人搭好的舞台来做编剧和表演
2.为什么要使用框架(从软件层面出发)?
用前创业公司经理经常说的一句话来概括,不要重复造轮子。
重复造轮子意味着花大量的时间做一些事情(而这件事情已经有了现成的解决方案),效率低,极度消耗时间。
反之,”不重复造轮子”意味着不必从头再来,而是站在巨人的肩上,这样一来,效率高,耗时少。这里从中也体现了框架一个重要特点:重用性。曾经二次开发的电商系统、在线教育、OJ测评、VsCode就是一个很有力的证明。
3.使用框架能带来哪些好处(从软件层面出发)?
- 重用代码,提高开发效率,节约时间;
- 简单易上手(例如SpringBoot简化了过去SSM或SSH框架一堆配置文件);
- 程序安全性高(前人已经考虑到了,无需再从头过一遍,当然了这不是绝对,毕竟技术不断更新迭代,黑客攻击的手段也亦如此);
- 统一的套路(框架在一定程度上统一了编码规范,降低了维护成本)。
4.使用框架所带来的弊端?
- 约束性(受到框架本身的制约);
- 泯然众人矣(大家都会,一定程度上造就了软件开发流水线,降低了门槛,加剧了内卷);
- 只会用而不懂工作原理(普遍性,面试的时候才会看,平时大多很少看);
- 兼容性(例如A框架和B框架存在冲突)。
5.这是一道思考题,工作三年或五年以上的人和刚工作的童鞋区别到底在哪?仅仅是工作经验上的积累吗?
老J在此提出,关键在于框架。框架包含的种类有很多?例如思维框架(不局限于当前)、工作框架(提高个人的工作效率或团队工作效率等)、为人处事框架(如何与人更好的打交道以及面对生活中的突发或非突发事情又是如何处理的等)。框架不仅仅是经验上的积累,更是从经验上提炼的精华,这种精华可以叫原则。
6.我对自己在研发上面的框架进行简要概率,有这么几个?
- 技术选型框架(如何做技术选型等);
- 技术实践框架(如何提高开发效率、如何有效地排查问题和解决问题、如何更快的学习新技术或深入理解现有技术等);
- 文档框架(如何写好文档);
- 设计框架(如何做好数据库的设计);
- 运维框架(从单体到分布式微服务的运维体系构建与实践等);
- 业务知识框架(所待的IT行业涉及的业务知识积累,如何更快的熟悉业务等)。
五、四大注意事项思考
老J提出四大注意事项:
- 资源消耗(文件、DB、IO、网络等)不能放入for循环。
- 能并行不要串行。
- 能触发不做结束。
- 事件监听要统一。
1.资源消耗(文件、DB、网络等)不能放入for循环
关于资源消耗文件如文件的输入输出、DB的增删改查、网络通信等相关代码,最好不要放入for循环中。以我工作中的观察与实践,看到的例子如下(仅仅列举几个,不做过多的列举):
- (1)服务之间的通信,例如A服务调B服务,而B服务里有个for循环需要根据A服务的传参来反复调用C服务,C服务不断的访问数据库拿到所需数据,会造成CPU飙升,如果同时多人访问,可能会造成系统假死(响应慢或迟迟得不到响应并陷入卡顿状态);
- (2)文件的读写,例如在for循环中不断创建文件并写入内容,直接会导致物理硬盘满了,硬盘一旦满了,直接会影响服务器上的所有微服务,导致所有微服务假死,虽然实际并未挂掉,但已停止对外服务,因为服务之间的通信也是需要消耗磁盘的,关于这个例子,可以阅读我写的这篇文章:Linux设备上没有空间之复盘;
- (3)DB的CRUD,例如前段时间公司一同事将代码从6到8个小时优化到几分钟,之所以变快了是因为将for循环代码进行优化,不在for循环中做DB的操作(关于这个例子,可以阅读我写的这篇文章:如何写好对外的API微服务)。
2.能并行不要串行
并行和串行都是通讯中的数据传输方式。
(1)什么是并行?
并行就是同事执行,就好比单个窗口有3个人要办事,只需要到空窗口即可立即办事。在计算机中,同一时刻,有多条指令,在多个CPU上执行,就是并行。
(2)什么是串行?
串行就是按顺序执行,好比银行只有1个窗口,有3个人要办事,那么必须排队,只有前面的人办完走人才可以继续下一个。
在计算机中,同一时刻,只能有一条指令,在一个CPU上执行,后面的指令必须等到前面指令执行完毕才能执行
通过解释并行和串行的概念,大家就能很容易得出一个结论,并行的效率高于串行。
3.能触发不做结束
这句话我不是很懂。但触发对于前端开发而言非常熟悉,例如鼠标点击事件、键盘点击事件等。从某个角度上看,由用户自己决定触发什么样的事件获取什么样的展示信息,一定程度上降低资源不必要的消耗。
4.事件监听要统一
(1)什么是事件监听?
事件监听可以理解为是一种观察者模式,有数据发布者(事件源)和数据接受者(监听器)。
(2)为什么要事件监听?
以Java文件监听器为例(文件监听也是事件监听的一种),文件监听器监听文件目录的生成或销毁。
假如我们不用文件监听器的话,可能我们就得写一个程序每时每刻监听着某个文件目录的生成或销毁,那样的话,太消耗程序所运行环境的CPU和内存了。
(3)事件监听的应用场景有哪些?
- 项目的初始化;
- 网站的在线人数统计;
- 文件状态的监听;
- 网站的用户行为;
- 网站的访问量。
(4)事件监听为何要统一?
统一是为了便于管理,事件监听如果不统一,会影响系统的稳定性。
六、全局思维
1.什么是全局思维?
全局思维是一切从系统整体及其全过程出发的思想和准则,从客观整体的利益出发,站在全局的角度看问题、想办法,做出决策。
2.为什么需要全局思维?
- 从不同的角度思考问题,能更接近问题的本质,找到规律和解决办法;
- 追求长远利益,避免蝇头小利蒙蔽双眼。
3.如何培养全局思维(某位管理顾问提供了如下方法)?
- 用心想(每天睡觉前花个十分钟,用心想一下:我今天做的事与全局性有什么关系,产生了什么价值,有什么需要改进的地方。天长日久,全局性思维就慢慢建立起来了);
- 事上练(做事的时候,心中牢记目标,偏离方向了立即调整);
- 多交流(和有全局思维的人多交流(不仅仅是有全局思维的人,与不同领域或某一项精深的人多多交流),听听他的反馈。用心想一想,我应该如何改进)。
3.程序员思维
(1)什么是程序员思维?
在理性思维的框架下,利用相应工具,来解决相应实际的问题。
例如有位知乎朋友这样概括程序员思维:
职业化的程序员会养成一种思维定式,在做任何事之前会去思考:“能不能将这件事中需要重复执行的部分抽象出来?能不能不用人工参与就可以完成?”
(2)程序员思维的优缺点有哪些?
a.优点
- 理性(基于逻辑思考问题,对就是对,错就是错,不掺杂主观);
- 实用主义(不管黑猫白猫,能抓住耗子就是好猫);
- 做事情更注重步骤、效率、可行性。
b.缺点
- 过去追求完美,死扣细节,钻牛角;
- 过于理性;
- 过于实用主义;
- 习惯于机器打交通,不善于与人沟通;
- 习惯单兵作战,易忽略团队合作。
(3)如何突破程序员思维的定势?
- 学会透过技术发现问题的本质(技术的目的在于问题的解决,必须要明确这一点);
- 像专家一样给出意见(参加需求会或其他讨论会思考并给出自己的意见);
- 从不同的视角出发(可能要求你你具备该领域不同岗位的基本职能如运维、测试、开发、项目实施、产品经理、UI设计等,不一定要做到精通,一个领域精通很难,更何况多个领域);
- 提高创造力(可以理解为创新,这样的创造力建立在长年不间断地编码实践、持续地学习借鉴、定期复盘的条件上);
- 提高全局思维(前面提到过,这里不在赘述)。
七、总结
此次老J的技术分享之总结包含六个方面分别为:
- 线程急需避免的两类情况;
- 缓存;
- 客户端与服务端数据交互;
- 框架;
- 四大注意事项思考;
- 全局思维。
其实技术领域的知识远远不止这些,但更重要的是深入其中,并结合过往的实践经验进行复盘。
本次分享就写到这,希望对大家有一定的帮助。
关注不迷路
扫码下方二维码,关注宇凡盒子公众号,免费获取最新技术内幕!
评论0