十、避免活跃性危险
1)死锁
死锁的产生:A线程持有锁1,尝试获取锁2,同时B线程持有锁2,尝试获取锁1。
避免死锁:使用支持定时的锁;控制锁顺序;多个锁转为1个锁。jstack -l pid可查看死锁。
2)饥饿
某个线程一直获取不到它所需要的资源,导致线程饥饿。常见的是cpu饥饿,一般不设置线程的优先级(因为本身也不是很靠谱)。
3)活锁
因为某些原因导致线程一直执行一个重复的操作。例如MQ的消费消息失败导致消息放回了MQ,然后又被消费到,如此循环。可以适当设置重试策略防止这种事情发生,例如可以重试3次,如果还是成功不了,则放到通知队列进行告警。
十一、性能与可伸缩性
1)线程引入的开销
-
上下文切换:线程切换时,线程所需要的数据可能不在处理器的本地缓存。
-
内存同步:内存栅栏会使缓存失效。
-
阻塞:两次上下文切换,用户态与内核态切换。
2)减少锁竞争
-
缩小锁的范围。能所一个代码块,就不锁一整个方法。
-
减少锁粒度。读写锁。
-
锁分段。1.7之前,ConcurrentHashMap使用分段所。1.7之后ConcurrentHashMap使用table的head作为同步对象。
-
避免锁热点。例如热点账户拆分子账户。
-
无锁。CAS原子操作。