在创建线程之前我们需要考虑几个问题
1.创建多线程的目的是什么?
2.创建多线程的场景有哪些?
3.创建多个线程合适呢?
为什么要使用多线程
使用多线程本质上就是要提高程序的效率,脑海中想到的就是快,快,快!这种快是无法度量的,所以提升性能之前首先要考虑为什么快呢?
体现线程快的核心是延迟和吞吐量,延迟就是每个发送请求到收到响应所消耗的时间;延迟越短,程序越快,性能也越好。吞吐量是指在单位时间内能处理的请求数量(QPS),吞吐量越大证明处理数据的越强,性能也就越好。
我们提升性能主要的目的就是降低延迟,提高吞吐量
多线程的使用场景
希望降低延迟,提高吞吐量的方法基本就两种,1:优化算法,2:将硬件的适用率达到最大化。硬件的提示主要是I/O和CPU的使用率
如果I/O和CPU的耗时是1:1
如果只有一个线程执行CPU的时候I/O空闲,执行I/O的时候CPU空闲,所以I/O和CPU的利用率为50%
如果是两个线程,线程A执行CPU操作的时候,线程B执行I/O,当线程A执行I/O操作的时候线程B执行CPU操作,这样CPU和I/O的利用率就是100%
例如:计算1 + 2 + ....+100亿如果在4核CPU和4个线程上执行就是A线程执行[1,25亿),线程B回显[25,50亿),线程C执行[50,75亿),线程D执行[75,100亿)理论上应该比一个线程执行的时间快乐将近4倍
创建多少个线程合适呢?
CPU密集型:
对于CPU密集型计算多线程本质上是提升CPU的使用率,所以对一个4核的CPU来说,理论上创建4个线程就可以了。在多创建只会增加线程切换的开销。所以对于CPU密集型的计算场景线程数 = CPU核数 + 1
《Java并发编程实战》加1的目的是计算(CPU)密集型的线程恰好在某时因为发生一个页错误或者因其他原因而暂停,刚好有一个“额外”的线程,可以确保在这种情况下CPU周期不会中断工作。
I/O密集型:
CPU和I/O的耗时如果是1:1那开2个线程是合适的,如果CPU和I/O的消耗是1:2那么开3个线程是合适的。
如图显示:CPU 在 A、B、C 三个线程之间切换,对于线程 A,当 CPU 从 B、C 切换回来时,线程 A 正好执行完 I/O 操作。这样 CPU 和 I/O 设备的利用率都达到了 100%。
(长远一点看,第一轮CPU等待,第二轮自动就错开了)
线程等待时间所占比例越高,需要越多线程;线程CPU时间所占比例越高,需要越少线程。最佳线程数 =CPU 核数 * [ 1 +(I/O 耗时 / CPU 耗时)]
通过APM工具可以可以得到准确的耗时时间。
个人总结:个人觉得定性的I/O密集和CPU密集很难在定量上反应性能的瓶颈,因为我们在使用公式的时候完全就是忽略了线程直接的切换的开销。如果IO已经成了瓶颈,CPU的使用率就会降低,创建在多的线程都是浪费。不能单纯的靠公式计算线程数,理论加经验加实际场景。
问题:
1.我们已经知道创建多少个线程合适了,为什么还要搞一个线程池出来(二)?
2.创建一个线程都要做哪些事情?为什么说频繁的创建线程开销很大(三)?
3.多线程通常要注意共享变量问题,为什么局部变量就没有线程安全问题呢(四)?