sleep、yield、wait和join

文章目录

  • 概述
  • `sleep`
  • `yield`
  • `wait`
  • `join`
  • 状态转移
    • 5个状态
  • `sleep`和`wait`的区别
  • `sleep`和`yield`的区别
  • 参考

概述

  • Thread.sleep(long)Thread.yield()都是Thread类的静态方法,由Thread直接调用。

  • join()Thread的非静态方法,由Thread对象调用。

  • wait()notify()notifyAll()java.lang.Object的方法。

    它们是用于协调多个线程对共享数据的存取,所以必须在Synchronized语句块内使用这三个方法。Synchronized用于保护共享数据,阻止其他线程对共享数据的存取。但是这样程序的流程就很不灵活了,如何才能在当前线程还没退出Synchronized数据块时让其他线程也有机会访问共享数据呢?此时就用这三个方法来灵活控制。

    • wait()方法使当前线程暂停执行并释放对象锁标志,让其他线程可以进入Synchronized数据块,当前线程被放入对象等待池中。
    • 当调用notify()方法后,将从对象的等待池中移走一个任意的线程并放到锁标志等待池中,只有锁标志等待池中的线程能够获取锁标志;如果锁标志等待池中没有线程,则notify()不起作用。
    • notifyAll()则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。

sleep

Thread类的静态方法,必须带一个时间参数。会让当前线程休眠进入阻塞状态并释放CPU,提供其他线程运行的机会且不考虑优先级,但如果有同步锁则sleep不会释放锁即其他线程无法获得同步锁 可通过调用interrupt()方法来唤醒休眠线程。

yield

让出CPU调度Thread类的方法,类似sleep只是不能由用户指定暂停多长时间 ,并且yield()方法只能让同优先级的线程有执行的机会。 yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。调用yield方法只是一个建议,告诉线程调度器我的工作已经做的差不多了,可以让别的相同优先级的线程使用CPU了,没有任何机制保证采纳。

wait

Object类的方法,必须放在循环体和同步代码块中,执行该方法的线程会释放锁,进入线程等待池中等待被再次唤醒(notify随机唤醒,notifyAll全部唤醒,线程结束自动唤醒)即放入锁池中竞争同步锁。

join

一种特殊的wait当前运行线程调用另一个线程的join方法,当前线程进入阻塞状态直到另一个线程运行结束等待该线程终止。 注意该方法也需要捕捉异常。等待调用join方法的线程结束,再继续执行。

状态转移

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-owUCdjNY-1584961184409)(D:\Tongwei\JAVA\ToWork\总结\线程\sleep_yield_wait_join_status.png)]

5个状态

  • 新建状态(New):当线程对象对创建后,即进入了新建状态;

  • 就绪状态(Runnable):当调用线程对象的start()方法,线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,获取CPU的使用权,并不是说执行了start()此线程立即就会执行

  • 运行状态(Running):可运行状态(runnable)的线程获得了CPU 时间片,执行程序代码。当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;

  • 阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,也即让出了CPU时间片,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:

    1. 等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;JVM会把该线程放入等待队列中。

    2. 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,获取synchronized同步锁失败,它会进入同步阻塞状态,则JVM会把该线程放入锁池(lock pool)中。

    3. 其他阻塞:通过调用线程的sleep()join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

  • 死亡状态(Dead):线程执行完了或者因异常退出,该线程结束生命周期。

只有runnable到running时才会占用CPU时间片,其他都会出让CPU时间片。

线程的资源有不少,但应该包含CPU资源和锁资源这两类。

sleepwait的区别

sleep(long mills):让出CPU资源,但是不会释放锁资源。

wait():让出CPU资源和锁资源。

锁是用来线程同步的,sleep(long mills)虽然让出了CPU,但是不会让出锁,其他线程可以利用CPU时间片了,但如果其他线程要获取sleep(long mills)拥有的锁才能执行,则会因为无法获取锁而不能执行,继续等待。但是那些没有和sleep(long mills)竞争锁的线程,一旦得到CPU时间片即可运行了。

sleepyield的区别

  1. sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
  2. 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;
  3. sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;
  4. sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。

参考

  • sleep、yield、wait、join的区别

你可能感兴趣的:(Java)