一、基本原理
1.1 进程基本原理
在了解线程之前肯定得知道进程是什么?
一个进程一般来说由程序段、数据段和进程控制块三部分组成。
- 程序段:程序段一般称为代码段,进程的程序指令存放在内存中的位置,包含需要执行的指令集合。
- 数据段:进程操作数据在内存中的位置,包含操作的数据集合。
- 程序控制块:俗称(PCB)包含进程的描述信息和控制信息,是进程存在的唯一标志。
PCB主要由四大部分组成:
进程的描述信:进程ID和进程名称:进程ID是唯一的代表每一个进程
进程状态:运行、就绪、阻塞
进程优先级:进程调度的重要依据进程的调度信息:程序的起始地址:程序的第一指令的内存地址,程序开始 执行的地址。
通信信息:进程间通信时的消息队列。进程资源信息:内存信息:内存占用情况和内存管理所用的数据结构。
I/O设备信息:所用的I/O设备编号及对应的数据结构。
文件句柄:所打开的文件信息。进程上下文:执行时各种CPU寄存器的值、当前程序计数器(PC)的值以及各栈的值等, 可视为进程的环境。
1.2、线程的基本原理
线程是什么?
线程是指“进程代码段”的一次顺序执行流程。线程是CPU调度的最小单位。
一个进程可以有一个或者多个线程,各个线程之间共享进程内存空间、系
统资源,进程仍然是操作系统资源分配的最小单位。
Java程序中,每当执行一个class类时,都会启动一个JVM进程。实际上,
在该进程中至少会启动两个线程,一个是main线程,一个是GC线程(垃圾回收)。
一个标准的线程主要由三部分组成:线程描述信息、程序计数器和栈内存。
在线程结构中,线程描述信息主要包括如下内容:
- 线程ID(线程标识符):线程的唯一标识,同一个进程内的不同线程的ID不会重叠。
- 线程名称:主要用于方便用户识别,用户可以指定线程名称(Thread中的setName方法设置),如果没有指定就会默认分配一个线程名称。
- 线程优先级:表示线程调度的优先级,优先级越高,获取CPU的执行机会就越大。
- 线程状态:表示当前线程执行状态,为新建、就绪、运行、阻塞、结束等状态。(在Java中的线程状态为新建 ->运行 -> 等待 -> 超时等待 -> 阻塞 ->销毁)
- 其他:例如守护线程等。(由于目前能力有限,在以后博客中介绍)
package com.like.thread;
import java.util.concurrent.TimeUnit;
/**
* @author like
* @date 2021年09月25日 16:29
* @className ThreadDmo
*/
public class ThreadDmo {
public static void main(String[] args) throws InterruptedException{
/**
* Thread.currentThread() 为Java.lang中的静态方法,用于获取正在执行的当前线程
*/
System.out.println("当前线程名称:" + Thread.currentThread().getName());
System.out.println("当前线程ID:" + Thread.currentThread().getId());
System.out.println("当前线程状态:" + Thread.currentThread().getState());
System.out.println("当前线程优先级:" + Thread.currentThread().getPriority());
int a = 1;
int b = 1;
int c = a/b;
threadTest();
TimeUnit.MILLISECONDS.sleep(1000);
}
private static void threadTest(){
int a = 1;
int b = 1;
int c = a/b;
threadTest1();
}
private static void threadTest1(){
int a = 1;
int b = 1;
int c = a/b;
}
}
执行结果
当前线程名称:main
当前线程ID:1
当前线程状态:RUNNABLE
当前线程优先级:5
在Java执行流程中的重要单位为方法,栈内存的分配单位是“栈帧(方法帧)”,方法每次执行都需要为其分配栈帧(方法帧),栈中主要保存局部变量、方法的返回信息以及方法的一些其他信息。当线程的执行流程进入方法时,JVM就会为方法分配一个对应的栈帧压入栈内存;每当线程执行流程跳出方法时,JVM就从栈内存弹出该方法的栈帧,随后栈帧的局部变量内存空间就会被回收。
以上诉代码为例,简单介绍一下main线程的栈内存。
其中三个方法main、threadTest、threadTest1,三个方法中存在a,b,c三个局部变量。
- 当执行main()方法时,JVM就会为main方法分配一个栈帧,用来保存a,b,c三个局部变量。然后再将栈帧压入main()线程栈内存中,然后main()还未执行完(利用TimeUnit.MILLISECONDS.sleep(1000)使得main()进入等待状态),执行threadTest方法。
- 执行threadTest()方法之前JVM为其分配栈帧,保存a,b,c三个局部变量。然后将栈帧压入main()线程栈内存。
- 执行threadTest1()方法,跟以上流程一样。
三个方法的栈帧弹出过程和压入过程恰好相反,threadTest1()方法执行完成以后,其栈帧从main线程中的栈内存弹出,以此类推,直到main栈内存中没有剩余的栈帧,main线程才结束。
栈帧(方法帧)采取后进先出的模式,这也是标准的栈操作模式,存放方法栈的内存也称为栈内存。
1.3、进程和线程的区别
- 线程是“进程代码段”的一次顺序执行流程。一个进程由一个或多个线程组成,一个进程中至少有一个线程
- 线程是CPU调度的最小单位,进程是操作系统分配资源的最小单位。线程的划分尺度小于进程,使得多线程程序 并发性高。。
- 线程是出于高并发的调度诉求从进程内部演进而来。线程的出现充分发挥了CPU的计算能力,还弥补了进程调度过于笨重的问题。
- 进程之间是相互独立的,但进程内部的各个线程之间并不是完全独立。各个线程之间共享进程的方法区内存、堆内存、系统资源。
- 切换速度不同:线程上下文切换比进程上下文切换快得多。所以有时候线程也称为轻量级进程。