1.进程
1.基本概念
- 1.
进程(Process)
:程序的一次启动执行
(程序在执行过程中分配和管理资源的基本单位,操作系统资源分配的最小单位)- 2.
程序
:存放在硬盘中的可执行文件,主要包括代码指令和数据
- 3.
一个进程是一个程序的一次启动和执行,是操作系统将程序装入内存,给程序分配必要的系统资源,并且开始运行程序的指令
(同一个程序可以多次启动/启动多个,对应多个进程)
2.基本原理
- 1.计算机各个组成的任务
- 1.
CPU
:承担所有的计算任务- 2.
内存
:承担运行时数据的保存任务- 3.
外存
:承担数据外部永久存储的任务- 4.
操作系统
:承担计算任务调度,资源分配的任务- 2.一个进程由
程序段
,数据段
,进程控制块
三部分组成
![]()
- 1.
程序段/代码段
:进程的程序指令在内存中的位置,包含需要执行的指令集合- 2.
数据段
: 进程的操作数据在内存中的位置,包含需要操作的数据集合- 3.
程序控制块(Program Control Block,PCB)
:包含进程的描述信息和控制信息等,是进程存在的唯一标志
- 1.
程序的描述信息
- 1.进程
ID
(pid
,进程标识符): 唯一,代表进程的身份- 2.进程名称
- 3.进程状态
- 1.三态模型:运行态,就绪态,阻塞态
- 2.五态模型:新建态,终止态,运行态,就绪态,阻塞态
- 4.进程优先级: 进程调度的依据
- 2.
进程的调度信息
- 1.程序起始地址: 程序第一行指令的内存地址
- 2.通信信息: 进程间通信时的消息队列
- 3.
进程的资源信息
- 1.内存信息: 内存占用情况和内存管理所用的数据结构
- 2.
I/O
设备信息: 所用的I/O
设置编号及相应的数据结构- 3.文件句柄: 也叫文件描述符,是所打开文件的信息
- 4.
进程上下文信息(进程环境)
- 1.执行时各种
CPU
寄存器的值- 2.当前程序计数器(
PC
)的值以及各种栈的值- 3.操作系统切换进程时当前进程被迫让出
CPU
,当前进程的上下文就保存在PCB
结构中,供下次恢复运行时使用- 3.注意
- 1.现代操作系统中,进程是并发执行的,任何进程都可以同其他进程一起执行(交替执行);
- 2.进程内部
程序段
和数据段
有自己独立的地址空间,不同进程的地址空间是相互隔离的
3.Java程序的进程
- 1.
Java
编写的程序都运行在Java
虚拟机(JVM
)中,当使用Java
命令启动一个Java
应用程序时,就会启动一个JVM
进程- 2.
JVM
进程内部,所有Java
程序代码都是以线程运行- 3.
JVM
找到程序的入口点main()
方法,然后运行main()
方法,产生一个线程,这该线程被称为主线程
- 4.当
main()
方法结束后,主线程运行完成,JVM
进程也随即退出
4.进程间共享数据
- 1.
管道
:使用管道(pipe
)实现进程间通信,管道是一种单向通信机制,其中一个进程向管道写入数据,另一个进程从管道读取数据,通过创建多个管道来实现多个进程之间的通信- 2.
共享内存
:使用共享内存(shared memory
)实现多个进程共享同一块内存区域,多个进程可同时访问该内存区域中的数据;使用共享内存可提高进程之间的数据传输效率,但需要确保进程对内存的访问是同步的以避免数据损坏或竟争条件- 3.
消息队列
:使用消息队列(message queue
)实现进程间通信,其中一个进程向消息队列发送消息,另一个进程从消息队列接收消息;消息队列提供了可靠的数据传输机制,并允许多个进程同时访问消息队列- 4.
套接字
:使用套接字(socket
)来实现进程间通信,套接字是一种网络通信机制,可在同一台计算机或不同计算机之间进行通信;套接字提供了一种通用的数据传输机制,可用于实现多种不同类型的进程间通信- 5.注意进程间通信需考虑进程安全性和数据一致性等问题;实现进程间通信时需要确保数据访问是同步的,避免数据损坏或竞争条件;建议使用操作系统提供的进程间通信机制而不是手动实现,以确保数据传输的可靠性和安全性
2.线程
1.基本概念
- 1.
线程(Threads)
:是进程代码段/程序段
的一次顺序执行流程(线程是CPU调度
的最小单位
)- 2.一个进程可以有一个或多个线程,各个线程之间共享进程的内存空间,系统资源
- 3.进程仍然是
操作系统资源分配
的最小单位
2.基本原理
- 1.一个标准的线程主要由三部分组成
- 1.
线程基本信息
![]()
- 1.线程
ID
(tid
,线程标识符): 线程的唯一标识,同一个进程内不同线程的ID
不会重叠- 2.线程名称: 方便用户识别,用户可以指定线程的名字,如果没有指定,系统会自动分配一个名称
- 3.线程优先级: 表示线程调度的优先级,优先级越高,获取
CPU
的执行机会就越大- 4.线程状态: 表示当前线程的执行状态:
新建
,就绪
,运行
,阻塞
,结束
- 5.其他: 是否为守护线程等
- 2.
程序计数器(Program Counter,PC)
- 1.记录着线程下一条指令的代码段内存地址,线程私有(
对应JVM中的程序计数器
)- 3.
栈内存
- 1.代码段中局部变量的储存空间,线程私有(
对应JVM中的虚拟机栈和本地方法栈
)- 2
JDK8
中每个线程在创建时默认被分配1MB
大小的栈内存(注意栈溢出错误)- 3.栈内存和堆内存不同,栈内存不受垃圾回收器管理
3.方法执行流程
- 1.
Java
中执行程序流程的重要单位是方法
- 2.每个线程在创建时默认被分配
1MB
大小的栈内存- 3.栈内存的分配单位是栈帧,方法的每一次执行都需要为其分配一个栈帧,栈帧主要保存该方法中的
局部变量
,操作数栈
,动态链接
,方法的返回地址
以及一些附加信息
- 4.当线程的执行流程进入方法时,
JVM
就会为方法分配一个对应的栈帧压入栈内存- 5.当线程的执行流程跳出方法时,
JVM
就从栈内存弹出该方法的栈帧,此时栈内存中栈帧的局部变量的内存空间就会被回收package threadDemo; public class StackAreaDemo { public static void main(String[] args) throws InterruptedException { System.out.println("当前线程ID:"+Thread.currentThread().getId()); System.out.println("当前线程名:"+Thread.currentThread().getName()); System.out.println("当前线程状态:"+Thread.currentThread().getState()); System.out.println("当前线程优先级:"+Thread.currentThread().getPriority()); System.out.println("当前线程类加载器:"+Thread.currentThread().getContextClassLoader()); System.out.println("当前线程堆栈帧数组:"+Thread.currentThread().getStackTrace()); System.out.println("当前线程线程组:"+Thread.currentThread().getThreadGroup()); System.out.println("当前线程此线程突然终止时调用的处理程序:"+Thread.currentThread().getUncaughtExceptionHandler()); System.out.println("当前线程类对象:"+Thread.currentThread().getClass()); int a = 1, b = 1; int c = a / b; anotherFun(); Thread.sleep(10000); } private static void anotherFun() { int a = 1, b = 1; int c = a / b; anotherFun2(); } private static void anotherFun2() { int a = 1, b = 1; int c = a / b; } }
- 1.上述代码中使用
java.lang
包中Thread.currentThread()
静态方法,用于获取正在执行的当前线程- 2.上述代码定义了三个方法
main
,anotherFun
,anotherFun2
,并且这三个方法有相同的三个局部变量a
,b
,c
- 3.上述代码中
JVM
的执行流程
- 1.当执行
main()
方法时,JVM
为main()
方法分配一个栈帧,保存三个局部变量,然后将栈帧压入main
线程的栈内存,接着执行流程进入anotherFun()
方法- 2.执行流程进入
anotherFun()
方法之前JVM
为其分配对应的栈帧,保存其三个局部变量,然后压入main
线程的栈内存(每个方法都会有自己独立的栈帧,负责保存该方法内部的局部变量,然后压入当前线程的栈内存)- 3.执行流程进入
anotherFun2()
方法之前JVM
为其分配对应的栈帧,保存其三个局部变量,然后将栈帧压入main
线程的栈内存,此时main
线程含有三个栈帧- 4.三个方法的栈帧弹出过程与压入的过程刚好相反
- 5.
anotherFun2()
方法执行完成后,其栈帧从main
线程的栈内存首先弹出,执行流程回到anotherFun()
方法,anotherFun()
方法执行完成后,其栈帧从main
线程的栈内存弹出,执行流程回到main()
方法,main()
方法执行完成后,其栈帧弹出,此时main
线程的栈内存已经全部弹空,没有剩余的栈帧,至此main
线程结束- 4.由于
栈帧
的操作是后进先出
,这是标准的栈操作模式,因此此存放栈帧的内存
也叫作栈内存
,对应JVM
中得虚拟机栈和本地方法栈
4.Java程序的线程
- 4.
Java
程序的进程执行过程
是标准的多线程的执行过程
- 1.每当使用
Java
命令执行一个类时,实际上就启动了一个JVM
进程- 2.理论上该进程的内部至少会启动两个线程,一个是
main
线程,另一个是GC(垃圾回收)
线程- 3.实际上执行一个
Java
程序后通过Process Explorer
观察线程数量远远不止两个
5.核心原理
- 1.现代操作系统提供了强大的线程管理能力,
Java
不需要再进行独立的线程管理和调度,而是将线程调度工作委托给操作系统的调度进程去完成- 2.某些系统上
JVM
将每个Java
线程一对一地对应到操作系统地本地线程,彻底将线程调度委托给操作系统
1.线程地调度和时间片
- 1.
时间片
- 1.由于
CPU
的计算频率非常高,每秒计算数十亿次,因此可以将CPU
的时间从毫秒的维度进行分段,每一小段叫做一个CPU
时间片- 2.不同的操作系统,不同的
CPU
,线程的CPU
时间片长度都不同- 3.目前操作系统中主流的线程调度方式是:基于
CPU
时间片方式进行线程调度- 4.线程只有得到
CPU
时间片才能执行指令,没有得到时间片的线程处于就绪状态- 5.由于时间片非常短,在各个线程之间快速地切换,因此表现出地特征是很多个线程在同时执行或者并发执行
- 2.
CPU
的主频:即CPU
内核工作的时钟频率,其表示CPU
数字脉冲信号发送的速度
![]()
- 1.
CPU
的时钟频率计量单位包含
- 1.赫(
Hz
)- 2.千赫(
KHz
) =1000Hz
- 3.兆赫(
MHz
) =1000KHz
- 4.吉赫(
GHz
)=1000MHz
- 3.
线程地调度
模型目前主要分为两种
- 1.
分时调度模型
- 2.
抢占式调度模型
1.分时调度模型
- 1.系统平均分配
CPU
的时间片,所有线程轮流占用CPU
,时间片调度的分配上所有线程人人平等
2.抢占式调度模型
- 1.系统按照
线程优先级
分配CPU
时间片,优先级高的线程优先分配CPU
时间片- 2.如果所有就绪线程的优先级相同,那么会随机选择一个,优先级高的线程获取的
CPU
时间片相对多一些- 3.由于目前大部分操作系统都是使用
抢占式调度模型
进行线程调度,Java
的线程管理和调度是委托给操作系统完成的,因此Java
的线程调度也是使用抢占式调度模型
,Java
线程都有优先级
2.线程的优先级
- 1.
Thread
类中有一个实例属性和两个实例方法,专门用于进行线程优先级相关的操作//属性一: private int priority; //该属性保存一个Thread实例的优先级,即1~10的值 //方法一: public final int getPriority() //获取线程优先级 //方法二: public final void setPriority() //获取线程优先级
- 2.
Thread
类中定义了三个优先级常量,priority
默认是级别5
,对应的类常量是NORM_PRIORITY
,优先级最大值为10
,最小值为1
public static final int MIN_PRIORITY = 1; public static final int NORM_PRIORITY = 5; public static final int MAX_PRIORITY = 10;
- 3.
Java
中使用抢占式调度模型
进行线程调度,priority
实例属性的优先级越高,线程获得CPU
时间片的机会就越多,但非绝对package threadDemo; public class PriorityDemo1 { public static final int SLEEP_GAP = 1000; public static void main(String[] args) throws InterruptedException { PriorityThread[] priorityThreads = new PriorityThread[10]; for (int i = 0; i < priorityThreads.length; i++) { priorityThreads[i] = new PriorityThread(); priorityThreads[i].setPriority(i+1); } for (int i = 0; i < priorityThreads.length; i++) { priorityThreads[i].start(); } Thread.sleep(SLEEP_GAP); for (int i = 0; i < priorityThreads.length; i++) { // 1.Thread类的stop()实例方法是一个过时的方法,也是一个不安全的方法 // 2.这里的安全指的是系统资源(文件、网络连接等)的安全——stop()实例方法可能导致资源状态不一致,或者说资源出现问题时很难定位 // 3.实际开发过程中,不建议使用stop()实例方法 // 4.这里是因为演示需要,不会存在安全问题,所以使用stop实例方法来终止线程的执行 priorityThreads[i].stop(); } for (int i = 0; i < priorityThreads.length; i++) { System.out.println(priorityThreads[i].getName() + "-优先级为-" + priorityThreads[i].getPriority() + "-机会值为-" + priorityThreads[i].opportunities); } } static class PriorityThread extends Thread{ static int threadNo = 1; public PriorityThread(){ super("thread-" + threadNo); threadNo++; } public long opportunities = 0; public void run(){ for (int i = 0; ; i++) { // 某个线程的实例属性opportunities的值越大,就表明该线程获得的CPU时间片越多 opportunities++; } } } }
- 4.注意
- 1.执行机会的获取具有
随机性
,优先级高的不一定获得的机会多,整体而言高优先级的线程获得的执行机会更多- 2.因为
Java
线程是通过映射到系统的原生线程
上来实现的,线程的调度最终还是取决于操作系统
3.线程的生命周期
- 1.
Java
中线程的生命周期分为6
种,其具体状态定义在Thread
类的内部枚举类State
中
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,//可执行状态,要么是在执行,要么是一切就绪等待执行 BLOCKED,//阻塞状态,等待锁,以便进入同步块 WAITING,//等待状态,等待其他线程去执行特定的动作,没有时间限制 TIMED_WAITING,//限时等待状态,等待其他的线程去执行特定的动作,这个是在一个指定的时间范围内 TERMINATED;//终止状态,线程执行结束 }
- 2.
Thread
类中定义了属性和方法专门用来保存和获取线程的状态//实例属性 private int threadStatue;//以整数的形式保存线程的状态 //实例方法 public Thread.State getState();//返回当前线程的执行状态,一个枚举类型值
- 3.进入
BLOCKED
状态、WAITING
状态、TIMED_WAITING
状态的线程
都会让出CPU的使用权
;等待或阻塞状态的线程被唤醒后会进入就绪状态
(操作系统层面),需重新获取时间片才能接着运行
1.New状态
- 1.
Java
源码对NEW
状态的说明:创建成功但是没有调用start()
方法启动的Thread
线程实例都处于NEW
状态
2.Runnable状态
- 1.调用
Thread
实例的start()
方法后,下一步如果线程获取CPU
时间片开始执行,JVM
将异步调用线程的run()
方法执行其业务代码- 2.当
Java
线程的Thread
实例的start()
方法被调用后,操作系统中对应线程进入的并不是运行状态而是就绪状态,而Java
线程并没有就绪状态- 3.
Thread
线程实例的start()
方法一经调用,其状态从NEW
状态切换RUNNABLE
状态,但此时并不意味着线程立即获取CPU
时间片并且立即执行,中间需要一系列操作系统的内部操作- 4.
run()
方法被异步执行前,JVM
幕后工作和操作系统的线程调度有关,Java
中的线程管理是通过JNI
本地调用的方式委托操作系统的线程管理API
完成的(JNI:Java Native Interface,即Java本地接口
)- 5.
JVM
的线程状态与其幕后的操作系统线程状态之间的转换关系如下图所示
- 6.说明
- 1.操作系统线程如果处于就绪状态,即该线程已经满足执行条件,但是还不能执行
- 2.处于
就绪状态
的线程需要等待系统的调度,一旦该就绪状态的线程被系统选中,获得CPU
时间片,线程就开始占用CPU
执行线程的代码,此时线程的操作系统状态进入了运行状态
- 3.操作系统中处于
运行状态
的线程在CPU
时间用完后又回到就绪状态
,等待CPU
的下一次调度- 4.操作系统线程在
就绪状态
和执行状态
之间被系统反复地调度,这种情况会持续直到线程的代码逻辑执行完成或异常终止- 5.此时线程的操作系统状态又发生了改变,进入线程的
TERMINATED
(终止)状态- 7.注意
- 1.
就绪状态
和运行状态
都是操作系统中的线程状态- 2.
Java
语言中并没有细分这两种状态,而是将这两种状态合并成同一种状态即RUNNABLE
状态- 3.因此
Thread.state
枚举类中并没有定义线程的就绪状态
和运行状态
而是只定义了RUNNABLE
状态,这是Java
线程状态和操作系统中线程状态不同地地方- 8.总结
- 1.
NEW
状态的Thread
实例调用了start()
方法后,线程的状态将变成RUNNABLE
状态- 2.但是线程的
run()
方法不一定会马上被并发执行,需要在线程获取了CPU
时间片之后才真正启动并发执行- 3.
Runnable
状态之所以包含就绪和运行两种状态是因为操作系统中每个线程不会一直占有CPU
时间片,所以需要在就绪和运行两种状态中反复切换直到该线程的业务代码执行完成或抛出异常- 4.不细分为就绪和运行是因为
JVM
不能决定哪个线程什么时候来运行,这取决于操作系统的时间片调度,另一方面说明JVM
无法对操作系统的调度做出积极的响应- 5.
就绪状态
仅仅表示线程具备运行资格,如果没有被操作系统的调度程序挑选中,线程就永远处于就绪状态,当前线程进入就绪状态的条件包括一下几种
- 1.调用线程的
start()
方法,此线程会进入就绪状态- 2.当前线程的执行时间片用完也会进入就绪状态
- 3.线程睡眠
sleep
操作结束也是进入就绪状态- 4.对其他线程合入
join
操作结束- 5.等待用户输入结束
- 6.线程争抢到对象锁
Object Monitor
(虽然这个线程获取到了锁,但不是立马获取时间片)- 7.当前线程调用
yield()
方法让出CPU
执行权限
3.BLOCKED状态
- 1.
BLOCKED
状态表示线程在等待获取监视器锁(synchronized
锁),可能是由于同步块或同步方法的争用导致- 2.
BLOCKED
状态是线程在试图访问某个同步代码块(同步块或同步方法
)时,发现锁对象已经被其它线程占用了,这时线程就会进入BLOCKED
状态,直到获取到该锁- 3.
IO阻塞
:线程发起了一个阻塞式IO
操作后,如果不具备IO
操作的条件,线程就会进入阻塞状态(IO
包括磁盘IO
、网络IO
等,如线程等待用户输入内容后继续执行)
4.WAITING状态
- 1.
WAITING
状态表示线程在等待其他线程的通知或中断,可能是由于调用了wait()、join(),park()
等方法导致(注意sleep()
方法会进入TIMED_WAITING
状态)- 2.
WAITING
状态是线程在获取锁对象之后,由于某些原因需要等待一些事件的完成才能继续执行,这时线程调用wait()、join(),park()
等方法进入WAITING
状态- 3.处于
WAITING
(无限期等待)状态的线程不会被分配CPU
时间片,需要被其他线程显式地唤醒
,才会进入就绪状态
- 1.
Object.wait()
方法对应的唤醒方式为Object.notify()/Object.notifyAll()
- 2.
Thread.join()
方法对应的唤醒方式为被合入的线程执行完毕
- 3.
LockSupport.park()
方法对应的唤醒方式为LockSupport.unpark(Thread)
- 4.注意这两种状态都属于线程的阻塞状态,但产生的原因和等待的条件是不同的
- 1.
BLOCKED
状态是由于争夺锁
而导致的- 2.
WAITING
状态是由于线程主动等待
而导致的
5.TIMED_WAITING状态
- 1.线程处于一种特殊的等待状态:
限时等待状态
- 2.处于
限时等待状态
的线程不会被分配CPU
时间片,如果指定时间之内没有被唤醒,限时等待的线程会被系统自动唤醒,进入就绪状态
- 2.线程处于限时等待状态的操作大致分为以下几种
- 1.
Thread.sleep(int n)
:使当前线程进入限时等待状态,等待时间为n
毫秒;对应的唤醒方式为sleep
睡眠时间结束- 2.
Object.wait(long var1)
:带时限的抢占对象的monitor
锁;对应的唤醒方式为调用Object.notify()/Object.notifyAll()
主动唤醒或限时结束- 3.
Thread.join(long millis, int nanos)
:带时限的线程合并,注意join
底层也是通过wait
机制实现的- 4.
LockSupport.parkNanos(long nanos)
:让线程等待,时间以纳秒为单位;对应的唤醒方式为线程调用配套的LockSupport.unpark(Thread)
方法结束或线程停止时限结束- 5.
LockSupport.parkUntil(long deadline)
:让线程等待,时间可以灵活设置;对应的唤醒方式为线程调用配套的LockSupport.unpark(Thread)
方法结束或线程停止时限结束
6.TERMINATED状态
- 1.处于
RUNNABLE
状态的线程在run()
方法执行完成之后会变成终止状态TERMINATED
- 2.如果
run()
方法执行过程中发生了运行时异常而没有被捕获,run()
方法将被异常终止,线程也会变成TERMINATED
状态
4.线程状态演示案例
package threadDemo; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.LockSupport; public class StatusDemo1 { public static final long MAX_TURN = 5; //每个线程执行的轮次 static int threadSeqNumber = 0; //线程编号 static List<Thread> threadList = new ArrayList<>(); //全局的静态线程列表 /** * 输出静态线程列表中每个线程的状态 */ private static void printThreadStatus(){ for (Thread thread : threadList) { System.out.println(thread.getName() + "状态为:" + thread.getState()); } } /** * 向全局的静态线程列表中加入线程 * @param thread 线程实例 */ private static void addStatusThread(Thread thread){ threadList.add(thread); } static class StatusDemoThread extends Thread{ public StatusDemoThread() { super("statusPrintThread" + (++threadSeqNumber)); addStatusThread(this); } public void run(){ System.out.println(getName() + "启动后状态为" + getState()); for (int turn = 0; turn < MAX_TURN; turn++) { sleepMilliSeconds(500); printThreadStatus(); } System.out.println(getName() + "- 运行结束"); } } public static void main(String[] args){ addStatusThread(Thread.currentThread()); Thread sThread1 = new StatusDemoThread(); System.out.println(sThread1.getName() + "-创建时状态为" + sThread1.getState()); Thread sThread2 = new StatusDemoThread(); System.out.println(sThread2.getName() + "-创建时状态为" + sThread2.getState()); Thread sThread3 = new StatusDemoThread(); System.out.println(sThread3.getName() + "-创建时状态为" + sThread3.getState()); sThread1.start(); sleepMilliSeconds(500); sThread2.start(); sleepMilliSeconds(500); sThread3.start(); sleepMilliSeconds(500); } public static void sleepMilliSeconds(int millisecond){ LockSupport.parkNanos(millisecond * 1000L * 1000L); } }
E:\JDK\bin\java.exe "-javaagent:E:\IDEA\IntelliJ IDEA 2020.2.1\lib\idea_rt.jar=50289:E:\IDEA\IntelliJ IDEA 2020.2.1\bin" -Dfile.encoding=UTF-8 -classpath "E:\JDK\jre\lib\charsets.jar;E:\JDK\jre\lib\deploy.jar;E:\JDK\jre\lib\ext\access-bridge-64.jar;E:\JDK\jre\lib\ext\cldrdata.jar;E:\JDK\jre\lib\ext\dnsns.jar;E:\JDK\jre\lib\ext\jaccess.jar;E:\JDK\jre\lib\ext\jfxrt.jar;E:\JDK\jre\lib\ext\localedata.jar;E:\JDK\jre\lib\ext\nashorn.jar;E:\JDK\jre\lib\ext\sunec.jar;E:\JDK\jre\lib\ext\sunjce_provider.jar;E:\JDK\jre\lib\ext\sunmscapi.jar;E:\JDK\jre\lib\ext\sunpkcs11.jar;E:\JDK\jre\lib\ext\zipfs.jar;E:\JDK\jre\lib\javaws.jar;E:\JDK\jre\lib\jce.jar;E:\JDK\jre\lib\jfr.jar;E:\JDK\jre\lib\jfxswt.jar;E:\JDK\jre\lib\jsse.jar;E:\JDK\jre\lib\management-agent.jar;E:\JDK\jre\lib\plugin.jar;E:\JDK\jre\lib\resources.jar;E:\JDK\jre\lib\rt.jar;E:\IDEA\IntelliJ IDEA 2020.2.1\Code\out\production\CoreJava_Day1_20211201;E:\IDEA\IntelliJ IDEA 2020.2.1\plugins\Kotlin\kotlinc\lib\kotlin-stdlib.jar;E:\IDEA\IntelliJ IDEA 2020.2.1\plugins\Kotlin\kotlinc\lib\kotlin-reflect.jar;E:\IDEA\IntelliJ IDEA 2020.2.1\plugins\Kotlin\kotlinc\lib\kotlin-test.jar;E:\IDEA\IntelliJ IDEA 2020.2.1\plugins\Kotlin\kotlinc\lib\kotlin-stdlib-jdk7.jar;E:\IDEA\IntelliJ IDEA 2020.2.1\plugins\Kotlin\kotlinc\lib\kotlin-stdlib-jdk8.jar" com.wd.Test.StatusDemo1 statusPrintThread1-创建时状态为NEW statusPrintThread2-创建时状态为NEW statusPrintThread3-创建时状态为NEW statusPrintThread1启动后状态为RUNNABLE main状态为:RUNNABLE statusPrintThread1状态为:RUNNABLE statusPrintThread2状态为:RUNNABLE statusPrintThread3状态为:NEW statusPrintThread2启动后状态为RUNNABLE main状态为:TIMED_WAITING main状态为:TIMED_WAITING statusPrintThread1状态为:BLOCKED statusPrintThread1状态为:RUNNABLE statusPrintThread2状态为:BLOCKED statusPrintThread2状态为:RUNNABLE statusPrintThread3状态为:RUNNABLE statusPrintThread3状态为:RUNNABLE statusPrintThread3启动后状态为RUNNABLE main状态为:RUNNABLE main状态为:RUNNABLE main状态为:RUNNABLE statusPrintThread1状态为:BLOCKED statusPrintThread1状态为:RUNNABLE statusPrintThread2状态为:BLOCKED statusPrintThread3状态为:BLOCKED statusPrintThread2状态为:BLOCKED statusPrintThread3状态为:RUNNABLE statusPrintThread1状态为:BLOCKED statusPrintThread2状态为:RUNNABLE statusPrintThread3状态为:TIMED_WAITING main状态为:TERMINATED statusPrintThread1状态为:BLOCKED statusPrintThread2状态为:RUNNABLE statusPrintThread3状态为:BLOCKED main状态为:TERMINATED statusPrintThread1状态为:RUNNABLE main状态为:TERMINATED statusPrintThread2状态为:TIMED_WAITING statusPrintThread1状态为:BLOCKED statusPrintThread2状态为:TIMED_WAITING statusPrintThread3状态为:RUNNABLE statusPrintThread3状态为:BLOCKED main状态为:TERMINATED statusPrintThread1状态为:RUNNABLE statusPrintThread2状态为:BLOCKED statusPrintThread3状态为:BLOCKED main状态为:TERMINATED statusPrintThread1状态为:BLOCKED statusPrintThread2状态为:BLOCKED statusPrintThread3状态为:RUNNABLE main状态为:TERMINATED statusPrintThread1- 运行结束 statusPrintThread1状态为:RUNNABLE statusPrintThread2状态为:RUNNABLE statusPrintThread3状态为:TIMED_WAITING main状态为:TERMINATED statusPrintThread1状态为:TERMINATED statusPrintThread2状态为:BLOCKED main状态为:TERMINATED statusPrintThread3状态为:RUNNABLE statusPrintThread1状态为:TERMINATED statusPrintThread2状态为:RUNNABLE statusPrintThread3状态为:TIMED_WAITING statusPrintThread2- 运行结束 main状态为:TERMINATED statusPrintThread1状态为:TERMINATED statusPrintThread2状态为:TERMINATED statusPrintThread3状态为:RUNNABLE statusPrintThread3- 运行结束 Process finished with exit code 0
- 1.当线程新建之后,没有调用
start()
方法启动之前状态为NEW
- 2.调用
start()
方法启动之后,其状态为RUNNABLE
- 3.调用
LockSupport.parkNanos()方
法使线程等待之后,线程的状态变成了TIMED_WAITING
(LockSupport.parkNanos()
方法使得当前线程限时等待,LockSupport
是来自JDK
中的锁辅助类)- 4.等待结束之后,其状态又变为了
RUNNABLE
- 5.线程完成之后,它的状态变成了
TERMINATED
5.Jstack工具查看线程状态
- 1.如果
CPU
使用率居高不下,说明有线程一直占用着CPU
资源,通过Jstack
工具可以查看线程的状态
1.Jstack工具
- 1.
Jstack.exe
工具是Java
虚拟机自带的一种堆栈跟踪工具,其位于JDK/bin
目录下- 2.
Jstack
作用:生成或导出JVM
虚拟机运行实例当前时刻的线程快照(DUMP
)- 3.线程快照(
DUMP
):是当前JVM
实例内每一个线程正在执行的方法堆栈的集合- 4.生成或导出线程快照的主要目的:定位线程出现长时间运行,停顿或者阻塞的原因(线程间死锁,死循环,请求外部资源导致长时间等待)
- 5.线程出现停顿的时候可以通过
Jstack
查看各个线程的调用堆栈,可以查看线程在后台做什么事情或等待什么资源
2.Jstack工具使用方法
//1.通过jps查看Java进程id<pid> jps //2.查看某个进程的线程快照 jstack pid
1.一般情况通过
Jstack
输出的线程信息主要包括JVM
线程,用户线程等2.其中
JVM
线程在JVM
启动时就存在,主要用于执行垃圾回收,低内存检测等后台任务,这些线程在JVM
初始化时就存在,而用户线程则是在程序创建了新的线程才会生成3.注意
- 1.实际运行中一次
DUMP
的信息不足以确认问题,建议产生三次DUMP
信息,如果每次都指向同一个问题才能确定问题的典型性- 2.不同的
Java
虚拟机的线程导出来的DUMP
信息格式是不一样的,并且同一个JVM
的不同版本,DUMP
信息也有差别
3.Jstack运行实例
E:\IDEA\IntelliJ IDEA 2020.2.1\Code\CoreJava_Day1_20211201>jps 15440 Jps 12648 Launcher 15272 StatusDemo1 18296 E:\IDEA\IntelliJ IDEA 2020.2.1\Code\CoreJava_Day1_20211201>jstack 15272 2022-07-31 17:20:51 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.92-b14 mixed mode): "Service Thread" #11 daemon prio=9 os_prio=0 tid=0x000000001a1f2000 nid=0x3164 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C1 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x000000001a1ee800 nid=0x510 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x000000001a1ec000 nid=0xb3c waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000000001a1e3800 nid=0x2170 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x000000001a1e2800 nid=0x3c08 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x000000001a1e1000 nid=0x2dec runnable [0x000000001a92e000] java.lang.Thread.State: RUNNABLE at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) at java.net.SocketInputStream.read(SocketInputStream.java:170) at java.net.SocketInputStream.read(SocketInputStream.java:141) at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284) at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326) at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178) - locked <0x00000000d5e8c658> (a java.io.InputStreamReader) at java.io.InputStreamReader.read(InputStreamReader.java:184) at java.io.BufferedReader.fill(BufferedReader.java:161) at java.io.BufferedReader.readLine(BufferedReader.java:324) - locked <0x00000000d5e8c658> (a java.io.InputStreamReader) at java.io.BufferedReader.readLin