JavaWeb~带你全面认识理解 进程 与 线程

进程

进程的初步认识

  • 对于操作系统来说,一个任务就是一个进程(Process)
  • 进程是操作系统分配资源的最小单位
  • 进程是操作系统中非常重要的软件资源,把一个可执行程序跑起来 (.exe文件) 系统就会创建一个对应的进程,如果这个程序执行结束系统随之会销毁所对应的进程
  • 所以进程就可以看做是一个程序执行的"过程"
  • 以Linux为例
  1. 每创建一个进程,就会同时创建至少一个PCB这样的类的实例
  2. 系统会使用双向链表将PCB对象串到一起
  3. 双击一个exe文件,创建一个进程的时候,本质是在内核中先创建一个PCB对象,然后将PCB根据进程管理加到这个链表中
  4. 关闭一个程序,就会在这个链表中先找到这个PCB然后删除他

PCB的数据结构

  1. pid 进程的身份标识
  2. 一组内存指针,指向该进程持有的一些重要数据(包括要执行的指令 指令依赖的数据) 在内存中的位置
  3. 状态
  4. 优先级
  5. 进程的记账信息
  6. 上下文

调度器用到哪些PCB信息

  • 进程的管理一定是先描述,再组织,而描述就是一句PCB对象里的信息
  • 实现进程调度目的就是为了让很多的进程能够很好的在有限的CPU上进行并发执行

优先级

  • 判断该进程在就绪队列的为位置,是优先执行还是稍后执行

并发与并行

  • 一个CPU同一时刻只能执行一个进程的指令
  • 并发执行
    并发执行是在宏观上看到是并发,在微观上来看,很多进程是串行执行的,只不过CPU的执行速度很快,人在宏观上感知不到
  • 并行执行
    并行执行宏观是同时执行,微观也是同时执行,如计算子有俩个CPU就可以同时执行俩个进程的指令

内核态与用户态

  • 操作系统内核作为直接控制硬件设备的底层软件,权限最高,称为内核态,或核心态。
  • 用户程序的权限最低,称为用户态。
  • 简而言之就是人宏观可以感受到的变化状态就可以称之为用户态, 人宏观感受不到的称之为内核态
  • 引进内核态就是为了操作系统的安全性

上下文

  • 上下文简单说来就是一个环境,进程在时间片轮转切换时,由于每个进程运行环境不同,就涉及到转换前后的上下文环境的切换就是一个进程在执行的时候,CPU的所有寄存器中的值、进程的状态以及堆栈上的内容。
  • 切换时需要保存当前进程的所有状态,即保存当前进程的进程上下文,以便再次执行该进程时,能够恢复切换时的状态,继续执行。

进程状态

  • 就绪状态: 正早CPU上执行, 或者即将执行
  • 睡眠状态(阻塞): 这些进程没法在CPU上执行,而是在等待
  • 深度睡眠状态: 进程在长时间忙于IO操作,没精力例会CPU
  • 退出状态(终止): 进程已经被销毁了
    在这里插入图片描述

时间片

  • 操作系统(如Windows、Linux)的任务调度是采用时间片轮转的抢占式调度方式,也就是说一个任务执行一小段时间后强制暂停去执行下一个任务,每个任务轮流执行。
  • 任务执行的一小段时间叫做时间片,任务正在执行时的状态叫运行状态,任务执行一段时间后强制暂停去执行下一个任务,被暂停的任务就处于就绪状态等待下一个属于它的时间片的到来。

记账信息

  • 记录了进程在CPU上一共执行了多长时间, 通过这个时间来限制不要让某个进程霸占CPU太久,导致其他进程无法执行

线程

