Java 多线程常见问题
上下文切换
多线程并不一定是要在多核处理器才支持的,就算是单核也是可以支持多线程的。
CPU 通过给每个线程分配一定的时间片,由于时间非常短通常是几十毫秒,所以 CPU 可以不停的切换线程执行任务从而达到了多线程的效果。
但是由于在线程切换的时候需要保存本次执行的信息(详见),在该线程被 CPU 剥夺时间片后又再次运行恢复上次所保存的信息的过程就成为上下文切换。
上下文切换是非常耗效率的。
通常有以下解决方案:
- 采用无锁编程,比如将数据按照
Hash(id)
进行取模分段,每个线程处理各自分段的数据,从而避免使用锁。 - 采用 CAS(compare and swap) 算法,如
Atomic
包就是采用 CAS 算法(详见)。 - 合理的创建线程,避免创建了一些线程但其中大部分都是出于
waiting
状态,因为每当从waiting
状态切换到running
状态都是一次上下文切换。
死锁
死锁的场景一般是:线程 A 和线程 B 都在互相等待对方释放锁,或者是其中某个线程在释放锁的时候出现异常如死循环之类的。这时就会导致系统不可用。
常用的解决方案如下:
- 尽量一个线程只获取一个锁。
- 一个线程只占用一个资源。
- 尝试使用定时锁,至少能保证锁最终会被释放。
资源限制
当在带宽有限的情况下一个线程下载某个资源需要 1M/S
,当开 10 个线程时速度并不会乘 10 倍,反而还会增加时间,毕竟上下文切换比较耗时。
如果是受限于资源的话可以采用集群来处理任务,不同的机器来处理不同的数据,就类似于开始提到的无锁编程。