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

文章目录
  1. 1. 前言
  2. 2. Java多线程
  3. 3. Java线程的生命周期
  4. 4. New
  5. 5. Runnable
  6. 6. Blocked
  7. 7. Waiting
  8. 8. Timed Waiting
  9. 9. Terminated
  10. 10. 结论

前言

网上有很多关于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
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枚举定义的六个状态,并通过示例演示出来。文中有不对的地方,欢迎大家指正,谢谢!