1 : 上下文切换
- CPU通过时间片分配算法来循坏执行任务,当前任务执行一个时间片后会切换到下一个任务,但是在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以加载这个任务状态
- 任务从保存到再加载的过程就是一次上下文切换
1.1 : 多线程一定快么
- 答案是不一定,线程有创建和上下文切换的开销,当并发操作不超过百万次的时候,并发执行耗时 > 串行执行耗时
1.2 : 测试上下文切换次数和时长
- 使用Lmbench3可以测量上下文切换的时长(Lmbench3是一个性能分析工具)
- 使用Vmstat可以测量上下文切换的次数,vmstat中的cs列表示上下文切换的次数
1.3 : 如何减少上下文切换
- 无锁并发编程 : 多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,可以用一些办法来避免使用锁,如将数据的ID按照hash算法取模分段,不同线程处理不同段的数据
- CAS算法 : Java的Atomic包使用CAS算法来更新数据,而不需要加锁
- 使用最少线程 : 避免创建不需要的线程,比如任务少,创建较多线程来处理,会造成大量线程都处于等待状态
- 协程 : 在单线程里实现多任务调度,并在单线程里维持多个任务间的切换
1.4 : 减少上下文切换实战
- 使用jstack命令dump线程信息
- 统计所有线程分别处于什么状态
- 打开dump文件查看处于WAITING(onobjectmonitor)在做什么
- 减少JBOSS的工作线程数,找到JBOSS线程池配置信息,将maxThreads降低
- 重启JBOSS,再dump线程信息
2 : 死锁
- 原因 : 线程T1在获得锁L1的时候,在没有释放锁L1的情况下,又去申请获得锁L2,线程T2已经获得了锁L2,在释放锁L2之前又得去访问锁L1
- 处理
- 避免在一个同步方法内调用其他对象的延时方法和同步方法
- 避免一个线程同时获取多个所
- 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源
- 尝试使用定时锁,使用lock.tryLock(timeout)来代替使用内部锁机制
- 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况
3 : 资源限制的挑战
- 资源限制 : 是指在进行并发编程时,程序的执行速度受限于计算机硬件资源或软件资源,硬件资源限制有带宽的上传/下载速度,硬盘读写速度,CPU的处理速度.软件资源限制有数据库连接等和Socket连接数等
- 资源限制问题 : 将代码速度加快的原则是将代码的串行执行部分换成并发执行,但是如果这段串行的代码并发执行,因为受限于资源,仍然在串行执行,这时候程序不仅不会加快执行,反而会更慢,因为增加了上下文切换和资源调度的时间
- 解决资源限制问题 : 对于硬件资源控制,可以考虑使用集群并发执行程序,对于软件资源限制,可以考虑使用资源池将资源服用,比如使用连接池将数据库和Socket连接复用,或者在调用对方webservice接口获取数据时,只建立一个连接
- 资源限制情况下并发编程 : 更具不用资源调整程序的并打度.
4 : 本章小结
并发下如果出现问题,应该使用JDK并发包提供的并发容器和工具类来解决并发问题,因为这些类都已经通过充分的测试和优化