并发编程
并发本质是cpu处理器在时间片内来回切换执行线程,因为时间片都是毫秒级别的,所以给用户的感觉是多个应用在同时运行。并发编程最直观的感受就是让应用同时支持更多的用户且响应的更快。
并发编程带来的问题
并发编程让人第一联想到的就是多线程,多个线程同时启动,执行多个任务。多线程的执行必然带来线程上下文切换的问题,于是有了线程池,减少线程的创建,销毁,带来的开销。多个线程访问同一资源时,又带来资源安全问题,要确保临界资源的安全性,于是有了锁,线程持锁才能访问临界资源。除了这些,资源的限制也会影响程序运行。举一个极端的例子,现在要在1T的文件中找出满足要求前五的数据,这不是多开几个线程就能解决的,于是有了分布式计算,只能大事化小,小事化了。
并发编程之无锁
并发是为了提高应用运行速度,加锁相对于无锁还是带来更多的资源开销。所以,在一些特定应用场景下,可以试着采用无锁的方式解决并发编程问题。
比如小麦之前去面试,面试官问:订单量特别大时,用oracle数据库序列生成唯一订单号,如何才能更快生成序列?
小麦第一感觉就是,一个序列不够用,咱多来几个序列,每个序列缓存尽量大一些,多个序列号采用不同的步长。这样订单号生成无疑会更快一些,且订单号唯一。
并发编程之锁的粒度最小化
如果无法避开使用锁时,那尽量减小锁的粒度。比如给数据库表使用行级锁,把锁加在同一行上,相比使用表级锁,能给我们带来更大的并发。Java中ConcurrentHashMap在大数据量下,性能要远远优于同样安全的HashTable也是类似的路子,Hashtable为整个map加上了锁,数据量大时性能无疑会下降,而ConcurrentHashMap使用分段锁,锁的力度小了,允许操作的数据范围扩大了,并发量也就上去了。
并发编程之分布计算
对于大的任务,可以采用分而治之的策略,比如Java的Fork/Join组件,可以把一个大任务分解成多个子任务,并把每个子任务的结果汇总到最终结果。小麦在刚接触这个框架的时候,就有一种似曾相识的感觉。类似HDFS分布式文件系统,把一个文件拆分成多个子文件,类似hadoop的MapReduce。
类似的数据库分库分区分表也是这种策略,既然一个表数据太大,导致响应变慢,可以横向拆分,把记录按条件分区,把大数据记录表化为多个小数据记录表;也可以根据业务数据的更新频次,把数据库表按列纵向分表,把多字段表化为几个少字段表,总之就是把表变小,提高响应。
并发编程之服务降级
并发编程虽然是为了程序更快的响应,但是每台服务器可承受的压力终归是有限的,为了一时的流量高峰成倍去增加服务器也非长久之计。所以,必要的时候,为了应对高并发,并保护服务器正常持续工作,需要做一些服务降级。常见的服务降级有,延迟加载,排队等待,限流分流,拒绝服务等。
常见应用表现:活动太火爆了,请稍后再来--可能已经直接把你的请求拒绝了;您好,小X正在拼命加载--拼命加载还是半天没出来,可能你的请求正在排队等待当中。