并发编程简介

从手机说起

由于智能手机横行!!!所以垃圾软件横行!!!

做垃圾软件呢,又和做网站有一点小小的区别,由于软件装在用户的手机上,而且还可以 后台运行 ,所以只要安装了软件,就经常有很多连服务器的小动作比如,消息推送、心跳维持、当然了,还有恶意获取用户信息上传,肉鸡操控等等。

之前做网站,普通的网站一般也不会考虑并发过大的问题,比如小米的抢购、12306抢票等头疼的问题一般的网站也不会太多考虑,大不了在集群上多扩几台么。

现在情况有变,如果你的手机应用有一定的用户量,那么可能平时的压力就和网站时代的高峰压力相当了。如果你还是用之前的 Web 服务器,比如 Apache、 Tomcat 之类的,很容易就会遇到阻塞。

资源利用率

比如现在广泛应用的 Tomcat,默认配置是 200connection,如果同时有 500 个用户访问,那么200个用户可能会 1秒 的时候收到请求的响应,(收到后200线程被释放,处理剩下的300个请求),然后,200的用户等了两秒钟后,收到了响应。假设我们设置的服务超时时间为2秒,那么剩下100个,就会出现错误,请求失败。在这两秒种的时间,服务器仅处理了400个请求,还有100个请求在等待2秒后还被拒绝服务(超时),虽然说用户感觉到很慢,但实际上计算机的计算能力可能运用了不到 1% ,多余的性能并没有发挥出来。

聪明的你,肯定会想到,那我把默认最大线程数配大点不就行了吗,当然是 OK 的,比如配置到 500,那么这 500 个请求就不需要等待就可以马上响应了,此时,1秒钟处理了500个请求,CPU利用率肯定高出了之前的。但实际上如此盲目的增加通常会出现问题。

本质情况是,Tomcat,Apache之类的服务器处理一个请求,自身需要耗费比较多的资源。虽然本身也作了很多优化,但还是不太适合应用于大范围的通信,就拿上面那个请求来说,自己用纯 Java 写个多线程并发管理,可能会比服务挂在 Tomcat 上有好几个数量级的提升。 在这种场景下,你花好几台服务器作的一个 tomcat 集群可能也比不上一个比较好的并行处理请求的服务器。

C10K问题

C10K Problem ,也就是 Connect 10k = 1W 个连接下,目前现有的大半部分的应用可能都会无法处理,最常见的就是Web应用,上面也说到了这个问题,这些问题当然是早就存在早就知道了的,之所以现在才变得热门,当然就是移动互联网、微服务、云架构等模式需要一个应用去承载更多的通信量。

再比如聊天室、消息通知类似的应用,如果是使用长连接实现,16G内存情况下,一般一个长连接算内存占用2M(这个和使用的技术栈及模型有很大关系,比如Golang,nodejs等新兴语言原先就会优化的比较多,基于C的nginx也处理的不错),极限也就差不多能容纳10000个用户同时在线(保持长连接在线)。

对于长连接的应用场景,并发并不能解决问题,所以这里并不谈太多,个人认为像QQ微信这样的应用,一台机连索引都绘制不全,就别说维护了,如果你的业务并发量达到了10K以上,需要根据级别制定不同的算法,通常都是hash归类,固定配址等等,在这个级别上,可能不会有现成的如WEB框架之类的,但也会有些并发的较为基础的框架,你需要自己构建模型,通信规范等,才能更贴合业务需要。

又比如说邮件发送,我搞个促销活动,对我的100W注册用户发送个邮件通知

参考性能排行榜 https://www.techempower.com/benchmarks/

Java 并发编程

并发编程可以更大程序的利用计算机资源,避免性能浪费,常见的有并发编程框架也有很多,如 NettyVert.x 等,但如果你不懂一些 Java 的并发编程基础理念,实际上也很难看明白这些框架到底是怎么回事。

在 Java 里面,并发编程的工具基本上都放在 java.util.concurrent 包下面,

主要作用
Runable 实现此接口有让线程具有被 Run 的能力
Callable 实际此接口让线程具有被 Call 的能力,并且可以有返回值,通过 Future 接口接收
ThreadPoolExecutor 常用的线程池管理
ScheduledThreadPoolExecutor 带有计划任务的线程池管理
Semaphore 限制并发线程的数量
Exchanger 可以使两个线程之间传输数据
CountDownLatch 计数器的线程策略,支持同步计数等功能
CyclicBarrier 周期线程策略,支持阶段性同步
Phaser 多段的线程策略,可以控制线程的执行批次阶段
ExecutorCompletionService 这个类具有边产生新任务、边处理完成任务的结果的能力
ExecutorService 有执行各种线程的能力
ForkJoinPook fork-join 式的任务池管理 管理 ForkJoinTask

本想写点代码展示一下,但还是嫌麻烦(太懒了), 官方文档里面有 Demo,而且实际情况下,你不一定要精通它们,因为你可能会直接基于 Netty 之类的框架进行编写应用,但还是熟悉一下基础为好。

多线程编程要考虑的问题要比普通编程多很多,常见的有 阻塞与非阻塞 问题,线程排序 问题 ,各种异常等等,后续有空会另写文章展开。