> 文档中心 > 线程的声明周期

线程的声明周期

嗯,线程,这东西听起来高深,但其实就像是我们生活中那些忙碌的“服务员”。它们从“入职”到“下班”,每个阶段都有自己的故事。首先,它们“入职”后只是静静躺在那儿,啥都不干。然后你一通“start()”,它们就活了过来,准备大展身手,这就进入了“就绪”状态。这时候,它们就像服务员等上菜,随时准备上场。接下来,当CPU一有机会,它们就赶紧“干活”,这就是“运行”状态。这时候要是它们碰上诸如“sleep()”或者“wait()”这些命令,就像是遇到了食材供应不上或者等电梯一样,就得“阻塞”了。最后,它们完成了任务,或者遇到了什么异常,或者被人直接“stop()”了,这就“死亡”啦。

那么,如何管理好这些线程呢?就像是 managing 一群忙碌的厨师,我们要确保他们高效工作,不会互相打架。比如说,如何避免线程饥饿,让每个线程都有公平的工作机会?又比如,如何有效利用线程池,让资源最大化?这些都是值得深入探讨的。总之,线程的生命周期就像一场精心编排的舞蹈,只有理解了每个舞步,才能让整个系统流畅优雅。

线程的声明周期

吴含宝宝

  • 线程生命周期状态:新建–就绪–运行–死亡
  1. 新建
    当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态,此时它和其他Java对象一样,仅仅有JVM为其分配了内存,并初始化了实例变量的值,此时的线程对象并没有任何线程的动态特征,程序也不会执行它的线程体run()。
  2. 就绪
    但是当线程对象调用了star()方法之后,就不一样了,线程就从新建出状态转为就绪状态。JVM会为其创建方法调用栈和程序计数器,当然,处于这个转台中的线程并没有开始运行,只是表示已具备了运行的条件,随时可以被调度。至于什么时候被调度,取决于JVM里线程调度器的调度。
    注意:
    程序只能对新建状态的线程调用start(),并且只能调用一次,如果对非新建状态的线程,如已启动的线程或已死亡的线程调用start()都会报错IllegalThreadStateException异常。
  3. 运行
    如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程体代码,则该线程处于运行状态。如果计算机只有一个CPU,在任何时刻只有一个线程处于运行状态,如果计算机有多个处理器,将会有多个线程并行(Parallel)执行。当然,美好的时光总是短暂的,而且CPU讲究雨露均沾。对于抢占式策略的系统而言,系统会给每个可执行的线程一个小时间段来处理任务,当该时间用完,系统会剥夺该线程所占用的资源,让其回到就绪状态等待下一次被调度。此时其他线程将获得执行机会,而在选择下一个线程时,系统会适当考虑线程的优先级。
  4. 阻塞
    当在运行过程中的线程遇到如下情况时,线程会进入阻塞状态:
    线程调用了sleep()方法,主动放弃所占用的CPU资源;
    线程试图获取一个同步监视器,但该同步监视器正被其他线程持有;
    线程执行过程中,同步监视器调用了wait(),让它等待某个通知(notify);
    线程执行过程中,同步监视器调用了wait(time)
    线程执行过程中,遇到了其他线程对象的加塞(join);
    线程被调用suspend方法被挂起(已过时,因为容易发生死锁);
    当前正在执行的线程被阻塞后,其他线程就有机会执行了。针对如上情况,当发生如下情况时会解除阻塞,让该线程重新进入就绪状态,等待线程调度器再次调度它:
    线程的sleep()时间到;
    线程成功获得了同步监视器;
    线程等到了通知(notify);
    线程wait的时间到了
    加塞的线程结束了;
    被挂起的线程又被调用了resume恢复方法(已过时,因为容易发生死锁);
  5. 死亡
    线程会以以下三种方式之一结束,结束后的线程就处于死亡状态:
    run()方法执行完成,线程正常结束
    线程执行过程中抛出了一个未捕获的异常(Exception)或错误(Error)
    直接调用该线程的stop()来结束该线程(已过时,因为容易发生死锁)