Java 线程到底有几种状态

网上对Java线程状态的讨论很多,有说是5种状态的,有说是6种状态的,甚至还有说是7种状态的。那到底有几种状态呢?
查看JDK源码java.lang.Thread.State这个枚举类,可以明确的看到,它只定义了6种状态

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

6大状态说明

新建状态(New)

查看jdk源码对NEW状态注释

1
2
3
4
/**
* Thread state for a thread which has not yet started.
*/
NEW

可以看到NEW状态是一种线程刚创建,但还没有开始的状态。
代码示例

1
2
3
4
public static void main(String[] args) {
Thread thread = new Thread();
System.out.println("thread线程状态: " + thread.getState());//输出结果thread线程状态: NEW
}

就绪状态(RUNNABLE)

查看jdk源码对RUNNABLE状态注释

1
2
3
4
5
6
7
/**
* Thread state for a runnable thread. 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

可以看到RUNNABLE状态是一种线程正在JVM虚拟机执行,但是它也可能正在等待操作系统其它资源的状态。
这句话有一点难理解,说它正在执行很好理解,但是为什么说它可能是在等待呢?等待状态不应该是WAITING状态吗?
可以这样理解: 主线程(main线程)调用了一个线程(thread线程)的start()方法之后,thread线程就加入到了一个线程池中,
该线程池中的线程状态都是就绪状态(RUNNABLE),都在等待被线程调度器选中,获取CPU的使用权。
线程池中还在等待,还没有获取CPU使用权的线程就是一种标准的就绪状态(RUNNABLE),
而那个被线程调度器选中,获取到CPU使用权的线程其实就是一种运行中状态(RUNNING),但是Java把这2种状态都称为就绪状态(RUNNABLE)。

代码示例

1
2
3
4
5
public static void main(String[] args) {
Thread thread = new Thread();
thread.start();
System.out.println("thread线程状态: " + thread.getState());//输出结果thread线程状态: RUNNABLE
}

阻塞状态(BLOCKED)

查看jdk源码对BLOCKED状态注释

1
2
3
4
5
6
7
8
/**
* 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 calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED

可以看到BLOCKED状态是一种线程因为正在等待一个监视器锁而阻塞的状态。monitor lock是Java对象的内置锁。
当线程进入到一个同步块或者同步方法的时候,或者因为调用了Object.wait()方法而重新进入到同步块或者同步方法的时候,线程就会处于阻塞状态(BLOCKED)。
这句话有些拗口,换一个思路来理解会好一些。
当线程(假设A线程)执行到synchronized修饰的代码块或者方法的时候,该线程需要去获取一把对象的内置锁(monitor lock),
但是此时这把对象的内置锁可能已经被其他的线程(假设B线程)获取到了,A线程就会处于阻塞状态(BLOCKED),一直到B线程释放对象的内置锁。
代码示例

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
public static void main(String[] args) {
Object monitorLock = new Object();
Thread A= new Thread(()-> {
synchronized (monitorLock) {
try {
System.out.println(Thread.currentThread().getName()+"线程状态1: " + Thread.currentThread().getState());//输出结果A线程状态1: RUNNABLE
Thread.sleep(300);//A线程休眠300,可以防止B线程执行的时候A线程已经执行结束释放锁了.
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
A.setName("A");
A.setPriority(Thread.MAX_PRIORITY);//可以防止线程B在A前面执行.
A.start();
Thread B= new Thread(()-> {
synchronized (monitorLock) {
System.out.println(Thread.currentThread().getName()+"线程状态2: " + Thread.currentThread().getState());//输出结果B线程状态2: RUNNABLE
}
});
B.setName("B");
B.setPriority(Thread.MIN_PRIORITY);//可以防止线程B在A前面执行.
B.start();
Thread.sleep(100);//主线程休眠100,可以防止主线程在线程A和线程B之前执行结束.
System.out.println("A线程状态3: " + A.getState());//输出结果A线程状态3: TIMED_WAITING
System.out.println("B线程状态4: " + B.getState());//输出结果B线程状态4: BLOCKED
}
/**
输出结果:
A线程状态1: RUNNABLE
A线程状态3: TIMED_WAITING
B线程状态4: BLOCKED
B线程状态2: RUNNABLE
**/

