一、什么是Java线程状态
在Java程序中,用于描述Java线程的六种状态:
- 新建(NEW):当前线程,刚刚新建出来,尚未启动。
- 运行(RUNNABLE):当前线程,处于竞争CPU时间分片或已经获得CPU时间片的状态。
- 等待(WAITTING):当前线程,处于休眠,不参与CPU时间片竞争的状态。
- 定时等待(TIMED_WAITTING):当前线程,处于定时休眠,暂时不参与CPU时间片竞争的状态。
- 阻塞(BLOCKED):当前线程,处于阻塞,不参与CPU时间片竞争的状态。
- 终止(TERMINATED):当前线程,处于最终停止的状态。
新建状态,只能进入运行状态。而终止状态无法再转为其他状态。
等待/定时等待与阻塞,差别就是后者需要一个事件信号(如其他线程放弃当前线程需要的排他锁),才可以进行状态切换。当然,强行关闭也是可以的。
Java线程的实现并不受JVM规范约束,故不同虚拟机的实现,往往不同。目前主流的HotSpot是将每个Java线程直接映射到一个操作系统的原生线程,从而由操作系统完成一系列的线程调度
二、哪里看Java线程状态
查看Java线程状态,主要存在三种方式:
- java.lang.Thread.State下可以直接看到Java的六种线程状态
- Java运行时,程序内部可以通过Thread.getState()获取目标线程状态
- Java运行时,程序外部可以通过jstack等工具,查看线程状态
有关jstack等工具等使用,后续会有博客,专门阐述。
三、什么时候变换Java线程状态
Java线程状态的切换嘛。不啰嗦,直接上图。
这张图涵盖了Java线程状态切换的各类方法。相较网上一些图片,更为详尽一些。
如果有所遗漏,可以告诉我,我会及时填补上。
四、谁在使用Java线程状态
日常开发中,我们并不会直接与线程状态进行交互。
我们往往直接使用JDK包装好的工具,如JUC包下的各类工具等。
举个栗子
线程池中的应用
位置:com.sun.corba.se.impl.orbutil.threadpool.ThreadPoolImpl#close
// Note that this method should not return until AFTER all threads have died.
public void close() throws IOException {
// Copy to avoid concurrent modification problems.
List<WorkerThread> copy = null;
synchronized (workersLock) {
copy = new ArrayList<>(workers);
}
for (WorkerThread wt : copy) {
wt.close();
while (wt.getState() != Thread.State.TERMINATED) {
try {
wt.join();
} catch (InterruptedException exc) {
wrapper.interruptedJoinCallWhileClosingThreadPool(exc, wt, this);
}
}
}
threadGroup = null;
}
实际查看JDK后发现,JDK中其实也没有辣么多的实例,并且大多数直接关联线程状态的,也是一些状态查看的东东。
这在文章开头就说,Java线程操作,是很底层的,甚至其实现都不包含在虚拟机规范中。
主流的HotSpot,也是直接将Java线程映射到系统线程,由系统进行一系列的线程调度处理。
所以,在JDK中,是直接对线程状态进行处理的部分是很少的。
五、为什么需要线程状态
1.为什么需要线程状态这一概念
这个问题,可以从两个角度来说明:生命周期与资源管理
- 一方面,线程状态很好地刻画了线程的整个生命周期,对生命周期中不同阶段进行了有效划分。
- 另一方面,资源是有限的,需求是无限的。所以需要将系统资源有意识地进行调度,合理利用比较优势,追求帕累托最优。
实现后者的,就是利用线程在生命周期的不同阶段这一天然属性带来的状态刻画,进行的分组。CPU调度只需要关注运行状态的线程。而阻塞,等待等线程,都有着属于自己的一套处理方式。最终获得资源(开发时对复杂性的应对,运行时对系统资源对消耗,应用者心智模型的成长等)的优化分配。
2.JDK中为什么需要定义Java线程状态
前文有说到,Java中很少直接使用到线程状态。那么为什么还要在JDK中定义Java的六种线程状态呢?
一方面,通过信息透明的方式,降低使用者使用时建立心智模型的成本。如,现在的我们可以通过打印日志,打断点,快速了解Java的各个线程状态,并清楚了解产生的原因。从而大大提高了我们对Java线程的认识,进而更为愉快地拥抱JUC包下诸如线程池等工具。
另一方面,通过可以直接应用的状态枚举,我们可以很好地对现有工具进行二次开发等。如我们可以通过扩展AQS,并在其中添加线程状态的校验,从而得到定制化的线程同步工具。
六、如何使用线程状态
使用的方式,我已经在上文说了:学习Java线程,定制线程相关工具开发。
这里给出一个有关线程学习的demo:
/**
* @program: learning
* @description: 用于确认线程状态问题
* @author: Jarry
* @create: 2020-12-26 22:25
**/
public class ThreadState {
public static void main(String[] args) {
threadStateTest();
// threadStateTest2();
// threadStateWithBlocked();
// threadStateWithException();
// threadStateWithSuspend();
}
/**
* 实践证明:Thread.suspend()与Thread.resume()不会改变线程状态
* 线程状态该是Waiting,就Waiting。该Timed_Waiting就Timed_Waiting
* Thread.suspend()与Thread.resume()只是挂起目标线程(并且不会释放锁资源)
*/
private static void threadStateWithSuspend() {
Thread thread1 = new Thread(() -> {
// LockSupport.park();
LockSupport.parkNanos(2000000000);
});
thread1.start();
printThreadState(thread1);
LockSupport.parkNanos(500000000);
printThreadState(thread1);
thread1.suspend();
printThreadState(thread1);
LockSupport.parkNanos(500000000);
printThreadState(thread1);
thread1.resume();
LockSupport.parkNanos(500000000);
printThreadState(thread1);
// LockSupport.unpark(thread1);
}
/**
* 展现线程阻塞状态
*/
private static void threadStateWithBlocked() {
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (ThreadState.class) {
// LockSupport.parkNanos(2000000000);
LockSupport.park();
}
}
};
Thread thread1 = new Thread(runnable);
Thread thread2 = new Thread(runnable);
thread1.start();
LockSupport.parkNanos(500000000);
thread2.start();
// 加上以下时间间隔,则结果:Runnable->Blocked
// 推论:Thread.start()会将线程状态设置为Runnable,然后在遇到sync的锁,再切换为Blocked状态
// LockSupport.parkNanos(500000000);
printThreadState(thread2);
LockSupport.parkNanos(500000000);
printThreadState(thread2);
LockSupport.parkNanos(500000000);
LockSupport.unpark(thread1);
LockSupport.unpark(thread2);
}
/**
* 由于底层实现机制的不同(相较于其他waiting的方法),无法直接进行Object.wait(),否则会抛出以下异常
* @exception java.lang.IllegalMonitorStateException
* object.wait()进行wait的方法,是直接对其wait_set进行操作
*/
private static void threadStateWithException() {
Thread thread1 = new Thread(() -> {
try {
ThreadState.class.wait(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread1.start();
LockSupport.parkNanos(1000000000);
printThreadState(thread1);
}
/**
* Object.wait()的使用
*/
private static void threadStateTest3() {
Thread thread1 = new Thread(() -> {
synchronized (ThreadState.class) {
try {
ThreadState.class.wait(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread1.start();
LockSupport.parkNanos(1000000000);
printThreadState(thread1);
}
/**
* 确定LockSupport.parkNacos()是否可以生成Time_Waiting状态
*/
private static void threadStateTest2() {
Thread thread1 = new Thread(() -> {
LockSupport.parkNanos(2000000000);
});
thread1.start();
printThreadState(thread1);
LockSupport.parkNanos(1000000000);
printThreadState(thread1);
}
/**
* 查看到除Blocked以外的所有线程状态
*/
private static void threadStateTest() {
Thread thread1 = new Thread(() -> {
synchronized (ThreadState.class) {
// Runnable
printThreadState(Thread.currentThread());
// 1.Thread.sleep(time)
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 2.Object.wait(time)
// try {
// ThreadState.class.wait(2000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// 3.Thread.join(time)
// try {
// Thread.currentThread().join(2000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// 4.LockSupport.parkNanos(time);
// LockSupport.parkNanos(2000000000);
// 5.LockSupport.parkUntil(timeStamp);
// LockSupport.parkUntil(System.currentTimeMillis()+2000);
LockSupport.park();
}
});
thread1.setName("test_thread");
// New
printThreadState(thread1);
thread1.start();
LockSupport.parkNanos(1000000000);
// Timed_waiting
printThreadState(thread1);
LockSupport.parkNanos(2000000000);
// Waiting
printThreadState(thread1);
LockSupport.unpark(thread1);
LockSupport.parkNanos(1000000000);
// Terminated
printThreadState(thread1);
}
private static void printThreadState(Thread thread) {
System.out.println("current Thread(" + thread.getName()+":" + thread.getId() + ") state:" + thread.getState());
}
}
代码中有一些细碎的知识点,就不在这里赘述了。感兴趣的小伙伴,可以自己看看注释,自行验证。
七、扩展:系统状态(三态&五态)
操作系统就包含进程管理,作业管理,文件管理等。其中进程管理,就涉及系统状态的三态模型与五态模型。
其中,三态模型包含以下三种状态:
- 就绪状态
- 运行状态
- 阻塞状态
而五态模型则包含以下五种状态:
网友评论