原创:享学课堂讲师Peter

转载请声明出处!

什么是上下文切换?

「漫画」揭秘上下文切换

「漫画」揭秘上下文切换

「漫画」揭秘上下文切换

「漫画」揭秘上下文切换

「漫画」揭秘上下文切换

「漫画」揭秘上下文切换

其实在单个处理器的时期,操作系统就能处理多线程并发任务。处理器给每个线程分配 CPU 时间片(Time Slice),线程在分配获得的时间片内执行任务。

CPU 时间片是 CPU 分配给每个线程执行的时间段,一般为几十毫秒。在这么短的时间内线程互相切换,我们根本感觉不到,所以看上去就好像是同时进行的一样。

时间片决定了一个线程可以连续占用处理器运行的时长。当一个线程的时间片用完了,或者因自身原因被迫暂停运行了,这个时候,另外一个线程(可以是同一个线程或者其它进程的线程)就会被操作系统选中,来占用处理器。这种一个线程被暂停剥夺使用权,另外一个线程被选中开始或者继续运行的过程就叫做上下文切换(Context Switch)。

多线程上下文切换的原因

「漫画」揭秘上下文切换

「漫画」揭秘上下文切换

「漫画」揭秘上下文切换

「漫画」揭秘上下文切换

上下文切换带来的性能问题

「漫画」揭秘上下文切换

 public class DemoApplication {
public static void main(String[] args) {
//运行多线程
MultiThreadTester test1 = new MultiThreadTester();
test1.Start();
//运行单线程
SerialTester test2 = new SerialTester();
test2.Start();
}

static class MultiThreadTester extends ThreadContextSwitchTester {
@Override
public void Start() {
long start = System.currentTimeMillis();
MyRunnable myRunnable1 = new MyRunnable();
Thread[] threads = new Thread[4];
//创建多个线程
for (int i = 0; i < 4; i++) {
threads[i] = new Thread(myRunnable1);
threads[i].start();
}
for (int i = 0; i < 4; i++) {
try {
//等待一起运行完
threads[i].join();
}
catch (InterruptedException e){
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
System.out.println("多线程运行时间: " + (end - start) + "ms");
System.out.println("计数: " + counter);
}
// 创建一个实现Runnable的类
class MyRunnable implements Runnable {
public void run() { while (counter < 100000000) {
synchronized (this) { if(counter < 100000000) {
increaseCounter();
}
}
}
}
}
}
//创建一个单线程
static class SerialTester extends ThreadContextSwitchTester{
@Override public void Start() {
long start = System.currentTimeMillis();
for (long i = 0; i < count; i++) { increaseCounter();
}
long end = System.currentTimeMillis();
System.out.println("单线程运行时间: " + (end - start) + "ms");
System.out.println("计数: " + counter);
}
}
//父类 static abstract c

lass ThreadContextSwitchTester {
public static final int count = 100000000;
public volatile int counter = 0;
public int getCount() {
return this.counter;
}
public void increaseCounter()
{
this.counter += 1;
}
public abstract void Start();
}
}

执行之后,看一下两者的时间测试结果:

「漫画」揭秘上下文切换

通过数据对比我们可以看到:串联的执行速度比并发的执行速度要快。这就是因为线程的上下文切换导致了额外的开销,一般来说使用 Synchronized 锁关键字,导致了资源竞争,从而引起了上下文切换,但即使不使用 Synchronized 锁关键字,并发的执行速度也无法超越串联的执行速度,这是 因为多线程同样存在着上下文切换。Redis、NodeJS 的设计就很好地体现了单线程串行的优势。

总结


「漫画」揭秘上下文切换

例如,我们前面讲到的 Redis,从内存中快速读取值,不用考虑 I/O 瓶颈带来的阻塞问题。而在逻辑相对来说很复杂的场景,等待时间相对较长又或者是需要大量计算的场景,我建议使用多线程来提高系统的整体性能。例如,NIO 时期的文件读写操作、图像处理以及大数据分析等。

「漫画」揭秘上下文切换

最后

如果你的技术提升遇到瓶颈了,或者缺高级进阶视频学习提升自己,这有大量大厂面试题为你面试做准备!

后台私信回复:“资料”获取。


喜欢本文的话可以关注我们的官方账号,第一时间获取资讯。

你的关注是对我们更新最大的动力哦~

posted on 2019-12-13 09:00  淡然~~浅笑  阅读(1490)  评论(0)    收藏  举报