# 线程状态及其转换

## 一、线程状态

Java中定义线程的状态有6种，可以查看Thread类的State枚举：

```java
public static enum State
  {
    NEW,  RUNNABLE,  BLOCKED,  WAITING,  TIMED_WAITING,  TERMINATED;

    private State() {}
  }
```

1. 初始（NEW）：新创建了一个线程对象，还没调用start方法；
2. 运行（RUNNABLE）：java线程中将就绪（ready）和运行中（running）统称为运行（RUNNABLE）。线程创建后调用了该对象的start方法，此时处于就绪状态，当获得CPU时间片后变为运行中状态；
3. 阻塞（BLOCKED）：表现线程阻塞于锁；
4. 等待（WAITING）：进入该状态的线程需要等待其他线程做出一些特定动作（通知或中断）；
5. 超时等待（TIMED\_WAITING）：该状态不同于WAITING，它可以在指定时间后自行返回；
6. 终止（TERMINATED）：表示该线程已经执行完毕。

## 二、线程状态转换

来看一张线程状态转换图：

![](/files/-M7h5bX1vzU55bbf4S-o)

下面从代码实例看线程的各个状态：

### 2.1 超时等待

调用`sleep`，`wait`，`join`这些加上时间参数的情况就会进入超时等待，表示等待了指定的时间后就不等待了。

```java
public class Test {
    public static void main(String[] args) throws Exception {
        System.out.println("start");
        Thread.sleep(100000);
        System.out.println("end");
    }
}
```

通过Java VisualVM打印线程dump可以看到此线程处于`TIMED_WAITING`状态：

```
...
"main" #1 prio=5 os_prio=0 tid=0x00000000055b3800 nid=0x4e8c waiting on condition [0x000000000558f000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at Test.main(Test.java:4)

   Locked ownable synchronizers:
        - None
...
```

### 2.2 等待

调用`wait`，`join`这些不加时间参数的情况就会进入等待，表示一直等待直到被触发继续执行。

```java
public class Test {
    public static void main(String[] args) throws Exception {
        Thread1 t = new Thread1();
        t.start();
        t.join();
    }

    static class Thread1 extends Thread {
        @Override
        public void run() {
            System.out.println("start");
            try {
                Thread.sleep(100000);
            } catch (InterruptedException e) {}
            System.out.println("end");
        }
    }
}
```

同样通过线程dump可以看到主线程处于`WAITING状态`，子线程处于`TIMED_WAITING`状态：

```
...
"Thread-0" #11 prio=5 os_prio=0 tid=0x0000000020bf7000 nid=0x4f94 waiting on condition [0x000000002189f000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at Test$Thread1.run(Test.java:13)

   Locked ownable synchronizers:
        - None
...
"main" #1 prio=5 os_prio=0 tid=0x0000000004f63800 nid=0x431c in Object.wait() [0x0000000004eef000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076b6e0898> (a Test$Thread1)
        at java.lang.Thread.join(Unknown Source)
        - locked <0x000000076b6e0898> (a Test$Thread1)
        at java.lang.Thread.join(Unknown Source)
        at Test.main(Test.java:5)

   Locked ownable synchronizers:
        - None
...
```

下面演示wait方法导致的等待状态：

```java
public class Test {
    public static int i = 0;
    public static void main(String[] args) throws Exception {
        Thread1 t = new Thread1();
        t.start();
        synchronized (t) {
            System.out.println("等待子线程");
            t.wait();
        }
        System.out.println("主线程结束");
    }

    static class Thread1 extends Thread {
        @Override
        public void run() {
            synchronized (this) {
                for (int i = 0; i < 10; i++) {
                    try {
                        System.out.println(i);
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                }
                notify();
            }
        }
    }

}
```

通过线程堆栈观察，主线程同样处于等待WAITING状态：

```
...
"main" #1 prio=5 os_prio=0 tid=0x0000000005983800 nid=0xb54 in Object.wait() [0x00000000058df000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076b6e0aa8> (a Test$Thread1)
        at java.lang.Object.wait(Unknown Source)
        at Test.main(Test.java:8)
        - locked <0x000000076b6e0aa8> (a Test$Thread1)

   Locked ownable synchronizers:
        - None
...
```

### 2.3 阻塞

```java
public class Test {
    public static void main(String[] args) throws Exception {
        Thread1 t = new Thread1();
        t.start();
        test();
    }

    static class Thread1 extends Thread {
        @Override
        public void run() {
            test();
        }
    }

    static synchronized void test() {
        System.out.println(Thread.currentThread().getName() + " -- start");
        try {
            Thread.sleep(100000);
        } catch (InterruptedException e) {}
        System.out.println(Thread.currentThread().getName() + " -- end");
    }
}
```

通过线程dump可以看到子线程处于阻塞（BLOCKED）状态，主线程处于超时等待（TIMED\_WAITING）状态：

```
...
"Thread-0" #11 prio=5 os_prio=0 tid=0x0000000020ef1800 nid=0x4df4 waiting for monitor entry [0x0000000021b9f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at Test.test(Test.java:16)
        - waiting to lock <0x000000076b6dea88> (a java.lang.Class for Test)
        at Test$Thread1.run(Test.java:11)

   Locked ownable synchronizers:
        - None
...
"main" #1 prio=5 os_prio=0 tid=0x00000000051e3800 nid=0x3ee8 waiting on condition [0x000000000517f000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at Test.test(Test.java:18)
        - locked <0x000000076b6dea88> (a java.lang.Class for Test)
        at Test.main(Test.java:5)

   Locked ownable synchronizers:
        - None
...
```

## 三、几种方法的对比

1. Thead.sleep(long millis)：一定是当前线程调用此方法，当前线程进入TIMED\_WAITING状态，但不释放对象锁，millis后线程自动苏醒进入就绪状态。作用：给其它线程执行机会的最佳方式。
2. Thread.yield()：一定是当前线程调用此方法，当前线程放弃获取的CPU时间片，但不释放锁资源，由运行状态变为就绪状态，让OS再次选择线程。作用：让相同优先级的线程轮流执行，但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的，因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。该方法与sleep()类似，只是不能由用户指定暂停多长时间。
3. obj.join()/obj.join(long millis)：当前线程里调用其它线程T的join方法，当前线程进入WAITING/TIMED\_WAITING状态，当前线程不会释放已经持有的对象锁。线程T执行完毕或者millis时间到，当前线程一般情况下进入RUNNABLE状态，也有可能进入BLOCKED状态（因为join是基于wait实现的）。
4. obj.wait()/obj.wait(long millis)：当前线程调用对象的wait()方法，当前线程释放对象锁，进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout) timeout时间到自动唤醒。
5. obj.notify()：唤醒在此对象监视器上等待的单个线程，选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。notify，notifyAll和wait一起使用，用于协调多个线程对共享数据的存取，所以必须在synchronized语句块内使用，也就是说，调用wait()，notify()和notifyAll()的任务在调用这些方法前必须拥有对象的锁。
6. LockSupport.park()/LockSupport.parkNanos(long nanos),LockSupport.parkUntil(long deadlines)：当前线程进入WAITING/TIMED\_WAITING状态。对比wait方法,不需要获得锁就可以让线程进入WAITING/TIMED\_WAITING状态，需要通过LockSupport.unpark(Thread thread)唤醒。

> 参考：
>
> Java线程的6种状态及切换：<https://blog.csdn.net/pange1991/article/details/53860651>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://jun-wang.gitbook.io/learnjava/ji-shu-xue-xi/java-duo-xian-cheng/xian-cheng-zhuang-tai-ji-qi-zhuan-huan.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
