精华内容
下载资源
问答
  • Java 线程Thread.Sleep详解

    千次阅读 2021-03-09 01:53:02
    我们可能经常会用到 Thread.Sleep 函数来使线程挂起一段时间。那么你有没有正确的理解这个函数的用法呢?思考下面这两个问题: 1、假设现在是 2008-4-7 12:00:00.000,如果我调用一下 Thread.Sleep(1000) ,在 2008-...

    我们可能经常会用到 Thread.Sleep 函数来使线程挂起一段时间。那么你有没有正确的理解这个函数的用法呢?

    思考下面这两个问题: 1、假设现在是 2008-4-7 12:00:00.000,如果我调用一下 Thread.Sleep(1000) ,在 2008-4-7 12:00:01.000 的时候,这个线程会 不会被唤醒? 2、某人的代码中用了一句看似莫明其妙的话: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,因此具有高优先级,于是她就可以经常来吃蛋糕。可能另外一个人是个丑男,而去很ws,所以优先级 特别低,于是好半天了才轮到他一次(因为随着时间的推移,他会越来越饥饿,因此算出来的总优先级就会越来越高,因此总有一天会轮到他的)。而且,如果一不 小心让一个大胖子得到了刀叉,因为他饭量大,可能他会霸占着蛋糕连续吃很久很久,导致旁边的人在那里咽口水。。。而且,还可能会有这种情况出现:操作系统 现在计算出来的结果,5号PPMM总优先级最高,而且高出别人一大截。因此就叫5号来吃蛋糕。5号吃了一小会儿, 觉得没那么饿了,于是说“我不吃了”(挂起)。因此操作系统就会重新计算所有人的优先级。因为5号刚刚吃过,因此她的饥饿程度变小了,于是总优先级变小 了;而其他人因为多等了一会儿,饥饿程度都变大了,所以总优先级也变大了。不过这时候仍然有可能5号的优先级比别的都高,只不过现在只比其他的高一点点 ——但她仍然是总优先级最高的啊。因此操作系统就会说:5号mm上来吃蛋糕……(5号mm心里郁闷,这不刚吃过嘛……人家要减肥……谁叫你长那么漂亮,获 得了那么高的优先级)。

    那么,Thread.Sleep 函数是干吗的呢?还用刚才的分蛋糕的场景来描述。上面的场景里面,5号MM在吃了一次蛋糕之后,觉得已经有8分饱了,她觉得在未来的半个小时之内都不想再 来吃蛋糕了,那么她就会跟操作系统说:在未来的半个小时之内不要再叫我上来吃蛋糕了。这样,操作系统在随后的半个小时里面重新计算所有人总优先级的时候, 就会忽略5号mm。Sleep函数就是干这事的,他告诉操作系统“在未来的多少毫秒内我不参与CPU竞争”。 看完了 Thread.Sleep 的作用,我们再来想想文章开头的两个问题。 对于第一个问题,答案是:不一定。因为你只是告诉操作系统:在未来的1000毫秒内我不想再参与到 CPU竞争。那么1000毫秒过去之后,这时候也许另外一个线程正在使用CPU,那么这时候操作系统是不会重新分配CPU的,直到那个线程挂起或结束;况 且,即使这个时候恰巧轮到操作系统进行CPU 分配,那么当前线程也不一定就是总优先级最高的那个,CPU还是可能被其他线程抢占去。 与此相似的,Thread有个Resume函数,是用来唤醒挂起的线程的。好像上面所说的一样,这个函数只是“告诉操作系统我从现在起开始参与CPU竞争 了”,这个函数的调用并不能马上使得这个线程获得CPU控制权。

    对于第二个问题,答案是:有,而且区别很明显。假设我们刚才的分蛋糕场景里 面,有另外一个PPMM 7号,她的优先级也非常非常高(因为非常非常漂亮),所以操作系统总是会叫道她来吃蛋糕。而且,7号也非常喜欢吃蛋糕,而且饭量也很大。不过,7号人品很 好,她很善良,她没吃几口就会想:如果现在有别人比我更需要吃蛋糕,那么我就让给他。因此,她可以每吃几口就跟操作系统说:我们来重新计算一下所有人的总 优先级吧。不过,操作系统不接受这个建议——因为操作系统不提供这个接口。于是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一样。 末了再说明一下,文中线程、进程有点混乱,其实在Windows原理层面,CPU竞争都是线程级的,本文中把这里的进程、线程看成同一个东西就好了。

    展开全文
  • 前言:java中Thread线程类中的sleep()查看API文档如下: static void sleep(longmillis) 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。 ...

    前言:java中Thread线程类中的sleep()查看API文档如下:

    static voidsleep(long millis)

    使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。

    static voidsleep(long millis, int nanos)

    导致正在执行的线程以指定的毫秒数加上指定的纳秒数来暂停(临时停止执行),这取决于系统定时器和调度器的精度和准确性。

    得知该方法为静态方法,返回值为void,并且有两种传参方式,作用是使当前正在执行的线程以指定的毫秒数暂停,进入睡眠阻塞状态,放弃CPU时间片执行权。

    然后我不禁陷入了思考:为啥是静态方法,静态方法一般可用类名直接调用。但是我如果创建一个线程对象,然后用该对象来调用该静态方法会产生咋样的结果?

    1.首先,我先用类名直接调用,代码如下:

    import java.lang.Thread;
    public class ThreadTest03{
    	public static void main(String[] args) 
    	{
    		//创建分支线程对象
            Thread t3=new MyThread03();
            //类名.sleep()
    		try{ 
    			Thread.sleep(1000*5);
    		}catch(InterruptedException e){
    			e.printStackTrace();
    		}
            t3.start(); //启动分支线程
        }
            
    }
    class MyThread03 extends Thread{
    	public void run(){
    		for(int i=0;i<100;i++)
    			System.out.println("当前分支线程--->"+i);
    	}
    }

      Thread.sleep()的作用就是让当前线程进入睡眠,请注意了,是当前线程对象,这个“当前”就很微妙哈哈,有点类似于关键字“this”。通俗解释是:Thread.sleep()出现在哪里,那么哪里就得sleep!

    你看,上面的Thread.sleep()不是出现在main方法中吗,所以自然而然 主线程这个“倒霉蛋”只能乖乖的睡眠1000*5ms=5s后再继续执行下一条代码t3.start(),然后才开启分支线程。

    2.然后,那如果我想要t3这个线程对象sleep一段时间,那要咋做呢,我立马想到了把Thread.sleep(1000*5)改成t3.sleep(1000*5)岂不是大功告成了?

    import java.lang.Thread;
    public class ThreadTest03{
    	public static void main(String[] args) 
    	{
    		//创建分支线程对象
            Thread t3=new MyThread03();
            //线程对象.sleep()
    		try{ 
                //在main中,我想这样让t3睡眠行不行
    			t3.sleep(1000*5);
    		}catch(InterruptedException e){
    			e.printStackTrace();
    		}
            t3.start(); //启动分支线程
        }
            
    }
    class MyThread03 extends Thread{
    	public void run(){
    		for(int i=0;i<100;i++)
    			System.out.println("当前分支线程--->"+i);
    	}
    }

    实际上,在main线程中使用另一个线程对象t调用sleep()方法是无法达到使得t线程对象sleep的目的的!

    原因如下: 在静态方法中,  对象.静态方法 最终会自动转换为类名.静态方法 。

    就比如,我定义了一个“人”类,定义了这个类的一个静态方法:“打闪电五连鞭”,然后我创建了一个对象“马保国”,  这个对象去调用静态方法:马保国.打闪电五连鞭。最终执行时会自动转换成 人.打闪电五连鞭 ,表面上看起来是马保国打,实际上并不是。(这时候马保国开始说了:年轻人不讲武德,你骗,欺负我一个69岁的老人。)

    所以, t3.sleep()最终会自动转换为Thread.sleep()执行(代表当前对象进入睡眠),表面上是t3进入sleep,实际上还是主线程main进入睡眠5s。

    sleep()设置为静态方法的目的: 如果让sleep()成为实例方法,当前线程可以直接sleep别的线程,会引入很多多线程的问题,比如死锁。

     

    所以最终得出结论:只能控制当前正在运行的线程,跟对象调用还是类名调用无关,只看你sleep()出现的位置

     

    参考学习视频:https://www.bilibili.com/video/BV1Rx411876f?p=771

     

     

    展开全文
  • 文章目录前言Thread.sleep()1.看看JDK中此方法上面的注释2.案例展示(线程状态:TIMED_WAITING)3.案例展示(InterruptedException异常会清除interrupted status)4.案例展示(sleep不会释放monitors锁)总结 前言 ...


    前言

    如果让一个线程进入休眠?
    我们第一个想到的就是Thread.sleep(long millis)方法吧!
    如果你对synchronized足够了解的话,那么你肯定还能想到Object.wait()方法。
    再如果你对java的Lock锁足够了解的话,那么你还能想到LockSupport类的park()方法。
    那么我们接下来就来对比分析一下这三种方式的使用场景和背后的区别,方便我们以后使用的时候,选择一个最合适的方式来处理我们的代码。


    提示:以下是本篇文章正文内容,下面案例可供参考

    Thread.sleep()

    1.看看JDK中此方法上面的注释

    Thread.sleep()方法必须有一个参数,此参数表示休眠的时长。
    此方法会使得当前线程进入TIMED_WAITING状态,但是不会释放当前线程所持有的锁。
    另外,此方法还会抛出一个InterruptedException的异常,这是受检查异常,调用者必须处理。而且当exception被抛出来的时候,这个“interrupted status”也会被清除。这句话怎么理解呢?等会我们举个例子看一下。
    下面我看一下此方法上面的注释都说了什么内容:
    在这里插入图片描述

    2.案例展示(线程状态:TIMED_WAITING)

    代码如下(展示状态为:TIMED_WAITING):

    package com.kinyang;
    
    /**
     * @author KinYang.Lau
     * @date 2021/6/10 6:37 下午
     */
    public class ThreadSleepTest {
    
        public static void main(String[] args) throws InterruptedException {
             创建一个 t1 线程
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("t1 线程执行 sleep()方法... ");
                        /// 线程 进入休眠 5s
                        Thread.sleep(5000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"t1-thread");
             启动t1线程
            t1.start();
            /// 这里让主线程休眠1s后,再查看 t1 线程的状态,确保t1线程已经执行
            Thread.sleep(1000L);
            System.out.println("t1 线程当前状态:"+t1.getState());
    
        }
    
    }
    
    

    输出结果我们看到,执行sleep之后,线程是TIMED_WAITING状态。
    在这里插入图片描述

    3.案例展示(InterruptedException异常会清除interrupted status)

    代码如下:

    package com.kinyang;
    
    /**
     * @author KinYang.Lau
     * @date 2021/6/10 6:37 下午
     */
    public class ThreadSleepTest {
    
        public static void main(String[] args) throws InterruptedException {
             创建一个 t1 线程
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("t1 线程执行 sleep()方法,休眠5s ");
                        /// 线程 进入休眠 5s
                        Thread.sleep(5000L);
                    } catch (InterruptedException e) {
                        System.out.println("捕获到 InterruptedException 异常!");
                        System.out.println("查看 t1.线程的 interrupted status :"+Thread.interrupted());
                    }
                }
            },"t1-thread");
             启动t1线程
            t1.start();
            /// 这里让主线程休眠1s后,再查看 t1 线程的状态,确保t1线程已经执行
            Thread.sleep(100L);
            System.out.println("t1 线程当前状态:"+t1.getState());
            System.out.println("此时执行:interrupt()方法 强制打断线程...");
            t1.interrupt();
        }
    }
    

    运行结果:
    在这里插入图片描述
    输出结果我们看到,线程被打断后,捕获异常,然后打印状态居然是 false。
    从表面上看,我们打断了线程,线程的interrupt 状态应该是 打断状态呀,也就是应该是 true!但是这里居然是false。为什么?
    原因就是被重置了!!就是上看到的jdk里面sleep方法上面的注解一样,当捕sleep获异常的时候,会把状态清除掉。sleep方法上的注解原话是:

    	 * @throws  InterruptedException
         *          if any thread has interrupted the current thread. The
         *          <i>interrupted status</i> of the current thread is
         *          cleared when this exception is thrown.
         * 翻译:如果有任何线程打断当前线程的话,当 异常(exception)被抛出的时候,
         * 线程的interrupted status 值会被清除掉,也就是会变为true

    也就是说,sleep方法抛出的异常,会把线程的打断状态清除掉。
    那么我们是怎么知道线程有这个状态呢?如果不调用sleep方法,我们打断线程,看看这个interrupt status会不是true? 我们再写个例子

    public class ThreadSleepTest2 {
        public static void main(String[] args) throws InterruptedException {
             创建一个 t1 线程
            Thread t1 = new Thread(() -> {
                while (true){
                    if (Thread.interrupted()){
                        System.out.println("t1 线程被打断了!我们马上查看 Thread的 interrupted status :"+Thread.interrupted());
                    }else {
                        System.out.println("t1 线程正常执行一项耗时操作,看电影....");
                        watchMV();
                    }
                }
            },"t1-thread");
             启动t1线程
            t1.start();
            /// 这里让主线程休眠1s后,再查看 t1 线程的状态,确保t1线程已经执行
            Thread.sleep(1000L);
            System.out.println("t1 线程当前状态:"+t1.getState());
            System.out.println("此时执行:interrupt()方法 强制打断线程...");
            t1.interrupt();
        }
    
        /**
         * 此方法,模拟一个耗时的操作
         */
        public static void watchMV(){
            try {
                FileInputStream inputStream = new FileInputStream("/Users/xxxx.mp4/");
                ///  这里将byte数组设置小一些,好让此方法执行慢点,可以看出效果
                byte[] bytes = new byte[10];
                // 读入多个字节到字节数组中,read为一次读入的字节数
                while (inputStream.read(bytes) != -1) {
                }
                System.out.println("看完了!");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    

    运行结果
    在这里插入图片描述
    从结果中我们可以看到,当执行了t1.interrupt();方法后,t1线程的看电影方法依然在执行,并未受到影响,但是当方法执行完成后,再次while循环的时候,执行Thread.interrupted()方法进行判断的时候,得到的结果是true,所以程序输出了t1 线程被打断了!我们马上查看 Thread的 interrupted status 。但是在这句输出的最后我们再调用Thread.interrupted()得到的结果却是 false
    这说明了什么???
    说明了Thread.interrupted()方法执行了以后,会改变Thread的 interrupted status 的值。
    我们看一下这个方法的注释。

    /**
         * Tests whether the current thread has been interrupted.  The
         * <i>interrupted status</i> of the thread is cleared by this method.  In
         * other words, if this method were to be called twice in succession, the
         * second call would return false (unless the current thread were
         * interrupted again, after the first call had cleared its interrupted
         * status and before the second call had examined it).
         *
         翻译:测试当前线程是否已经被打断了。 此方法调用后,会清除掉打断状态(interrupted status)
    	换句话说,如果次方法连续调用两次,第二次将会返回false(除非在调用第二次方法之前,线程又被打断)。
         * <p>A thread interruption ignored because a thread was not alive
         * at the time of the interrupt will be reflected by this method
         * returning false.
         *
         * @return  <code>true</code> if the current thread has been interrupted;
         *          <code>false</code> otherwise.
         * @see #isInterrupted()
         * @revised 6.0
         */
        public static boolean interrupted() {
            return currentThread().isInterrupted(true);
        }
    

    从这个案例我们可以看到,线程如果被interrupted的话,是会有状态变化的,证明了线程存在这个状态。
    现在我们回过头来sleep方法注释上面写的下面这段话就能理解所谓的interrupted status是什么意思了。

    if any thread has interrupted the current thread. 
    The <i>interrupted status</i> of the current thread is cleared 
    when this exception is thrown.`
    

    try catch sleep的方法后,执行Thread.interrupted()得到的结果永远都是false,因为catch后会清除了打断状态。

    4.案例展示(sleep不会释放monitors锁)

    package com.kinyang.thread;
    
    import java.util.Scanner;
    
    /**
     * @author KinYang.Lau
     * @date 2021/6/10 6:37 下午
     */
    public class ThreadSleepTest3 {
    
        private static Object monitor = new Object();
        public static void main(String[] args) throws InterruptedException {
             创建一个 t1 线程
            Thread t1 = new Thread(() -> {
                System.out.println("t1 尝试获取锁");
                synchronized (monitor){
                    System.out.println("t1 获取到锁");
                    System.out.println("t1 执行 Thread.sleep() 方法,休眠该线程!");
                    /// 我们执行 sleep 操作,让线程进入休眠,看看其他线程是否能抢到 monitor 锁,
                    ///  如果其他线程可以获取到 monitor 锁后,那么说明sleep会释放monitor锁
                    ///  否则,说明sleep不会释放monitor锁
                    try {
                        Thread.sleep(5000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("t1 线程执行完成");
                }
            },"t1-thread");
             启动t1线程
            t1.start();
            /// 这里让主线程休眠1s后,确保t1线程已经执行,并获取到锁
            Thread.sleep(1000L);
            Thread t2 = new Thread(() -> {
                System.out.println("t2 尝试获取锁");
                synchronized (monitor){
                    System.out.println("t2 获取到锁");
                    System.out.println("执行 t2 方法");
                }
            },"t2-thread");
            t2.start();
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNextLine()) {
                if (scanner.nextLine().equalsIgnoreCase("bye")){
                    System.out.println("再见");
                    break;
                }else {
                    System.out.println("指令错误");
                }
    
            }
        }
    
    }
    
    

    执行结果
    在这里插入图片描述
    我们可以看出 t1 线程sleep后,t2 尝试去获取锁,但是没有获取到,一直到t1线程执行完成后,t2才获取到锁。
    这也说明了,t1线程的sleep方法,并不会释放掉 monitor锁。

    这一点以后我们会和Object.wait()方法做对比,做完对比后你就知道什么是释放锁,什么是不释放锁了。


    总结

    简单总结一下,Thread的sleep方法的特性是:
    1、必须指定休眠时长,休眠后线程转为TIMED_WAITING状态
    2、sleep方法会抛出一个受检查异常InterruptedException,我们必须try catch处理。
    3、sleep抛出异常后,会清除掉“线程打断状态”。所以在catch的代码块里,执行Thread.interrupted()得到的结果永远都是false
    4、sleep不会丢掉monitors,也就是不会释放锁。

    展开全文
  • Thread.sleep(0)的意义

    2021-03-27 20:57:33
    Thread.sleep(0)的作用 Thread.sleep(0)表示挂起0秒,你可能觉得没有作用,但是你觉得写Thread.slep(1000)就有感觉了,其实在MSDN上有相关定义,指定0s休眠,指示应挂起此线程以使其他等待线程能够执行。 Thread....

    Thread.sleep(0)的作用

    Thread.sleep(0)表示挂起0秒,你可能觉得没有作用,但是你觉得写Thread.slep(1000)就有感觉了,其实在MSDN上有相关定义,指定0s休眠,指示应挂起此线程以使其他等待线程能够执行。

    Thread.sleep(0)并非是真的让线程挂起0毫秒,意义在于调用Thread.sleep(0)的当前线程确实被冻结了一下,让其他线程有机会优先执行,Thread.sleep(0)是使你的线程暂时放弃cpu,也是释放一些未使用的时间片给其他线程或者进程使用,就相当于一个让位动作。

    在线程中,调用sleep(0)可以释放cpu时间,让线程马上重新回到就绪队列而非等待队列,sleep(0)释放当前剩余时间片(如果有剩余的话),这样可以让操作系统切换到其他的线程来执行,提高效率,我们可能会经常性的用到Thread.sleep()函数来使线程挂起一段时间,那么你有没有正确理解这个函数的用法呢。

    思考以下两个问题

    • 假设现在时间是2017-4-7 12:00:00.000,如果我们调用Thread.sleep(1000),那么在2017-4-7 12:00:01.000的时候,这个线程会不会被唤醒
    • 某人在代码中使用了Thread.sleep(0)这段代码,既然是0毫秒,那么和去掉这段代码相比,有什么区别呢。

    回顾下操作系统原理

    在操作系统中,cpu有很多的竞争策略,Unix系统使用的是时间片算法,而对于Windows而言是采用的抢占式的,在时间片算法中,所有的进程拍成一个队列,操作系统按照他们的顺序,给每个进程分配一定的时间,即该进程允许运行的时间,如果在时间片结束时该进程还在运行,则cpu讲被剥夺并分配给另外一个进程,如果进程在时间片内阻塞或者结束,则cpu当即进行切换。调度程序所要做的就是维护一张就绪进程表,当进程用完他的时间片,则他就会被移到队列的末尾。

    所谓的抢占式算法,就是说如果一个进程得到了cpu,除非他自己放弃使用cpu,否则讲完全霸占cpu。因此可以看出,在抢占式算法中,操作系统假设所有的进程都是"人品很好的",会主动退出cpu。在抢占式操作系统中,假设有若干个进程,操作系统会根据他们的优先级,饥饿时间(已经多长时间没有使用cpu),给他们算出一个总的优先级来。操作系统就会把cpu交给总优先级最高的这个进程,当这个进程执行完毕或者自己挂起后,操作系统就会重新计算一次所有进程的优先级,然后再挑一个优先级最高的进程,把cpu的控制权交给他。

    我们也可以使用分蛋糕的形式描述这两种算法。假设有源源不断的蛋糕(源源不断的时间),一副刀叉(一个cpu),10个等待吃蛋糕的人(10个进程)。

    Unix分配策略

    如果是Unix系统来负责分配蛋糕,那么他就会这样规定:每个人上来吃1分钟,时间到了换下一个。到最后一个人吃完了就重头开始,于是,不管这10个人是否优先级不同,饥饿程度不同,饭量不同,每个人都只是上来吃1分钟,当然如果有的人本来就不太饿,或者饭量很小,吃了30s之后就饱了,那么他可以和操作系统说:我已经吃饱了(挂起),于是操作系统就会让下一个人来。

    Windows分配策略

    如果是Windows操作系统来负责分蛋糕,那么就会这样规定:根据你们的优先级,饥饿程度去给你们计算一个优先级。优先级最高的那个人,可以上来吃蛋糕——吃到你不想吃为止。等到这个人吃完了,我会重新计算你们的优先级,然后再次将蛋糕分配给优先级最高的人来吃。

    这样看来,这个场面就会有意思了,可能有些人是漂亮美眉,因此具有很高的优先级,于是他就可以经常性的来吃蛋糕,可能另外一个人是个丑男,而且很猥琐,所以优先级特别低,于是好半天了才轮到他一次(以为随着时间的推移,他会越来越饥饿,因此算出来的优先级就会比较高,因此总有一天会轮到他)。而且,如果一不小心是一个大胖子得到了刀叉,因为饭量很大,可能会霸占着蛋糕吃很久,导致其他人在那里咽口水…

    Thread.sleep()函数详解

    那么Thread.sleep()函数是干嘛的呢,还用刚才的蛋糕场景来描述,5号美眉在吃了一次蛋糕之后已经有8分饱了,他觉得在未来的半小时内都不想再吃蛋糕了,那么他就会和操作系统说:在未来半小时内不要再叫我上来吃蛋糕了。这样,操作系统会在随后的半小时里面计算所有人总优先级的时候,就会忽略5号美眉,sleep()函数就是干这个的,他告诉操作系统“在未来的多长时间内我不参与cpu的竞争”。

    所以对于第一个问题:线程是否会被唤醒,答案是不一定,因为你只是告诉操作系统在未来的1000毫秒内我不想再参与cpu的竞争,那么在1000毫秒过去之后,这个时候可能另外一个线程正在使用cpu(正在吃蛋糕),那么这个时候操作系统是不会重新分配cpu的,直到这个线程挂起或者结束,况且,即使这个时候恰巧轮到操作系统重新计算优先级,他也不一定是优先级最高的了,所以cpu还是有可能被其他线程所抢去。于此类似,Thread的Resume函数,就是用来唤醒挂起的线程的,就像上面说的一样,这个函数就是告诉操作系统:从现在开始我要开始竞争cpu了,这个函数的调用并不能使线程立刻获取cpu控制权。

    对于第二个问题:删除Thread.sleep(0)是否有区别,有区别,而且区别很明显。假设刚刚分蛋糕的场景,另外一个7号美眉,他的优先级也非常非常高(非常非常的漂亮),所以操作系统总是让他过来吃蛋糕。而且7号美眉也非常喜欢吃蛋糕,而且饭量也很大,不过7号美眉人品很好,人很善良,没吃几口就会想,如果有别人比我更需要蛋糕,那我就让给他,因此,他每吃几口就会和操作系统说:我们重新计算下所有人的优先级吧,不过操作系统不接受他这个建议,因为操作系统没有这个接口,所以7号美眉就换了个方式说:在未来0s内不要再让我上来吃蛋糕了,这个指令操作系统是接受的,于是操作系统就会重新计算大家的优先级——注意这个时候7号美眉也是一并被计算的,因为0s已经过去了,因此如果没有比7号优先级更好的人出现的话,7号还是会被叫上去吃蛋糕。

    因此,Thread.sleep(0)的作用,就是“触发操作系统重新进行一次cpu竞争”,竞争的结果可能仍然是当前线程获取cpu控制权,但是也许会换成其他线程获取。这也是我们在大循环里经常性的会写一句Thread.sleep(0)的原因,因为这样就给了其他线程,比如Paint线程获取cpu的权利,这样界面就不会卡死在那里。

    其实,虽然上面说到:除非他自己放弃使用cpu,否则将会完全霸占cpu,这个行为其实是收到制约的——操作系统会监控你霸占cpu的情况,如果发现某个线程长时间霸占cpu,会强制这个线程挂起,因此不会出现“一个线程长时间霸占着cpu不放的情况”,至于我们的大循环假死,其实并不是这个线程一直霸占着cpu,实际上这段时间操作系统已经进行多次cpu竞争了,只不过其他的线程在获取cpu控制权之后在短时间内很快就退出了,于是又轮到了这个线程继续执行循环,于是就用了很久才会被操作系统强制挂起,因此反映到界面上,看起来好像一个线程一直在霸占着cpu一样。

    主动放弃运行让系统调度的意义是什么呢?

    为了等待资源、时间,那么你需进入等待队列,如果你已经拥有了所需资源,却还让系统进行调度,这属于资源的浪费,并且调度也是需要时间的
    因为你要等待资源,你需要排队,加入有A和B两个线程为合作关系,A处理一些原始数据,数据处理到一定程度之后,交给B线程处理,在A处理原始数据的时候,B也要做些准备工作,所以,AB是并发的,但是B做好准备之后,需要等待A处理好那些数据,接过A的数据进行继续处理,因此,这个等待,如果A不使用信号或者等待条件来通知B的话,那么B需要一直轮询等待,,查看A是否完成,B所做的这个轮询是否一直会占用cpu来做无用功的循环查看呢?因此,当B查看A没处理完数据的时候,B马上sleep(0)交出b的时间片,让操作系统调度A来运行(假设只有AB两个线程),那么这个时候,A就会得到充分的时间去处理他的数据,代码如下:

    代码示例:

    thread_fun(){
        //prepare work
        while(true){
            if(A is finish){
                break;
            }else{
                thread.sleep(0);  //这里会交出时间片,下一次调度B度的时候,接着执行这个循环
            }
        }
    }
    

    没有sleep(0)的版本

    thread_fun(){
        //prepare work
        while(true){  //这里会一直浪费cpu的时间做死循环轮询,无用功
            if(A is finish){
                break;
            }
        }
    }
    

    如果没有sleep(0),那么B线程可能会执行上万次的while循环,直到他的时间片消耗完,这些都是无用功,而使用了sleep(0)后,B线程每执行一次就会把剩余的时间片让出给A,能让A得到更多的执行次数。

    在线程没退出前,线程有三个状态:就绪,运行,等待。sleep(n)之所以在n毫秒内不会参与cpu的竞争,是因为当调用sleep(n)的时候,线程由运行态转换为等待状态,线程被放入等待队列,等待定时器n毫秒之后,线程就会从等到状态转换为就绪状态,被放入就绪队列,等待队列的线程是不会参与cpu竞争的,只有就绪状态的线程才会参与cpu的竞争,所谓的调度就是根据一定的算法,从就绪队列中选择一个线程来执行。

    而sleep(0)之所以会马上参与cpu竞争,只因为调用sleep(0)后,因为0的原因,线程直接回到就绪状态,只要进入就绪状态,就会参与cpu竞争。

    展开全文
  • 大多数问题都有自己的特定用例:Java – alternative to thread.sleepIs there any better or alternative way to skip/avoid using Thread.sleep(1000) in Java?我的问题是非常通用的用例.等待条件完成.做一些操作....
  • Thread.sleep()概述

    2021-01-17 00:31:49
    Thread.sleep()被用来暂停当前线程的执行,会通知线程调度器把当前线程在指定的时间周期内置为wait状态。当wait时间结束,线程状态重新变为Runnable并等待CPU的再次调度执行。所以线程sleep的实际时间取决于线程调度...
  • 开始活动onPostExecute()EndpointsTask您的终端任务的应该是一个public final class EndpointsTask extends AsyncTask {private final Intent mUserProfileDisplayIntent;private final WeakReference ...
  • 前言线程并发系列文章:前面几篇文章深入分析...通过本篇文章,你将了解到:1、Thread.sleep 应用及原理2、Thread.yield 应用及原理3、Thread.join 应用及原理4、Object.wait 应用及原理5、Condition.await 应用及原...
  • 在代码分析运行期间,我看到了一个令人惊讶的违规行为:Thread.Sleep()的使用是有缺陷设计的标志 .这种违规导致Peter Richie's article为什么这究竟构成了糟糕的设计 .我们都知道线程创建很昂贵,线程中的阻塞意味着...
  • /*** @Description: 线程休眠Sleep()方法* @Author: weishihuai* @Date: 2018/11/11 21:31* * 1. 注意:sleep()方法使用的位置,如果使用在main线程执行代码中,则阻塞的是main线程。如果在其他线程执行的代码中,则...
  • Java中的Thread.Sleep替代

    2021-07-17 01:49:47
    I've been told that using Thread.Sleep() is a bad solution at times that one would want to make some time interval within a loop of actions in a synchronized method.On the other hand, I have two diffe...
  • I have a requirement to pause a while loop for a specific number of ... I have tried using Thread.sleep(duration) but it is not accurate, especially in a looping scenario. Millisecond accura...
  • Thread.sleep()为什么要抛出中断异常

    千次阅读 2021-08-22 10:47:05
    假设sleep()方法不抛出中断异常,也就是线程没有中断响应能力,会怎么样? 考虑如下场景: 线程A:sleep中 线程B:A别睡了,要关机啦(向A发送中断信号) 线程A:sleep中 这样好吗?这不好。因为线程A对外界...
  • 我被告知使用Thread.Sleep()是一个糟糕的解决方案,有时人们希望在同步方法的动作循环中产生一些时间间隔.另一方面,我有两个不同的线程,它们在我的程序的运行时间和一个共享对象中都是活动的,当我在该共享对象中使用...
  • 我测试的一个关于synchronize关键字的例子,如下当我为静态方法inc()加入syn关键字的时候,如果我把Thread.sleep()注释掉,同步效果发生作用,main方法输出了正确的结果1000可是,如...我测试的一个关于synchronize...
  • Thread.sleep(0)的作用

    2021-03-18 17:18:20
    Thread.sleep(0)的作用 Thread.Sleep(0)作用,就是“触发操作系统立刻重新进行一次CPU竞争”。竞争的结果也许是当前线程仍然获得CPU控制权,也许会换成别的线程获得CPU控制权。这也是我们在大循环里面经常会写一句...
  • sleep()可以使任务暂停执行一段时间,这个时间过后任务重新回到可执行状态。...public class ThreadSleep {public static void main(String[] args) {ThreadSleep ts = new ThreadSleep();new Thread...
  • must be caught or declared to be thrown" and Eclipse says, "Unhandled exception type InterruptedException" 解决方案 Thread.sleep can throw an InterruptedException which is a checked exception....
  • while循环中的Thread.sleep()

    千次阅读 2021-02-12 20:17:31
    我注意到NetBeans警告我在我的Java代码中使用while循环中的Thread.sleep(),所以我已经对这个主题进行了一些研究 . 主要问题似乎是性能问题,当计数器仍处于休眠状态时,你的状态可能会变为真,因此在等待下一次迭代...
  • 线程没有睡觉我有问题.我不能把我的整个代码放在这里.所以,为了重现,这里是一个等待5秒的基本代码.try {int millisec = 5000;System.out.println(new Date());...Thread.sleep(millisec);System.out.p...
  • I have read some threads that said that calling Thread.sleep() in a loop is problematic and is a serious performance issue. But in some cases it seems the most natural thing to do.For example if I wan...
  • 明显是阻塞态。因为就绪态的线程,cpu是...而Thread.sleep()方法可以指定sleep的时间,在这段时间内cpu不可能调度到该线程,故这段时间内该线程应处于阻塞态。当sleep时间结束后,该线程会由阻塞态转为就绪态; ...
  • And then inside one of its methods is callThread(), which I want to print something out, sleep, and return control to MyClass.java's method. How do I do that? Currently, my code goes something like ...
  • 有一个桌面应用程序使用Thread.sleep()来实现长(几分钟或几小时)延迟.这个应用程序从Windows XP到(至少)Windows 7都运行良好.应用程序计算未来需要做多少事情,然后点击Thread.sleep(msToWait).即使系统在等待期间...
  • sleep()是使线程暂停执行一段时间的方法。wait()也是一种使线程暂停执行的方法。例如,当线程执行wait()方法时候,会释放当前的锁,然后让出CPU,进入等待...sleep()方法是Thread类的静态方法,是线程用来控制自身..
  • 不要吞下中断 有时,抛出InterruptedException并不是一种选择,例如Runnable定义的任务调用可... } } catch (InterruptedException e) { // Restore the interrupted status Thread.currentThread().interrupt(); } } }
  • Thread.sleep方法 public static native void sleep(long millis) throws InterruptedException; 1.sleep是一个静态的本地方法 2.参数是毫秒long millis

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 641,535
精华内容 256,614
关键字:

thread.sleep