上一篇文章我们聊了多线程的基础内容,比如为什么要使用多线程,线程和进程之间的不同,以及创建线程的 4 种方式。本文已收录至我的 Github: https://github.com/xiaoqi6666/NYCSDE

今天我们来说一下线程的生命周期和常用 APIs:我们需要非常清楚的知道线程的各种状态,比如排查程序运行慢的原因时,就需要看下是不是哪里被阻塞了;另外它也是面试时非常喜欢问的,如果基础内容都答不好,恐怕直接就挂了。

本文分为两大部分,

  1. 线程的 6 大状态;
  2. 多线程常用的 APIs:

    1. join()
    2. wait()
    3. notify()
    4. yield()
    5. sleep()
    6. currentThread()
    7. getName()
    8. getId()
    9. getPriority()
    10. setPriority()
    11. stop()

线程状态

关于线程的状态,网上各种说法都有,比较流行的是 5 种或者 6 种。关于 5 种状态的那个版本我没有找到理论依据,如果有小伙伴清楚的也欢迎留言指出。

我这里所写的是根据 java.lang.Thread 的源码,线程有以下 6 大状态:

public enum State {
  NEW,
  RUNNABLE,
  BLOCKED,
  WAITTING,
  TIMED_WAITTING,
  TERMINATED;
}

先上图,我们再依次来看。

1. New

A thread that has not yet started is in this state.

就是指线程刚创建,还没启动的时候,比如刚 new 了一个 thread

MyThread myThread = new MyThread();

2. Runnable

A thread is executing in the Java virtual machine but it may be waiting for other resources from the operating system such as processor.

那么接下来,自然就是要启动线程了,也就是调用 threadstart() 方法。

myThread.start();

启动之后,线程就进入了 Runnable 状态。

此时所有的线程都会添加到一个等待队列里,等待“CPU 调度”。

如果抢占到 CPU 的资源,那就执行;如果没抢到,就等着呗,等当前正在执行的线程完成它能执行的时间片之后,再次抢占。

要注意这里在等待的一般是系统资源,而不是锁或者其他阻塞。

3. Blocked

Thread state for a thread blocked waiting for a monitor lock.

A thread in the blocked state is waiting for a monitor lock to enter a synchronized block/method or reenter a synchronized block/method after calling wait() Object.

这里给出了非常明确的 use case,就是被锁在外面的才叫阻塞。所以这里必须要有至少 2 个线程。

4. Waiting

A thread in the waiting state is waiting for another thread to perform a particular action.

那具体有哪些原因呢?

A thread is in the waiting state due to calling one of the following methods:

  • Object.wait with no timeout
  • Thread.join with no timeout
  • LockSupport.park

所以说,当调用了

  • wait()
  • join()
  • park()

    方法之后,线程进入等待状态。

这里的等待状态是没有时间限制的,可以无限的等下去... 所以需要有人来唤醒

  1. 如果是通过 wait() 进入等待状态的,需要有 notify() 或者 notifyAll() 方法来唤醒;
  2. 如果是通过 join() 进入等待状态的,需要等待目标线程运行结束。

比如在生产者消费者模型里,当没有商品的时候,消费者就需要等待,等待生产者生产好了商品发 notify()。下一篇文章我们会细讲。

5. Timed_waiting

导致这个状态的原因如下:

  • Thread.sleep
  • Object.wait with timeout
  • Thread.join with timeout
  • LockSupport.parkNanos
  • LockSupport.parkUntil

其实就是在上一种状态的基础上,给了具体的时间限制

那么当时间结束后,线程就解放了。

6. Terminated

A thread that has exited is in this state.

这里有 3 种情况会终止线程:

  • 执行完所有代码,正常结束;
  • 强制被结束,比如调用了 stop() 方法,现在已经被弃用;
  • 抛出了未捕获的异常。

线程一旦死亡就不能复生。

如果在一个死去的线程上调用 start() 方法,那么程序会抛出 java.lang.IllegalThreadStateException

接下来我们说说多线程中常用的 11 个 APIs。

APIs

1. join()

join() 方法会强制让该线程执行,并且一直会让它执行完。

