多线程详解(通俗举例助你理解)

1、程序

程序是指令和数据的有序集合,程序可以作为一种软件(还包括各种数据资源)资料存储在外部存储器上而长期存在,是永久的,一般程序被设计成可以针对所有可能的用例数据,也就是说其用例数据是不确定的。其本身没有任何运行的含义,是一个静态的概念。
但是程序可以被调入内存运行,而且一旦被运行,我们就把一个正在运行的针对特定的用例数据的程序称为进程,所以进程是程序在处理机上的针对某个应用用例(数据集)的执行实体,它是一个动态的概念,而且它的用例数据必须是每次运行都是确定的,进程是暂时的、有生命周期的。
不同的程序被计算机运行当然产生多个不同的进程,就是同样一个程序也可以被多次(同时)运行产生不同的进程,例如:同样一个“Microsoft Edge”的程序可以启动多个运行实例(进程)浏览多个网站。

2、进程

进程是60年代初首先由麻省理工学院的MULTICS系统和IBM公司的CTSS/360系统引入的。

[1]XiaoManon. 计算机操作系统原理.https://www.cnblogs.com/xiaomanon/p/4201006.html

进程(Process)是程序在处理机上的针对某个应用用例(数据集)的执行实体,操作系统需要为其分配资源(内存、CPU使用权、外部设备使用权)、还需要调度它使用CPU(CPU使用权)或外部设备(外部设备使用权),所以,进程是操作系统分配资源和进行调度的基本单位。
狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)。
广义定义:进程是一个具有一定独立功能的程序关于某个数据集合(特定用例数据)的执行实体。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
进程的概念主要有两点:
第一,进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括:文本区域(text region):存储执行的代码;数据区域(data region):存储变量和进程执行期间使用的动态分配的内存;堆栈(stack region):存储着各个独立活动过程的运行现场信息和局部变量。
第二,进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行之),它才能成为一个活动的实体,我们称其为进程。

[2]线程和进程的基本概念 百度文库.2014-03-28 引用日期2020-05-16.

进程是操作系统中最基本、重要的概念。是多道程序系统出现后,为了刻画系统内部出现的动态情况,描述系统内部各道程序的活动规律引进的一个概念,所有多道程序设计操作系统都建立在进程的基础上。
进程更能真实地描述并发,而程序不能;
进程是由进程控制块、程序段、数据段三部分组成;
进程具有创建其他进程的功能,而程序没有。
同一程序同时运行于若干个数据集合上,它将属于若干个不同的进程,也就是说同一程序可以对应多个进程。
在传统的操作系统中,程序并不能独立运行,作为资源分配和独立运行的基本单元都是进程。
而我们有很多程序是多任务处理程序,那我们启动这种程序产生的进程也被称为多任务进程。在早期的多进程操作系统中(如:UNIX)多任务进程执行多任务采用的方式是当需要处理新任务时,由当前进程(父进程)派生一个子进程(通过调用fork()函数完成),每个子进程相当于一个独立进程(相当于父进程的副本,但是用例数据可能不一样),也可以被操作系统分配资源并调度运行,因此,每个子进程都拥有独立资源。这样做的坏处是浪费资源(因为,每个子进程都拥有独立资源);好处是方便协调调度,不需要复杂的同步控制机制。但是,我们可以确定的是在同一个进程派生出来的子进程实际上很多数据是可以共享的(即内存资源可以共享),不需要每个子进程都拥有独立的资源,而且也只有在两个以上的子进程同时写某个共享资源时才需要启用同步控制机制。那么有没有更节省资源的多任务运行机制呢?

3、线程

上世纪80年代一种新的更轻型的可被独立调度且能完成独立功能(任务)的运行实体被科学家提出了,科学家把这种轻型的运行实体称为:线程。为什么说它是轻型的呢?这是因为,它除了基本的TCB(线程控制块)、实体的局部变量(临时内存)和上下文之外,不需要占用其它资源。
而且这种操作系统一经推出,人们发现它特别适合于多CPU或多核CPU的计算机系统处理并发的应用程序,所以,很快风靡全球,现在的操作系统几乎都是多线程操作系统,而且并发程序的编写基本也是采用多线程的处理方式。
同一个进程可以同时运行多个不同的线程,例如:我们在同一个运行的LOL游戏进程中同时运行着有很多个人控制的英雄(线程)和NCP(线程)。所以,在现代多进程与多线程的操作系统中,进程是线程的容器,一个进程可以同时(并行或并发)的运行多个线程。

4、多线程的理解

