上一篇文章我们聊了多线程的基础内容,比如为什么要使用多线程,线程和进程之间的不同,以及创建线程的 4 种方式。本文已收录至我的 Github: https://github.com/xiaoqi6666/NYCSDE
今天我们来说一下线程的生命周期和常用 APIs:我们需要非常清楚的知道线程的各种状态,比如排查程序运行慢的原因时,就需要看下是不是哪里被阻塞了;另外它也是面试时非常喜欢问的,如果基础内容都答不好,恐怕直接就挂了。
本文分为两大部分,
关于线程的状态,网上各种说法都有,比较流行的是 5 种或者 6 种。关于 5 种状态的那个版本我没有找到理论依据,如果有小伙伴清楚的也欢迎留言指出。
我这里所写的是根据 java.lang.Thread 的源码,线程有以下 6 大状态:
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITTING,
TIMED_WAITTING,
TERMINATED;
}
先上图,我们再依次来看。

A thread that has not yet started is in this state.
就是指线程刚创建,还没启动的时候,比如刚 new 了一个 thread。
MyThread myThread = new MyThread();
A thread is executing in the Java virtual machine but it may be waiting for other resources from the operating system such as processor.
那么接下来,自然就是要启动线程了,也就是调用 thread 的 start() 方法。
myThread.start();
启动之后,线程就进入了 Runnable 状态。
此时所有的线程都会添加到一个等待队列里,等待“CPU 调度”。
如果抢占到 CPU 的资源,那就执行;如果没抢到,就等着呗,等当前正在执行的线程完成它能执行的时间片之后,再次抢占。
要注意这里在等待的一般是系统资源,而不是锁或者其他阻塞。
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 callingwait()Object.
这里给出了非常明确的 use case,就是被锁在外面的才叫阻塞。所以这里必须要有至少 2 个线程。
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()这里的等待状态是没有时间限制的,可以无限的等下去... 所以需要有人来唤醒:
wait() 进入等待状态的,需要有 notify() 或者 notifyAll() 方法来唤醒;join() 进入等待状态的,需要等待目标线程运行结束。比如在生产者消费者模型里,当没有商品的时候,消费者就需要等待,等待生产者生产好了商品发 notify()。下一篇文章我们会细讲。
导致这个状态的原因如下:
Thread.sleep Object.wait with timeout Thread.join with timeout LockSupport.parkNanos LockSupport.parkUntil
其实就是在上一种状态的基础上,给了具体的时间限制。
那么当时间结束后,线程就解放了。
A thread that has exited is in this state.
这里有 3 种情况会终止线程:
stop() 方法,现在已经被弃用;线程一旦死亡就不能复生。
如果在一个死去的线程上调用 start() 方法,那么程序会抛出 java.lang.IllegalThreadStateException。
接下来我们说说多线程中常用的 11 个 APIs。
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");
}
}
}

所以 join() 能够保证某个线程优先执行,而且会一直让它执行完,再回归到公平竞争状态。
join() 方法其实是用 wait() 来实现的,我们来看下这个方法。
wait() 其实并不是 Thread 类的方法,而是 Object 里面的方法。
该方法就是让当前对象等待,直到另一个对象调用 notify() 或者 notifyAll()。
当然了,我们也可以设定一个等待时长,到时间之后对象将会自动苏醒。
yield 本身的中文意思是屈服,用在这里倒也合适。
yield() 表示当前线程主动让出 CPU 资源一下,然后我们再一起去抢。
注意这里让一下真的只是一下,从“执行中”回到“等待 CPU 分配资源”,然后所有线程再一起抢占资源。
顾名思义,这个方法就是让当前线程睡一会,比如说,
myThread.sleep(1000); // 睡眠 1 秒钟
它会抛出一个 InterruptedException 异常,所以还要 try catch 一下。
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 类型。
该方法可以获取当前线程名称。
这个名称可以自己设置,比如:
Thread t = new Thread(new MyRunnable(), "壹齐学");
该方法是获取线程的 Id.
线程也有优先级的哦~
虽然优先级高的线程并不能百分百保证一定会先执行,但它是有更大的概率被先执行的。
优先级的范围是 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() 的异常。
当然啦,我们也是可以自己设置某个线程的优先级的。
设置的优先级也需要在规定的 1-10 的范围内哦,如果不在这个范围也会抛异常。
最后我们来说下 stop() 方法,也是前文提到过的强制停止线程的一种方式,但现在已被弃用,因为会引起一些线程安全方面的问题。
好了,以上就是有关线程状态和常用 API 的介绍了。相信大家看完之后对线程的整个流程应该有了清晰的认识,其实里面还有很多细节我没有展开,毕竟这是多线程的第 2 讲,更深入的内容我们慢慢来。
如果你喜欢这篇文章,记得给我点赞留言哦~你们的支持和认可,就是我创作的最大动力,我们下篇文章见!
我是小齐,纽约程序媛,终生学习者,每天晚上 9 点,云自习室里不见不散!
**更多干货文章见我的 Github: https://github.com/xiaoqi6666/NYCSDE
原文:https://www.cnblogs.com/nycsde/p/14022631.html