美文网首页
多线程与进程(面试03)

多线程与进程(面试03)

作者: 小慧sir | 来源:发表于2019-11-27 11:55 被阅读0次

    线程与线程池

    什么是线程,

    提到线程就要说一下进程,

    进程: 进程就是正在执行的程序,(任务管理器)

    线程: 是程序执行的一条路径,一个进程中可以包含多条进程,

    举个例子,你打开你的微信,这叫打开了一个进程,你在微信里跟微信好友视频聊天就是开启了一个线程,

    两者之间的关系****一个进程中至少有一个线程,当然也可以有多条线程

    一条线程一定会在进程里面

    一、说说概念

    1、进程(process)

    狭义定义:进程就是一段程序的执行过程。

    广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。

    它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。

    简单的来讲进程的概念主要有两点:第一,进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。

    文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。

    第二,进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时,它才能成为一个活动的实体,我们称其为进程。

    进程状态:进程有三个状态,就绪、运行和阻塞。就绪状态其实就是获取了出cpu外的所有资源,只要处理器分配资源就可以马上执行。

    就绪状态有排队序列什么的,排队原则不再赘述。运行态就是获得了处理器分配的资源,程序开始执行。

    阻塞态,当程序条件不够时候,需要等待条件满足时候才能执行,如等待i/o操作时候,此刻的状态就叫阻塞态。

    2、程序

    说起进程,就不得不说下程序。先看定义:程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。

    而进程则是在处理机上的一次执行过程,它是一个动态的概念。这个不难理解,其实进程是包含程序的,进程的执行离不开程序,进程中的文本区域就是代码区,也就是程序。

    3、线程

    通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。

    线程可以利用进程所拥有的资源,在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位,

    由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统多个程序间并发执行的程度。

    4、多线程

    在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”。

    多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的。

    最简单的比喻多线程就像火车的每一节车厢,而进程则是火车。车厢离开火车是无法跑动的,同理火车也不可能只有一节车厢。多线程的出现就是为了提高效率。

    二、说说区别

    1、进程与线程的区别:

    进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,

    而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,

    所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

    1) 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.

    2) 线程的划分尺度小于进程,使得多线程程序的并发性高。

    3) 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。

    4) 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。

    但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

    5) 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。

    但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。

    线程的5种状态:

    新生状态(New)****: 当一个线程的实例被创建(new)后,此时该线程处于新生(new)状态,处于新生状态的线程有自己的内存空间,但该线程并没有运行,此时线程还不是活着的(not alive)。****就绪状态(Runnable)****: 通过调用线程实例的start()方法来启动线程使线程进入就绪状态(runnable);处于就绪状态的线程已经具备了运行条件,在系统为其分配CPU之后才会执行;若线程未被分配到CPU的话不会执行,就绪状态并不是运行状态;此时线程是活着的(alive)。****运行状态(Running)****: 一旦获取CPU(被JVM选中),线程就进入运行(running)状态,线程的run()方法才开始被执行;在运行状态的线程会执行自己的run()方法中的操作,直到调用其他的方法而终止、或者等待某种资源而阻塞、或者完成任务而死亡;如果在给定的时间片内没有执行结束,就会被系统给换下来回到线程的等待状态;此时线程是活着的(alive);****阻塞状态(Blocked)****:通过调用join()、sleep()、wait()或者资源被占用使线程处于阻塞(blocked)状态;处于Blocking状态的线程仍然是活着的(alive)****死亡状态(Dead)****:当一个线程的run()方法运行完毕或被中断或被异常退出,该线程到达死亡(dead)状态。此时可能仍然存在一个该Thread的实例对象,但该Thread已经不可能在被作为一个可被独立执行的线程对待了,线程的独立的call stack已经被dissolved。一旦某一线程进入Dead状态,他就再也不能进入一个独立线程的生命周期了。对于一个处于Dead状态的线程调用start()方法,会出现一个运行期(runtime exception)的异常;处于Dead状态的线程不是活着的(not alive)。

    创建线程的三种方式

    第一种:

    1定义一个类继承Thread,并重写Run方法

    2将要执行的代码写到Run方法中,

    3创建这个类的实例,得到对象调用start方法,开始这个条线程

    [图片上传失败...(image-10fc6a-1574826880818)]

    第二种:

    1、定义一个类MyRunnable实现Runnable接口,并重写run方法。****2、将要执行的代码写在run方法中。****3、创建Thread对象, 传入MyRunnable的实例,并调用start()方法开启线程。

    [图片上传失败...(image-be5943-1574826880818)]

    第三种:

    1、自定义一个类 MyCallable 实现 Callable 接口,并重写call()方法****2、将要执行的代码写在call()方法中****3、创建线程池对象,调用submit()方法执行MyCallable任务,并返回Future对象****4、调用Future对象的get()方法获取call()方法执行完后的值

    [图片上传失败...(image-578d8b-1574826880818)]

    创建线程的三种方式对比

    一、继承 Thread 类与实现 Runnable 接口的区别

    我们都知道****java****支持单继承,多实现。实现 Runnable 接口还可以继承其他类,而使用继承 Thread 就不能继承其他类了。所以当你想创建一个线程又希望继承其他类的时候就该选择实现 Runnable 接口的方式。

    二、实现 Callable 接口与 Runnable 接口的区别

    Callable 执行的方法是 call() ,而 Runnable 执行的方法是 run()。****call() 方法有返回值还能抛出异常, run() 方法则没有没有返回值,也不能抛出异常。

    多线程

    一个进程中有N个线程,

    多线程的优点,为什么要实现多线程

    1、提高CPU的使用率****例如朋友圈发表图片,当你上传9张图片的时候,如果开启一个线程用同步的方式一张张上传图片,假设每次上传图片的线程只占用了CPU 1%的资源,剩下的99%资源就浪费了。但是如果你开启9个线程同时上传图片,CPU就可以使用9%的资源了。****2、提高程序的工作效率****还是拿朋友圈发表图片来说,假设开启一个线程上传一张图片的时间是1秒,那么同步的方式上传9张就需要9秒,但是你开启9个线程同时上传图片,那么就只需要1秒就完成了。

    缺点 会影响CPU的性能,因为CPU需要在线程之间来回切换,还会出现线程安全的问题(死锁)

    多线程中的并行与并发

    概念:

    并行:多个处理器或者多核处理器同时执行多个不同的任务。****一台手机上开了好几个App,这是并行****并发:一个处理器处理多个任务。

    比如 双十一,6.18,下单

    为什么多线程会出现线程安全的问题:

    原因是有多个线程在操作共享的数据。即一个线程在操作共享数据的过程中CPU切换到其他线程又对该数据进行操作,这就是所谓的多线程并发。

    解决办法:

    把操作数据的那段代码用****synchronized 进行同步, 这样就能保证在同一时刻只能有一个线程能够访问。

    线程池

    什么是线程池,为什么要有线程池,他有什么优点

    为了提高工作效率,提高CPU的使用率,我们有的时候会创建多个线程去完成任务,但是线程

    的创建和销毁是非常消耗CPU和内存的,因为他涉及到与操作系统的交互,这样性价比太低,

    导致创建与销毁线程的消耗比你实际要完成的业务还大,那么就出现了线程池,在线程池中

    的线程每一条线程结束后,并不会被销毁,而是回到线程池中成为空闲等待状态,等到下一个

    对象来使用,

    所以一句话总结线程池,他减少了创建和销毁线程带来的性能开销,还可以控制最大并发线程的数量,避免出现对内存的过多消耗

    线程池是存在于内存的堆中

    主要了解一下****java.uitl.concurrent.ThreadPoolExecutor****(思瑞得,扑哦,伊克塞Q他)****类,是线程池中最核心的一个类,

    CorePoolSize 线程池的核心线程数

    默认情况下,核心线程数会在线程中一直存活,即使它们处于闲置状态。

    maximumPoolSize 线程池所能容纳的最大线程数。

    当活动线程数达到这个数值后,后续的新任务将会被阻塞。

    keepAliveTime 非核心线程闲置时的超时时长,超过这个时长,非核心线程就会被回收,当ThreadPoolExector的allowCoreThreadTimeOut属性设置为True时,keepAliveTime同样会作用于核心线程。

    unit 用于指定keepAliveTime参数的时间单位,这是一个枚举,常用的有TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)以及TimeUnit.MINUTES(分钟)等。

    TimeUnit.NANOSECONDS 纳秒

    TimeUnit.MICROSECONDS 微秒

    TimeUnit.MILLISECONDS 毫秒

    TimeUnit.SECONDS 秒

    TimeUnit.MINUTES 分钟

    TimeUnit.HOURS 小时

    TimeUnit.DAYS 天

    workQueue 线程池中的任务队列,通过线程池execute方法提交的Runnable对象会存储在这个参数中。

    这个任务队列是BlockQueue类型,属于阻塞队列,就是当队列为空的时候,此时取出任务的操作会被阻塞,等待任务加入队列中不为空的时候,才能进行取出操作,而在满队列的时候,添加操作同样被阻塞。

    核心方法:

    execute() 执行

    submit() 提交

    shutdown()

    shutdownNow() 关闭

    线程和进程区别、如何进行进程间通信

    首先我阐述以下我所理解的进程和线程。

    进程(Processes)是系统进行资源分配的独立单元。

    线程是 cpu 调度的基本单元。

    我所理解的进程和线程的区别是这样的。

    a) 进程中可以创建多个线程,进程是系统资源分配的单位,线程是 cpu 调度的单位。

    b) 进程之间不能共享资源,而线程共享所在进程的地址空间和其它资源。同时线程还有自

    己的栈和栈指针,程序计数器等寄存器。

    c) 进程有自己独立的地址空间,而线程没有,线程必须依赖于线程而存在。

    d) 进程切换的开销较大,线程相对较小。

    比如当我们的在 Android 系统中启动一个应用程序,这个时候系统就给这个应用启动一个进

    程,同时再创建一个主线程。默认情况下同一个应用程序的所有组件都在同一进程的主线程

    中运行。如果你需要设置某个组件需要再特定的进程中执行,可以再 manifest 清单文件中

    设置。像 activity,service,receiver 和 provider 都有 android:process 属性,就是用来指定该组件

    运行在哪个进程中。系统不会为每个组件创建一个单独的线程,运行在同一进程中的所有组

    件,都在该进程的 UI 线程也就是主线程中实例化,系统从 UI 线程发出对每个组件的调用。

    Android 的 UI toolkit 不是线程安全的,因此不能从工作线程操作你的 UI,你必须从 UI 线程

    对用户界面操作,UI 线程是单线程模型,所以我们不要阻塞 UI 线程,不要在 UI 线程中执行

    耗时操作,不要在非 UI 线程中操作 UI 控件。

    我所了解的线程和线程之间的通信有这样几种方法:

    a) 在 runOnUiThread 方法中调用 UI 控件的刷新。

    b) 用 View.post 方法调用 UI 控件的刷新。

    c) 使用 View.postDelayed 方法调用 UI 控件的刷新。

    d) AsyncTask 实现异步处理,在 doInBackground 方法中执行耗时操作,在 onPostExecute

    中刷新 UI。

    e) Handler 实现线程通信。

    f) 第三方插件 EventBus 可以帮我们实现线程通信。

    线程和线程通信我们需要考虑线程安全。一般我们会采用 synchronization,wait,sleep。

    比 如 在 多 线 程 使 用 数 据 存 储 的 时 候 , 我 们 一 般 可 以 选 择 线 程 安 全 的 ,

    LinkedList,ConcurrentHashMap. 进程和进程间的通信方式可以分为:

    a) Bundle/Intent 传递数据。

    b) 文件共享。

    c) Messenger。

    d) AIDL 接口编程。

    e) ContentProvider。

    f) Socket。

    相关文章

      网友评论

          本文标题:多线程与进程(面试03)

          本文链接:https://www.haomeiwen.com/subject/qphpwctx.html