初识多线程(代码片段)

caiyec caiyec     2022-12-18     176

关键词:


前言

我们先简单介绍操作系统来进一步了解多线程

一、操作系统

1.冯诺依曼体系结构

冯诺依曼体系结构是由CPU(运算器,控制器) 存储器 输入设备 和输出设备组成
CPU(运算器,控制器): 算术运算和逻辑判断
存储器:主要功能是存储设备 分为内存和外存 内存一般空间比较小,但是访问速度快,内存空间大,但是访问速度慢
输入输出设备输入设备 键盘鼠标 输出设备 显示器,打印机等

简单描述CPU是如何工作的:

CPU的核心功能就是执行一些指令,指令就是一些二进制的数据,用来表示一些特定的含义,例如我们写的java代码先被编译器编译为字节码之后被JVM翻译为成一条条的CPU指令,最终在CPU上执行,而 C++代码直接把源代码就编译成二进制的机器指令了,Java/C++ 代码在编译好之后就得到了一些二进制的机器指令,这些机器指令是保存在硬盘上的,当我们进行运行的时候,这时候操作系统先把这些指令从磁盘加载到内存中,再由CPU从内存中读取指令进行执行

2.操作系统

操作系统是一个软件,一个管理的软件,管理硬件设备(CPU,内存,磁盘,鼠标等)和软件资源(进程,线程,内核中特殊的资源),操作系统是计算机最重要的软件。

操作系统在其中运筹帷握,才能让这些硬件和软件能够很好的搭配工作
当前主流的操作系统 PC端 Windows Linux Mac 移动端 IOS Android 鸿蒙

二、进程

进程(Process)操作系统中的核心概念,也和平时运行程序密切相关,进程是按照一定的流程(代码中所编写的CPU指令)来具体执行一些计算工作,通俗来说:当运行某个程序的时候,操作系统就创建了一个对应的进程

进程的定义:
1)进程是程序的动态执行
2)进程是一个程序及其数据在处理机上顺序执行时所发生的活动
3)进程是具有独立功能的程序在其数据集合上运行的过程,他是系统调度和资源分配的一个独立单位
我们可以通过任务管理器来查看当前电脑上由多少进程,

为什么引入进程

因为现代操作系统,都需要支持“多任务”“并发” 执行,多任务并发执行能更充分利用多核资源,还有更充分利用CPU资源
1)系统支持多任务
2)更充分利用CPU资源

2.1操作系统管理进程

描述:使用task struct 结构
进程的组成方式:使用双向链表把很多的task struct 变量给串起来(例如,当我们看到任务管理器可以认为,是操作系统内核遍历了双向链表,然后把每个节点的信息获取出来,并展示)
当我们创建一个进程时,实际上就是创建一个task struct 放到双向链表中,当进程结束了就是从双向链表中删除该节点。

2.2进程的组成

一个进程的task struct 里面大概都有啥样的信息
1)pid 进程ID 在任务管理器中也能看到

2)进程的内存指针:描述了进程持有的内存资源是哪些范围(进程依赖的代码和数据在哪里)
下面的信息进程的组成为了辅助进行进程的“调度”
调度 如果在系统某一刻可能有100个进程,我们应该如何执行,
并行从微观角度,每个进程和进程之间,是同时进行的,比如 8个CPU核心,可以同时进行8个进程
并发从微观角度讲,进程是串行执行的,从宏观角度讲,进程是“同时”进行的,
例如一个CPU负责执行3个进程,CPU先执行进程一,之后进行进程二,最后执行进程三,执行依次来回切换,由于CPU切换速度极快,从宏观角度来看好像是三个进程同时执行,但是微观角度依然是串行执行的。
3)进程的优先级多个进程存在,哪个进程要先执行
4)进程的上下文记录一次活动的相关状态(某个进程在CPU上执行一段时间之后,就需要调度下去了,这时进程就需要把上次执行的状态给记录下来)记录的内容:当时CPU的各个寄存器的值,记录到内存中 记录的目的,下次运行的时候,就能够恢复上次的信息,继续向下执行
5)进程的记账信息:统计工具(每个进程在CPU上执行多久了,调度多少次了)
6)进程的状态当前进程的状态,影响调度

