上下文切换
上下文切换发生于计算机CPU从一个进程或线程切换到不同的进程或线程。
上下文切换允许一个CPU处理多个进程或线程而不需要额外的处理器。任务允许多任务的操作系统允许同一时间运行不同的进程。有3种典型的场景必须使用上下文切换。
- 多任务 (Multitasking)
- 内核/用户模式切换 (Kernel/User Switch)
- 中断 (Interrupts)
多线程不一定快
public class ConcurrencyTest {
private static final long count = 10000;
public static void main(String[] args) throws Exception {
concurrency();
serial();
}
private static void concurrency() throws InterruptedException {
long start = System.currentTimeMillis();
Thread thread = new Thread(new Runnable() {
public void run() {
int a = 0;
for (long i = 0; i < count; i++) {
a += 5;
}
}
});
thread.start();
int b = 0;
for (long i = 0; i < count; i++) {
b--;
}
thread.join();
long time = System.currentTimeMillis() - start;
System.out.println("concurrency: " + time + "ms, b=" + b);
}
private static void serial() {
long start = System.currentTimeMillis();
int a = 0;
for (long i = 0; i < count; i++) {
a += 5;
}
int b = 0;
for (long i = 0; i < count; i++) {
b--;
}
long time = System.currentTimeMillis() - start;
System.out.println("serial: " + time + "ms, b=" + b);
}
}
测试机:i5-6500 CPU @ 3.20GHz, 8GB RAM
测试结果:
循环次数 | 串行执行耗时 /ms | 并发执行耗时 | 并发比串行快多少 |
---|---|---|---|
1w - 100w | 0 | 10 | 慢 |
1000w | 10 | 10 | 差不多 |
10000w | 70 | 37 | 约1倍 |
100000w | 668 | 361 | 约1倍 |
当循环次数较少的情况下,因为线程上下文切换有开销,导致多线程比串行要慢一些。
减少上下文切换
- 无锁并发编程
- CAS算法
- 使用最少线程
- 协程
死锁
避免死锁方法
- 避免一个线程同时获取多个锁
- 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源
- 尝试使用定时锁 Lock.tryLock(timeout)
- 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况
资源限制的挑战
资源限制
- 硬件资源:带宽的上传下载速度,硬盘读写速度和CPU的处理速度
- 软件资源:数据库连接数和socket连接数等
资源限制引发的问题
因资源受限,并发执行仍然在串行执行,反而更慢,因为多了上下文切换与资源调度的时间
如何解决资源限制
- 集群
- 资源池
参考资料
- 《Java并发编程的艺术》第1章