比如上一篇文章的例子是两个线程交替执行的,那么我们这里该下,改成调用小齐线程.join(),那么效果就是先输出 小齐666

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for(int i = 0; i < 100; i++) {
            System.out.println("小齐666:" + i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(new MyRunnable());
        t.start();
        t.join();

        for(int i = 0; i < 100; i++) {
            System.out.println("主线程" + i + ":齐姐666");
        }
    }
}

图2

所以 join() 能够保证某个线程优先执行,而且会一直让它执行完,再回归到公平竞争状态。

join() 方法其实是用 wait() 来实现的,我们来看下这个方法。

2. wait() and notify()

wait() 其实并不是 Thread 类的方法,而是 Object 里面的方法。

该方法就是让当前对象等待,直到另一个对象调用 notify() 或者 notifyAll()

当然了,我们也可以设定一个等待时长,到时间之后对象将会自动苏醒。

4. yield()

yield 本身的中文意思是屈服,用在这里倒也合适。

yield() 表示当前线程主动让出 CPU 资源一下,然后我们再一起去抢。

注意这里让一下真的只是一下,从“执行中”回到“等待 CPU 分配资源”,然后所有线程再一起抢占资源。

5. sleep()

顾名思义,这个方法就是让当前线程睡一会,比如说,

myThread.sleep(1000); // 睡眠 1 秒钟

它会抛出一个 InterruptedException 异常,所以还要 try catch 一下。

6. currentThread()

Returns a reference to the currently executing thread object.

该方法是获取当前线程对象。

注意它是一个 static 方法,所以直接通过 Thread 类调用。

比如打印当前线程

System.out.println(Thread.currentThread());

前文的例子中,它会输出:

Thread[Thread-0,5,main]
Thread[main,5,main]

没错,它的返回值也是 Thread 类型。

7. getName()

该方法可以获取当前线程名称。

这个名称可以自己设置,比如:

Thread t = new Thread(new MyRunnable(), "壹齐学");

8. getId()

该方法是获取线程的 Id.

9. getPriority()

线程也有优先级的哦~

虽然优先级高的线程并不能百分百保证一定会先执行,但它是有更大的概率被先执行的。

优先级的范围是 1-10,我们来看源码:

    /**
     * The minimum priority that a thread can have.
     */
    public final static int MIN_PRIORITY = 1;

   /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;

如果不在这个范围,JDK 抛出 IllegalArgumentException() 的异常。

10. setPriority()

当然啦,我们也是可以自己设置某个线程的优先级的。

设置的优先级也需要在规定的 1-10 的范围内哦,如果不在这个范围也会抛异常。

11. stop()

最后我们来说下 stop() 方法,也是前文提到过的强制停止线程的一种方式,但现在已被弃用,因为会引起一些线程安全方面的问题。

好了,以上就是有关线程状态和常用 API 的介绍了。相信大家看完之后对线程的整个流程应该有了清晰的认识,其实里面还有很多细节我没有展开,毕竟这是多线程的第 2 讲,更深入的内容我们慢慢来。

如果你喜欢这篇文章,记得给我点赞留言哦~你们的支持和认可,就是我创作的最大动力,我们下篇文章见!

我是小齐,纽约程序媛,终生学习者,每天晚上 9 点,云自习室里不见不散!

更多干货文章见我的 Github: https://github.com/xiaoqi6666/NYCSDE