认识线程

  • 进程是系统分配资源的最小单位,线程是系统调度的最小单位。一个进程内的线程之间是可以共享资源的。每个进程至少有一个线程存在,即主线程。

  • 所以可得出结论:

  1. 线程是包含在进程中的
  2. 当创建一个进程的时候,会自动随之至少创建一个线程(主线程)
  3. 一个进程可以有多个线程(通过每个线程的PCB对象的tgroupid来判断那几个线程属于一个进程) 把属于同一个进程的多个线程称为 线程组
  4. 每个线程都有自己要执行的逻辑(指令),每个线程是独立的"执行流"
  5. 同一进程中的很多线程自检,是共享了一些资源(不是全部资源)
    在这里插入图片描述
  • 共享的资源主要有俩部分:
    一是内存资源, 二是打开的文件

  • 不共享的资源有:
    PCB对象里的一个信息如上下文 优先级 状态 记账信息, 二是 内存中一块特殊区域的栈

为什么要引入线程

  1. 线程可以理解为一个轻量级进程,也是实现并发编程的一种方式
  2. 因为线程之间可以共享大部分资源,不需要为新线程分配很多的资源,所以创建一个线程的成本较低,而创建一个新进程就需要把它所需要的资源再创建一份.
  3. 在实际编程的时候,多线程的方式比多进程方式更常见,也更高效
  • 那一个进程对多有多少个线程?
  1. 和CPU个数有关
  2. 和线程执行的任务类型也有关系(是CPU密集型 还是 IO密集型)
  • 引入线程也是有一定缺点的
    降低了进程的安全性, 比如因为线程是部分资源共享, 当俩个或者多个线程需要相同资源时,就有可能发生不安全的情况

多种创建线程的方式

  • java标准库提供了一个Thread类帮助我们实现线程
  • 通过Thread 与 Runnable / lambda 方式创建的线程本质上没什么区别核心都是依靠thread类 ,但是细节上(站在耦合性的角度) 在使用Runnable和 Lambda创建的线程在run中没有涉及到Thread相关的内容, 这就意味着很容易把这些逻辑从线程中剥离出来,去搭配其他并发编程的方式来执行,也可能会容易改成不并发的方式去执行
    下面展示6种多线程的创建方式:
public class ThreadTest3 {
    static class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("hello");
        }
    }

    static class MyRunnable implements Runnable {

        @Override
        public void run() {
            System.out.println("hello");
        }
    }
    public static void main(String[] args) {

        //1定义一个类继承Thread
        Thread t = new MyThread();
        t.start();

        //2定义匿名内部类
        Thread t1 = new Thread() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        };
        t1.start();

        //3 lambda表达式
        Thread t2 = new Thread(() -> {
            System.out.println("hello");
        });
        t2.start();

        //4定义一个类继续Runnable接口,但注意要将runnable对象关联到Thread对象上
        Runnable r1 = new MyRunnable();
        Thread t3 = new Thread(r1);
        t3.start();

        //5匿名内部类继续Runnable接口
        Runnable r2 = new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        };
        Thread t4 = new Thread(r2);
        t4.start();

        //6对Runnable进行Lambda表达式
        Runnable r3 = () -> {
            System.out.println("hello");
        };
        Thread t5 = new Thread(r3);
        t5.start();

    }
}

多线程的优势-增加运行速度

public class ThreadTest2 {
    private static long count = 10_0000_0000;
    public static void main(String[] args) {
        Danxianc();
        Duoxianc();
    }

    private static void Danxianc() {
        long beg = System.currentTimeMillis();
        for (int i = 0; i < count; i++) {
            int a;
            a = i;
        }
        for (int i = 0; i < count; i++) {
            int b;
            b = i;
        }
        System.out.println(System.currentTimeMillis() - beg + "ms");
    }

    private static void Duoxianc() {
        long beg = System.currentTimeMillis();
        Thread thread = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < count; i++) {
                    int a;
                    a = i;
                }
            }
        };
        Thread thread1 = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < count; i++) {
                    int b;
                    b = i;
                }
            }
        };
        thread.start();
        thread1.start();
        //让方法这个线程等待,等thread 和thread1这两线程跑完自己再执行
        try {
            thread.join();
            thread1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(System.currentTimeMillis() - beg + "ms");
    }
}

运行结果:

1753ms
1044ms

猜你喜欢

转载自blog.csdn.net/Shangxingya/article/details/106567018