深入理解并发编程 -- 多线程(二)底层运行原理、线程状态
并发编程 -- 多线程底层运行原理、线程状态
作者 : Stanley 罗昊
多线程 -- 并发编程(一) : https://www.cnblogs.com/StanleyBlogs/p/10890906.html
【转载请注明出处和署名,谢谢!】
多线程底层执行原理
说道底层运行,那么是不是就是需要依靠CPU啊;
那,各位之前有没有听过一句话叫做,一个CPU在同一个时间片只能执行一个程序;
什么意思呢?
就是,你的程序是不是都运行在一个CPU上啊,那你真正一个CPU在同一个时间片里是不是只能执行一个程序呀,那这个程序究竟要执行那个程序,是不是就需要通过线程之间时间片的一个争抢;
时间片:微小的时间段;
多线程说白了就是时间片的争夺,那个线程获取了时间片,就执行那个线程的代码;
假设,t1线程先获得时间片,那么,t1线程就优先执行;
但是,它不可能拿着那个时间片不放吧,因为在CPU执行的过程中,底层运用轮循制的;
多线程执行的时候,CPU分配时间片是采取轮循的方式进行分配的;
就是轮流,有点像值日的时候,轮流值日一样;
那,CPU在分配时间片的时候,第一个t1先抢占到了之后,他先执行了一段时间之后,CPU把这个t1执行完了以后,CPU是不是接着把时间片分配给t2去执行了;
那事实上,也是t2也在去抢占时间片;
当t1执行完毕后,那么,CPU就将迎来新的一轮争夺,这个时候t2抢到了,就开始执行t2的代码;
这就是多线程的底层执行原理;
多线程它在本质上运行的时候,他是同时执行的,还是轮流执行的呢?
肯定不是同时执行的,也就是不是我们常说的并发执行;那在你们看来,实际就是宏观上你来看就是同时执行,但是在微观上是不是的;
线程的状态
线程总共有五种状态;
第一个状态 新建状态
新建状态,就是你新建一个线程是的状态,也就是你新建了一个线程但还没有启动时的状态;
当线程执行start方法的时候,就会进入就绪状态;
第二个状态 就绪状态
进入就绪状态的时候,事实上就是准备抢占CPU的时间片;
一旦抢占到了CPU的时间片它就会立即进入运行状态;
第三个状态 运行状态
当线程抢占到了CPU时间片的时候,它才会运行,所以第三个状态是,运行状态;
在它的运行状态中,还有可能执行一个代码,Throad.sleep();睡眠;
就是在你执行的时候,突然让你睡眠了,我都让你这个线程睡眠了,你还有必要去争夺这个CPU资源吗?
就肯定没有必要再去争夺这个CPU资源了,那这个时候你就需要释放CPU啊,对不对,你释放之后,你下次再运行的时候,你就需要重新获取CPU的时间片,所以这种状态就叫做堵塞状态;
第四个状态 堵塞状态与sleep方法
想让线程阻塞,最常用的方式就是使用sleep,用sleep这个方法,可以使运行中的线程回到就绪状态;
因为它需要重新抢占CPU资源的,所以,sleep状态的最终目的是让改线程回到就读状态;
就比如,我现在想让这个线程,进我想让它每次进入run方法中的for循环打印里写一个睡眠,一遍循环遍历输出,一边睡眠看会发生什么:
我在run方法中业务写完后,我们测试一下该线程:
在上图中,可以发现,我同时调用了两次start方法,说明,我执行了两次我一次性开启了两次线程,并且执行了两次,我们看看会不会出现交替执行的情况:
从输出结果来看,确实交替执行了并且,是俩俩执行的:
每过一秒,就会执行一次:
我就不继续演示了;
所以,我们从中可以看出,不管哪个线程过来,t1也好t2也好,执行的时候,均睡眠一秒钟,睡眠完一秒钟之后,谁先醒了,谁就继续向下执行,这个就是到点自然醒的;
也可以使用join来造成线程堵塞;
join
刚刚,我们在上面介绍了sleep,我们来看看join;
join():是线程加入
它底层执行的是,当你在执行一个线程的时候,如果遇到其他线程加入,则会先执行加入的线程,直到的加入的线程执行完成,才会继续执行原来线程的任务;
什么意思呢?
就是说,还是上面那个t1,与t2的例子,那假设说,t1在执行的过程中,突然遇到了一个代码t2.join,这时候,就会在这个时间片,优先执行t2的线程;
join()方法可以给一个参数,参数代表执行的毫秒;
第五个状态 死亡状态
线程执行完了,或因异常退出了,都会结束生命周期,这就是死亡状态;