还一种情况是A线程已经获取到了对象的内置锁(monitor lock),之后调用了对象的wait()方法,此时线程A会释放对象的内置锁,
线程B就可以获取到对象内置锁执行,线程B调用了对象的notify()/notifyAll()方法之后,线程A会被唤醒,此时线程B释放CPU资源,线程A获取到CPU使用权,线程A变成就绪状态(RUNNABLE),
线程A重新进入到synchronized修饰的代码块或者方法,需要去获取对象的内置锁(monitor lock),但是线程B此时还持有对象内置锁,线程A就会变成阻塞状态(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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public static void main(String[] args) {
Object monitorLock = new Object();
Thread A= new Thread(()-> {
synchronized (monitorLock) {
try {
System.out.println(Thread.currentThread().getName()+"线程状态1: " + Thread.currentThread().getState());//输出结果A线程状态1: RUNNABLE
monitorLock.wait();
System.out.println(Thread.currentThread().getName()+"线程状态2: " + Thread.currentThread().getState());//输出结果A线程状态2: RUNNABLE
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
A.setName("A");
A.setPriority(Thread.MAX_PRIORITY);//可以防止线程B在A前面执行.
A.start();
Thread B= new Thread(()-> {
synchronized (monitorLock) {
System.out.println(Thread.currentThread().getName()+"线程状态3: " + Thread.currentThread().getState());//输出结果B线程状态3: RUNNABLE
monitorLock.notifyAll();
try {
Thread.sleep(300); // 休眠100,可以防止线程B直接执行结束,这样的话对象锁会直接释放,线程A会直接获取到变成就绪状态(RUNNABLE)
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"线程状态4: " + Thread.currentThread().getState());//输出结果B线程状态4: RUNNABLE
}
});
B.setName("B");
B.setPriority(Thread.MIN_PRIORITY);//可以防止线程B在A前面执行.
B.start();
Thread.sleep(100);//主线程休眠100,可以防止主线程在线程A和线程B之前执行结束.
System.out.println("A线程状态5: " + A.getState());//输出结果A线程状态5: BLOCKED
System.out.println("B线程状态6: " + B.getState());//输出结果B线程状态6: TIMED_WAITING
}
/**
输出结果:
A线程状态1: RUNNABLE
B线程状态3: RUNNABLE
A线程状态5: BLOCKED
B线程状态6: TIMED_WAITING
B线程状态4: RUNNABLE
A线程状态2: RUNNABLE
**/

等待状态(WAITING)

查看jdk源码对WAITING状态注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING

可以看到WAITING状态是一种线程正在等待另外一个线程执行特殊指令的状态,
当线程调用Object.wait()/Thread.join()/LockSupport.park()方法的时候,就会进入WAITING状态。
什么是特殊指令呢?
举2个例子:
当一个线程调用Object.wait()进入到WAITING状态时候,
需要另外一个线程调用Object.notify()/Object.notifyAll()方法,这个线程才会从WAITING状态唤醒,变成RUNNABLE状态;
需要注意的是:调用线程的interrupt()方法也会将线程以抛异常的方式唤醒
代码示例

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 static void main(String[] args) {
Object monitorLock = new Object();
Thread A= new Thread(()-> {
synchronized (monitorLock) {
try {
System.out.println(Thread.currentThread().getName()+"线程状态1: " + Thread.currentThread().getState());//输出结果A线程状态1: RUNNABLE
monitorLock.wait();//没有其他线程调用notify()/notifyAll()的话,程序会一直卡在这里
System.out.println(Thread.currentThread().getName()+"线程状态2: " + Thread.currentThread().getState());//永远不会输出
} catch (InterruptedException e) {
//如果有其它线程调用了A.interrupt()方法,程序会抛出中断异常走到这里,方法执行结束
e.printStackTrace();
}
}
});
A.setName("A");
A.start();
Thread.sleep(100);//主线程休眠100,可以防止主线程在线程A之前执行结束.
System.out.println("A线程状态3: " + A.getState());//输出结果A线程状态3: WAITING
}
/**
输出结果:
A线程状态1: RUNNABLE
A线程状态3: WAITING
**/

网上看到一个很形象的例子:
1:女客人去卫生间上厕所,关上门;
2:女客人发现厕所没厕纸了,条件不满足;
3:女客人不上厕所了,打开门;
4:女客人等待工作人员拿厕纸来;
5:工作人员拿着厕纸进入厕所,关上门;
6:工作人员增加厕纸;
7:工作人员通知女客人有厕纸可以上厕所了;
8:工作人员离开厕所,打开门;
9:女客人顺利上厕所,关上门;
avatar
当一个线程调用Thread.join()进入到WAITING状态时候,
需要指定的线程执行结束,这个线程才会从WAITING状态唤醒,变成RUNNABLE状态。
代码示例

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
public static void main(String[] args) {
Thread main = Thread.currentThread();
Thread A= new Thread(()-> {
try {
System.out.println(Thread.currentThread().getName()+"线程状态1: " + Thread.currentThread().getState());//输出结果A线程状态1: RUNNABLE
Thread.sleep(300);//休眠300,是执行结果更加明显
System.out.println("main线程状态2: " + main.getState());//输出结果main线程状态2: WAITING
} catch (InterruptedException e) {
e.printStackTrace();
}
}
);
A.setName("A");
A.start();
A.join();//主线程执行A.join(),主线程会进入到WAITING状态
System.out.println("A线程状态3: " + A.getState());//输出结果A线程状态3: TERMINATED
System.out.println("main线程状态4: " + main.getState());//输出结果main线程状态4: RUNNABLE
}
/**
输出结果:
A线程状态1: RUNNABLE
main线程状态2: WAITING
A线程状态3: TERMINATED
main线程状态4: RUNNABLE
**/

超时等待状态(TIMED_WAITING)

查看jdk源码对TIMED_WAITING状态注释

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING

可以看到TIMED_WAITING状态是一种线程在一定时间内等待的状态,
当线程调用Thread.sleep/Object.wait(long)/Thread.join(long)/LockSupport.parkNanos/LockSupport.parkUntil方法的时候,就会进入TIMED_WAITING状态。
为什么需要这个状态呢?
考虑到下面的一个场景:
线程A正在等待线程B唤醒,但是当线程B执行完自己的业务逻辑之后,正准备执行Object.notify()/Object.notifyAll()方法时,
线程B因为某种原因被杀死了,那线程A将永远不会被唤醒了。
代码示例

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 static void main(String[] args) {
Object monitorLock = new Object();
Thread A= new Thread(()-> {
synchronized (monitorLock) {
try {
System.out.println(Thread.currentThread().getName()+"线程状态1: " + Thread.currentThread().getState());//输出结果A线程状态1: RUNNABLE
monitorLock.wait(100);//没有线程唤醒,等待100之后,主动唤醒
System.out.println(Thread.currentThread().getName()+"线程状态2: " + Thread.currentThread().getState());//输出结果A线程状态2: RUNNABLE
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
A.setName("A");
A.start();
Thread.sleep(100);//主线程休眠100,可以防止主线程在线程A之前执行结束.
System.out.println("A线程状态3: " + A.getState());//A线程状态3: TIMED_WAITING
}
/**
输出结果:
A线程状态1: RUNNABLE
A线程状态3: TIMED_WAITING
A线程状态2: RUNNABLE
**/

可以看到Java API唤醒一个线程有3种方式:
1: 调用Object.notify()/Object.notifyAll()通知唤醒;
2: 调用Thread.interrupt()中断唤醒;
3: TIMED_WAITING状态超时唤醒;
同时JavaDoc中也提到一种虚假唤醒(spurious wakeup)的状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* A thread can also wake up without being notified, interrupted, or
* timing out, a so-called <i>spurious wakeup</i>. While this will rarely
* occur in practice, applications must guard against it by testing for
* the condition that should have caused the thread to be awakened, and
* continuing to wait if the condition is not satisfied. In other words,
* waits should always occur in loops, like this one:
* <pre>
* synchronized (obj) {
* while (&lt;condition does not hold&gt;)
* obj.wait(timeout);
* ... // Perform action appropriate to condition
* }
* </pre>
*/

虚假唤醒(spurious wakeup)的状态是一种现实中罕见的,极少发生的状态,JavaDoc没说是什么原因,
只是告诉我们线程被唤醒之后,我们需要循环判断业务条件是否满足,不满足的时候,需要让线程继续等待,
换句话说,wait方法需要永远在循环中调用。

终止状态(TERMINATED)

查看jdk源码对TERMINATED状态注释

1
2
3
4
5
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED

可以看到TERMINATED状态是一种线程已经执行完毕的状态。这个就没什么好说的了。