首页 - 通讯 - 【第375期】闲鱼专访:Thread.sleep(0)有什么用?

【第375期】闲鱼专访:Thread.sleep(0)有什么用?

2023-09-29 13:05
2022年5月17日下午5:35 • 面试问题 • 阅读 22 围观者: 推荐一个我每天都会看的账号主 我们可能经常使用Thread.Sleep函数来将线程挂起一段时间。那么你正确理解了这个函数的用法吗? 考虑这两个问题: 假设是 2008-4-7 12:00:00.000。如果我调用Thread.Sleep(1000),这个线程会在2008-4-7 12:00:01.000被唤醒吗? 有人的代码使用了一个看似难以理解的短语:Thread.Sleep(0)。既然是Sleep 0毫秒,那和去掉这段代码相比有什么区别吗? 我们先回顾一下操作系统原理。 在操作系统中,CPU竞争的策略有很多种。 Unix系统使用时间片算法,而Windows是抢占式的。 在时间片算法中,所有进程都被放置在一个队列中。操作系统按照进程的顺序为每个进程分配一段时间,即允许进程运行的时间。如果时间片结束时进程仍在运行,则CPU将被夺走并分配给另一个进程。如果进程在时间片结束之前阻塞或结束,则CPU立即切换。调度程序所要做的就是维护一个就绪进程列表,当一个进程用完它的时间片时,它就会被移动到队列的末尾。 所谓抢占式操作系统,是指如果一个进程获得了CPU时间,那么它将完全占用CPU,除非它放弃使用CPU。因此,可以看出,在抢占式操作系统中,操作系统假定所有进程都是“品格良好”的,会主动退出CPU。 在抢占式操作系统中,假设有多个进程,操作系统会根据它们的优先级和饥饿时间(没有使用CPU的时间)计算它们的总优先级。操作系统会将CPU交给总体优先级最高的进程。当进程执行完毕或者主动挂起时,操作系统会重新计算所有进程的总优先级,然后选择优先级最高的一个,交给它CPU控制权。 我们用分蛋糕的场景来描述这两种算法。假设有无穷无尽的蛋糕(无穷无尽的时间),一对刀叉(一个CPU),还有10个人等着吃蛋糕(10个进程)。 如果Unix操作系统负责分蛋糕,那么他会设定这样的规则:每个人上来吃1分钟,时间到了,换下一个。当最后一个人吃完后,他们又从头开始。所以,不管这10个人的优先级不同,饥饿程度不同,胃口不同,每个人上来都可以吃1分钟。当然,如果有人不是很饿,或者吃的很少,吃完30秒就吃饱了,那么他可以告诉操作系统:我吃饱了(挂起)。然后操作系统就会让下一个人来。 如果Windows操作系统负责分蛋糕的话,那么场面将会非常有趣。他会制定这样的规则:我会根据你们的优先级和饥饿程度计算你们每个人的优先级。优先级最高的人可以来吃蛋糕——直到你不想再吃为止。当这个人吃完后,我会根据优先级和饥饿程度重新计算每个人的优先级,然后分配给优先级最高的人。 从这个角度来看,这个场景很有趣——也许有些人是PPMM,所以他们的优先级很高,所以她可以经常来吃蛋糕。也许对方是个丑陋的人,优先级很低,所以他的优先级很低,所以需要很长时间才能轮到他(因为随着时间的推移,他会越来越饿,所以计算出的总优先级会越来越高,所以有一天会轮到他)。而且,如果一个大胖子因为饭量大,不小心拿到了刀叉,他可能会霸占蛋糕,连续吃很长时间,导致旁边的人咽口水。 。 。 而且,还可能出现这种情况:根据操作系统计算的结果,PPMM No.5的总体优先级最高,并且比其他的要高得多。于是我请五号来吃蛋糕。 5号吃了一会儿,感觉不那么饿了,就说“我不吃”(挂)。所以操作系统会重新计算每个人的优先级。因为5号刚刚吃过饭,她的饥饿程度变小了,所以总优先级变小了;并且因为其他人等待了一段时间,他们的饥饿程度变得更大,所以总优先级也变得更大。但这个时候,还是有可能五号的优先级比其他人高,但现在只比其他人高一点点——但她的整体优先级仍然是最高的。于是,操作系统就会说:5号MM上来吃蛋糕了……(5号MM郁闷了,她不是才吃这个吗……她要减肥……谁叫你的?如此美丽并获得如此高的优先级)。 那么,Thread.Sleep函数有什么作用呢?我们用刚才分蛋糕的场景来形容吧。上面的场景中,5号MM吃了一次蛋糕后,感觉自己已经8分饱了。她觉得接下来的半小时内她不想再吃蛋糕了,所以她会告诉操作系统:接下来的半小时内不要再叫我上来吃蛋糕了。这样,当操作系统在接下来的半小时内重新计算每个人的总优先级时,就会忽略mm 5。睡眠功能可以做到这一点。它告诉操作系统“接下来的几毫秒我不会参与CPU竞争”。 看完了Thread.Sleep的作用,我们来思考一下文章开头的两个问题。 第一个问题? 对于第一个问题,答案是:不一定。因为你只是告诉操作系统:我不想在接下来的1000毫秒内参与CPU竞争。那么1000毫秒过去后,此时可能有另一个线程正在使用CPU,因此操作系统不会重新分配CPU,直到该线程挂起或结束;另外,即使此时恰好轮到操作系统分配CPU,那么当前线程也不一定是总体优先级最高的线程,CPU仍然可能被其他线程抢占。 同样,Thread有一个Resume函数,用于唤醒挂起的线程。如上所述,这个函数只是“告诉操作系统我从现在开始参与CPU竞争”。该函数的调用并不立即使该线程获得CPU控制权。 第二个问题? 对于第二个问题,答案是:是的,而且区别很明显。假设我们刚才分享的分享蛋糕的场景中,还有一个7号PPMM,她的优先级也非常非常高(因为她非常非常漂亮),所以操作系统会一直叫她去吃蛋糕。而且,七号还非常喜欢吃蛋糕,胃口很大。不过七号的性格很好,很善良,吃了几口就会想:如果现在还有人比我更需要蛋糕,那我就给他吧。因此,她每隔几口就可以告诉操作系统:让我们重新计算一下每个人的总优先级。 然而,操作系统不接受这个建议——因为操作系统不提供这个接口。于是7号MM就改变了说法:“接下来的0毫秒别叫我上来吃蛋糕。”操作系统接受这个命令,所以这时候操作系统会重新计算每个人的总优先级——注意,这次是和数字7一起计算的,因为“已经过去了0毫秒”。因此,如果没有人比7号更需要吃蛋糕,下次仍然会叫7号吃蛋糕。 因此,Thread.Sleep(0)的作用就是“触发操作系统立即重新开始CPU竞争”。竞争的结果可能是当前线程仍然获得CPU控制权,也可能被另一个线程取代获得CPU控制权。这就是为什么我们经常在大循环中写Thread.Sleep(0),因为这样可以让其他线程如Paint线程获得CPU控制权,这样界面就不会卡在那里。另外,虽然上面提到“除非放弃使用CPU,否则就会完全占用CPU”,但这种行为仍然受到限制——操作系统会监控你的CPU占用情况。如果发现某个线程长时间占用CPU,CPU就会强制这个线程挂起,所以在实践中不会出现“一个线程一直占用CPU”的情况。至于我们的大循环导致程序卡住,并不是因为这个线程一直占用CPU。 其实这期间操作系统已经多次竞争CPU了,但是其他线程获得CPU控制权后很快就退出了,于是又轮到该线程继续执行循环了,于是又开始了。过了好久才被操作系统强制挂起。 。 。因此,反映在界面上,看起来这个线程一直在占用CPU。 来源:www.gsm-guard.net/keyyang/p/4128424.html 主流java进阶技术(学习资料分享) 而不是在网上搜索问题?还不赶快关注我们吧~ PS:因为公众号平台改变了推送规则,如果不想错过内容,看完后记得点击“阅读”并加个“星”,这样每次推送新文章,它将尽快出现在您的订阅中。在列表中。点击“关注”即可支持我们! 版权声明:本文内容由网友自愿贡献,本文所表达的观点仅代表作者自己的观点。本网站仅提供信息存储空间服务,不拥有任何所有权,也不承担相关法律责任。如果您发现本站有任何涉嫌侵权/非法内容,请发送邮件举报。一经核实,该网站将立即删除。 本文由斑马博客整理。本文链接:https://www.gsm-guard.net/index.php/post/9269.html