什么是线程和进程? 线程与进程的关系,区别及优缺点?

线程、进程的基本概念

  1. 线程:线程是一个比进程更小的执行单位,一个进程在执行的过程中可以有多个线程。多个线程共享同一个进程的资源。并且各个线程之间切换所消耗的资源较进程间的切换少。
  2. 进程:进程是资源分配的基本单位,是运行中的程序,进程拥有一系列的资源:比如CPU时间,内存空间、IO设备等。

关系:一个进程中可以有多个线程

区别:

  1. 进程拥有独立的资源,线程不拥有独立的资源,线程可以访问隶属于进程的资源
  2. 进程创建和撤销的开销较线程大,在创建进程时,系统要为进程分配或回收资源,如内存空间、I/O设备等。同理在进程切换时,开销较线程大,涉及到保存当前CPU环境以及加载新调度进程的CPU环境,而线程切换只需要保存和加载少量寄存器内容。
  3. 进程通信需要借助IPC,线程通信可以通过访问统一进程的资源。

说说并发与并行的区别?

并发:指同一时刻只有一条指令执行,但多个进程被快速轮换执行,达到在宏观上多个进程共同执行的效果。并发在微观上并不是同时执行,只是把时间分成多段,使进程得到轮流得到时间片快速交替执行。

并行:指在同一时刻,有多条指令在处理器上同时执行,无论在宏观还是在微观上,都是在一起执行的。

并行一般在多处理器系统中存在 ,并发在单处理和多处理器系统中都存在。

为什么要使用多线程呢?

  1. 搭配和适应多处理器核心,如果进行一个复杂的运算,只有一个线程的话,CPU只有一个核心被用到,而创建多个线程可以让多个CPU核心被用到,这样提高了CPU的利用率。
  2. 针对IO事件较多的情况,可以使用多线程来分别处理IO操作和计算操作,这样CPU不必专门空闲来等待IO事件处理完毕。
  3. 提高响应时间,提高用户体验,可以将一些数据一致性不强的操作使用其他线程处理,达到异步处理的效果,这样使响应用户请求可以先处理完成,缩短了响应的时间。

使用多线程可能带来什么问题?(内存泄漏、死锁、线程不安全等等)

出现内存泄漏、死锁、线程不安全的情况。

创建线程有哪几种方式?

  1. 继承Thread类
  2. 实现Runnable接口
  3. 实现Callable接口,并用FutureTask封装

说说线程的生命周期和状态?

  1. 线程被创建之后处于NEW被创建的状态
  2. 调用start()方法后进入ready就绪状态
  3. 线程获得了时间片后处于Running运行状态,时间片用完或者使用yield()函数回到ready就绪状态
  4. 当线程执行wait()方法后进入WATING等待状态,而等待状态需要其他线程调用notify相关的通知函数才能返回到可运行的状态
  5. 而线程使用sleep()或者把毫秒数作为参数的wait()方法后会进入TIME_WATING超时等待状态,到达一定时间后线程会返回到可运行的状态
  6. 当线程调用同步方法时,若没有获取到锁,会进入BLOCK阻塞状态。
  7. 当线程执行任务完成之后进入到TERMINATED终止状态

什么是上下文切换?

CPU核心为了让多个进程并发进行,采取的策略是为每个进程分配时间片并轮转快速切换,当一个进程时间片用户按就会处于就绪状态,让出计算资源给其他进程,这个过程就属于一次上下文切换。

上下文切换主要做了这些事情,在当前任务执行完CPU时间片切换到另外一个任务之前会保存自己的状态,以便下次得到时间片时加载运行环境继续执行任务。任务从保存到再加载的过程就是一次上下文切换。

什么是线程死锁?

多个线程同时被阻塞,他们之中一直在等待某个互斥资源的释放,因为线程在无限期等待,所以此程序不能正常完成并终止运行。

一般死锁需具备以下条件:

  1. 互斥条件:该资源为互斥资源,只能被一个线程占用
  2. 保持与请求条件:该线程对已拥有资源不放,并因请求其他资源而阻塞
  3. 不可抢占剥夺:线程已获得的资源在使用完成之前不能被其他线程抢占,只能有该线程显式的释放。
  4. 环路等待条件:若干个进程形成了一个互相请求资源并等待的环路。

如何避免死锁?

只需要破坏死锁必要条件中的一个就可以

  1. 破坏互斥条件:例如脱机打印机技术,它允许多个进程同时输出,但唯一获得物理打印机资源的是打印机守护进程。
  2. 破坏保持和请求条件:一次性申请并拥有所需要的所有资源
  3. 破坏不可剥夺条件:若线程申请不到其他资源,主动释放所占有的资源
  4. 破坏环路等待条件:给资源统一编号,进程只能按照编号顺序来请求资源

说说 sleep() 方法和 wait() 方法区别和共同点?

两者都可以暂停线程的执行,区别在于

  1. sleep方法不会释放已持有的锁,而wait方法会释放已持有的锁
  2. wait()空参方法 被调用后不会自动苏醒,需要其他的线程调用同一个对象的notify()或notifyAll()方法。sleep()方法或者使用带时间参数的wait(long timeout)方法在超时后线程自动会苏醒。

为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?

调用了start方法线程会进入就绪状态,在线程获得时间片后会进入运行状态并自动调用run方法。而直接运行run方法会把run方法当做一个当前线程环境下的普通方法去运行,而不是我们期望的多线程运行方式。

所以调用start方法可以让线程进入就绪状态并开始自动调用run方法,而直接调用run方法只是一个普通方法调用,还是在主线程里执行。