并发是指在单个CPU核心上,多个线程占用不同的CPU时间片,但由于每个线程占用的CPU时间片非常短(比如10ms),看起来就像是多个线程在共同执行一样,但在微观物理上看其实还是串行执行的。这样的场景称作并发。
并行是指在CPU多个核心上,多个线程是在真正的同时执行。(每个CPU核心也在并发的执行)
多线程程序一定就好吗?不一定,要看具体的应用场景:
IO密集型,运算较少,涉及磁盘或网络、外部设备等IO操作较多,会阻塞等待情况较频繁。
无论是CPU单核、CPU多核、多CPU,都是比较适合多线程程序的
CPU密集型,运算多,IO操作少,阻塞情况少。
CPU单核下,多线程存在上下文切换,是额外的花销,线程越多上下文切换所花费的额外时间也越多,不如一个线程一直进行计算,不适合多线程。
CPU多核下,多个线程可以并行执行,可以提高CPU利用率。
为了完成任务,创建很多的线程可以吗?线程真的是越多越好?
线程的创建和销毁都是非常"重"的操作
线程的创建需要由系统来完成,需要内核的参与,这就涉及到用户空间到内核空间的切换。要给线程分配内核栈、页目录、页表等描述线程的资源和数据结构。创建完成后是要交给用户来使用,需要再切回用户空间。
线程创建完成后,在用户空间处理执行业务,执行完就需要销毁线程,同样也需要内核的参与。
操作系统上创建线程和销毁线程都是很"重"的操作,耗时耗性能都比较多,那么在服务执行的过程中,
如果业务量比较大,实时的去创建线程、执行业务、业务完成后销毁线程,那么会导致系统的实时性能
降低,业务的处理能力也会降低。
线程栈本身占用大量内存
在32位地址空间下,内核给每个进程分配了4G大小的地址空间,用户空间3个G,内核空间1个G,一个进程创建的所有线程共享进程的地址空间。Linux系统下栈的空间大小默认是8M:
3G的空间除去代码段、数据段等,一个进程大概能创建380个线程。
如果程序一开始就创建了大量的线程,程序的地址空间都被线程栈占了,连变量都定义不了多少,还怎么做业务呢?
线程的上下文切换要占用大量时间
线程数量如果过多,就会涉及到线程的调度,线程调度是需要进行上下文切换的,切耗费的是CPU的时间,切换的时候CPU什么都没做,就会降低CPU的利用率。
大量线程同时等待同一个事件,唤醒时会使系统经常出现锯齿状负载或者瞬间负载量很大导致宕机
为了减少创建线程和销毁线程的性能和时间耗费,可以在服务进程启动之初,就事先创建好线程池里面的线程,当业务流量到来时需要分配线程,直接从线程池中获取一个空闲线程执行task任务即可,task执行完成后,也不用释放线程,而是把线程归还到线程池中继续给后续的task提供服务。
线程池里面的线程个数是固定不变的,一般是ThreadPool创建时根据当前机器的CPU核心数量进行指定。
线程池里面的线程个数是可动态增长的,根据任务的数量动态的增加线程的数量,但是会设置一个线程数量的阈值(线程过多的坏处上面已经讲过了),任务处理完成,如果动态增长的线程空闲了设定时间还没有处理其它任务,那么关闭线程,保持池中最初数量的线程即可。
线程池模型: