目录
2、进程与进程之间不能共享内存空间,而线程之间可以共享内存资源
一、进程
1、概念
进程是操作系统对一个正在运行的程序的一种抽象,可以把进程看作程序的一次运行,是操作系统进行资源分配的基本单位
2、描述与组织
1.PCB
是一个用于描述进程的结构体,一个进程可以有一个PCB也可以有多个
2.PCB的成员变量
1.pid:进程的身份标识,同一个主机同一时刻进程的pid是唯一的
2.内存指针:用于描述进程持有的内存资源,一个可执行程序运行后,操作系统会把核心资源加载到内存里
3.文件描述符表:用于描述一个进程持有的文件资源,用于记录进程打开的文件
4.进程状态:用于描述一个进程能不能被调度到CPU执行,有两种状态,一种是就绪状态,可以被调度到CPU执行,还有一种是堵塞状态,不可以被调度到CPU执行
5.进程优先级:描述进程执行的优先关系
6.进程上下文:一个进程在CPU执行了一会后,切换到别的进程,就需要保存存档,下次执行该进程时,就恢复原来继续往下执行
7.进程记账信息:记录一个进程执行的时间
3.组织
每个PCB是通过双向链表来组织的
3、虚拟地址空间
给每个进程分配的内存资源,叫虚拟地址空间,不是真实的物理地址,通过专门的设备MMU来检测进程是否访问越界,后访问物理地址,使用虚拟地址空间相当于增加了进程之间的隔离性,一个进程不能访问另一个进程的内存空间
4、进程间的通信
有了虚拟地址空间增加了隔离性但也有了新的问题---进程之间的交互,进程之间的交互就设计到进程间的通信,进程之间的通信,是通过一个多个进程都能访问到的公共资源比如,一块内存,一个网卡等
5、进程创建与销毁
1.进程的创建
创建PCB-》分配资源-》插入链表
2,进程的销毁
从链表删除 -》释放持有的资源-》销毁PCB
二、线程
1、概念
一个线程就是一个人执行流,每个线程都可以按照顺序执行自己的代码,多个线程同时执行多个代码
2、为什么要有线程
并发编程的刚需,当需要多个进程时,进程的创建与销毁操作效率比较低,而多个线程创建与销毁,相比比较高效
3、进程与线程的关系
1、一个进程必须有一个线程叫主线程,也可以有多个线程
2、进程与进程之间不能共享内存空间,而线程之间可以共享内存资源
3、线程是轻量级的进程
4、进程是系统分配资源的基本单位,线程是系统调度的基本单位
每一个线程都是一个执行流,都可以在CPU进行调度,如果把进程比作工厂,线程就相当于是工厂里的流水线
4、线程的创建销毁
创建第一个线程时系统分配资源,后续创建线程不需要申请资源,线程销毁时也只是销毁到最后一个线程才释放资源,一个PCB 也就是一个线程
5、线程的效率
在一定程度上线程使用,是高效的,而并不是越多越好,CPU的核心数是有限的,如果线程的数量超出一定范围反而会降低效率
三、Java里线程的创建
在操作系统里面原生的线程API是C语言的,在Java里将其封装为Java风格的API
1、方法1:继承Thread类
让一个类继承Thread然后重写run方法
run方法里面就是这个线程要做的事,run方法就是线程的入口方法
class MyThread extends Thread{
@Override
public void run() {
while(true){
System.out.println("这个线程要执行的逻辑");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class demo1 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
使用这个类实例化一个对象并不是创建了线程,而通过对象调用了父类里的start方法才是创建了一个线程,main方法执行的逻辑是主线程,main就是主线程的入库口方法,线程是并发执行的,也就是run里面的代码与main函数里面的代码是同时执行的
2、方法2:实现Runnable接口
实现接口后重写run方法,实例一个该类的对象,然后实例化一个Thread的对象,在构造函数传入实例化的实现Runnable接口的类的对象,通过Thread对象调用start方法就创建了一个线程,这种创建方法将创建线程与线程所执行的逻辑分开,实现了代码的解耦合
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("这是这个线程要执行的逻辑");
}
}
public class demo2 {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
}
}
3、方法3:匿名内部类创建Thread的子类
public class demo3 {
public static void main(String[] args) {
Thread thread = new Thread(){
@Override
public void run() {
System.out.println("线程执行的逻辑");
}
};
thread.start();
}
}
4、方法4:匿名内部类实现Runnable接口
public class demo4 {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程执行的逻辑");
}
});
}
}
5、lambda表达式
public class demo4 {
public static void main(String[] args) {
Thread thread = new Thread(()->{
System.out.println("线程执行的逻辑");
});
}
}
四、串行与线程代码的效率
让a自增到一个值重复两次自增,通过执行时间来判断两种方式的效率
public class demo4 {
public static final long NUM = 10_0000_0000L;
public static void main(String[] args) {
func1();
func2();
}
//串行代码
public static void func1(){
long begin = System.currentTimeMillis();
long a = 0L;
for (int i = 0; i < NUM; i++) {
a++;
}
a = 0;
for (int i = 0; i < NUM; i++) {
a++;
}
long end = System.currentTimeMillis();
System.out.println("串行代码执行时间"+(end-begin)+"ms");
}
//线程代码
public static void func2(){
long begin = System.currentTimeMillis();
Thread thread1 = new Thread(()->{
long a = 0L;
for (int i = 0; i < NUM; i++) {
a++;
}
});
Thread thread2 = new Thread(()->{
long a = 0L;
for (int i = 0; i < NUM; i++) {
a++;
}
});
thread1.start();
thread2.start();
//让线程执行完毕
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("线程代码执行时间"+(end-begin)+"ms");
}
}