在讲解深入学习Java并发编程的方法之前,先分析如下若干错误的观点和学习方法。
错误观点1:学习Java编程主要是学习多线程。
这话其实是说明了表面现象,多线程其实还真是并发编程的实现方式,但在实际高并发的项目里,程序员一般不会通过多线程去实现并发的需求,而是通过使用一些后文会提到的高并发组件来实现高并发的需求。
甚至可以这样说,线程方面的技能对实现高并发需求的帮助很少,与其用很多精力去学多线程并发,还不如花精力去学组件乃至应对高并发的集群。
错误观点2:为了学好并发编程,要深入了解相关算法和底层实现原理。
严格来说,这句话不能算错,但学习并发编程算法和底层原理时,要讲究学习的时机。
一般来说,学并发编程的过程是,会用组件以及API,能(通过日志)解决并排查相关问题,能搭建解决高并发的架构。在刚开始学并发编程乃至用并发编程技术干活时,其实没必要用大量的精力去了解算法和底层原理,只有当进阶成架构师乃至资深架构师时,才有必要学原理和底层源码,而且还不是全学,而是针对要解决的问题,针对性地看算法和源码。
错误观点3:单凭看书和看视频等资料,能学好Java并发编程。
通过看书看资料,确实能了解并发编程的一些技术和方法,但如果不实践,甚至连并发编程相关的API都用不好,更别论解决并发问题和搭建能应对高并发的框架。
而且,由于在个人电脑上不能模拟高并发场景,所以通过学习,只能掌握个皮毛,能学到能应对高并发面试的程度,如果学习方法不当,甚至连通过面试的程度都达不到。要学好并发编程,只能是先通过面试得到相关的实践机会,在项目中学。
学习Java并发编程的目的是,掌握应对类似双十一等场景的高并发技术,并能以此进入好公司,拿到更高的工资。从应用角度来看,Java并发编程包含了如下方面的技术。
1 在应对高并发需求的项目中,一般会把业务模块部署在多个服务器上,以此形成集群,用nginx等组件进行负载均衡,同时限流。
2 为了减缓因高并发请求对数据库产生的压力,一般会在系统里引入Redis缓存,甚至是Redis集群,或者用数据库主从集群来进行读写分离,或者引入MyCAT组件来进行分库分表,把一张大表拆分成若干子表,降低每次请求数据的数据样本。
3 系统里不同的模块处理请求的速度是不同的,比如订单模块一秒能处理5000个请求,而风控模块一秒才能出3000个,对此可以引入消息中间件,或者可以用消息中间件来异步处理业务。
4 为了应对高并发,系统里的模块一般不会用RestFul的方式相互调用,而会直接用RPC的方式,所以在学习Java并发技术的过程中,程序员还要掌握Dubbo以及对应的Zookeeper注册中心技术。
在实际项目开发过程中,实现高并发的方式是通过组件,进而用组件搭建集群,而不是程序员在理解限流熔断负载均衡等算法和原理的基础自己实现限流负载均衡和熔断等模块。
看到这里大家可以自己想一下,哪怕你根据一些所谓的学习路线,学会了final、violate、选举算法、多线程对象和锁的相关原理和算法等知识以后,其实用处不大,再啰嗦一下,在项目中还是没法入门并发编程,因为项目是靠组件,而不是算法和底层对象等来实现并发。
再具体地讲Java并发编程,分为如下几个层次。
1 了解理论,会用API开发功能,比如会用Redis的API缓存数据。
2 能根据项目的高并发需求,使用组件解决实际问题,比如会用Kafka组件实现高并发场景下的异步处理或消息缓存的流程,同时能根据实际需求,配置Kafka或dubbo等组件的参数。
3 使用组件实现高并发需求时,能解决遇到的实际问题。比如项目中遇到Netty或Dubbo方面的OOM问题,能通过查资料和查日志解决。
4 熟练掌握扩容、搭建集群和部署组件和业务模块的技术,能在一无所有的前提下,帮助项目组搭建一个能应对高并发的系统,同时能解决里面的问题。
其中对Java初级开发而言,最好需要掌握第一个层次的技能,对Java高级开发而言,需要掌握第二层次的技能,最好再要有一定的解决分布式组件问题的经验,即需要部分达到第三层次的标准。而Java架构师一定得达到第三层次的标准,至于第四层次的标注,是针对资深架构而言的。
讲到这里大家其实可以理解,平时在并发层面出现频率不少的锁、原子类或Synchronized等技术,其实是包含在Netty,Dubbo或Kafka等组件背后的原理或实现机制,学好了不能说没用,但也就是聊胜于无,或者说顶多面试时能用到,平时项目开发中未必会直接用到这些原理或底层算法。
举例来说吧,比如要实现高并发场景下的分布式锁,大多数项目的做法是用Seata等组件,通过配置和API实现分布式锁里的提交或回滚等功能,而绝不是自己根据二阶段提交等算法,自己设计开发一套实现分布式锁的组件。
上文用了不少篇幅,给出了项目组实现java高并发的一些方式以及实现并发编程的几个层次,在这基础上,大家就能很好地理解后文给出的Java并发编程的学习路径和学习技巧了。
讲到这里,本人忍不住要吐槽,其实在不少公司的项目里,程序员是只要求用增删改查的API开发业务,项目没有高并发的需求,对应的程序员其实是没有机会全面接触到高并发技术的,或者顶多用到些组件的API,或者是用到高并发组件或技术里的一部分。
在没有在项目实践中全面接触到Java高并发技术,并解决过高并发产线问题的前提下,认为并发编程技术就是Java多线程中的一些技术,或者是一些诸如线程互斥或调度等算法,这也是可以理解的,但本人好歹在一些大厂呆过,多少接触过一些高并发需求和技术,也多少解决过一些实际问题,所以下文给出的学习路径和学习技巧应当对大家有帮助。
1 一步步来,刚开始的时候,就学一些高并发组件的API,这里的组件是指Redis,nginx,dubbo,mycat,dubbo或zookeeper组件,或者是spring cloud alibaba体系下的nacos,sentinel,gateway,ribbon或seata组件,这是两个不同的方向,学好一个方向即可。
这里需要说明的是,学的时候绝不能但看理论,更要动手实践,如果项目里有用这些组件的机会,就从项目里学,否则的话,就在自己的电脑上搭建个环境学。
2 在自学阶段,由于缺乏项目实践机会,其实只能掌握到“熟悉组件API”的程度。有些程序员在工作中有分布式组件的实践机会,或者还能结合项目来学,但大多数程序员,真是因为在平时项目开发过程中只接触到基本技能,所以才要通过自学来掌握分布式高并发的技能。
自学阶段需要达成的目标一般不是熟练掌握高并发相关的实践技能,而是能在未来的面试中证明自己有高并发相关的项目经验。
3 通过自学组件或其他技能,能让自己掌握基本但必需的分布式高并发技能后,就要准备面试,争取挑战大厂或互联网公司的那些真正能提供高并发项目实践机会的职位。
在这个阶段,由于程序员一般只具有并发方面的理论知识,或是api技能,顶多外带些实际应用和解决相关问题的经验,所以在这个准备面试阶段,可以采用“项目嫁接”的技巧,即尽量在你当前做的项目里,结合该技术的项目使用情况,这样就能证明该并发技术项目实践经验,进而能证明大多数并发技术有项目实践经验。
比如对于基于redis的缓存技术,大家可以在熟悉基本缓存和读取数据的api前提下,为缓存找个项目需求落脚点,比如因为在本项目里,多次去查询用户数据会导致性能慢,所以会引入redis。引入后redis的键是什么,值是什么,同时设的超时是多少,解决过缓存穿透问题,同时用RDB的方式做redis持久化。
当然,在面试中证明并发技术的项目实践经验也是个互动的过程,你说了以后,面试官会问细节问技术,所以在为技术找好项目实践背景后,还要背题,比如背redis面试题,甚至去看redis背后的线程和缓存处理流程。但还是这句话:如果但学算法和细节,不证明相关技术的项目实践经验,一定没发通过面试。
4 通过了解并发技术外带按上文准备好面试说辞,当然再需要经过多次面试试错,程序员一般有机会找到能真正提供并发技术实践机会的公司和项目。
上文说了,这些公司和项目不多,但程序员如果准备得当,不是找不到这种机会,毕竟不少互联网大厂的面试机会摆在那里,而且也有不少程序员,甚至是初级开发的程序员通过准备面试技巧成功地进大厂。
当大家真正在互联网等公司全面接触到高并发技术以后,甚至都不用问,就能背项目推动着,自行不断地积累并深化java高并发技术,因为在这种项目里,不仅需要大量接触高并发的需求,还需要不断地搭建集群,同时解决限流熔断等实际问题,当然还需要不断解决并发方面的线上问题。
到了这个阶段,其实大家就能发现,我上文给出的一些并发学习的观点不能说没有道理,甚至可能有不少架构师会赞同我的上述论断。科学技术招标
1 那些java多线程、锁或并发对象,以及相关的算法和原理,不能说一点用都没有,可能一些资深架构在排查问题时,需要接触到这些底层源码或对象,但这已经是很后面的事情。但至少在学习并发技术的开始阶段,不需要用这些技术打基础。这些技术的价值顶多是让你背,让你以此过面试。
2 在开始阶段,学java并发技术就是学和用分布式组件,这句话虽然会引起争议,但通过分布式组件入门java并发技术,至少也是个学习路线。毕竟在真正的高并发项目里,是通过相关组件来实现限流,熔断和负载均衡等高并发需求的。
3 学并发技术可以分两阶段走,第一步通过项目或自学,了解组件或相关技术的基本api以及其他基本用法,随后通过相应的面试技巧,结合项目证明并发技术,以此找到能提供并发实践机会的项目,在项目中真正提升并发技术。