一. 锁优化
针对于synchronized获取锁方式,jvm使用了锁升级的优化方式,就是先使用 **偏向锁**,优先同一线程然后再次获取锁,如果失败,就升级为 **CAS轻量级锁**,如果失败就会短暂**自旋**, 防止线程被系统挂起, 最后如果都失败就升级为 **重量级锁**.
因此总结了以下几点经验:
1.减少锁的时间
不需要同步执行的代码,能不放在同步代码块里就不要放在同步代码块里,可以让锁尽快释放.
2.减少锁的粒度
共享资源数决定锁的数量,有一组资源定义一把锁,而不是多组资源公用一把锁,增加并行度,从而降低锁竞争,典型如分段锁..
3.减少加锁的次数
假如有一个循环,循环内的操作需要加锁,我们需要把锁放到循环外面,否则每次进出循环,都要加锁.
4.使用读写锁
业务细分,读操作加读锁,写操作加写锁,只能单线程写,参考计数器案例
例如: 原来实现方式是采用 Reentrantlock + map实现, 可以改为ReentrantReadWriteLock + ConcurrentHashMap进行优化
ps: 使用场景是 -->读多写少
5.善用volatile
volatile的控制比synchronized更强量化,在某些变量上不涉及多步打包操作,和原子性问题,可以加以运用..如ConcurrentHashMap的get操作,而不是加锁.
二. 线程池参数优化
1.corePoolSize
核心线程数, 一旦有任务进来,在core范围内会立即创建线程进入工作,所以这个值应该参考业务并发量在绝大多数时间内的并发情况,同时分析任务的特性.
高并发执行时间短的,要尽可能小的线程数,如配置cpu个数+1,减少线程上下文切换,因此他不怎么占时间,让少量线程快跑干活.
并发不高,任务执行时间长的要分开看: 如果时间都花在了io上,那就调大CPU,如配置两倍的CPU个数+1,不能让CPU闲下来,线程多了并行处理更快.如果时间都花在了运算上,运算的任务还很重,本身就很占CPU,那尽量减少CPU,减少切换时间.参考第一条...
2.workqueue
任务队列,用于传输和保存等待执行任务的阻塞队列,这个需要根据你的业务可接受的等待时间.是一个需要权衡时间还是空间的地方,如果你的机器CPU资源紧张,jvm内存够大,同时任务又不是那么紧迫,减少coresize,加大这里,如果你的CPU不是问题,堆内存比较敏感比较害怕内存溢出,同时任务要求快点相应,那么减少这里..
3.maximumPoolSize
线程池最大数量,这个值和队列搭配使用,如果你采用了无界队列,这个参数失效,同时要注意,这个队列盛满,同时达到max的时候,再来任务可能会丢失,
如果你的任务波动比较大,同时对任务波峰来的时候,实时性要求比较高,也就是来的时候很突然并且都是着急的,那么调小队列,加大这里, 如果你的任务不着急,可以慢慢做,那就扔队列吧.
队列与max是一个权衡. 队列空间换时间,多花内存少占cpu,轻视任务紧迫度,. max舍得cpu线程开销.少占内存,给任务最快的响应.
4.keepalivetime
线程存活的时间,超出该时间后,线程会从max下降到core,很明显,这个决定了你养闲人所话的代价,如果你不缺cpu,同时任务来的时间没法琢磨,波峰波谷间隔比较短.经常性的来一波. 那么适当的延长销毁时间,避免频繁创建和销毁线程带来的开销. 如果你的任务波峰出现后,很长一段时间不在出现,间隔比较久,那么适当的调小该值,让闲着不干活的线程尽快销毁,不要占据资源.
网友评论