10 张图聊聊线程的生命周期和常用 APIs的更多相关文章

  1. Java 多线程(三)—— 线程的生命周期及方法

    这篇博客介绍线程的生命周期. 线程是一个动态执行的过程,它也有从创建到死亡的过程. 线程的几种状态 在 Thread 类中,有一个枚举内部类: 上面的信息以图片表示如下: 第一张图: 第二张图:把等待 ...

  2. Java多线程学习(三)---线程的生命周期

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

  3. Java并发编程:线程的生命周期是个怎样的过程?

    前言 在日常开发过程中,如果我们需要执行一些比较耗时的程序的话,一般来说都是开启一个新线程,把耗时的代码放在线程里,然后开启线程执行.但线程是会耗费系统资源的,如果有多个线程同时运行,互相之间抢占系统 ...

  4. JAVA面试题 线程的生命周期包括哪几个阶段?

    面试官:您知道线程的生命周期包括哪几个阶段? 应聘者: 线程的生命周期包含5个阶段,包括:新建.就绪.运行.阻塞.销毁. 新建:就是刚使用new方法,new出来的线程: 就绪:就是调用的线程的star ...

  5. Java多线程之线程的生命周期

    Java多线程之线程的生命周期 一.前言 当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态.在线程的生命周期中,它要经过新建(New).就绪(Runnable).运行(R ...

  6. java线程的生命周期及五种基本状态

    一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下下面这张较为经典的图: 上图中基本上囊括了Java中多线程各重要知识点.掌握了上图中的各知识点,Java中的多线程也就基本上掌 ...

  7. Java多线程 2 线程的生命周期和状态控制

    一.线程的生命周期 线程状态转换图: 1.新建状态 用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态.处于新生状态的线程有自己的内存空间,通过调用start方法进入就 ...

  8. 第24章 java线程(3)-线程的生命周期

    java线程(3)-线程的生命周期 1.两种生命周期流转图 ** 生命周期:**一个事物冲从出生的那一刻开始到最终死亡中间的过程 在事物的漫长的生命周期过程中,总会经历不同的状态(婴儿状态/青少年状态 ...

  9. Java多线程——线程的生命周期和状态控制

    一.线程的生命周期 线程状态转换图: 1.新建状态 用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态.处于新生状态的线程有自己的内存空间,通过调用start方法进入就 ...

随机推荐

  1. c++之广度优先搜索

    广度优先搜索BFS(Breadth First Search)也称为宽度优先搜索,它是一种先生成的结点先扩展的策略. 在广度优先搜索算法中,解答树上结点的扩展是按它们在树中的层次进行的.首先生成第一层 ...

  2. 关于什么时候用,怎么用:ExecuteNonQuery(),还有其它返回值

    ExecuteScalar方法返回的类型是object类型,这个方法返回sql语句执行后的第一行第一列的值,由于不知到sql语句到底是什么样的结构(有可能是int,有可能是char等等),所以Exec ...

  3. 44. Spring Security FAQ春季安全常见问题

    第44.1节,“一般问题” 第44.2节,“常见问题” 第44.3节,“春季安全架构问题” 第44.4节,“常见”如何“请求 44.1 General Questions 第44.1.1节,“Spri ...

  4. 关于数据库新建用户提示“用户、组或角色‘’XXX‘’在当前数据库中已已存在”的解决办法

    一般在还原数据库后,给这个数据库添加一个登录名时出现. 例如数据库备份文件中已经包含了用户abc,现在还原了数据库,然后发现现有数据库中没有abc这个用户,想要新建一个abc用户,作为该数据库的own ...

  5. Ubuntu操作系统(文件传输)

    首先选择Ubuntu版本为偶数版本--(系统比较稳定软件源比较齐全) Ubuntu和windows之间的文件传输首先在Windows上安装连接工具winscp 在Ubuntu开启SSH服务(https ...

  6. 火题大战Vol.1 A.

    火题大战Vol.1 A. 题目描述 给定两个数\(x\),\(y\),比较\(x^y\) 与\(y!\)的大小. 输入格式 第一行一个整数\(T\)表示数据组数. 接下来\(T\)行,每行两个整数\( ...

  7. Istio Routing 实践掌握virtualservice/gateway/destinationrule/AB版本发布/金丝雀发布

    原文 在学习像 Istio 这样的新技术时,看一下示例应用程序总是一个好主意. Istio repo 有一些示例应用程序,但它们似乎有各种不足. 文档中的 BookInfo 是一个很好的示例. 但是, ...

  8. Url跳转漏洞常见

    Url跳转漏洞常见出现点: 1.用户登录.统一身份认证处,认证完后会跳转. 2.用户分享.收藏内容过后,会跳转. 3.跨站点认证.授权后,会跳转. 4.站内点击其它网址链接时,会跳转. Url跳转漏洞 ...

  9. java 工具类Integer

    Integer 是lang包下的工具类 为了更加熟悉Integer中的方法使用和理解 进行了一部分代码和原代码的总结 Intrger工具类方法: * * int parseInt(String s) ...

  10. vmware 虚拟机共享 windows 目录

    1.vmware 配置: 2.虚拟机进行配置: 虚拟机安装vmware-tools 3.虚拟机中挂载sr0(cdrom): [root@bogon ~]# mount /dev/sr0 /mnt/ m ...