【并发编程】线程的基本原理和Thread Dump线程分析
创始人
2025-06-01 22:04:35
0

线程的基本原理和Thread Dump线程分析

    • 线程的基本原理
    • 线程的运行状态
    • 如何中断线程
      • 存在循环的线程中断
      • 处于阻塞状态的线程中断
    • Thread Dump线程分析
      • CPU不高,但响应很慢
      • CPU很高,且响应很慢

线程的基本原理

放一张线程的原理图:
java代码创建线程后,我们通过调用start()方法启动线程,调用thread.cpp的方法来启动线程,底层还是通过操作系统的create_threadstart_thread方法来操作的线程
在这里插入图片描述

线程的运行状态

线程在运行过程中,会存在几种不同的状态,一般来说,在Java中,线程的状态一共是6种状态,分别是
NEW: 初始状态,线程被构建,但是还没有调用start方法
**RUNNABLED:**运行状态,JAVA线程把操作系统中的就绪和运行两种状态统一称为“运行中”
**BLOCKED:**阻塞状态,表示线程进入等待状态,也就是线程因为某种原因放弃了CPU使用权,阻塞
也分为几种情况

  • 等待阻塞:运行的线程执行wait方法,jvm会把当前线程放入到等待队列
  • 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被其他线程锁占用了,那么jvm会把当前的线程放入到锁池中
  • 其他阻塞:运行的线程执行Thread.sleep或者t.join方法,或者发出了I/O请求时,JVM会把当前线程设置为阻塞状态,当sleep结束、join线程终止、io处理完毕则线程恢复

WAITING: 等待状态
TIME_WAITING : 超时等待状态,超时以后自动返回
TERMINATED: 终止状态,表示当前线程执行完毕

在这里插入图片描述

如何中断线程

Thread提供了线程的一些操作方法,比如stop、suspend等,这些方法可以终止一个线程或者
挂起一个线程,但是这些方法都不建议大家使用。因为这些方法都是强制中断的方法,线程任务执行了一半被强制中断,可能会导致数据产生问题。这种行为类似于在linux系统中执行 kill -9命令,它是一种不安全的操作。

一般情形下线程是不需要用户手动去干预的, 哪些线程的中断需要外部干预呢?

  1. 线程中存在无限循环
  2. 线程中存在一些阻塞的操作,比如sleep、wait、join等

存在循环的线程中断

public class ThreadA extends Thread {public void run() {while(true){System.out.println("ThreadA .... ");}}public static void main(String[] args) {ThreadA myThread1 = new ThreadA();myThread1.start();}
}

对于线程中有无限循环的情形,代码中必须要有一个结束条件,并且用户可以在其他地方能够修改这个结束条件从而让线程感知到变化。像上面的代码,我们把while(true)改成while(flag),然后这个flag作为共享变量可以被外部修改,修改之后使得不满足循环条件然后退出循环并且结束线程

Java里提供了一个 interrupt 方法,这个方法就是实现线程中断操作的,其实现原理就是通过修改线程的中断标识来实现线程中断

public class InterruptDemo {private static int i = 0;public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()->{while(!Thread.currentThread().isInterrupted()){//默认情况下isInterrupted返回false、通过thread.interrupt变成了truei++;}System.out.println("final num:"+ i);},"InterruptDemo");thread.start();Thread.sleep(1000);thread.interrupt();}}

处于阻塞状态的线程中断

public class InterruptDemo {private static int i = 0;public static void main(String[] args) throws InterruptedException {Thread thread=new Thread(()->{while(!Thread.currentThread().isInterrupted()){try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {System.out.println("线程中断");e.printStackTrace();//当前异常只是响应了外部的中断命令,同时线程的中断状态也会复位,如果需要中断该线程,我们需要在这里再执行一次中断Thread.currentThread().interrupt();}}},"InterruptDemo");thread.start();Thread.sleep(1000);thread.interrupt();}}

我们平时在线程中使用的sleep、wait、join等操作,它都会抛出一个InterruptedException异常,为什么要抛出这个异常,是因为它在阻塞期间,必须要能够响应被其他线程发起的中断请求,而这个响应是通过InterruptedException 来实现的

InterruptedException异常的抛出并不意味着线程必须终止,而是提醒当前线程有中断的操作发生,至于接下来怎么处理取决于线程本身,比如

  1. 直接捕获异常不做任何处理
  2. 将异常往外抛出
  3. 停止当前线程,并打印异常信息

Thread Dump线程分析

我们在使用线程的时候,如果出现问题,怎么排查?
比如说CPU占用率很高,响应很慢;CPU占用率不高,但响应很慢;线程出现死锁的情况

 nohup java -jar -Dserver.port=8580 thread-demo-0.0.1-SNAPSHOT.jar > localhost.log &

CPU不高,但响应很慢

出现死锁的时候就会有这个问题,因为两个线程彼此等待,所以虽然响应很慢,但是cpu并不高

   @GetMapping("/dead")public String dumpDeadLock(){Thread a = new ThreadA();Thread b = new ThreadB();a.start();b.start();return "ok";}
public class ThreadA extends Thread {@Overridepublic void run() {synchronized (ThreadA.class) {System.out.println("thread A" + Thread.currentThread().getName());try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (ThreadB.class) {}}}
}
public class ThreadB extends Thread {@Overridepublic void run() {synchronized (ThreadB.class) {System.out.println("thread A" + Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (ThreadA.class) {}}}
}

查看死锁问题的操作步骤如下:

  1. 通过 jps 命令,查看java进程的pid
  2. 通过jstack 查看线程日志
    如果存在死锁情况,Thread Dump日志里面肯定会给出Found one Java-level deadlock:信息。只要找到这个信息就可以定位到问题。

在这里插入图片描述
在这里插入图片描述

CPU很高,且响应很慢

如果线程中出现死循环,就会有这样的问题

 @GetMapping("/loop")public String dumpWhile(){new Thread(new LoopThread()).start();return "ok";}
public class LoopThread implements Runnable {@Overridepublic void run() {while (true) {System.out.println("Thread run...");}}
}

排查cpu过高问题的步骤

  1. top -c 该命令可以动态显示进程及占用资源的排行榜,从而找到占用CPU最高的进程PID ,假设为PID=89831
  2. top -H -p [PID] 查找到该进程中最消耗CPU的线程,得到PID2=88765
  3. printf "0x%x\n" 88765 把对应的线程PID转化为16进制
  4. jstack 89831| grep -A 30 0x15abd 查看线程Dump日志,其中-A 30表示展示30行, 89831表示进程ID, 0x15abd表示16进制的线程ID

可以从打印的dump日志看出是哪个方法的执行逻辑导致的cpu过高

上一篇:JVM知识整理

下一篇:2023面试题汇总二

相关内容

热门资讯

华大九天:公司建立了完善的董事... 证券之星消息,华大九天(301269)06月03日在投资者关系平台上答复投资者关心的问题。 投资者提...
原创 蹦... “蹦床外长”履新?德国前外长贝尔伯克当选联大主席,发言承诺:成为公正调解人! 据路透社报道,联合国...
华大九天:建立完善的董事高管薪... 金融界6月3日消息,有投资者在互动平台向华大九天提问:公司业绩表现不佳,股价萎靡不振,但公司高管却拿...
哪吒汽车违反劳动保障监察条例被... 企查查APP显示,近日,哪吒汽车关联公司合众新能源汽车股份有限公司新增2条行政处罚信息,处罚单位均为...
原创 美... 据澎湃新闻报道,5月30日《关于建立国际调解院的公约》签署仪式在香港举行,近60国及20个国际组织派...
阿右旗:法援唱响三部曲 推动服... 阿右旗: 法援唱响三部曲 推动服务规范化 阿拉善右旗坚持“援助多服务 群众少跑腿”的法律援助服务理念...
“广东好人”陈德善:成立“德叔... 在肇庆市广宁县人民法院新楼法庭里,一位银发老者正伏案工作,他推了推老花镜,专心致志地整理着调解记录。...
乐山市司法局:“四个优化”协同... 近年来,乐山市司法行政系统坚持以紧扣人民群众法律服务需求,落实市委市政府和省司法厅以及市委政法委工作...
哪吒汽车违反劳动保障条例被罚 ... IT之家 6 月 3 日消息,哪吒汽车关联公司合众新能源汽车股份有限公司于 5 月 23 日新增 2...
常铮律师在蓟门决策论坛深度阐述... “蓟门决策”是由中国政法大学公共决策研究中心主办的法律、经济类开放性论坛。每星期举办一期,每一期选择...