Java多线程(1)-为什么需要多线程

为什么需要多线程?

简单来说, 因为CPU与内存以及IO之间存在着巨大的性能差异。

推荐阅读文章 这个世界慢死了 https://zhuanlan.zhihu.com/p/58431253 。

CPU运行的时间单位是cycle, 称为时钟周期,一次简单的运算大概在1-2个时钟周期。

例如一个3GHz的CPU,意味着每秒可以执行30亿次的时钟周期,也就是大概1纳秒可以执行一次简单的操作。

内存的一次寻址时间是1-10微秒, CPU如果等内存一次寻址,就意味着大概要等1000-10000个时钟周期。

普通硬盘的寻址时间是1-10毫秒,意味着硬盘做一次寻址,CPU要等上百万个时钟周期,

如果把CPU看作一个人,1个时钟周期看作1秒,那么就要等100万秒,一天是8万秒左右,那么这个人大概要等12天左右,

正是因为一次IO等待,需要太久的时间,所以当一个线程等待IO时,我们可以让CPU去调度其他的线程做想要做的事情,充分利用起来,这就是需要多线程的原因之一。

在100MHz - 1GHz的CPU中,1个时钟周期的时间是1纳秒-10纳秒,随着Hz的上升,CPU执行操作所需时钟周期也就越来越短,

那么Hz能不能无限上升呢,比如到100Hz,1THz,这样CPU执行是不是就更快了,答案是不行,因为Hz的提升需要受到物理规律的限制,频率的上升会使得发热量以平方为单位上升,一旦频率超过5GHz后,家用的计算机就要用油或者水去冷却它,

但是在Hz有限的情况下,又想让CPU执行的更快怎么办,这时候就出现了多核CPU,原本只有1核CPU,变成了后来的4核、8核,使得每个核都可以单独的做一份工作,并行的调用线程去做事,

例如现在有1个线程,但是CPU是8核的,这时候只有1个核被利用起来,其他的都在空闲等待,白白浪费了资源,

所以此刻我们需要将其他的核也利用起来,创建8个线程,就可以让每个核都去运行单独的1个线程,这时候就将原本1核可能8秒才能处理完8个线程,现在就可以1秒全部做完。

线程的本质

线程的本质是一个执行代码的工人, 例如我们在java中写的方法,以及其中的代码,最终启动的时候都会被放在一个线程里进行执行,

这些代码更像是一份说明书,是你告诉工人需要怎么去帮你做你需要的事情,然后这个工人就会阅读你写的代码,一行一行的读,一行一行的做。

如下图,我们在下面的main方法中执行for循环,jvm中有一个main线程,在做这件事情。

当我们new了一个线程执行时,for循环将在新的线程中去执行,我们还可以多创建一些线程执行一些for循环操作。

优点

所以线程的优点是:可以并发的去执行,多个线程同时的工作,同时的for循环。

如下图,一个WebAPP,如果有4个线程的话,那么就可以同时处理4个用户发送过来的请求,从Rest接口到Dao数据层,但是想象一下,如果只有一个线程,那么同时只能处理一个用户请求,其他的就需要排队等待处理了,

就像银行大厅的办事窗口一样,总是有很多个,这样可以做到分流并行为客户处理业务,如果只有一个窗口,相信做事的人很累,办业务的人等的也很累。

缺点

占用资源:每个线程都有自己独立的方法栈。

例如下图的线程,一共有三个栈桢,这个还好,但是真正的企业级应用中,web容器,一个请求创建的方法栈桢是相当之多的,每个方法栈桢中还有自己的局部变量各种,所以一个线程占用的内存资源很多,而内存是很宝贵的资源。

慢:上下文切换。

这里可能会有点纳闷,上面不是刚说优点是快,可以并发执行处理事情吗,这里怎么又慢了呢?

原因是这样的,

由于线程数量总是比CPU核心多,所以CPU就在某一个时刻去执行一个线程,下一个时刻执行另一个线程,

在执行下一个线程时,由于当前线程工作还没结束,正在执行的状态以及数据怎么办呢?就要保存起来,然后换下一个线程执行,这个过程就叫做上下文切换,

这个过程,作为人类的我们肉眼是感觉不到的,就好像一个人速度非常的快,1秒内先把饭吃完了,又把碗洗了,卫生打扫了,

明明是三件事,但是在肉眼看来一秒就同时完成了,但真正意义上并不是同时,而是他在很短的时间内,做完了一件事,又接着把剩下的事情用很短的时间结束了,

CPU就是这样,速度非常的快,但是上下文切换还是拖慢了他的时间了,因为一次上下文切换需要的时间是 1微秒 左右,如果CPU 1纳秒 就能执行结束一个程序运算的话,那么一次切换,就浪费了1000个时钟周期,CPU有这个等待的时间,可以处理完1000个程序运算。

能不能让上下文切换减少?

答案是可以,Golang中有一个概念叫做协程,就是用户态线程,这需要程序语言层面自己实现一个调度器。

猜你喜欢

转载自blog.csdn.net/cainiao1412/article/details/114241041