进程的调度
所谓的进程调度,其实就是一个抢占式执行的过程,这里的调度工作完全是由操作系统内核来实现,每个进程自身,不知道自己啥时候能获取到CPU的执行,也不知道自己啥时候失去CPU,更不知道下一次获取到CPU是啥时候,一切都是听操作系统的,一切对于应用程序来说都是透明的。

2.3时间片

现代操作系统都支持多任务,就是操作系统可以同时运行多个任务
操作系统的任务调度是采用时间片轮转的抢占式调度方式,也就是说任务执行一小段时间后强制暂停去执行下一个任务,每个任务轮流执行。
由于CPU执行的效率非常高,时间片非常短,在各个任务之间快速切换,给人一种多个任务同时执行的感觉,这就是我们所说的并发

2.4并行和并发

并发:多个进程在一个CPU下采用时间片轮转的方式,在一段时间内,让多个进程都得以推进,称为并发
并行:多个进程在多个CPU下分别,同时进行运行,这称为并行。

2.5内核态和用户态

内核态,操作系统内核来执行任务
用户态,应用程序来执行任务
完整的操作系统=操作系统内核+配套的应用程序
如果某个操作系统进入内核操作,就会变得不可控

2.6进程状态

三种基本状态

就绪已经具备运行条件,但是由于没有空闲CPU,而暂时不能运行
运行占有CPU,正在CPU上运行
堵塞等待某一事件的发生,暂时不能运行

另外两种状态

创建进程正在被创建,操作系统为进程分配资源,初始化PCB
结束进程正在从系统中撤销,操作系统会回收进程拥有的资源,撤销PCB

三、多线程

3.1线程是什么?

进程是为了实现并发编程的效果,但是为了追求效率就引入了线程(创建一个进程/销毁一个进程,开销比较大)因此就希望能够更高效,更轻量的完成并发编程。通过线程来完成,线程也被称为“轻量级进程”
每个线程就对应到一个“独立的执行流”在这个执行流里就能够完成一系列的指令,多个线程,就有了多个执行流,就可以并发的完成多个系列的指令了。

进程和线程的关系:
一个进程包含了多个线程
一个进程其实从系统这里申请了很多的系统资源~~,进程同一对这些资源进行管理,这个进程内的多个线程,共享了这些资源
进程具有独立性:一个进程掉了,不会影响到其他进程
线程则不是这样,如果一个线程坏了,其他的线程可能受到影响。

举个例子来说明线程和进程
如果我们要吃100个西瓜

如果是引入多进程的方式就是

但是当我们创建新的进程时利用系统大量的开销,也不太高效

这样我们就引入多线程,把单线程变为多线程

在这种情况下,同样可以达到并发编程,调度器也是可能会把两个线程安排到不同的CPU上,开销比多进程更低

但是线程越多越好吗? 我们引入100个人吃西瓜,每个人只吃一个,岂不是更快?
所以增加线程可能会提高效率,但是一旦线程数量太多,就会拥挤不堪,这个时候,多个线程为了竞争CPU资源就会占更多的开销(线程多了,调度的开销也变大了)

3.2进程和线程的区别

1)进程时包含线程的~一个进程可以包含一个线程,也可以包含多个线程。
2)进程是资源分配的基本单位,线程是系统调度执行的基本单位
3)进程和进程之间是相互独立的,进程A挂了,不会影响进程B,同一个进程的若干线程之间,共享这一资源(内存资源),如果某个线程出现异常,可能会导致整个进程终止,因此其他线程也就无法工作了。

3.3Java实现多线程

继承Thread 重写run 方法

public class Demo3 
    public static void main(String[] args) 
        MyThread1 myThread1=new MyThread1();
        myThread1.start();
        while (true)
            System.out.println("这是主线程!!!");
            try 
                Thread.sleep(1000);
             catch (InterruptedException e) 
                e.printStackTrace();
            
        
    

class MyThread1 extends Thread
    @Override
    public void run() 
    //线程要执行的任意方法
        while(true)
            System.out.println("这是新线程!!!");
            try 
                Thread.sleep(1000);
             catch (InterruptedException e) 
                e.printStackTrace();
            
        
    

