Java多线程学习(三)

Java 中线程的基本状态

Java 线程状态简介

Java 语言中,线程共有六种状态,分别简介如下:

  • NEW :线程已经创建,未调用 start 方法
  • RUNNABLE :运行状态,该状态包括线程的就绪、运行两个状态
  • BLOCKED :阻塞状态,通常线程在等待锁的过程为该状态
  • WAITING :等待状态,当前线程需要等待其他线程作出通知或中断
  • TIME_WAITING :超时等待状态,其与 WAITING 状态不同点为超时等待可在一段时间后自行返回
  • TERMINATED :终止状态,当前线程已经执行完毕

Java线程状态转换关系图如下图:
java_thread
由线程状态转换图中可以很清晰的看出Java语言中线程状态的转换关系,下面展示一个示例,该示例主要是通过构造线程并模拟出线程当前的状态。具体代码及查看如下。

Java线程状态实探

从一个实例谈起

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/**
* @description: 模拟线程运行时状态
* @author: jayden
* @date: 2017/11/29
*/
public class ThreadStatus {
public static void main(String[] args) {
new Thread(new TimeWaiting(), "TimeWaiting Thread").start();
new Thread(new Waiting(), "Waiting Thread").start();
new Thread(new Blocked(), "GetLock Thread").start();
new Thread(new Blocked(), "Blocked Thread").start();
}
}
/**
* 构造线程的time_waiting状态
*/
class TimeWaiting implements Runnable {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 模拟线程的waiting状态
*/
class Waiting implements Runnable {
@Override
public void run() {
synchronized (this) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 模拟线程的blocked状态
*/
class Blocked implements Runnable {
@Override
public void run() {
synchronized (Blocked.class) {
try {
TimeUnit.SECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

运行上述代码,我们可以通过 Java 自带的小工具 jstack 查看线程信息。具体操作如下:

  1. 打开 commond,输入 jps 命令,查看全部 Java 线程的 pid,如下图:
    jps
    由图中可以看出,当前系统中有三个 Java 进程在运行,我们要查看的便是 pid 为 17628 的 ThreadStatus 进程。
  2. 输入 jstack [pid],查看进程信息,如下图:
    jstack
  3. 分析线程信息。
    查看 jstack 输出的信息,我们可以找到我们在程序中启动的四个进程,如下图:
    stack_info
    由图中信息可以看出:
  • Blocked Thread 线程当前为 BLOCKED 状态,正在等待获取 Blocked.class 对象的锁;
  • GetLock Thread 线程当前为 TIMED_WAITING 状态,这是因为它获取到了 Blocked.class 对象的锁,并通过 sleep() 进入了超时等待状态;
  • Waiting Thread 线程当前为 WAITING 状态,这是因为它获取到了自身对象的锁,并通过 wait() 方法进入了等待状态;
  • TimeWaiting Thread 线程当前为 TIMED_WAITING 状态,其并未获得任何锁,仅是通过 TimeUnit.SECONDS.sleep() 方法使得当前线程进入超时等待状态。

再看示例程序

TimeWaiting

1
2
3
4
5
6
7
8
9
10
11
12
class TimeWaiting implements Runnable {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

TimeWaiting 类的定义中,未获得任何对象的锁,仅是通过 TimeUnit.SECONDS.sleep() 方法使得当前线程进入 超时等待 状态。
注:此处 TimeUnit.SECONDS.sleep() 方法与 Thread.sleep() 方法使用类似。但在可读性上,TimeUnit.SECONDS.sleep() 方法更优。

Waiting

1
2
3
4
5
6
7
8
9
10
11
12
13
class Waiting implements Runnable {
@Override
public void run() {
synchronized (this) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

Waiting 类的定义中,我们通过 Object.wait() 方法使得线程进入等待状态。此处获取对象锁的原因是由于 Java 语言中,当某个线程试图等待一个自己并不拥有的对象(O)的监控器或者通知其他线程等待该对象(O)的监控器时,会抛出 java.lang.IllegalMonitorStateException 异常。
调用 wait() 方法后,当前线程状态便变成了 等待 状态。

Blocked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Blocked implements Runnable {
@Override
public void run() {
synchronized (Blocked.class) {
try {
TimeUnit.SECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

Blocked 类的定义中,首先该线程需要获取 Blocked.class 对象的对象锁,然后对当前线程执行 sleep,进入超时等待。
主方法中,我们创建了两个任务为 Blocked 的线程,由于第一个线程首先会获取 Blocked.class 对象的对象锁,然后进入等待状态(不释放锁),所以 GetLock Thread 会在获取锁的状态下,进入等待状态;而 Blocked Thread 则会由于等待 Blocked.class 对象的对象锁而进入 阻塞状态

总结

本文仅是一篇真对 Java 线程的基本介绍,针对 Thread 类源码分析以及常见方法的使用,将在日后的文章中有所介绍。由于初学且行文时间较赶,如果错误多多指教。有相关讨论可以联系 Email:JaydenRansom@outlook.com