【面试】线程进程区别

3,092 阅读5分钟

个人理解

面试时候多次被问到,自己回答感觉很一般,回答了以下几点:

  • 线程是独立调度的基本单位,进程是资源分配的基本单位
  • 线程基本不拥有资源,只拥有一点在运行中必不可少的资源,进程拥有 CPU 资源
  • 一个进程可以包括多个线程,这些线程共享进程资源

资料总结

  1. 拥有资源

进程是资源分配的基本单位,但是线程不拥有资源,线程可以访问同一进程的资源

  1. 调度

线程是独立调度的基本单位,同一进程中,线程的切换不会引起进程切换,从一个进程内的线程切换到另一个进程内的线程会引起进程切换

  1. 系统开销

创建和撤销进程时,系统都要为之分配或回收资源,所付出的开销远大于创建或撤销线程时的开销。同样的在进程切换时,也会涉及当前执行进程 CPU 环境的保存以及新调度进程 CPU 环境的设置,而线程的切换只需保存和设置少量寄存器的内容,开销很小

  1. 通信方面

进程间通信需要进程同步和互斥手段的辅助,以保证数据的一致性。而线程间可以通过直接读/写同意进程中的数据段(如全局变量)来进行通信。

补充

  • 执行过程中区别,每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口,但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制
  • 逻辑角度来看,多线程的意义在于在一个应用程序中,有多个执行部分可以同时执行,但是操作系统没有将这多个线程看做多个独立的应用,来实现进程资源的调度和管理以及资源分配
  • 一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行

线程好处

  1. 在进程内创建、终止线程要比创建、终止进程快得多
  2. 同一进程内的线程间切换比进程间切换要快,尤其是用户级线程间的切换。

后续问题

  1. 线程资源抢占可能出现什么问题?

这是面试腾讯的面试题,同一进程中有多个线程共享资源,肯定会出现资源的不够用,简单来说就是可能出现线程死锁,这里我当时有点不肯定,以为只能出现进程死锁,实际上线程也会出现死锁,面试官问我的时候我就直接按照进程死锁的来说了

  1. 如何避免这种问题出现?

这个问题就是要考察死锁发生的条件,如何避免死锁。我先是简单介绍了死锁发生的四个条件,当时也是想既然是死锁,不管线程还是进程发生死锁的条件应该是一致的,就直接说了,后面查资料证明事实就是这样的,唯一不同之处就是死锁的基本单元不同,一个是进程之间,一个是线程之间。

死锁的四个必要条件:

  • 互斥条件:一个资源只能被一个进程(线程)使用
  • 请求与保持条件:一个进程(线程)因请求资源而阻塞时,对已获得的资源保持不放
  • 不剥夺条件:进程(线程)已获得的资源,在未完成之前,不能强行剥夺
  • 循环等待条件:若干进程(线程)之间形成一种头尾相接的循环等待资源关系

死锁案例:

package opersystem;

/**
 * @author YaboSun
 */
public class DeadLock {
    // 通过一个简单的例子测试线程死锁

    public static void main(String[] args) {
        // 线程a
        Thread a = new Thread(new Runnable() {
            @Override
            public void run() {
                DeadLock.method1();
            }
        });

        // 线程b
        Thread b = new Thread(new Runnable() {
            @Override
            public void run() {
                DeadLock.method2();
            }
        });

        a.start();
        b.start();
    }

    private static void method1() {
        synchronized (String.class) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程 a 尝试获取integer.class");
            synchronized (Integer.class){

            }
        }
    }

    private static void method2() {
        synchronized (Integer.class) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程 b 尝试获取 string.class");
            synchronized (String.class){

            }
        }
    }
}

那么避免死锁的出现就是破坏其中的必要条件:

  • 破坏互斥条件:例如脱机打印机技术允许若干个进程同时输出,唯一真正请求物理打印机的进程是打印机守护进程
  • 破坏占有和等待条件:一种实现方式是规定所有进程在开始执行前请求所需要的全部资源
  • 破坏不可抢占条件
  • 破坏环路等待条件:给资源统一编号,进程只能按编号的顺序来请求资源
  1. 银行家算法

参考: 一句话+一张图说清楚银行家算法

当一个进程申请使用资源的时候,银行家算法通过试探分配给该进程资源,然后通过安全性算法判断分配后的系统是否处于安全状态,若不安全则试探分配作废,让该进程继续等待

如何判断系统处于安全状态?

如果没有死锁发生, 并且即使所有进程突然请求对资源的最大需求,也仍然存在某种调度次序能够使得每一个进程运行完毕,则称该状态是安全的。