0%

Java Thread 的状态

概述

Java中线程的状态(也可以理解为生命周期),主要有以下几种:NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED 。
这些状态存在于Thread类中的一个枚举中如下:

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

具体状态

NEW

  • JDK原生的注释说的是一个还没有开始(started)的线程, 也就是说当new了一个线程之后,并没有调用start方法的时候,线程的状态就是NEW。

RUNNABLE

  • 这个状态代表线程处于一个可运行的状态,但是不一定会执行,因为要考虑cpu核心数等影响,直到CPU时间分片给到当前线程,才会真正的执行。

BLOCKED

  • 阻塞状态,通常都是在等待获取某个监视器的锁的时候会处于当前状态。
  • 已知:
    • 等待synchronized获取监视器对象锁的时候,线程的状态为:BLOCKED (on object monitor)
    • 线程从WAITING/TIMED_WAITING(object.wait()方法)状态唤醒后,因为需要重新获取synchronized监视器对象,会先进入BLOCKED状态,待获取了监视器对象锁后,变为RUNNABLE状态。

WAITING

  • 等待状态,一般是由于调用了如下方法而进入等待状态:
    • Object#wait()
    • Thread#join()
    • LockSupport#park()
  • 线程进入WAITING状态后,等待其他线程调用对应的通知对象才会唤醒。比如:
    • Object.notify()
    • Object.notifyAll()

TIMED_WAITING

  • 一样是等待状态,但是区别是带有一个超时时间,超过这个时间后,线程会被自动唤醒。以下几个方法会进入该状态:
    • Thread.sleep(long)
    • Object#wait(long)
    • Thread#join(long)
    • LockSupport#parkNanos(long)
    • LockSupport#parkUntil(long)

TERMINATED

  • 线程的终止状态,也就是当前线程已经执行完成。

状态转换

关于释放资源方面

锁的释放

  • 通过synchronized获取监视器对象锁之后,有如下几个方式会释放锁资源:
    • 方法或者代码块正常执行完成
    • 方法或者代码块抛出异常,代码终止执行
    • 调用监视器对象的wait方法,会释放锁资源
  • 补充:
    • 调用Thread.sleep方法不会释放锁资源

CPU资源的释放

- Thread.sleep(),会释放CPU资源,但是不会释放锁。
- Thread.yield(),会尝试放弃CPU资源,但是不会释放锁。(可能放弃后又立即获取到)
- 还有suspend()方法,由于已经过时,不再解释。

其他

obj.notify/notifyAll区别

  • notify会随机唤醒监视obj的一个线程,具体是哪个无法指定,由JVM确定。
  • notifyAll会唤醒所有监视obj的线程,然后重新去竞争,只有一个可以获取到资源。

wait/sleep/yield区别

  • sleep()方法会释放CPU资源但是不会释放锁资源。
  • wait()方法会释放CPU资源和锁资源。
  • yield()方法仅释放CPU执行权,锁仍然占用,线程会被放入就绪队列,会在短时间内再次执行。

虚假唤醒

  • 就是在没有调用obj.notify/notifyAll的前提下,obj.wait被唤醒了。
  • 等待线程即使没有收到正确的信号,也能够执行后续的操作,这就可能影响程序执行的正常逻辑。
  • 为了防止假唤醒,保存信号的成员变量将在一个while循环里接受检查,而不是在if表达式里。
  • 示例如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class Sign {
    private final Object obj = new Object();
    private boolean flag = false;

    public void doWait() throws InterruptedException {
    synchronized (obj) {
    while (!flag) {
    obj.wait();
    }
    flag = false;
    }
    }
    public void doNotify() {
    synchronized (obj) {
    flag = true;
    obj.notifyAll();
    }
    }
    }

RUNNABLE状态

  • Java中没有线程所谓的RUNNING和READY状态,这两个状态合并到一起,为RUNNABLE状态。
  • 也就是说即使线程处于RUNNABLE状态,也不一定就正在执行,可能正在等待CPU时间分片。