带你通俗易懂进程和线程(小白一学就会)

一、什么是进程/任务(Process/Task

1、进程定义

  1)进程是操作系统对一个正在运行的程序的一种抽象,换言之,可以把进程看做程序的一次运行过程。进程是操作系统进行资源分配的最小单位。

   2)进程是程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程,是一个动态的实体,它有自己的生命周期。它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而被撤消。

      .exe是一个可执行文件(程序),当我们双击这个文件时,这个程序就跑起来,于是系统就形成了一个进程(跑起来的程序就是进程)

2、描述进程

使用结构体(进程控制块抽象 Process Control Block)/类,把一个进程有哪些信息表示出来。

      组织进程:使用一定的数据结构。

// 以下代码是 Java 代码的伪码形式,重在说明,无法直接运行
class PCB {
    // 进程的唯一标识 —— pid;
    // 进程关联的程序信息,例如哪个程序,加载到内存中的区域等
   // 分配给该资源使用的各个资源
    // 进度调度信息(留待下面讲解)
}

 pid:(内存指针)当前这个进程使用的内存那一部分(使用那些内存上的资源)

文件描述符表:把文件放到一个顺序表这样的结构里,构成了文件描述符表,

      文件:比如硬盘上的存储的数据,往往是以文件为单位进行整理的。

操作系统在通过这种数据结构,例如线性表、搜索树等将PCB对象组织起来,方便管理时进行增删查改的操作。

  操作系统使用双向链表组织PCB:

          1)创建一个进程,就是创建一个链表的节点;

          2)销毁一个进程,就是删除一个链表的节点;

          3)遍历进程列表,就是在遍历链表。

    

3、进程间通信(Inter Process Commuication) 

     1) 产生原因:要完成一个复杂的业务需求:往往无法通过一个进程独立完成,总是需进程和进程进行配合达到应用的目的,进程之间需要进行“信息交换”。

      2)通俗理解:就是在隔离的前提下,找一个公共区域,让两个进程借助这个公共区区来完成数据交换。

      3)主流操作系统提供的进程通信机制:(在Java中,主要使用文件、socket来完成进程通信

1、管道

2、共享内存

3、文件

4、网络

5、信号量

6、信号

4、进程调度(PCB的属性)

1)进程状态:就绪态、阻塞态

2)进程优先级:最高、其次、最低

3)进程的上下文:指在进程运行过程中,CPU内部的一系列寄存器的值。   存档

4)统计了每个进程,在CPU上执行多久,可以作为调度的参考依据。  

 5、区分并行和并发

      并行:同一时刻,两个核心,同时执行两个进程,此时这两个进程就是并行执行的;

      并发:一个核心,先执行进程1;执行一会之后,再去执行进程2,3等,此时只要切换速度足够快,看起来,他们就是“同时”执行的。

并发+并行统称为并发,完全是操作系统自身控制的。

二、认识线程(Thread)

1、定义

  线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。

举例:一家公司要去银行办理业务,既要进行财务转账,又要进行福利发放,还得进行缴社保。 如果只有张三一个会计就会忙不过来,耗费的时间特别长。为了让业务更快的办理好,张三又找 来两位同事李四、王五一起来帮助他,三个人分别负责一个事情,分别申请一个号码进行排队, 自此就有了三个执行流共同完成任务,但本质上他们都是为了办理一家公司的业务。 此时,我们就把这种情况称为多线程,将一个大任务分解成不同小任务,交给不同执行流就分别 排队执行。其中李四、王五都是张三叫来的,所以张三一般被称为主线程(Main Thread)。

注:每个线程都是一个独立的执行流,多个线程之间,也是并发执行的。

       多个线程可能是多个CPU核心上,同时运行,也可能是在一个CPU核心上,通过快速调度,进行运行,操作系统真正调度的,是在调度线程,而不是进程。

2、产生原因

首先, "并发编程" 成为 "刚需".

  • 单核 CPU 的发展遇到了瓶颈. 要想提高算力, 就需要多核 CPU. 而并发编程能更充分利用多核 CPU 资源.
  • 有些任务场景需要 "等待 IO", 为了让等待 IO 的时间能够去做一些其他的工作, 也需要用到并发编 程.
  • 其次, 虽然多进程也能实现 并发编程, 但是线程比进程更轻量. 创建线程比创建进程更快. 销毁线程比销毁进程更快. 调度线程比调度进程更快.
  • 最后, 线程虽然比进程轻量, 但是人们还不满足, 于是又有了 "线程池"(ThreadPool) 和 "协程" (Coroutine)

3、 进程与线程的区别

  • 进程是包含线程的. 每个进程至少有一个线程存在,即主线程。
  • 进程和进程之间不共享内存空间. 同一个进程的线程之间共享同一个内存空间.

详解请点击: https://blog.csdn.net/leyhe/article/details/139046831?spm=1001.2014.3001.5502

4、 如何在Java中实现线程? 

(1)继承Thread类实现多线程

继承Thread类,然后重写run方法.(由于Java单继承的特性,这种方式用的比较少)