在多核CPU的计算机系统中(目前基本是这种计算机系统),我们的多任务处理程序通常采用多线程编程,这样可以充分利用多CPU(多个锅)资源,当一个程序有一部分代码可以并行处理多个线程(比如:WEB服务器可以为多个用户提供WEB服务,每个用户的WEB服务都可以由一个线程处理),这样就相当于你要炒多个菜,这就可以利用多个锅同时炒多个菜,这样你炒菜的速度明显加快了,所以在多核系统下对于可以并行运行的程序或代码,单体上的速度提升也是非常明显的,对于整个系统效果就更加显著。
但是当线程太多,CPU处理内核比运行的线程少时,对于CPU的控制权就会出现竞争,这样就有可能会出现多个线程等待运行,这样我们就需要操作系统来调度线程的运行,通常采取多进程(多线程)分时操作系统,这是因为,CPU运行速度往往比其它设备处理速度更快,有些进程(线程)在进行输入输出(读写文件、网络I/O)的时候(输入/输出比较慢),如果这时候该进程(线程)还占用CPU资源就是浪费,这时候就要求该进程(线程)交出CPU控制权,系统将CPU控制权交给其它进程(线程),运行其它进程(线程);这就好比“锅”就是CPU,你正在炒菜,但是发现少了盐,必须去买盐(I/O操作)才能继续炒菜,怎么办,这时候就要把“锅”腾出来交给其它在排队等待“锅”的人炒菜。你买了盐回来端着你的半成品的菜(未运行完的进程(线程))排队等待下一次获得"锅“的控制权(CPU的控制权)。这样对于整个系统的多个进程则速度优势明显(因为很多人都减少了等待时间)。

多线程编程-Java

每个线程的作用是使计算机完成一定的任务,实际上就是一段在内存中的可被执行的指令序列。
Java使用 java.lang.Thread 类代表线程,所有的线程对象都必须是Thread类或其子类的实例。

创建多线程应用程序的第一种方式:

创建Thread类的子类。

/*
Java中通过继承Thread类的子来创建并启动多线程的步骤如下:
   1. 定义Thread类的子类,并重写Thread类的run()方法,该run()方法的方法体就是子类实
   例(线程)需要完成的任务,因此把run()方法称为线程执行体。
   2. 创建Thread子类的对象(实例),即创建了一个线程(这一步在父线程的类中完成)
   3. 调用线程对象的start()方法来启动该线程(这一步在父线程的类中完成)
 */
package chapter12.例子1;
public class SpeakCar extends Thread {
   public void run() {
      for(int i=1;i<=20;i++) {
         System.out.print("轿车"+i+"  ");
      }  
   } 
}
package chapter12.例子1;
public class SpeakElephant extends Thread {
   public void run() {
      for(int i=1;i<=20;i++) {
         System.out.print("大象"+i+"  ");
      }  
   } 
}
/*
Java中通过继承Thread类的子来创建并启动多线程的步骤如下:
   1. 定义Thread类的子类,并重写Thread类的run()方法,该run()方法的方法体就是子类实
   例(线程)需要完成的任务,因此把run()方法称为线程执行体。
   2. 创建Thread子类的对象(实例),即创建了一个线程(这一步在父线程的类中完成)
   3. 调用线程对象的start()方法来启动该线程(这一步在父线程的类中完成)
 */
package chapter12.例子1;
public class Example12_1 {
   public  static void main(String args[]) { //主线程
       SpeakElephant  speakElephant;
       SpeakCar  speakCar;  
       speakElephant = new SpeakElephant() ;//创建SpeakElephant线程
       speakCar = new SpeakCar();           //创建SpeakCar线程
       speakElephant.start();               //启动SpeakElephant线程
       speakCar.start();                    //启动SpeakCar线程
       for(int i=1;i<=15;i++) {
          System.out.print("主人"+i+"  ");
       }  
   }
}
创建多线程应用程序的第二种方式:

实现java.lang.Runnable 接口,这种方式我们只需要重写Runnable 接口的run()抽象方法即可。
具体实现步骤:

  1. 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的执行体。
package chapter12.例子2;
public class CarTarget implements Runnable {
   public void run() {
      for(int i=1;i<=20;i++) {
         System.out.print("轿车"+i+"  ");
      }  
   } 
}
package chapter12.例子2;
public class ElephantTarget implements Runnable {
   public void run() {
      for(int i=1;i<=20;i++) {
         System.out.print("大象"+i+"  ");
      }  
   } 
}
  1. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象(这一步在父线程的类中完成)。
  2. 调用线程对象的start()方法来启动线程(这一步在父线程的类中完成)。
package chapter12.例子2;
public class Example12_2 {
   public static void main(String args[]) { 
       Thread speakElephant;		//用Thread声明线程
       Thread speakCar;			//用Thread声明线程
       ElephantTarget elephant;		//speakElephant线程的目标对象
       CarTarget car;			//speakCar线程的目标对象
       elephant = new ElephantTarget();
       car = new CarTarget();
       speakElephant = new Thread(elephant) ;//创建ElephantTarget线程
       speakCar = new Thread(car);  //创建CarTarget线程
       speakElephant.start();       //启动elephant线程
       speakCar.start();            //启动car线程
       for(int i=1;i<=15;i++) {
          System.out.print("主人"+i+"  ");
       }  
   }
}

参考文献

[1] XiaoManon. 计算机操作系统原理.2015-05-XX 引用日期:2020-05-16
[2] 线程和进程的基本概念 百度文库.2014-03-28 引用日期:2020-05-16.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值