背景:一个人的成长在于你经历了多少,正如古语有云“读万卷书,不如行万里路。”做技术尤其如此,要想快速成长,必须先多写代码,多思考,多总结,当然还可以通过帮助别人解决问题来验证或者激励自己的成长。今天这篇文章主要是基于一个朋友在实际开发中出现的一个并发案例,在帮助其解决的过程中,发现自己也有很多的知识误区,遂写此篇以作记录,同时也分享给大家。
一,案例描述
①,业务需求
用户通过系统生成邀请二维码图片分享给朋友,朋友通过扫描二维码关注公众号,记录扫码关注人数,当邀请关注人数达到十个时,用户将获得平台免费提供的学习电子书。
②,原代码实现逻辑
每当用户扫码关注公众号后,系统获得消息通知,这是先查询数据库做判断,如果目前通过扫码关注的人数少于10人,则进行++自增操作,否者提示用户,扫码关注已经达到十人,赠送电子书给用户。
③,问题描述
当出现多个人同时扫码关注的时候,出现并发问题,系统累计关注人数统计出现误差,实际关注人数大于10人,但是系统提示还是差一个人。如下是案例截图:
④,问题分析
当多个用户同时扫码时,向服务发送请求,此时tomcat容器默认是NIO的运行模式,通过线程池创建了多个线程,并开始执行业务代码,朋友的业务服务是基于SpringBoot实现的,当然SpringBoot Web就是基于SpringMVC来做的,通常在默认的情况下,SpringMVC的Controller控制器是单例模式,也就是说多线程并发的时候,控制器的成员变量都是共享的,此时如果产生并发的时候,业务代码是数据库的非原子操作,或者Java代码里面有非线程安全的代码块,没有考虑线程安全问题的话就可能会出现如上Bug。(在此,我也反省一下,当时我第一反应就是,这个线程安全问题应该是 出现在++自增操作运算符,因为在Java里面自增运算符是非线程安全的,哎,发现自己还是 too young too simple ! )。
二,技术延伸
多线程并发安全问题一直困扰着广大的程序员,但是其实如果,我们搞清楚了“背后的故事”其实也就那么回事,所以在讲解决方案之前,我还是抛砖迎玉给大家稍微科普一下基础知识 。
名词解释:
多线程:多线程是指,对于一个进程,同一时刻上下文环境中存在大于1的线程数量,通常我们就称之为多线程。
并发:并发是指,在同一时间区间内,有多个任务在同时进行,通常称之为并发。(当然这个代表个人观点,参考资料源于黄文海老师的《多线程编程实战指南》)。
并行:并行是指,在同一时刻,有多个任务同时进行,通常称之为并行。(当然这个代表个人观点,参考资料源于黄文海老师的《多线程编程实战指南》)。
线程安全:当多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到