Welcome to xpt’s blog! 2021年准备秋招期间整理的一些笔记,分享给大家!

文档分享的初衷是给师弟师妹们作为参考,主要是适合想去大厂+测试开发岗的朋友们。
建议大家自己整理文档,把我的文档作为参考,有些东西自己整理,自己去写出来,才是最适合你自己的!
文章还未精细整理,如存在错误之处,可以邮件or微信反馈给我呀,感激不尽!

想进大厂,要抓住提前批免笔试的机会!(例如京东、字节、百度等报名时间一般为七月,面试时间为报名后的一周内,面试一般为3轮,面试相关经验后续我会单独再写blog分享^_^,也欢迎大家来跟我talk,一定知无不言。)

本人情况:普通211、研究生、有京东、百度、以及字节提前批测开岗offer。7月初开始准备,准备太迟,一边准备一边投简历+面试。

  • 投递简历时间:京东(7.14),字节(7.30),百度(7.30)
  • 三轮面试时间:京东(7.21-7.22-7.26),字节(8.4-8.6-8.9),百度(8.9-8.12-8.16)
  • 意向书时间:京东(8.12),字节(8.16),百度(9.9)

京东提前批开始很早,我投的时候已经是第二批。经过京东几轮面试,熟悉了面试流程,大概掌握了测开岗会问些什么问题。
字节和百度提前批我是在ddl前一天投递,其实已经算很迟了,hc不多了。
投递要趁早,很多岗位有固定hc。
多拿offer,才有谈薪资的底气。

我面试的岗位有以下:
1、测试开发岗(京东、百度、以及字节提前批)
2、银行java开发岗(所以我会整理一点java,银行问的都很简单,所以我这里对java的整理比较少)

整理的内容均来源于历年网络上分享的面经(主要来源于牛客),以及我面试时被问过的问题,list如下:
(1)——计算机网络
(2)——操作系统
(3)——数据库
(4)——数据结构
(5)——python
(6)——java
(7)——linux
(8)——常考编程题
(9)——测试开发相关知识

面试QA整理(2)——操作系统

进程、线程的区别

区别:

1.一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程依赖于进程而存在。

2.进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。(资源分配给进程,同一进程的所有线程共享该进程的所有资源。同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量。)

3.进程是资源分配的最小单位,线程是CPU调度的最小单位;

4.系统开销: 由于在创建或撤消进程时,系统都要为之分配或回收资源,如内存空间、I/o设备等。因此,操作系统所付出的开销将显著地大于在创建或撤消线程时的开销。类似地,在进行进程切换时,涉及到整个当前进程CPU环境的保存以及新被调度运行的进程的CPU环境的设置。

而线程切换只须保存和设置少量寄存器的内容,并不涉及存储器管理方面的操作。可见,进程切换的开销也远大于线程切换的开销。

5.通信:由于同一进程中的多个线程具有相同的地址空间,致使它们之间的同步和通信的实现,也变得比较容易。进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。在有的系统中,线程的切换、同步和通信都无须操作系统内核的干预

6.进程编程调试简单可靠性高,但是创建销毁开销大;线程正相反,开销小,切换速度快,但是编程调试相对复杂。

7.进程间相互独立,不会相互影响;线程一个线程挂掉可能会导致整个进程挂掉

​ 问题:浏览器是多进程还是多线程?——浏览器是多进程,一个页面挂掉不会整个挂掉

8.进程适应于多核、多机分布;线程适用于多核


1. 何为进程?

进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。

在 Java 中,当我们启动 main 函数时其实就是启动了一个 JVM 的进程,而 main 函数所在的线程就是这个进程中的一个线程,也称主线程。

在 windows 中通过查看任务管理器的方式,我们就可以清楚看到 window 当前运行的进程(.exe 文件的运行)。

2. 何为线程?

线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程

与进程不同的是同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。

为什么要有线程

在多任务环境中,不可能说让所有任务排队,前面的处理完了才处理后面的任务。如果要让用户感觉到任务都是一起执行的,那么就必须在进程之间频繁切换。

问题在于如果要进行进程的切换需要做很多的工作,必须要保存好当前CPU的上下文,好让CPU下次被分配到当前进程时可以继续往前执行,然后还需要设置新的进程的CPU上下文,在这个过程中会花费很多时间。由于这个原因就限制了系统中进程数目不能多。
  
为了解决这个限制,后来提出将进程的两个属性分开,由操作系统分开处理,即对于作为调度和分派的基本单位,但不同时作为拥有资源的单位;而对于拥有资源的基本单位,又不对其进行频繁的切换。正是在这种思想的指导下,形成了线程的概念。

多线程会出现那些问题

各个线程执行的顺序不同,会导致执行的结果不一致。

并发访问的安全性问题

当多个线程无序的访问同一个共享资源时,可能会导致共享资源的最后结果不是我们期望的结果。

在并发编程中,我们通常会遇到以下三个问题:原子性问题,可见性问题,有序性问题
  1.原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
例如,如果i=32;不具备原子性的话,当给它的低16赋值完时,突然中断,其他线程在去读这个值时,就是错误的值.

  2.可见性:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
这就是可见性问题,线程1对变量i修改了之后,线程2没有立即看到线程1修改的值。

  3.有序性:即程序执行的顺序按照代码的先后顺序执行,首先什么是指令重排序,一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,
