并发编程的挑战(Java并发编程的艺术)
1.上下文切换
CPU通过给每个线程分配CPU时间片来实现并发,切换过程中线程的信息从保存到再加载就是一个上下文切换。由于频繁的进行上下文切换,会消耗资源,所以并发不一定比串行快。
可以通过Lmbench3测量上下文切换的时长;可以通过vmstat测量上下文切换的次数。
减少上下文切换的方法:
(1)无锁并发编程,例如通过ThreadLocal,不变模式等;
(2)CAS算法;
(3)使用最小的线程;
(4)协程
2.死锁
有个叫 Coffman 的牛人早就总结过了,只有以下这四个条件都发生时才会出现死锁:
(1)互斥,共享资源 X 和 Y 只能被一个线程占用;
(2)占有且等待,线程 T1 已经取得共享资源 X,在等待共享资源 Y 的时候,不释放共享资源 X;
(3)不可抢占,其他线程不能强行抢占线程 T1 占有的资源;
(4)循环等待,线程 T1 等待线程 T2 占有的资源,线程 T2 等待线程 T1 占有的资源,就是循环等待。
避免死锁的方法:
(1)避免一个线程同时获取多个锁;
(2)避免一个线程在锁内同时占用多个资源,尽量一个锁一个资源;
(3)尝试使用定时锁,使用lock.tryLock(timeout)代替内部锁机制;
(4)对于数据库锁,加锁和解锁必须在一个数据库连接里面,否则会出现解锁是吧;
(5)如果必须使用锁锁住多个资源,例如转账,需要一次锁住转账账户、收款账户,可以用一定规则顺序锁定资源,即根据账户ID从小到大的规则,依次锁定账户,则可避免死锁;另外可以写个单例类来实现锁资源的申请、释放,代码示例如下:
class Allocator {
private List