public class MyThread extends Thread {
	public MyThread() {
		
	}
	public void run() {
		for(int i=0;i<10;i++) {
			System.out.println(Thread.currentThread()+":"+i);
		}
	}
	public static void main(String[] args) {
		MyThread mThread1=new MyThread();
		MyThread mThread2=new MyThread();
		MyThread myThread3=new MyThread();
		mThread1.start();
		mThread2.start();
		myThread3.start();
	}
}

(2)实现Runnable()接口定制执行目标(target)类,实现其run()方法

推荐此方式。两个特点:

  • a.覆写Runnable接口实现多线程可以避免单继承局限

  • b.实现Runnable()可以更好的体现共享的概念

  • c.当执行目标类实现Runnable接口,此时执行目标(target)类和Thread是代理模式(子类负责真是业务的操作,thread负责资源调度与线程创建辅助真实业务。

    public class MyTarget implements Runnable{
    	public static int count=20;
    	public void run() {
    		while(count>0) {
    			try {
    				Thread.sleep(200);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			System.out.println(Thread.currentThread().getName()+"-当前剩余票数:"+count--);
    		}
    	}
    	public static void main(String[] args) {
    		MyThread target=new MyTarget();
    		Thread mThread1=new Thread(target,"线程1");
    		Thread mThread2=new Thread(target,"线程2");
    		Thread mThread3=new Thread(target,"线程3");
    		mThread1.start();
    		mThread2.start();
    		myThread3.start();
    	}
    }

    (3)实现Callable接口创建多线程(JDK1.5)

    ​ a.执行目标核心方法叫call()方法
    ​ b.有返回值

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
 
public class MyTarget implements Callable<String> {
	private int count = 20;
 
	@Override
	public String call() throws Exception {
		for (int i = count; i > 0; i--) {
//			Thread.yield();
			System.out.println(Thread.currentThread().getName()+"当前票数:" + i);
		}
		return "sale out";
	} 
 
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		Callable<String> callable  =new MyTarget();
		FutureTask <String>futureTask=new FutureTask<>(callable);
		Thread mThread=new Thread(futureTask);
		Thread mThread2=new Thread(futureTask);
		Thread mThread3=new Thread(futureTask);
//		mThread.setName("hhh");
		mThread.start();
		mThread2.start();
		mThread3.start();
		System.out.println(futureTask.get());
		
	}
}

 其他变形

  • 匿名内部类创建
// 使用匿名类创建 Thread 子类对象
Thread t1 = new Thread() {
    @Override
    public void run() {
        System.out.println("使用匿名类创建 Thread 子类对象");
   }
};
  • 匿名内部类创建 Runnable 子类对象
// 使用匿名类创建 Runnable 子类对象
Thread t2 = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("使用匿名类创建 Runnable 子类对象");
   }
});

  • lambda 表达式创建 Runnable 子类对象
    // 使用 lambda 表达式创建 Runnable 子类对象
    Thread t3 = new Thread(() -> System.out.println("使用匿名类创建 Thread 子类对象"));
    Thread t4 = new Thread(() -> {
        System.out.println("使用匿名类创建 Thread 子类对象");
    });
    

    5、多线程的优势

   可以观察多线程在一些场合下是可以提高程序的整体运行效率的。

   使用 System.nanoTime() 可以记录当前系统的 纳秒 级时间戳. serial 串行的完成一系列运算. concurrency 使用两个线程并行的完成同样的运算 

public class ThreadAdvantage {
    // 多线程并不一定就能提高速度,可以观察,count 不同,实际的运行效果也是不同的
    private static final long count = 10_0000_0000;
    public static void main(String[] args) throws InterruptedException {
        // 使用并发方式
        concurrency();
        // 使用串行方式
        serial();
   }
    private static void concurrency() throws InterruptedException {
        long begin = System.nanoTime();
        
        // 利用一个线程计算 a 的值
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                int a = 0;
                for (long i = 0; i < count; i++) {
                    a--;
               }
           }
       });
        thread.start();
        // 主线程内计算 b 的值
        int b = 0;
        for (long i = 0; i < count; i++) {
            b--;
       }
        // 等待 thread 线程运行结束
        thread.join();
        
        // 统计耗时
        long end = System.nanoTime();
        double ms = (end - begin) * 1.0 / 1000 / 1000;
        System.out.printf("并发: %f 毫秒%n", ms);
   }
    private static void serial() {
        // 全部在主线程内计算 a、b 的值
        long begin = System.nanoTime();
        int a = 0;
        for (long i = 0; i < count; i++) {
            a--;
       }
        int b = 0;
        for (long i = 0; i < count; i++) {
  private static void serial() {
        // 全部在主线程内计算 a、b 的值
        long begin = System.nanoTime();
        int a = 0;
        for (long i = 0; i < count; i++) {
            a--;
       }
        int b = 0;
        for (long i = 0; i < count; i++) {


            b--;
       }
        long end = System.nanoTime();
        double ms = (end - begin) * 1.0 / 1000 / 1000;
        System.out.printf("串行: %f 毫秒%n", ms);
   }
}
并发: 399.651856 毫秒
串行: 720.616911 毫秒

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值