它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。处理器在进行重排序时也是会考虑指令之间的数据依赖性
  指令重排序不会影响单个线程的执行,但是会影响到线程并发执行的正确性。

线程通信的方法、线程的五种状态

python线程通信的方法

java线程通信的方法:

①同步:多个线程通过synchronized关键字这种方式来实现线程间的通信。

②while轮询的方式

③wait/notify机制

④管道通信就是使用java.io.PipedInputStream 和 java.io.PipedOutputStream进行通信

线程的五种状态:

  1. 新建(NEW):新创建了一个线程对象。

  2. 可运行(RUNNABLE):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。

  3. 运行(RUNNING):可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。

  4. 阻塞(BLOCKED):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分三种:

    (一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。

    (二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。

    (三). 其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。

  5. 死亡(DEAD):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。

进程的通信方式

  1. 管道:单向的传输,这样的管道我们叫做”匿名管道”。速度慢,容量有限,只有父子进程能通讯。

    netstat -nlp | grep XXX

    管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。

    FIFO 命名式管道任何进程间都能通讯,但速度慢;半双工的通信方式,但是它允许无亲缘关系进程间的通信。mkfifo test

    简单,但效率低下。因为假设现在有AB两个进程,A进程将数据写入管道,B进程需要等待A进程将信息写完以后才能读出来,所以这种方案不适合频繁的通信。

  2. 消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题

    用消息队列的通信模式来解决这个问题,例如 a 进程要给 b 进程发送消息,只需要把消息放在对应的消息队列里就行了,b 进程需要的时候再去对应的消息队列里取出来

    如果 a 进程发送的数据占的内存比较大,并且两个进程之间的通信特别频繁的话,消息队列模型就不大适合了。因为 a 发送的数据很大的话,意味发送消息(拷贝)这个过程需要花很多时间来读内存。

  3. 共享内存:我们知道每个进程都有自己的虚拟内存空间,不同的进程映射到不同的物理内存空间。那么我们可以让两个进程各自拿出一块虚拟地址空间来,不同进程通过这块虚拟地址空间映射到相同的物理地址空间。这就完成了内存共享机制了。

    能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要, 线程间本来就已经共享了同一进程内的一块内存

  4. 信号量:线程安全问题,如何解决,我们得有个约束或者说一种保护机制。使得同一份共享的资源只能一个进程使用,这里就出现了信号量机制。信号量主要实现进程之间的同步和互斥,而不是存储通信内容。不能传递复杂消息,只能用来同步。

    信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

  5. 信号:在操作系统中,不同信号用不同的值表示,每个信号设置相应的函数,一旦进程发送某一个信号给另一个进程,另一进程将执行相应的函数进行处理。也就是说先把可能出现的异常等问题准备好,一旦信号产生就执行相应的逻辑即可。kill -l

  6. socket通信:(包括同一主机上的socket通信和不在同一主机上的远程 socket 通信) 可用于不同及其间的进程通信。

    上面的几种方式都是单机情况下多个进程的通信方式(多个进程在一台主机之间的通信),那两个相隔几千里的进程能够进行通信吗?这就需要套接字socket了。其实这玩意随处可见,我们平时的聊天,我们天天请求浏览器给予的响应等

消息队列的作用,应用耦合,异步消息,流量削锋

  • 解耦,将消息写入消息队列,需要消息的系统自己从消息队列中订阅,从而系统不需要做任何修改。

    • 用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统调用库存系统的接口。假如库存系统无法访问,则订单减库存将失败,从而导致订单失败。订单系统与库存系统耦合

      image-20210815224202718
    • 引入消息队列

      订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功

      库存系统:订阅下单的消息,获取下单信息,库存系统根据下单信息,进行库存操作

      假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦

      image-20210815224302288
  • 异步,将消息写入消息队列,非必要的业务逻辑以异步的方式运行,加快响应速度

    • 用户注册后,需要发注册邮件和注册短信。传统的做法有两种 1.串行的方式;2.并行方式

      串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端

      image-20210815223958365

      并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间

      image-20210815224016034

      假设三个业务节点每个使用50毫秒钟,不考虑网络等其他开销,则串行方式的时间是150毫秒,并行的时间可能是100毫秒。

    • 引入消息队列,将不是必须的业务逻辑,异步处理。

    • img

      用户的响应时间相当于是注册信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。因此架构改变后,系统的吞吐量提高到每秒20 QPS。比串行提高了3倍,比并行提高了两倍

  • 削峰,系统慢慢的按照数据库能处理的并发量,从消息队列中慢慢拉取消息。在生产中,这个短暂的高峰期积压是允许的

    流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛

    秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。可以控制活动的人数、可以缓解短时间内高流量压垮应用

    image-20210815224429376

    用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面

    秒杀业务根据消息队列中的请求信息,再做后续处理

复制粘贴 操作系统做了什么

这涉及到进程之间的通信,通过消息队列实现通信过程,是间接通信(在Windows中如果复制之后没有执行粘贴操作,会一直存在,就相当于放在了存储空间中,当关机的时候就消失了)

消息的发送不需要接收方准备好,随时可发送。

消息缓冲:在内存中开设缓冲区,发送进程将消息送入缓冲区,接收进程接收传递来的缓冲区
间接方式:发送进程发消息时不指定接收进程的名字,而是指定一个中间媒介,即信箱。进程间通过信箱实现通信 发送原语:send(MB,Message) 接收原语:receive(MB,Message)