在java开发中,经常会用到Thread.sleep(1000);类似的语句来使线程挂起一段时间,那么底层的逻辑是怎么操作的呢?
问题1:假设现在时间是2022-1-1 8:00:00.000,若是调用了Thread.sleep(1000); 在2022-1-1 8:00:01.000 时,这个线程会不会继续执行?
问题2:Thread.sleep(1000);是挂起了0毫秒,那与去掉这句代码有什么区别?
说起线程,其底层就要了解一下操作系统原理了,操作系统主要使用window和linux,windows中CPU竞争的策略是抢占式的,linux中CPU竞争策略是轮询的。
轮询策略中,所有的线程排成一个队列,操作系统按照它们的顺序,给每个线程分配一段时间,若是时间结束时线程还在运行,则CPU将被剥夺分配给另一个线程;若是在时间片结束前阻塞或结束,则CPU当即切换。当线程用完它的时间片后,被移动到队列的末尾。
抢占式策略中,所有的线程按照优先级排成一个队列,操作系统会把CPU交给优先级最高的线程,当一个线程获得了CPU时间,除非自己放弃使用CPU,否则将完全占用。当线程执行结束或自己挂起后,操作系统会重新计算一次优先级进行排列,然后把CPU交给队列中优先级最高的。
场景说明两种策略,吃蛋糕,一个刀叉(一个CPU),10个小伙伴(10个线程)
linux的轮询策略,每个人上来吃1分钟,时间到了换下一个,最后一个吃完了再从头开始。该种策略不管饥饿程度(优先级)的情况、饭量情况。若有人不饿、饭量小,吃了30秒就吃饱了,可以根操作系统说,吃饱了(Thread.sleep()挂起),于是换下一个。
windows抢占式策略,根据饥饿程度(优先级)的情况、饭量情况进行排列,最前面的人先上来吃,吃到不想吃为止,等这个人吃完了,再重新排列,再让最前面的人上来吃。若有人吃了30秒说不想吃了(Thread.sleep()挂起),操作系统重新计算进行排列。若有人吃了30秒说1小时之内都不要叫我上来吃了,就告诉操作系统,在未来多少毫秒内不参与CPU竞争。
问题1,不一定。因为你告诉操作系统,在未来1000毫秒内不参与CPU竞争,那么1000毫秒过去之后:轮询策略下,这时候也行另一个线程正在使用CPU,那么这时操作系统是不会重新分配CPU的,直到那个线程挂起或结束;抢占式策略下,这时候操作系统正进行CPU分配,那么当前线程也不一定是优先级最高的那个,CPU还是可能被其他优先级高的线程抢占去。
问题2,有。吃蛋糕的场景里,有个女同学的优先级最高(最漂亮),操作系统总是叫她上来吃,而且女同学也非常喜欢吃蛋糕,但又很善良,没吃几口就想,会不会有很饿的同学,那么我就让给他。因此没吃几口就跟操作系统说重新计算一下优先级,在未来0毫秒内不要叫我上来吃了。注意,此时是连同女同学本人也参与计算的,因为0毫秒已经过去了,所有若没有很饿的同学,那么下一次还是她被叫上来吃蛋糕。因此,Thread.sleep(0)的作用,就是“触发操作系统立刻重新进行一次CPU竞争”。竞争的结果也许是当前线程仍然获得CPU控制权,也许会换成别的线程获得CPU控制权。