摘自:https://www.cnblogs.com/nycsde/p/14022631.html
10 张图聊聊线程的生命周期和常用 APIs
上一篇文章我们聊了多线程的基础内容,比如为什么要使用多线程,线程和进程之间的不同,以及创建线程的 4 种方式。本文已收录至我的 Github: https://github.com/xiaoqi6666/NYCSDE
今天我们来说一下线程的生命周期和常用 APIs:我们需要非常清楚的知道线程的各种状态,比如排查程序运行慢的原因时,就需要看下是不是哪里被阻塞了;另外它也是面试时非常喜欢问的,如果基础内容都答不好,恐怕直接就挂了。
本文分为两大部分,
- 线程的 6 大状态;
- 多线程常用的 APIs:
- join()
- wait()
- notify()
- yield()
- sleep()
- currentThread()
- getName()
- getId()
- getPriority()
- setPriority()
- 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.
那么接下来,自然就是要启动线程了,也就是调用 thread
的 start()
方法。
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 callingwait()
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()
方法之后,线程进入等待状态。
这里的等待状态是没有时间限制的,可以无限的等下去... 所以需要有人来唤醒:
- 如果是通过
wait()
进入等待状态的,需要有notify()
或者notifyAll()
方法来唤醒; - 如果是通过
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