多线程编程一定快嘛
在面试中经常被问到多线程一定比单线程快嘛?希望看完这篇文章你可以后一个很好的答案
运行测试
我们使用下面这段代码测试一下多线程和单线程的运行速度,代码来源—java并发编程的艺术
private static final long count = 100000000L;
public static void main(String[] args) throws InterruptedException {
concurrency();
serial();
}
private static void concurrency() throws InterruptedException {
long start = System.currentTimeMillis();
Thread thread = new Thread(new Runnable() {
@Override
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--;
}
long time = System.currentTimeMillis() - start;
thread.join();
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+",a="+a);
}
测试结果
不同机器结果可能存在不同,但是规律相同
计算次数 | 多线程计算时间(ms) | 单线程计算时间(ms) |
---|---|---|
10 | 6 | 0 |
1000 | 4 | 0 |
1万 | 1 | 0 |
10万 | 6 | 2 |
1000万 | 70 | 295 |
1亿 | 84 | 282 |
从测试结果可以看出多线程在计算量逐渐变大之后优势逐渐显示了处理,但是在较少的计算量情况下甚至还不如单线程的计算时间,
那么为什么会出现这种情况的?
原因
上下文切换
即使是单核处理器也支持多线程处理代码,CPU 是通过给每个线程分配时间片的方式实现这个机制,这个机制叫做
并发执行机制原理:简单地说就是把一个处理器划分为若干个短的时间片,每个时间片依次轮流地执行处理各个应用程序,由于一个时间片很短,相对于一个应用程序来说,就好像是处理器在为自己单独服务一样,从而达到多个应用程序在同时进行的效果 。
时间片一般是几十毫秒左右。
CPU通过时间片分配算法来循环执行任务,当前任务时间片执行完毕之后会去执行下一个任务,但是在切换的过程中会保存上一个任务的执行状态,可以再加载这个线程的状态,所以任务从保存到再加载就是一次上下文切换。
频繁的上下文切换也会影响程序的执行效率。
我们假设一个任务的计算量是 1
那么 两个线程的计算量近似等于 二分之一 加上 上下文切换时间乘次数
当上下文切换时间乘次数 大于 计算量的 二分之一时,双线程的效率就不会很高
如何有效的避免上下文切换
- 无锁并发编程:当多线程竞争锁时,会引起上下文切换,可以使用一些方式来避免锁的使用锁,比如说把一个任务分割成多段小任务,每个线程执行固定的一段,比如说:ConcurrentHashMap的扩容机制,fork/join 框架。
- cas算法 : java的Atomic包使用cas算法更新数据,而不需要加锁,从而减少上下文切换
- 使用最少线程:防止大量线程处于等待状态
- 协程:单线程里实现多任务的调度