线程生命周期

现代操作系统在运行一个程序时,会为其创建一个进程。例如,启动一个Java程序,操作系统就会创建一个Java进程。现代操作系统调度的最小单元是线程,也叫轻量级进程(Light Weight Process),在一个进程里可以创建多个线程,这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。处理器在这些线程上高速切换,让使用者感觉到这些线程在同时执行。

CPU再切换线程是会导致线程存在各种状态,线程从创建到死亡其中存在不同的生命状态;本文将对线程生命周期进行全面的介绍。

Java线程的状态可以从java.lang.Thread的内部枚举类java.lang.Thread$State得知:

public enum State {

    NEW,

    RUNNABLE,

    BLOCKED,

    WAITING,

    TIMED_WAITING,

    TERMINATED;
}

当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,它要经过 新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked、WATING、TIMED_WATING)和死亡(Dead)5种状态。尤其是当线程启动以后,它不可能一直"霸占"着CPU独自运行,所以CPU需要在多条线程之间切换,于是 线程状态也会多次在运行、阻塞之间切换

  • 新建状态(New)

    API注释

    /**
    * Thread state for a thread which has not yet started.
    * 线程实例尚未启动时候的线程状态
    */
    NEW,

    当线程对象创建后,即进入新建状态,如:Thread t = new MyThread();

  • 就绪状态(Runnable)

    API注释

    /**
    * Thread state for a runnable thread. A thread in the runnable
    * state is executing in the Java virtual machine but it may
    * be waiting for other resources from the operating system
    * such as processor.
    */
    RUNNABLE, 可运行状态下线程的线程状态。可运行状态下的线程在Java虚拟机中执行,但它可能执行等待操作系统的其他资源,例如处理器。

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

    当Java线程实例调用了Thread#start()之后,就会进入RUNNABLE状态。RUNNABLE状态可以认为包含两个子状态:READYRUNNING

    • READY:该状态的线程可以被线程调度器进行调度使之更变为RUNNING状态。
    • RUNNING:该状态表示线程正在运行,线程对象的run()方法中的代码所对应的的指令正在被CPU执行。
    • 当Java线程实例Thread#yield()方法被调用时或者由于线程调度器的调度,线程实例的状态有可能由RUNNING转变为READY,但是从线程状态Thread#getState()获取到的状态依然是RUNNABLE。例如:
    • public class ThreadState1 {
      
          public static void main(String[] args) throws Exception {
      Thread thread = new Thread(()-> {
      while (true){
      Thread.yield();
      }
      });
      thread.start();
      Thread.sleep(2000);
      System.out.println(thread.getState());
      }
      }
      // 输出结果
      RUNNABLE
  • 运行状态(Running)

    当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。

  • WATING状态

WAITING无限期的等待状态,这种状态下的线程不会被分配CPU执行时间。当一个线程执行了某些方法之后就会进入无限期等待状态,直到被显式唤醒,被唤醒后,线程状态由WAITING更变为RUNNABLE然后继续执行。

  • TIMED WATING状态

TIMED WAITING就是有限期等待状态,它和WAITING有点相似,这种状态下的线程不会被分配CPU执行时间,不过这种状态下的线程不需要被显式唤醒,只需要等待超时限期到达就会被VM唤醒,有点类似于现实生活中的闹钟。

  • 阻塞状态(Blocked)

处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态。

阻塞状态分类

  1. 等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
  2. 同步阻塞:线程在获取synchronized同步锁失败(因为锁被其它线程占用),它会进入到同步阻塞状态;
  3. 其他阻塞:通过调用线程的sleep()或join()或发出I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
  • 死亡状态

    线程执行完毕或者是异常退出,该线程结束生命周期。

线程相关方法

public class Thread{
// 线程的启动
public void start();
// 线程体
public void run();
// 已废弃
public void stop();
// 已废弃
public void resume();
// 已废弃
public void suspend();
// 在指定的毫秒数内让当前正在执行的线程休眠
public static void sleep(long millis);
// 同上,增加了纳秒参数
public static void sleep(long millis, int nanos);
// 测试线程是否处于活动状态
public boolean isAlive();
// 中断线程 - 进入阻塞状态
public void interrupt();
// 测试线程是否已经中断
public boolean isInterrupted();
// 测试当前线程是否已经中断
public static boolean interrupted();
// 等待该线程终止
public void join() throws InterruptedException;
// 等待该线程终止的时间最长为 millis 毫秒
public void join(long millis) throws InterruptedException;
// 等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒
public void join(long millis, int nanos) throws InterruptedException;
}

笔者的微信公众号,每天一篇好文章:

