Java线程生命周期:你知道Java线程有几种状态吗? | Java提升营

Java线程生命周期:你知道Java线程有几种状态吗?

前言

网上有很多关于Java线程生命周期的文章,大部分都说Java线程有5种状态,你认可他们的说法吗?反正我不认可,java中的线程实际上有6种状态。贴出证据:

1
2
3
4
5
6
7
8
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}

Thread.class 中定义了线程状态的State枚举类.

官方的注释:A thread can be in only one state at a given point in time. These states are virtual machine states which do not reflectany operating system thread states.

翻译出来为:一个线程在某一时刻只能有一种状态,这些状态是虚拟机状态,它不反映任何操作系统的线程状态。

再看一下RUNNABLE状态的注释: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虚拟机中执行,但它可能正在等待操作系统的其他资源,比如处理器。

Java中的RUNNABLE状态实际上包含了Ready与Running的状态,所以你可以完全无视网上那些不准确的说法,这种问题的答案往往就在源码与javadoc中。

接下来,我们将详细讨论Java的核心概念-线程的生命周期。

Java多线程

在Java语言中,多线程由Thread的核心概念驱动。在其生命周期中,线程会经历各种状态:

Java线程的生命周期

java.lang.Thread类含一个State状态枚举。在任何给定的时间点,线程只能处于以下某一个状态:

  1. NEW: 新创建的尚未开始执行的线程
  2. RUNNABLE: 正在运行或正在等待资源准备执行
  3. BLOCKED: 等待获取锁以进入或重新进入同步块或方法
  4. WAITING: 没有时间限制地等待其他线程执行特定操作
  5. TIMED_WAITING: 在指定时间段内等待其他线程执行特定操作
  6. TERMINATED: 执行完毕

New

一个新线程是指已创建但尚未启动的线程。它保持这种状态,直到我们使用start()方法启动它为止。

1
2
3
Runnable runnable = new NewState();
Thread t = new Thread(runnable);
Log.info(t.getState());

由于我们尚未启动提到的线程,因此方法t.getState()会输出:

1
NEW

Runnable

当我们创建了一个新线程并调用start()方法时,它会从NEW变为RUNNABLE状态。处于此状态的线程正在运行可运行(正在等待系统分配资源)。

在多线程环境中,线程调度程序为每个线程分配固定的时间。因此它会运行特定的时间,然后将资源让给其他RUNNABLE线程。

例如,让我们在之前的代码中添加t.start()方法,然后尝试访问其当前状态:

1
2
3
4
Runnable runnable = new NewState();
Thread t = new Thread(runnable);
t.start();
Log.info(t.getState());

此代码极大概率会输出:

1
RUNNABLE

请注意,在此示例中,并不能保证在执行t.getState()时,它仍然处于RUNNABLE状态。

可能是线程调度程序立即对其进行了调度,并已经完成了执行。在这种情况下,我们可能会得到不同的输出。

Blocked

当前线程等待锁并尝试访问被其他线程持有锁的一段代码时,它将进入此状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class BlockedState {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new DemoThreadB());
Thread t2 = new Thread(new DemoThreadB());

t1.start();
t2.start();

Thread.sleep(1000);

Log.info(t2.getState());
System.exit(0);
}
}

class DemoThreadB implements Runnable {
@Override
public void run() {
commonResource();
}

public static synchronized void commonResource() {
while(true) {
// Infinite loop to mimic heavy processing
// 't1' won't leave this method
// when 't2' try to enter this
}
}
}

解释代码:

  1. 我们创建了两个不同的线程– t1和t2
  2. 定义了一个同步的commonResource方法,进入该方法的线程将无限循环,也就是持有锁的线程将不会释放锁
  3. 当t1进入此方法时,它将保持无限循环。
  4. 现在,我们启动t2时,它将尝试访问commonResource()方法,该方法已经被t1访问,因此,t2将保持在BLOCKED状态

在这种状态下,我们调用t2.getState()并获得以下输出:

1
BLOCKED

Waiting

一个线程在等待其他线程执行特定操作时处于WAITING状态。 根据JavaDocs的说法,任何线程都可以通过调用以下任何一种方法进入此状态:

  1. object.wait()
  2. thread.join()
  3. LockSupport.park()

请注意,在wait()和join()中,我们没有定义任何超时期限,下一节将介绍这种情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class WaitingState implements Runnable {
public static Thread t1;

public static void main(String[] args) {
t1 = new Thread(new WaitingState());
t1.start();
}

public void run() {
Thread t2 = new Thread(new DemoThreadWS());
t2.start();

try {
t2.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Log.error("Thread interrupted", e);
}
}
}

class DemoThreadWS implements Runnable {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Log.error("Thread interrupted", e);
}

Log.info(WaitingState.t1.getState());
}
}

解释代码:

  1. 我们创建并开始了t1
  2. t1创建一个t2并启动它
  3. 当t2执行时,我们调用t2.join(),这会将t1置于等待状态,直到t2完成执行
  4. 由于t1等待t2完成,因此我们在t2中调用t1.getState()

如您所料,这里的输出是:

1
WAITING

Timed Waiting

当一个线程在规定的时间内等待另一个线程执行特定操作时,该线程处于TIMED_WAITING状态。

根据JavaDocs的介绍,有五种方法可以将线程置于TIMED_WAITING状态:

  1. thread.sleep(long millis)
  2. wait(int timeout) or wait(int timeout, int nanos)
  3. thread.join(long millis)
  4. LockSupport.parkNanos
  5. LockSupport.parkUntil
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class TimedWaitingState {
public static void main(String[] args) throws InterruptedException {
DemoThread obj1 = new DemoThread();
Thread t1 = new Thread(obj1);
t1.start();

// The following sleep will give enough time for ThreadScheduler
// to start processing of thread t1
Thread.sleep(1000);
Log.info(t1.getState());
}
}

class DemoThread implements Runnable {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Log.error("Thread interrupted", e);
}
}
}

我们创建并启动了线程t1,该线程进入睡眠状态的超时时间为5秒。输出将是:

1
TIMED_WAITING

Terminated

完成执行或异常终止时,它处于TERMINATED状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class TerminatedState implements Runnable {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new TerminatedState());
t1.start();
// The following sleep method will give enough time for
// thread t1 to complete
Thread.sleep(1000);
Log.info(t1.getState());
}

@Override
public void run() {
// No processing in this block
}
}

在这里,当我们启动线程t1时,下一条语句Thread.sleep(1000)为t1的完成提供了足够的时间,因此该程序将输出显示为:

1
TERMINATED

结论

通过本文,我们了解了Java线程的生命周期。讲解了Thread.State枚举定义的六个状态,并通过示例演示出来。文中有不对的地方,欢迎大家指正,谢谢!

给老奴加个鸡腿吧 🍨.