线程系列目录
- Thread线程从零认识到深层理解——初识
- Thread线程从零认识到深层理解——六大状态
- Thread线程从零认识到深层理解——wait()与notify()
- Thread线程从零认识到深层理解——线程安全
- 线程池从零认识到深层理解——初识
- 线程池从零认识到深层理解——进阶
线程状态及转换
注意:本系列博文源码分析取自于Android SDK=30,与网络上的一些源码可能不一样,可能他们分析的源码更旧,无需大惊小怪。
前言
本篇博客主要对于线程进阶知识点进行学习讲解
1. 线程六种状态
线程总共有六种状态,在给定的时间点,线程只能处于一种状态。这些状态是虚拟机状态,不会反映任何操作系统线程状态。
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* 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,
/**
* 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,
/**
* 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,
/**
* 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,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
NEW
尚未启动的线程的线程状态,比如刚刚new Thread()刚刚构造初始化完毕。
RUNNABLE
可运行线程的线程状态,即就绪状态
处于可运行状态的线程正在Java虚拟机中执行,但它可能正在等待来自操作系统(例如处理器)的其他资源。
说人话就是即使调用了start()方法,不一定就会立马改变状态为RUNNABLE,只有当它处于等待操作系统分配资源(如CPU)、等待IO连接、正在运行状态时,才是RUNNABLE表示Running状态和Ready状态。
BLOCKED
线程阻塞状态,锁被其他线程占用正在等待锁释放,这个时候线程被操作系统挂起。说人话就是阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。
WAITING
无条件等待状态,由于调用以下方法之一,线程处于等待状态:
- 调用无超时参数的wait()方法
- 调用无超时参数的Thread#join()方法
- 调用LockSupport#park()
上面三种方法都是不加超时时间的方法,调用三种其中之一后会处于WAITING状态。如果没有被唤醒或等待的线程没有结束,那么将一直等待,当前状态的线程不会被分配CPU资源和持有锁。
处于等待状态的线程正在等待另一个线程执行特定操作,例如
- 在线程对象A上调用了Object.wait()需要在线程对象B中调用a.notifyAll()或者 a.notify()
- 调用了join()方法的线程等待指定的线程终止
TIMED_WAITING
指定等待时间的线程线程状态,由于以指定的正等待时间调用以下方法之一,因此线程处于定时等待状态:
- Thread#sleep(long)
- Object#wait(long)方法
- Thread#join(long)
- LockSupport#parkNanos(long nanos)
- LockSupport#parkUntil(long)
在指定的时间没有被唤醒或者等待线程没有结束,会被系统自动唤醒,正常退出。
TERMINATED
线程终止状态,线程已执行完run方法,线程终止。
其实这只是Java语言级别的一种状态,在操作系统内部可能已经注销了相应的线程,或者将它复用给其他需要使用线程的请求,而在Java语言级别只是通过Java代码看到的线程状态而已。
2. 线程状态关系图
经过个人的理解整理出来的图片,觉得关系转换基本差不多就这样了
3. 状态转换案例
我们可以通过代码实例对于状态转换进行测试理解。
1. RUNNABLE与NEW状态
public static void testStateRunnable() {
ConnectThread thread = new ConnectThread();
System.out.println("状态1:" + thread.getState() + ".");
thread.start();
System.out.println("状态2:" + thread.getState() + ".");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("状态3:" + thread.getState() + ".");
}
class ConnectThread extends Thread {
@Override
public void run() {
System.out.println("状态4:" + getState() + ".");
Socket socket = new Socket();
try {
System.out.println("状态5:" + getState() + ".");
System.out.println("Try to connect socket address which not exist...");
socket.connect(new InetSocketAddress(InetAddress.getByAddress(new byte[]{
(byte) 192, (byte) 168, 1, 14}), 5678));
System.out.println("状态6:" + getState() + ".");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
输出结果:
状态1:NEW.
状态2:RUNNABLE.
状态4:RUNNABLE.
状态5:RUNNABLE.
Try to connect socket address which not exist...
状态3:RUNNABLE.
java.net.ConnectException: Connection timed out: connect
at java.net.DualStackPlainSocketImpl.connect0(Native Method)
at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:607)
at java.net.Socket.connect(Socket.java:556)
at com.xuanyuan.codecollect.Test1$IOThread.run(Test1.java:93)
结论:
- 当执行new Thread()线程处于NEW状态
- 当Thread#start()执行后,一般线程处于RUNNABLE状态,但是有特殊情况,前面已说过。
- 状态4在状态2后面打印,说明线程的start()方法执行完毕后,准备工作做好之后才开始run()执行代码。
2. BLOCKED与TIMED_WAITING
public static void testBlocked() {
Object lock = new Object();
SleepThread t1 = new SleepThread("t1", lock);
SleepThread t2 = new SleepThread("t2", lock);
t1.start();
t2.start();
System.out.println("打印索引="+index+++" 打印时间=" + System.currentTimeMillis() + " 线程" + t1.getName() + " 状态1:" + t1.getState());
System.out.println("打印索引="+index+++" 打印时间=" + System.currentTimeMillis() + " 线程" + t2.getName() + " 状态1:" + t2.getState());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("打印索引="+index+++" 打印时间=" + System.currentTimeMillis() + " 线程" + t1.getName() + " 状态2:" + t1.getState());
System.out.println("打印索引="+index+++" 打印时间=" + System.currentTimeMillis() + " 线程" + t2.getName() + " 状态2:" + t2.getState());
try {
Thread.sleep(30 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("打印索引="+index+++" 打印时间=" + System.currentTimeMillis() + " 线程" + t1.getName() + " 状态3:" + t1.getState());
System.out.println("打印索引="+index+++" 打印时间=" + System.currentTimeMillis() + " 线程" + t2.getName() + " 状态3:" + t2.getState());
try {
Thread.sleep(30 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("打印索引="+index+++" 打印时间=" + System.currentTimeMillis() + " 线程" + t1.getName() + " 状态4:" + t1.getState());
System.out.println("打印索引="+index+++" 打印时间=" + System.currentTimeMillis() + " 线程" + t2.getName() + " 状态4:" + t2.getState());
}
static class SleepThread extends Thread {
private final Object lock;
public SleepThread(String name, Object lock) {
super(name);
this.lock = lock;
}
@Override
public void run() {
System.out.println("打印索引="+index+++" 打印时间=" + System.currentTimeMillis() + " 线程" + getName() + " start run:" + getState());
synchronized (lock) {
System.out.println("打印索引="+index+++" 打印时间=" + System.currentTimeMillis() + " 线程" + getName() + " hold the lock:" + getState());
try {
Thread.sleep(30 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("打印索引="+index+++" 打印时间=" + System.currentTimeMillis() + " 线程" + getName() + " return the lock:" + getState());
}
}
}
这个测试的运行结果有多种情况,我只拿几种进行解释说明:
- 运行结果一
运行结果1:
打印索引=1 打印时间=1601271769056 线程t1 状态1:RUNNABLE
打印索引=3 打印时间=1601271769056 线程t2 状态1:RUNNABLE
打印索引=2 打印时间=1601271769056 线程t1 start run:RUNNABLE
打印索引=4 打印时间=1601271769056 线程t1 hold the lock:RUNNABLE
打印索引=5 打印时间=1601271769056 线程t2 start run:RUNNABLE
打印索引=6 打印时间=1601271769157 线程t1 状态2:TIMED_WAITING
打印索引=7 打印时间=1601271769157 线程t2 状态2:BLOCKED
打印索引=8 打印时间=1601271799057 线程t1 return the lock:RUNNABLE
打印索引=9 打印时间=1601271799057 线程t2 hold the lock:RUNNABLE
打印索引=10 打印时间=1601271799158 线程t1 状态3:TERMINATED
打印索引=11 打印时间=1601271799158 线程t2 状态3:TIMED_WAITING
打印索引=12 打印时间=1601271829057 线程t2 return the lock:RUNNABLE
打印索引=13 打印时间=1601271829158 线程t1 状态4:TERMINATED
打印索引=14 打印时间=1601271829158 线程t2 状态4:TERMINATED
根据结果进行分析,有如下结论:
- 从打印索引1~7分析,根据线程t1的路线途经,当执行thread.sleep(long)后,线程会进入TIMED_WAITING状态。根据线程t2的路线途经,当线程进入synchronized方法块时,因为锁被其他线程占用,则线程会进入BLOCKED状态
- 根据打印索引8~14分析,当t2获得锁时,可以重新进入synchronized代码块执行,此时程序进入RUNNABLE状态,当线程休眠结束后线程状态可以由TIMED_WAITING转为RUNNABLE,程序执行完毕后转为TERMINATED状态
- 运行结果二
打印索引=1 打印时间=1601272676632 线程t1 id=1 状态1:RUNNABLE
打印索引=3 打印时间=1601272676632 线程t2 id=1 状态1:BLOCKED
打印索引=2 打印时间=1601272676632 线程t2 id=12 start run:RUNNABLE
打印索引=4 打印时间=1601272676632 线程t2 id=12 hold the lock:RUNNABLE
打印索引=5 打印时间=1601272676632 线程t1 id=11 start run:RUNNABLE
打印索引=6 打印时间=1601272676732 线程t1 id=1 状态2:BLOCKED
打印索引=7 打印时间=1601272676732 线程t2 id=1 状态2:TIMED_WAITING
打印索引=8 打印时间=1601272706632 线程t2 id=12 return the lock:RUNNABLE
打印索引=9 打印时间=1601272706632 线程t1 id=11 hold the lock:RUNNABLE
打印索引=10 打印时间=1601272706733 线程t1 id=1 状态3:TIMED_WAITING
打印索引=11 打印时间=1601272706733 线程t2 id=1 状态3:TERMINATED
打印索引=12 打印时间=1601272736632 线程t1 id=11 return the lock:RUNNABLE
打印索引=13 打印时间=1601272736734 线程t1 id=1 状态4:TERMINATED
打印索引=14 打印时间=1601272736734 线程t2 id=1 状态4:TERMINATED
该次打印结果和上面的打印结果不一样,根据结果进行分析,有如下结论:
- 根据打印索引1~8分析,线程t1和t2都有出现过BLOCKED状态,但是切记它们是有区别的。线程t2出现BLOCKED是申请执行代码是,操作系统锁被占用无法继续只执行而堵塞,而线程t1是因为进入synchronized是需要的对象锁lock被占用了
- 根据打印索引1~4分析,程序执行是无序的,线程t1先一步执行,但不一定先一步获取到锁对象lock
3. WAITING与TIMED_WAITING
static class WaitThread extends Thread {
private int timeout = 0;
private final Object lock;
public WaitThread(String name, Object lock) {
this(name, lock, 0);
}
public WaitThread(String name, Object lock, int timeout) {
super(name);
this.timeout = timeout;
this.lock = lock;
}
@Override
public void run() {
synchronized (lock) {
if (timeout == 0) {
try {
System.out.println("打印索引=" + index++ + " 打印时间=" + System.currentTimeMillis() + " 线程" + getName() + " start wait:" + getState());
lock.wait();
System.out.println("打印索引=" + index++ + " 打印时间=" + System.currentTimeMillis() + " 线程" + getName() + " end wait:" + getState());
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
try {
System.out.println("打印索引=" + index++ + " 打印时间=" + System.currentTimeMillis() + " 线程" + getName() + " start wait:" + getState());
lock.wait(timeout);
System.out.println("打印索引=" + index++ + " 打印时间=" + System.currentTimeMillis() + " 线程" + getName() + " end wait:" + getState());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
- 执行代码方案一
public static void testWait() {
Object lock = new Object();
WaitThread thread = new WaitThread("WaitThread", lock, 180);
thread.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("打印索引=" + index++ + " 打印时间=" + System.currentTimeMillis() + " 线程" + thread.getName() + " end code:" + thread.getState());
}
执行结果:
打印索引=1 打印时间=1601274723938 线程WaitThread start wait:RUNNABLE
打印索引=2 打印时间=1601274724039 线程WaitThread end code:TIMED_WAITING
打印索引=3 打印时间=1601274724119 线程WaitThread end wait:RUNNABLE
打印索引=4 打印时间=1601274725540 线程WaitThread end code:TERMINATED
根据打印索引可知,当执行wait(long)方法后,线程会进入TIMED_WAITING状态,当超过指定时间后线程会由TIMED_WAITING>>RUNNABLE>>TERMINATED
- 执行代码方案二
public static void testWait() {
Object lock = new Object();
WaitThread thread = new WaitThread("WaitThread", lock, 0);
thread.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("打印索引=" + index++ + " 打印时间=" + System.currentTimeMillis() + " 线程" + thread.getName() + " end code:" + thread.getState());
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("打印索引=" + index++ + " 打印时间=" + System.currentTimeMillis() + " 线程" + thread.getName() + " end code:" + thread.getState());
}
执行结果:
打印索引=1 打印时间=1601275413568 线程WaitThread start wait:RUNNABLE
打印索引=2 打印时间=1601275413669 线程WaitThread end code:WAITING
打印索引=3 打印时间=1601275415170 线程WaitThread end code:WAITING
根据打印信息可知,如果使用的是wait()方法,则线程会一直处于WAITING状态,直到通过notify()、notifyAll()等方法获得锁转为RUNNABLE状态
4. Thread#join()方法与WAITING、TIMED_WAITING
thread.join()方法其实就是synchronized(thread),其内部一直判断thread的状态,如果为存活状态,就wait一个指定的时间,默认为0,然后继续循环判断,直到状态不为存活状态。
static class JoinThread extends Thread {
private int timeout;
private Thread thread;
public JoinThread(String name, Thread thread) {
this(name, thread, 0);
}
public JoinThread(String name, Thread thread, int timeout) {
super(name);
this.timeout = timeout;
this.thread = thread;
}
@Override
public void run() {
if (timeout == 0) {
try {
System.out.println("打印索引=" + index++ + " 打印时间=" + System.currentTimeMillis() + " 线程" + getName() + " start join:" + getState());
thread.join();
System.out.println("打印索引=" + index++ + " 打印时间=" + System.currentTimeMillis() + " 线程" + getName() + " end join:" + getState());
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
try {
System.out.println("打印索引=" + index++ + " 打印时间=" + System.currentTimeMillis() + " 线程" + getName() + " start join:" + getState());
thread.join(timeout);
System.out.println("打印索引=" + index++ + " 打印时间=" + System.currentTimeMillis() + " 线程" + getName() + " end join:" + getState());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- 执行代码方案一
public static void testWaitByJoin() {
Object lock = new Object();
WaitThread waitThread = new WaitThread("WaitThread", lock);
waitThread.start();
JoinThread joinThread = new JoinThread("JoinThread", waitThread);
joinThread.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("打印索引=" + index++ + " 打印时间=" + System.currentTimeMillis() + " 线程" + waitThread.getName() + " end code:" + waitThread.getState());
System.out.println("打印索引=" + index++ + " 打印时间=" + System.currentTimeMillis() + " 线程" + joinThread.getName() + " end code:" + joinThread.getState());
}
执行结果:
打印索引=1 打印时间=1601276507526 线程WaitThread start wait:RUNNABLE
打印索引=2 打印时间=1601276507526 线程JoinThread start join:RUNNABLE
打印索引=3 打印时间=1601276507627 线程WaitThread end code:WAITING
打印索引=4 打印时间=1601276507627 线程JoinThread end code:WAITING
根据打印信息,当WaitThread一直处于WAITING状态时,JoinThread也一直处于WAITING状态
- 执行代码方案二
public static void testWaitByJoin() {
Object lock = new Object();
WaitThread waitThread = new WaitThread("WaitThread", lock,70);
waitThread.start();
JoinThread joinThread = new JoinThread("JoinThread", waitThread);
joinThread.start();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("打印索引=" + index++ + " 打印时间=" + System.currentTimeMillis() + " 线程" + waitThread.getName() + " end code:" + waitThread.getState());
System.out.println("打印索引=" + index++ + " 打印时间=" + System.currentTimeMillis() + " 线程" + joinThread.getName() + " end code:" + joinThread.getState());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("打印索引=" + index++ + " 打印时间=" + System.currentTimeMillis() + " 线程" + waitThread.getName() + " end code:" + waitThread.getState());
System.out.println("打印索引=" + index++ + " 打印时间=" + System.currentTimeMillis() + " 线程" + joinThread.getName() + " end code:" + joinThread.getState());
}
执行结果:
打印索引=1 打印时间=1601276654294 线程WaitThread start wait:RUNNABLE
打印索引=2 打印时间=1601276654295 线程JoinThread start join:RUNNABLE
打印索引=3 打印时间=1601276654343 线程WaitThread end code:TIMED_WAITING
打印索引=4 打印时间=1601276654343 线程JoinThread end code:WAITING
打印索引=5 打印时间=1601276654364 线程WaitThread end wait:RUNNABLE
打印索引=6 打印时间=1601276654364 线程JoinThread end join:RUNNABLE
打印索引=7 打印时间=1601276654444 线程WaitThread end code:TERMINATED
打印索引=8 打印时间=1601276654444 线程JoinThread end code:TERMINATED
根据打印信息描述可知执行waitThread.join()时,当WaitThread为TIMED_WAITING或WAITING状态时,JoinThread会是WAITING状态,当WaitThread状态由WAITING或TIMED_WAITING转RUNNABLE状态时,JoinThread的状态也会变为RUNNABLE,直到程序最终执行完毕。
- 执行代码方案三
public static void testWaitByJoin() {
Object lock = new Object();
WaitThread waitThread = new WaitThread("WaitThread", lock,90);
waitThread.start();
JoinThread joinThread = new JoinThread("JoinThread", waitThread,50);
joinThread.start();
try {
Thread.sleep(70);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("打印索引=" + index++ + " 打印时间=" + System.currentTimeMillis() + " 线程" + waitThread.getName() + " end code:" + waitThread.getState());
System.out.println("打印索引=" + index++ + " 打印时间=" + System.currentTimeMillis() + " 线程" + joinThread.getName() + " end code:" + joinThread.getState());
try {
Thread.sleep(120);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("打印索引=" + index++ + " 打印时间=" + System.currentTimeMillis() + " 线程" + waitThread.getName() + " end code:" + waitThread.getState());
System.out.println("打印索引=" + index++ + " 打印时间=" + System.currentTimeMillis() + " 线程" + joinThread.getName() + " end code:" + joinThread.getState());
}
执行结果:
打印索引=1 打印时间=1601277260491 线程WaitThread start wait:RUNNABLE
打印索引=2 打印时间=1601277260492 线程JoinThread start join:RUNNABLE
打印索引=3 打印时间=1601277260542 线程JoinThread end join:RUNNABLE
打印索引=4 打印时间=1601277260562 线程WaitThread end code:TIMED_WAITING
打印索引=5 打印时间=1601277260562 线程JoinThread end code:TERMINATED
打印索引=6 打印时间=1601277260581 线程WaitThread end wait:RUNNABLE
打印索引=7 打印时间=1601277260683 线程WaitThread end code:TERMINATED
打印索引=8 打印时间=1601277260683 线程JoinThread end code:TERMINATED
根据打印信息描述可知执行waitThread.join(long)和lock.wait(timeout)时,当超过join(long)设定的时间,不管WaitThread是何状态,JoinThread都会获取锁结束等待。
总结
本篇博客主要对于线程的六种状态进行讲解分析,对于各状态的转换和状态的生成通过代码测试的方式进行了详细的描述。
除了已有相关说明,额外总结如下结论:
- RUNNABLE状态其实是就绪(ready)和运行中(running)两种状态的统称。当处于NEW状态的线程位于可运行线程池中,等待被线程调度选中,此时处于就绪状态(ready)。当它获取到CPU的使用权且获得CPU时间片后变为运行中状态(running)
- 线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式,也即是线程获得了CPU时间片。
- WAITING状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。
- TIMED_WAITING处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。
- 当线程的run()方法完成时,或者主线程的main()方法完成时,我们就认为它终止了。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。
- BLOCKED状态其实是没有获得锁的,而TIMED_WAITING或者WAITING是获得了锁只是暂时不进行代码执行而已
博客书写不易,您的点赞收藏是我前进的动力,千万别忘记点赞、 收藏 ^ _ ^ !
相关链接: