高并发 - 1.进程和线程

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中的虚拟机栈和本地方法栈
      • 2JDK8中每个线程在创建时默认被分配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.上述代码定义了三个方法mainanotherFunanotherFun2,并且这三个方法有相同的三个局部变量abc
  • 3.上述代码中JVM的执行流程
    • 1.当执行main()方法时,JVMmain()方法分配一个栈帧,保存三个局部变量,然后将栈帧压入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_WAITINGLockSupport.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
<think>好的,用户想了解进程线程的区别。首先,我需要回忆操作系统中的基本概念。进程线程都是执行单位,但层次不同。根据引用[3],进程是资源分配的基本单位,而线程是调度的基本单位。这点很重要,要首先点明。 接下来,用户提供的引用提到进程像工厂,线程像生产线,这个比喻很形象,可以用来帮助理解。进程拥有独立的内存空间,而线程共享进程的资源,比如堆方法区。需要解释为什么这样设计,比如资源隔离轻量级协作。 然后,用户可能想知道具体区别,比如资源分配、切换开销、通信方式等。根据引用[1],进程切换开销大,线程切换开销小,因为线程共享地址空间。此外,进程间通信需要IPC机制,而线程可以直接读写进程数据,但需要同步机制避免竞态。 还要考虑系统结构,比如JVM角度,引用[1]提到程序计数器、栈为什么是私有的。每个线程有自己的执行路径,所以需要独立的程序计数器记录下一条指令,栈用于方法调用,私有保证线程独立运行。而堆方法区是共享的,存放对象类信息。 用户可能还想知道多进程线程的应用场景。比如需要安全隔离用进程,需要高效协作用线程。比如浏览器多标签用进程防止崩溃影响整体,而Web服务器用线程处理请求提高并发。 最后,需要检查是否有遗漏点,比如协程,但用户问题集中在进程线程,可能不需要深入协程,引用[3]提到但可以简略带过。确保回答结构清晰,分点对比,使用比喻帮助理解,并引用提供的资料支持观点。</think>### 进程线程的区别 #### 1. **基本定义** - **进程**:是操作系统**资源分配的基本单位**,代表一个正在执行的程序实例,拥有独立的内存空间系统资源[^3]。 - **线程**:是**进程内的执行单元**,共享进程的资源(如内存、文件句柄),是CPU调度的最小单位[^2]。 #### 2. **核心区别** | 特性 | 进程 | 线程 | |-------------------|----------------------------------------------------------------------|----------------------------------------------------------------------| | **资源分配** | 独立内存空间(堆、代码段、数据段),资源隔离性高[^3] | 共享进程内存资源,仅私有程序计数器、栈等[^1] | | **切换开销** | 高(需切换内存空间、寄存器等) | 低(共享内存,仅需切换线程私有数据) | | **通信方式** | 需进程间通信(IPC),如管道、信号、共享内存 | 可直接读写进程共享数据(需同步机制,如锁) | | **健壮性** | 单个进程崩溃不影响其他进程 | 单个线程崩溃可能导致整个进程终止 | | **创建/销毁成本** | 高(需分配独立资源) | 低(复用进程资源) | #### 3. **从JVM角度的解释[^1]** - **私有资源**: - **程序计数器**:记录当前线程执行位置(避免执行路径混乱)。 - **虚拟机栈/本地方法栈**:存储方法调用局部变量(保证线程独立运行)。 - **共享资源**: - **堆**:存储对象实例。 - **方法区**:存储类信息、常量、静态变量。 #### 4. **比喻说明** - 进程像一个**工厂**,拥有独立场地、设备资金; - 线程像工厂中的**生产线**,共享工厂资源但各自执行任务[^2]。 #### 5. **应用场景** - **多进程**:需高隔离性(如浏览器多标签页、安全敏感任务)。 - **多线程**:需高效协作(如Web服务器并发处理请求、实时数据处理)。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值