02. 线程的五种状态

java 多线程系列文章列表, 请查看目录: 《java 多线程学习笔记》

1. 线程的状态

java 中, 线程有五种状态: 新建(New), 就绪(Ready), 运行(Running), 阻塞(Blocked), 死亡(Dead). 线程启动后, 不可能一直霸占着CPU 独自运行, CPU 需要在多个线程之间切换, 因此线程状态也就会在运行, 就绪之间来回切换.

1.1 线程的五种状态之间的转换

  • 新建状态: 通过new 创建线程后, 线程便进入了新建状态
  • 新建 -> 就绪: 线程执行start()方法之后便进入了就绪状态, 开始等待cpu 资源
  • 就绪 -> 运行: 当线程抢到了cpu资源, 则线程进入运行状态. 但线程不可能一直占有cpu资源, 会轮转.
  • 运行 -> 阻塞:
    • 线程调用sleep()休眠, 主动放弃所占有的处理器资源
    • 线程调用了一个阻塞式IO 方法, 在该方法返回结果前, 线程处于阻塞状态
    • 线程视图获得一个同步监视器, 但该同步监视器正被其它线程所持有
    • 线程等待某个通知(notify)
    • 线程调用了suspend()方法, 将自身挂起. 此方法容易产生死锁, 不建议使用
  • 阻塞 -> 就绪:
    • sleep() 休眠时间到了, 自动进入就绪状态
    • 调用的IO 阻塞方法执行结束
    • 成功获取到了同步监视器
    • 等到了notify 通知
    • 挂起的线程通过resume 重新唤醒
  • 就绪 -> 死亡:
    • 执行stop()方法, 容易产生死锁, 不推荐使用
    • run()/call() 方法正常执行结束
    • 线程执行过程中发送Error 或 Exception 异常

02. 线程的五种状态_第1张图片

1.2 线程的死亡

  • 当线程正常或异常结束后, 线程便处于死亡状态
  • 当线程死亡后, 不要尝试再次调用start()方法重新启动线程, 因为已死亡的线程不能被重新执行.
  • 不建议直接调用stop()方法杀死线程, 容易产生死锁.

2. 线程休眠

2.1 休眠相关API

方法签名 方法描述
public static native void sleep(long millis) throws InterruptedExceptio 休眠多少毫秒, 线程进入阻塞状态
public static native void yield() 使线程暂停依稀, 直接进入就绪状态

2.2 sleep()和yield()区别

  • sleep()方法暂停当前线程后, 会给其它任意线程机会, 不区分线程优先级; yield 方法休眠后, 只会给比当前线程优先级相同或更高的线程机会抢占资源
  • sleep()方法会将线程转入阻塞状态, 等阻塞结束后再转入就绪状态; yield 方法直接将线程转为就绪状态, 不进入阻塞状态
  • sleep()方法抛出InterruptedException 异常, yield()方法不抛出任何异常
  • sleep()方法比yield()方法具有更好的移植性, 因此通常不建议使用yield()方法

3. 知识扩展

3.1 操作系统的线程调度策略

操作系统的线程调度细节取决于底层平台所采用的策略, 常见的有两种调度策略:

  • 抢占式调度策略: 这是现代的桌面和服务器操作系统广泛使用的调度策略. Cpu 会自动中断线程的运行, 使cpu 资源被不同的线程持有, 以保证线程的并发执行
  • 协作式调度策略: 只有当线程主动调用了sleep()或yield()方法后, 才会放弃所占有的cpu资源. 也就是说必须由线程主动放弃资源后, 其它线程才有执行的机会

3.2 线程的阻塞与运行

  • 一个CPU, 在同一时刻只能有一个线程处于运行状态. 只是由于CPU 在不同线程之间的切换速度很快, 导致我们看起来像多线程并发执行一样.
  • 当一个线程获得cpu 资源后, 便开始执行run()方法的执行体, 也就是处于运行状态. 但是线程开始运行后, 并不可能一直处于运行状态(触发执行体执行时间瞬间结束).
  • 线程在执行过程中会被中断, 以进入阻塞状态, 目的是为了给其它线程执行的机会.

你可能感兴趣的:(juc)