结果:

多线程创建:
1)创建一个类继承Thread
2)重写Thread 的run方法 ,在新的run里面编写线程要执行的执行流,
3)创建子类实例
4)调用子类的start 方法.

3.4通过代码演示多线程提高效率

1.先不使用多线程,直接串行执行

public class Demo4 
    //演绎多线程来提高程序的运行效率
    //假设把两个long类型的整数,给自增100亿次
    //1.先不使用多线程,直接串行执行
    private static long count=100_0000_0000L;
    public static void serial()
        long start=System.currentTimeMillis();//毫秒级时间戳
        long a=0;
        for(int i=0;i<count;i++)
            a++;
        
        long b=0;
        for(int i=0;i<count;i++)
            b++;
        
        long end=System.currentTimeMillis();
        System.out.println("总执行时间"+(end-start)+"ms");
    
    public static void main(String[] args) 
        serial();
    


执行时间是10S左右:


2.使用多线程

public class Demo4 
    //演绎多线程来提高程序的运行效率
    //假设把两个long类型的整数,给自增100亿次
    //1.先不使用多线程,直接串行执行
    private static long count=100_0000_0000L;
    public static void serial()
        long start=System.currentTimeMillis();//毫秒级时间戳
        long a=0;
        for(long i=0;i<count;i++)
            a++;
        
        long b=0;
        for(long i=0;i<count;i++)
            b++;
        
        long end=System.currentTimeMillis();
        System.out.println("总执行时间"+(end-start)+"ms");
    
    public static void concurrency()
        //2.并发执行
        long beg=System.currentTimeMillis();
        //创建两个线程
        Thread t1=new Thread()
            @Override
            public void run() 
                long a=0;
                for(long i=0;i<count;i++)
                    a++;
                
            
        ;
        t1.start();

        Thread t2=new Thread()
            @Override
            public void run() 
                long b=0;
                for(long i=0;i<count;i++)
                    b++;
                
            
        ;
        t2.start();

        try 
            t1.join();
            t2.join();
         catch (InterruptedException e) 
            e.printStackTrace();
        
        long end=System.currentTimeMillis();
        System.out.println("执行时间"+(end-beg)+"ms");
    
    public static void main(String[] args) 
        //serial();
        concurrency();
    



时间缩短了4S,为什么创建两个线程时间不是缩短5S,因为创建出来的两个线程不能保证完全是并行执行的~

初识多线程(代码片段)

初始Java多线程1)线程的创建(五种方式)1.创建一个继承Thread的类2.创建实现Runnable接口的类3.创建Thread实例,使用匿名内部类4.创建Thread实例,在Thread类的构造方法中传入一个Runnable实例创造一个Runnable的匿名... 查看详情

java多线程-初识并发问题(代码片段)

场景1:买票多线程抢一些票packagecom.wyh.thread;/***@program:Thread*@description:火车票抢票*@author:魏一鹤*@createDate:2021-12-2500:18**///发现问题多个线程操作同一个资源的情况下线程不安全了数据混乱publicclassTestThread3implemen 查看详情

多线程(代码片段)

多线程初识threading包:threading.Thread创建多线程的两种方式多线程的组件:threading包中的模块:GIL—全局解释器锁  一、多线程初识:#线程特点#1.轻型实体,占用非常小的资源#2.独立调度和分派的基本单位:即cup实际上调... 查看详情

ios底层探索之多线程—初识gcd(代码片段)

回顾在前两篇博客,已经介绍了进程和线程,还介绍了自旋锁和互斥锁等相关内容,在iOS开发中,使用最多的还是GCD,那么从本篇开始讲陆续介绍GCD。iOS底层探索之多线程(一)—进程和线程iOS底层探索之多线程... 查看详情

java核心知识---初识线程(代码片段)

最早的并发编程是多进程并发编程。多个进程不能共享资源。目录线程(Thread)线程的创建方式线程休眠使用线程打印"AABBCCDD"线程优先级线程分组(ThreadGroup)线程分类Thread几个常见属性代码线程中断两种... 查看详情

初识多线程(代码片段)

初始Java多线程1)线程的创建(五种方式)1.创建一个继承Thread的类2.创建实现Runnable接口的类3.创建Thread实例,使用匿名内部类4.创建Thread实例,在Thread类的构造方法中传入一个Runnable实例创造一个Runnable的匿名... 查看详情

两步帮你搞定多线程之第一步(代码片段)

目录导论:初识多线程一:动手来创建多线程1.1创建一个主线程1.2多线程抢占式执行二:创建线程的几个常用方法2.2  继承Thread类2.2 实现Runnable接口2.3匿名类创建 三:Thread的几个常见属性记:导论:初... 查看详情

linux系统编程多线程(代码片段)

多线程初识线程线程的概念线程的优缺点优点缺点了解pid(轻量级线程号)与tgid(线程组id)进程与线程概念解释同一所属组下多线程之间共享与独立的空间进程与线程的对比多进程与多线程对比线程控制线程的创建线程终止线程等... 查看详情

linux系统编程多线程(代码片段)

多线程初识线程线程的概念线程的优缺点优点缺点了解pid(轻量级线程号)与tgid(线程组id)进程与线程概念解释同一所属组下多线程之间共享与独立的空间进程与线程的对比多进程与多线程对比线程控制线程的创建线程终止线程等... 查看详情

初识多线程之基础知识与常用方法

1.线程与进程的描述: 1.1进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1~n个线程。(进程是资源分配的最小单位)  1.2线程:同一类线程共享代码和数据空间,每... 查看详情

进程线程区别,和线程初识

进程是计算机中最小的资源分配单位在利用多个CPU执行的过程中,对多个程序的资源进行管理和隔离进程的弊端开启和关闭以及切换都会带来很大的时间开销过多的进程还会造成操作系统调度的压力线程线程是CPU调度的最小单位... 查看详情

ios底层探索之多线程(十五)—@synchronized源码分析(代码片段)

...探索之多线程(二)—线程和锁iOS底层探索之多线程(三)—初识GCDiOS底层探索之多线程(四)—GCD的队列iOS底层探索之多线程(五)—GCD不同队列源码分析iOS底层探索之多线程(六)—GCD源码分析(sync同步函数、async异步函数) 查看详情

多线程初识

线程入门理解线程与进程的关系是学习线程的基础。进程是指在系统中正在运行的一个应用程序实例。线程是比进程更小的执行单位。所谓多线程是指一个进程在执行过程中可以产生多个同时存在,同时运行的线程。多线程机制... 查看详情

ios底层探索之多线程—gcd的队列(代码片段)

...探索之多线程(二)—线程和锁iOS底层探索之多线程(三)—初识GCD1.不同队列举例主队列添加同步任务看看下面这个例子🌰//主队列同步//不会开线程 NSLog(@"star 查看详情

java并发编程初识-线程池(代码片段)

0.前言Java中线程池是运用场景最多的并发框架,合理使用线程池可以带来诸多好处:降低资源消耗:重复利用已创建的线程,降低了线程创建和销毁时造成的资源消耗提高响应速度:任务到达时,不需要等... 查看详情

java多线程系列1-初识多线程多线程4种实现方式

1、继承Thread2、实现Runnable接口3、实现Callable<V>接口4、线程池importjava.util.concurrent.*;publicclassTest{publicstaticvoidmain(String[]args)throwsExecutionException,InterruptedException{/***继承Thread重写run方法,实际上 查看详情

java多线程系列1-初识多线程多线程4种实现方式

1、继承Thread2、实现Runnable接口3、实现Callable<V>接口4、线程池importjava.util.concurrent.*;publicclassTest{publicstaticvoidmain(String[]args)throwsExecutionException,InterruptedException{/***继承Thread重写run方法,实际上 查看详情

ios底层探索之多线程(十四)—关于@synchronized锁你了解多少?(代码片段)

...探索之多线程(二)—线程和锁iOS底层探索之多线程(三)—初识GCDiOS底层探索之多线程(四)—GCD的队列iOS底层探索之多线程(五)—GCD不同队列源码分析iOS底层探索之多线程(六)—GCD源码分析(sync同步函数、async异步函数) 查看详情