java并发编程-线程生命周期的更多相关文章

  1. java并发编程基础—生命周期与线程控制

    一.线程生命周期 线程被创建启动以后,他既不是一启动就进入执行状态,也不是一直处于执行状态,在线程的生命周期中,它要经过新建(New).就绪(Runnable).运行(Running).阻塞(Bloc ...

  2. Java 并发编程 | 线程池详解

    原文: https://chenmingyu.top/concurrent-threadpool/ 线程池 线程池用来处理异步任务或者并发执行的任务 优点: 重复利用已创建的线程,减少创建和销毁线程造 ...

  3. java并发编程 线程基础

    java并发编程 线程基础 1. java中的多线程 java是天生多线程的,可以通过启动一个main方法,查看main方法启动的同时有多少线程同时启动 public class OnlyMain { ...

  4. java并发编程 | 线程详解

    个人网站:https://chenmingyu.top/concurrent-thread/ 进程与线程 进程:操作系统在运行一个程序的时候就会为其创建一个进程(比如一个java程序),进程是资源分配 ...

  5. Java并发编程:线程间通信wait、notify

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  6. Java并发编程:线程和进程的创建(转)

    Java并发编程:如何创建线程? 在前面一篇文章中已经讲述了在进程和线程的由来,今天就来讲一下在Java中如何创建线程,让线程去执行一个子任务.下面先讲述一下Java中的应用程序和进程相关的概念知识, ...

  7. Java并发编程——线程的基本概念和创建

    一.线程的基本概念: 1.什么是进程.什么是是线程.多线程? 进程:一个正在运行的程序(程序进入内存运行就变成了一个进程).比如QQ程序就是一个进程. 线程:线程是进程中的一个执行单元,负责当前进程中 ...

  8. Java并发编程——线程池的使用

    在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统 ...

  9. Java并发编程——线程池

    本文的目录大纲: 一.Java中的ThreadPoolExecutor类 二.深入剖析线程池实现原理 三.使用示例 四.如何合理配置线程池的大小 一.Java中的ThreadPoolExecutor类 ...

随机推荐

  1. 如何使用JPA的@Formula注解?

    背景描述 我们经常会在项目中用到一些数据字典,在存储和传输时使用Code,在前端展示时使用Name,这样做的好处是便于系统维护,比如项目中用到了"医院"这个名称,如果后期需求发生变 ...

  2. 7.SortSet排序集合类型操作

    Sort Set排序集合类型 (1)介绍 和set一样sorted set也是string类型元素的集合,不同的是每个元素都会关联一个权.通过权值可以有序的获取集合中的元素 该Sort Set类型适合 ...

  3. 【C++】变量

    注意:以下内容摘自文献[1],修改了部分内容. 1.变量:在程序运行期间其值可以改变的量称为变量.一个变量应该有一个名字,并在内存中占据一定的存储单元,在该存储单元中存放变量的值.变量名代表内存中的一 ...

  4. Linux实验

    实验一 Linux系统安装与简单配置 一.实验目的 1.掌握Linux系统安装的分区准备. 2.掌握Linux系统的安装步骤. 3.掌握Linux系统分区的挂载和卸载. 4.掌握Linux系统的启动和 ...

  5. TCP 半连接队列和全连接队列满了会发生什么?又该如何应对?

    前言 网上许多博客针对增大 TCP 半连接队列和全连接队列的方式如下: 增大 TCP 半连接队列的方式是增大 /proc/sys/net/ipv4/tcp_max_syn_backlog: 增大 TC ...

  6. nmap script 总结

    一些常用脚本用法 nmap 脚本一些支持笼统扫描 usag. nmap -F --script auth 10.0.0.1 auth: 负责处理鉴权证书(绕开鉴权)的脚本 broadcast: 在局域 ...

  7. 02 . Tomcat多实例并用Nginx反代

    Tomcat虚拟主机 ​ 一个应用程序在某一个端口启动运行产生了一系列的进程就是一个实例,让tomcat启动两个不同的相互独立的进程,产生两个不同的套接字,分别运行在不同的端口,让不同的端口响应不同的 ...

  8. [03]HTML基础之行内标签

    1.<ruby>标签 显示东亚字符的发音(如中文,日文等),与<rp>,<rt>标签搭配. //<ruby>为单个发音字符的容器,<rp>为 ...

  9. docker出现相同的image条目的删除办法

    一.问题:在测试docker安装的prometheus系统时,由于异常操作,使用docker image ls出现了两条一模一样的条目,如下: [root@ELK prometheus]# docke ...

  10. Beta冲刺——测试随笔

    这个作业属于哪个课程 软件工程 这个作业要求在哪里 团队作业第五次--Beta冲刺 这个作业的目标 Beta冲刺 作业正文 正文 github链接 项目地址 其他参考文献 无 一.测试工作安排 项目 ...