并发编程 -- 多线程底层运行原理, 线程状态
作者 : 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()方法可以给一个参数, 参数代表执行的毫秒;
第五个状态 死亡状态
线程执行完了, 或因异常退出了, 都会结束生命周期, 这就是死亡状态;
来源: http://www.bubuko.com/infodetail-3065836.html