一面(45min):
1.自我介绍
介绍了自己的大学学习成绩和两个项目,一个是大二暑期小学期的java期刊投稿管理系统(主要问的也是这个),还有一个是自己写的博客。
java期刊投稿管理系统:
我负责的是组织队员任务,代码编写部分有稿件管理部分、期刊管理部分、审稿管理部分以及代码汇总调试和优化。
针对我的任务主要问了:
①出于什么目的做的项目
②做项目的过程中遇到了什么问题
③(针对我说的问题:代码汇总和数据库连接出现问题)怎么解决的
④初次接触多线程,查找了什么资料,用了什么方法
⑤通过项目学到了什么?得到了哪些成长?
⑥团队合作中遇到过哪些问题?
个人博客:
让我发了网址看了一下,没有过多提问,因为实现原理很简单并且在自我介绍过程中说过了
2.基础知识
操作系统
①什么是线程,什么是进程?有什么区别?
- 进程是资源分配的基本单位,线程是CPU调度的基本单位
- 线程是进程的一部分,一个线程只属于一个进程,一个进程可以有很多线程
- 因为每个进程都有独立的代码和数据空间,所以进程间切换开销很大;而同一进程的不同线程间共享代码和数据空间, 只独立享有自己的运行栈和程序计数器,所以切换开销小。
问完之后让我用自己的话说一下线程和进程的区别以及对他们的理解
②死锁是什么,如何预防并处理?(没准备,只能用自己的话说)
死锁产生条件:
1、 竞争资源引起进程死锁。当系统中供多个进程共享的资源如打印机、公用队列的等,其数目不足以满足诸进程的需要时,会引起诸进程对资源的竞争而产生死锁; 2、 可剥夺资源和不可剥夺资源。系统中的资源可以分为两类,一类是可剥夺资源,是指某进程在获得这类资源后,该资源可以再被其他进程或系统剥夺; 3、 竞争不可剥夺资源。在系统中所配置的不可剥夺资源,由于它们的数量不能满足诸进程运行的需要,会使进程在运行过程中,因争夺这些资源而陷于僵局; 4、 竞争临时资源。指由一个进程产生,被另一个进程使用,短时间后便无用的资源,故也称为消耗性资源,它也可能引起死锁。
为使系统不发生死锁,必须设法破坏产生死锁的四个必要条件之一,或者允许死锁产生,但当死锁发生时能检测出思索,并有能力实现恢复。 一般有死锁的预防、死锁避免、死锁的检测与恢复三种方法。 (1) 死锁预防:破坏导致死锁必要条件中的任意一个就可以预防死锁。例如,要求用户申请资源时一次性申请所需要的全部资源,这就破坏了保持和等待条件;将资源分层,得到上一层资源后,才能够申请下一层资源,它破坏了环路等待条件。预防通常会降低系统的效率。
死锁避免:避免是指进程在每次申请资源时判断这些操作是否安全,例如,使用银行家算法。死锁避免算法的执行会增加系统的开销。
死锁检测:死锁预防和避免都是事前措施,而死锁的检测则是判断系统是否处于死锁状态,如果是,则执行死锁解除策略。
死锁解除:这是与死锁检测结合使用的,它使用的方式就是剥夺。即将某进程所拥有的资源强行收回,分配给其他的进程。
计算机网络
①TCP和UDP的区别以及应用场景
简单说,tcp,复杂,可靠,速度慢;udp正相反
- TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
- TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
- TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的。UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
- 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
- TCP首部开销20字节;UDP的首部开销小,只有8个字节
- TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
应用场景 tcp:文件下载,收发邮件,远程登录 udp:视频通讯(要求速度,但是对稳定性要求一般),广播通信
②TCP三次握手
- 客户端向服务器发送连接请求(SYN=1 seq=x)
- 服务器向服务器发送(SYN=1, ACK=1, ack=x+1)
- 客户端向服务器发送(ACK=1)此次可以带数据
数据库
①多表查询的连接方式
主要分为三种:内连接、外连接、交叉连接
关键字为:
内连接:inner join
外连接:left join、right join、outer join
交叉连接:cross join
3.算法
①给定一个字符串,查询出现次数最多的字符以及次数。
②测试这个代码用什么方式?(我说了直接测试功能、测试特殊字符和是否能够输出多个出现次数相同的字符,以及输入一个字符和空字符时的结果)
4.现实场景
①针对微信发文字朋友圈用例设计?
②我发的朋友圈对方看不到会有什么原因?
5.有什么问题问面试官
因为0准备,所以我问的是:感觉自己这次面试面的不好,会不会对后面投简历面试有什么影响?
回答是:不会,最多也就是记录面试情况
二面(30min):
1.自我介绍
①和一面差不多,但是准备充分很多,详细介绍了锁机制和进程同步的实现
②问了遇到的问题和解决方法
2.算法
删除链表倒数第n个结点,时间复杂度为O(n)
1 | class Solution { |
①问我用了什么思想,我说双指针,一前一后,两个指针的距离是n,后面一个指针先到最后一个节点,这样前一个节点就是倒数第n个节点
②问了测试用例,我说会测试普通的功能,以及空链表,只有一个节点的链表等
3.操作系统
堆和栈的区别:
(1)数据结构场景下,堆与栈表示两种常用的数据结构。
栈由操作系统自动分配释放 ,用于存放函数的参数值、局部变量等,其操作方式类似于数据结构中的栈。
堆由开发人员分配和释放, 若开发人员不释放,程序结束时由 OS 回收,分配方式类似于链表。
(2)程序内存布局场景下,堆与栈表示两种内存管理方式;
栈是一种运算受限的线性表,其限制是指只仅允许在表的一端进行插入和删除操作,这一端被称为栈顶(Top),相对地,把另一端称为栈底(Bottom)。把新元素放到栈顶元素的上面,使之成为新的栈顶元素称作进栈、入栈或压栈(Push);把栈顶元素删除,使其相邻的元素成为新的栈顶元素称作出栈或退栈(Pop)。这种受限的运算使栈拥有“先进后出”的特性(First In Last Out),简称FILO。
栈分顺序栈和链式栈两种。栈是一种线性结构,所以可以使用数组或链表(单向链表、双向链表或循环链表)作为底层数据结构。使用数组实现的栈叫做顺序栈,使用链表实现的栈叫做链式栈,二者的区别是顺序栈中的元素地址连续,链式栈中的元素地址不连续。
堆是一种常用的树形结构,是一种特殊的完全二叉树,当且仅当满足所有节点的值总是不大于或不小于其父节点的值的完全二叉树被称之为堆。堆的这一特性称之为堆序性。因此,在一个堆中,根节点是最大(或最小)节点。如果根节点最小,称之为小顶堆(或小根堆),如果根节点最大,称之为大顶堆(或大根堆)。堆的左右孩子没有大小的顺序。
4.熟悉的语言相关的问题(我说的java)
垃圾回收机制
java中对象的引用
(1)强引用(Strong Reference):如“Object obj = new Object()”,这类引用是Java程序中最普遍的。只要强引用还存在,垃圾收集器就永远不会回收掉被引用的对象。
(2)软引用(Soft Reference):它用来描述一些可能还有用,但并非必须的对象。在系统内存不够用时,这类引用关联的对象将被垃圾收集器回收。JDK1.2之后提供了SoftReference类来实现软引用。
(3)弱引用(Weak Reference):它也是用来描述非须对象的,但它的强度比软引用更弱些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK1.2之后,提供了WeakReference类来实现弱引用。
(4)虚引用(Phantom Reference):最弱的一种引用关系,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的是希望能在这个对象被收集器回收时收到一个系统通知。JDK1.2之后提供了PhantomReference类来实现虚引用。
判断对象是否是垃圾的算法
Java语言规范没有明确地说明JVM使用哪种垃圾回收算法,但是任何一种垃圾回收算法一般要做2件基本的事情:(1)找到所有存活对象;(2)回收被无用对象占用的内存空间,使该空间可被程序再次使用。
标记-清除算法:
标记-清除算法对根集合进行扫描,对存活的对象进行标记。标记完成后,再对整个空间内未被标记的对象扫描,进行回收。
优点: 实现简单,不需要进行对象进行移动。
缺点: 标记、清除过程效率低,产生大量不连续的内存碎片,提高了垃圾回收的频率。
复制算法 这种收集算法解决了标记清除算法存在的效率问题。它将内存区域划分成相同的两个内存块。每次仅使用一半的空间,JVM生成的新对象放在一半空间中。当一半空间用完时进行GC,把可到达对象复制到另一半空间,然后把使用过的内存空间一次清理掉。
优点: 按顺序分配内存即可,实现简单、运行高效,不用考虑内存碎片。
缺点: 可用的内存大小缩小为原来的一半,对象存活率高时会频繁进行复制。
标记-整理算法 标记-整理算法 采用和 标记-清除算法 一样的方式进行对象的标记,但后续不直接对可回收对象进行清理,而是将所有的存活对象往一端空闲空间移动,然后清理掉端边界以外的内存空间。
优点: 解决了标记-清理算法存在的内存碎片问题。
缺点: 仍需要进行局部对象移动,一定程度上降低了效率。
- 分代收集算法 当前商业虚拟机都采用分代收集的垃圾收集算法。分代收集算法,顾名思义是根据对象的存活周期将内存划分为几块。一般包括年轻代、老年代 和 永久代。
新生代(Young generation) 绝大多数最新被创建的对象会被分配到这里,由于大部分对象在创建后会很快变得不可达,所以很多对象被创建在新生代,然后消失。对象从这个区域消失的过程我们称之为 minor GC。 新生代 中存在一个Eden区和两个Survivor区。新对象会首先分配在Eden中(如果新对象过大,会直接分配在老年代中)。在GC中,Eden中的对象会被移动到Survivor中,直至对象满足一定的年纪(定义为熬过GC的次数),会被移动到老年代。 可以设置新生代和老年代的相对大小。这种方式的优点是新生代大小会随着整个堆大小动态扩展。参数 -XX:NewRatio 设置老年代与新生代的比例。例如 -XX:NewRatio=8 指定 老年代/新生代 为8/1. 老年代 占堆大小的 7/8 ,新生代 占堆大小的 1/8(默认即是 1/8)。 例如: -XX:NewSize=64m -XX:MaxNewSize=1024m -XX:NewRatio=8 复制代码 老年代(Old generation) 对象没有变得不可达,并且从新生代中存活下来,会被拷贝到这里。其所占用的空间要比新生代多。也正由于其相对较大的空间,发生在老年代上的GC要比新生代要少得多。对象从老年代中消失的过程,可以称之为major GC(或者full GC)。 永久代(permanent generation) 像一些类的层级信息,方法数据 和方法信息(如字节码,栈 和 变量大小),运行时常量池(JDK7之后移出永久代),已确定的符号引用和虚方法表等等。它们几乎都是静态的并且很少被卸载和回收,在JDK8之前的HotSpot虚拟机中,类的这些“永久的” 数据存放在一个叫做永久代的区域。 永久代一段连续的内存空间,我们在JVM启动之前可以通过设置-XX:MaxPermSize的值来控制永久代的大小。但是JDK8之后取消了永久代,这些元数据被移到了一个与堆不相连的称为元空间 (Metaspace) 的本地内存区域。
5.计算机网络
①网络七层协议栈
- 应用层(HTTP,FTP,DNS) 文件传输,电子邮件,文件服务,虚拟终端
- 表示层(JPEG,ASII)数据格式化,代码转换,数据加密
- 会话层(RPC,NFS) 解除或建立与别的接点的联系
- 传输层(TCP,UDP) 提供端对端的接口
- 网络层 (IP,ARP,ICMP) 为数据包选择路由
- 数据链路层(MAC,VLAN,PPP) 传输有地址的帧以及错误检测功能
- 物理层。
②TCP和IP分别对应哪一层
TCP对应传输层,IP对应网络层
③syn攻击(了解一点,不是很会,没答)
SYN 洪水攻击利用 TCP 连接的握手过程发动攻击。正常情况下,TCP 连接将完成三次握手以建立连接。
首先,客户端向服务器发送 SYN 数据包以发起连接。 接着,服务器通过 SYN/ACK 数据包对该初始数据包做出响应,以便确认通信。 最后,客户端返回 ACK 数据包以确认接到服务器发出的数据包。完成这一系列数据包发送和接收操作后,TCP 连接将处于打开状态并且能够发送和接收数据。
为发起拒绝服务攻击,攻击者需利用这样一项事实:收到初始 SYN 数据包后,服务器将通过一个或多个 SYN/ACK 数据包做出回响,等待完成握手过程的最后一步。工作方式如下:
攻击者通常使用伪造的 IP 地址向目标服务器发送大量 SYN 数据包。 然后,服务器分别对每一项连接请求做出响应,并确保打开的端口做好接收响应的准备。 在服务器等待最后一个 ACK 数据包(永远不会到达)的过程中,攻击者将继续发送更多 SYN 数据包。每当有新的 SYN 数据包到达,服务器都会临时打开一个新的端口并在一段特定时间内保持连接;用遍所有可用端口后,服务器将无法正常运行。
在网络中,如果服务器连接处于打开状态但另一端的机器连接未打开,则视为半开连接。在此类 DDoS 攻击中,目标服务器将使连接一直处于打开状态,静待各个连接超时,避免再次开放端口。因此,此类攻击可视为“半开连接攻击”。
SYN Cookie 此策略要求服务器创建 Cookie。为避免在填充积压工作时断开连接,服务器使用 SYN-ACK 数据包响应每一项连接请求,而后从积压工作中删除 SYN 请求,同时从内存中删除请求,保证端口保持打开状态并做好重新建立连接的准备。如果连接是合法请求并且已将最后一个 ACK 数据包从客户端机器发回服务器,服务器将重建(存在一些限制)SYN 积压工作队列条目。虽然这项缓解措施势必会丢失一些 TCP 连接信息,但好过因此导致对合法用户发起拒绝服务攻击。
④拥塞控制
TCP 发送方要维持一个拥塞窗口(cwnd)的状态变量。拥塞控制窗口的大小取决于网络的拥塞程度,并且动态变化。发送方让自己的发送窗口取为拥塞窗口和接收方的接受窗口中较小的一个。
TCP的拥塞控制采用了四种算法,即慢开始、拥塞避免、快重传和快恢复。在网络层也可以使路由器采用适当的分组丢弃策略(如主动队列管理 AQM),以减少网络拥塞的发生。
- 慢开始:慢开始算法的思路是当主机开始发送数据时,如果立即把大量数据字节注入到网络,那么可能会引起网络阻塞,因为现在还不知道网络的符合情况。经验表明,较好的方法是先探测一下,即由小到大逐渐增大发送窗口,也就是由小到大逐渐增大拥塞窗口数值。cwnd初始值为1,每经过一个传播轮次,cwnd加倍。
- 拥塞避免:拥塞避免算法的思路是让拥塞窗口cwnd缓慢增大,即每经过一个往返时间RTT就把发送放的cwnd加1.
- 快速重传与快恢复:在 TCP/IP 中,快速重传和恢复(fast retransmit and recovery,FRR)是一种拥塞控制算法,它能快速恢复丢失的数据包。没有 FRR,如果数据包丢失了,TCP 将会使用定时器来要求传输暂停。在暂停的这段时间内,没有新的或复制的数据包被发送。有了 FRR,如果接收机接收到一个不按顺序的数据段,它会立即给发送机发送一个重复确认。如果发送机接收到三个重复确认,它会假定确认件指出的数据段丢失了,并立即重传这些丢失的数据段。有了 FRR,就不会因为重传时要求的暂停被耽误。 当有单独的数据包丢失时,快速重传和恢复(FRR)能最有效地工作。当有多个数据信息包在某一段很短的时间内丢失时,它则不能很有效地工作。
⑤DNS劫持(不了解)
域名劫持是互联网攻击的一种方式,通过攻击域名解析服务器(DNS),或伪造域名解析服务器(DNS)的方法,把目标网站域名解析到错误的IP地址从而实现用户无法访问目标网站的目的或者蓄意或恶意要求用户访问指定IP地址(网站)的目的。
劫持的方法
1.本机DNS劫持 攻击者通过某些手段使用户的计算机感染上木马病毒,或者恶意软件之后,恶意修改本地DNS配置,比如修改本地hosts文件,缓存等
2.路由DNS劫持 很多用户默认路由器的默认密码,攻击者可以侵入到路由管理员账号中,修改路由器的默认配置 3.攻击DNS服务器 直接攻击DNS服务器,例如对DNS服务器进行DDOS攻击,可以是DNS服务器宕机,出现异常请求,还可以利用某些手段感染dns服务器的缓存,使给用户返回来的是恶意的ip地址
DNS的防范
1.加强本地计算机病毒检查,开启防火墙等,防止恶意软件,木马病毒感染计算机 2.改变路由器默认密码,防止攻击者修改路由器的DNS配置指向恶意的DNS服务器 3.企业的话可以准备两个以上的域名,一旦一个域名挂掉,还可以使用另一个 4.用HTTP DNS 代替 Local DNS
⑥DNS的用途
1、请求一旦发起,浏览器首先要做的事情就是解析这个域名,一般来说,浏览器会首先查看本地硬盘的 hosts 文件,看看其中有没有和这个域名对应的规则,如果有的话就直接使用 hosts 文件里面的 ip 地址。
2、如果在本地的 hosts 文件没有能够找到对应的 ip 地址,浏览器会发出一个 DNS请求到本地DNS服务器 。本地DNS服务器一般都是你的网络接入服务器商提供,比如中国电信,中国移动。
3、查询你输入的网址的DNS请求到达本地DNS服务器之后,本地DNS服务器会首先查询它的缓存记录,如果缓存中有此条记录,就可以直接返回结果,此过程是递归的方式进行查询。如果没有,本地DNS服务器还要向DNS根服务器进行查询。
4、根DNS服务器没有记录具体的域名和IP地址的对应关系,而是告诉本地DNS服务器,你可以到域服务器上去继续查询,并给出域服务器的地址。这种过程是迭代的过程。
5、本地DNS服务器继续向域服务器发出请求,在这个例子中,请求的对象是.com域服务器。.com域服务器收到请求之后,也不会直接返回域名和IP地址的对应关系,而是告诉本地DNS服务器,你的域名的解析服务器的地址。
6、最后,本地DNS服务器向域名的解析服务器发出请求,这时就能收到一个域名和IP地址对应关系,本地DNS服务器不仅要把IP地址返回给用户电脑,还要把这个对应关系保存在缓存中,以备下次别的用户查询时,可以直接返回结果,加快网络访问。
6.用例设计
问我平常用什么软件?我说微信,面试官说我一面问的是微信,换一个,我就说了微博。然后问我用啥功能,我说发微博,发图片。让我设计发图片的测试用例。
7.发展方向和职业规划
这个坑了自己一把,我说我想今年保研之后,10月份到明年3、4月份都可以实习,面试官表示了解,说今天没有时间面下一轮了,后面再约时间面。当时感觉面试官还是很满意的。
然后!!!hr打电话告诉我暑期实习可能没法保留到10月,要先和leader商量才能告诉我能不能继续面试。
感觉像是愚人节给我开了个玩笑……
还好比较幸运,出门玩耍了一会就接到电话说面试表现比较好,可以继续面试,等清明节之后约时间!
开心子~
8.反问
我问的是面试有什么缺点?
面试官答:基础知识很牢固,但是语言组织逻辑方面还要加强。
前两轮技术面总结
测试岗好像算法考的不是很难,但是每个算法都会让你设计测试用例。
一面问的比较基础,当时什么也没准备,基础知识也只能靠着印象回答,感觉面的不是很好。
二面问的比较深,感觉会着重根据你的简历问一些问题。比如我写的精通C++,java,就会问java问的比较深入。计网的知识也不仅仅是停留在表面了。
总之,好好准备才是最重要的!
三面(1h):
最紧张的一次面试,比较难,而且一开始面试官在开会,忘记面试的事情了,等待的时间比较漫长……
1.自我介绍
不多赘述
2.项目相关
问了项目持续的时间,期间出现的问题等。
也看了个人博客,差点就要问人群异常检测这个关于深度学习的大创项目了= =
3.计算机网络
①输入一个url之后发生了什么?(DNS)
输入地址:当我们开始在浏览器中输入网址的时候,浏览器其实就已经在智能的匹配可能得 url 了,他会从历史记录,书签等地方,找到已经输入的字符串可能对应的 url,然后给出智能提示,让你可以补全url地址。对于 google的chrome 的浏览器,他甚至会直接从缓存中把网页展示出来,就是说,你还没有按下 enter,页面就出来了。
浏览器查找域名的 IP 地址:
1、请求一旦发起,浏览器首先要做的事情就是解析这个域名,一般来说,浏览器会首先查看本地硬盘的 hosts 文件,看看其中有没有和这个域名对应的规则,如果有的话就直接使用 hosts 文件里面的 ip 地址。
2、如果在本地的 hosts 文件没有能够找到对应的 ip 地址,浏览器会发出一个 DNS请求到本地DNS服务器 。本地DNS服务器一般都是你的网络接入服务器商提供,比如中国电信,中国移动。
3、查询你输入的网址的DNS请求到达本地DNS服务器之后,本地DNS服务器会首先查询它的缓存记录,如果缓存中有此条记录,就可以直接返回结果,此过程是递归的方式进行查询。如果没有,本地DNS服务器还要向DNS根服务器进行查询。
4、根DNS服务器没有记录具体的域名和IP地址的对应关系,而是告诉本地DNS服务器,你可以到域服务器上去继续查询,并给出域服务器的地址。这种过程是迭代的过程。
5、本地DNS服务器继续向域服务器发出请求,在这个例子中,请求的对象是.com域服务器。.com域服务器收到请求之后,也不会直接返回域名和IP地址的对应关系,而是告诉本地DNS服务器,你的域名的解析服务器的地址。
6、最后,本地DNS服务器向域名的解析服务器发出请求,这时就能收到一个域名和IP地址对应关系,本地DNS服务器不仅要把IP地址返回给用户电脑,还要把这个对应关系保存在缓存中,以备下次别的用户查询时,可以直接返回结果,加快网络访问。
②如果在这个过程中DNS服务器出错,可能是发生了什么?
回答的很磕巴,面试官很温柔,一直在引导我,我还很傻X的说了个输错了IP地址……然后我说了个可能发生DNS劫持,就继续问了DNS劫持是什么。(前面有答案,这里不写了)
③如果输入url之后网页打开比较慢,可能是什么原因?
针对这个题目我们可以简单理解成是server端出现的问题,而不是client端出现了问题(用户网络不好包括域名服务器解析等可能),当然面试官要考你用户端的知识,例如域名解析,也是有挺多可以考到的知识点,但单就这个问题,更强调的是server端的知识点。下面逐一来剖析可能的原因:
(1)可能的原因一:服务器出口带宽不够用。这是一个很常见的瓶颈。一方面,可能是本身购买的服务器出口带宽就很小(企业购买带宽相当昂贵),一旦用户访问量上来了,并发量大了,自然均分给用户的出口带宽就更小了,所以某些用户的访问速度就会下降了很多。另一个,就是跨运营商网络导致带宽缩减,例如很多公司的网站(服务器)是放在电信的网络上的,而如果用户这边对接的是长城或者说联通的宽带,运营商之间网络传输在对接时是会有限制的,这就可能导致带宽的缩减。
(2)可能原因二:服务器负载过大忙不过来,比如说CPU和内存消耗完了,这个容易理解,不展开。
(3)可能原因三:网站的开发代码没写好,例如mysql语句没有进行优化,导致数据库的读写相当耗费时间。
(4)可能原因四:数据库的瓶颈,也是很常见的一个瓶颈,这点跟上面第三个原因可以一起来说。当我们的数据库变得愈发庞大,比如好多G好多T这么大,那对于数据库的读写就会变得相当缓慢了,索引优化固然能提升一些效率,但数据库已经如此庞大的话,如果每次查询都对这么大的数据库进行全局查询,自然会很慢。这个学过数据库的话也是挺容易理解的。
4.Java相关
①用过多线程吗?怎么实现的?
用过,实现方法有下面四种:
继承Thread类,启动线程通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。优点:代码简单。缺点:该类无法继承别的类。
实现Runnable接口,优点:继承其他类。同一实现该接口的实例可以共享资源。缺点:代码复杂
实现Callable接口,通过FutureTask包装器来创建Thread线程:优点:可以获得异步任务的返回值
线程池:实现自动化装配,易于管理,循环利用资源。
②java多线程的安全是怎么实现的呢?
锁
③说一说锁的升级
在 Java 语言中,使用 Synchronized 是能够实现线程同步的,即加锁。并且实现的是悲观锁,在操作同步资源的时候直接先加锁。
加锁可以使一段代码在同一时间只有一个线程可以访问,在增加安全性的同时,牺牲掉的是程序的执行性能,所以为了在一定程度上减少获得锁和释放锁带来的性能消耗,在 jdk6 之后便引入了“偏向锁”和“轻量级锁”,所以总共有4种锁状态,级别由低到高依次为:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态。这几个状态会随着竞争情况逐渐升级。
注意:锁可以升级但不能降级
无锁:无锁是指没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功。无锁的特点是修改操作会在循环内进行,线程会不断的尝试修改共享资源。如果没有冲突就修改成功并退出,否则就会继续循环尝试。如果有多个线程修改同一个值,必定会有一个线程能修改成功,而其他修改失败的线程会不断重试直到修改成功。
偏向锁:偏向锁是指当一段同步代码一直被同一个线程所访问时,即不存在多个线程的竞争时,那么该线程在后续访问时便会自动获得锁,从而降低获取锁带来的消耗,即提高性能。当一个线程访问同步代码块并获取锁时,会在 Mark Word 里存储锁偏向的线程 ID。在线程进入和退出同步块时不再通过 CAS 操作来加锁和解锁,而是检测 Mark Word 里是否存储着指向当前线程的偏向锁。轻量级锁的获取及释放依赖多次 CAS 原子指令,而偏向锁只需要在置换 ThreadID 的时候依赖一次 CAS 原子指令即可。
偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程是不会主动释放偏向锁的。
关于偏向锁的撤销,需要等待全局安全点,即在某个时间点上没有字节码正在执行时,它会先暂停拥有偏向锁的线程,然后判断锁对象是否处于被锁定状态。如果线程不处于活动状态,则将对象头设置成无锁状态,并撤销偏向锁,恢复到无锁(标志位为01)或轻量级锁(标志位为00)的状态。
偏向锁在 JDK 6 及之后版本的 JVM 里是默认启用的。可以通过 JVM 参数关闭偏向锁:-XX:-UseBiasedLocking=false,关闭之后程序默认会进入轻量级锁状态。
轻量级锁:轻量级锁是指当锁是偏向锁的时候,却被另外的线程所访问,此时偏向锁就会升级为轻量级锁,其他线程会通过自旋(关于自旋的介绍见文末)的形式尝试获取锁,线程不会阻塞,从而提高性能。
轻量级锁的获取主要由两种情况:① 当关闭偏向锁功能时;② 由于多个线程竞争偏向锁导致偏向锁升级为轻量级锁。
在代码进入同步块的时候,如果同步对象锁状态为无锁状态,虚拟机将首先在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的 Mark Word 的拷贝,然后将对象头中的 Mark Word 复制到锁记录中。
拷贝成功后,虚拟机将使用 CAS 操作尝试将对象的 Mark Word 更新为指向 Lock Record 的指针,并将 Lock Record 里的 owner 指针指向对象的 Mark Word。
如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象 Mark Word 的锁标志位设置为“00”,表示此对象处于轻量级锁定状态。
如果轻量级锁的更新操作失败了,虚拟机首先会检查对象的 Mark Word 是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行,否则说明多个线程竞争锁。
若当前只有一个等待线程,则该线程将通过自旋进行等待。但是当自旋超过一定的次数时,轻量级锁便会升级为重量级锁(锁膨胀)。
另外,当一个线程已持有锁,另一个线程在自旋,而此时又有第三个线程来访时,轻量级锁也会升级为重量级锁(锁膨胀)。
重量级锁:重量级锁是指当有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态。
重量级锁通过对象内部的监视器(monitor)实现,而其中 monitor 的本质是依赖于底层操作系统的 Mutex Lock 实现,操作系统实现线程之间的切换需要从用户态切换到内核态,切换成本非常高。
简言之,就是所有的控制权都交给了操作系统,由操作系统来负责线程间的调度和线程的状态变更。而这样会出现频繁地对线程运行状态的切换,线程的挂起和唤醒,从而消耗大量的系统资源,导致性能低下。
③有用Java写过别的项目吗?
写过一个J2EE的项目
5.唠嗑
①什么时候能来实习?
②考虑读研吗?
③有什么优缺点?
6.测试相关
①为什么选择测试?
②平常用什么软件?(QQ)用什么功能?(发红包)设计个测试用例
功能测试 在红包钱数,和红包个数的输入框中只能输入数字 红包里最多和最少可以输入的钱数 200 0.01 拼手气红包最多可以发多少个红包 100 超过最大拼手气红包的个数是否有提醒 当红包钱数超过最大范围是不是有对应的提示 当发送的红包个数超过最大范围是不是有提示 当余额不足时,红包发送失败 在红包描述里是否可以输入汉字,英文,符号,表情,纯数字,汉字英语符号,(是否可以输入它们的混合搭配) 输入红包钱数是不是只能输入数字 红包描述里许多能有多少个字符 10个 红包描述,金额,红包个数框里是否支持复制粘贴操作 红包描述里的表情可以删除 发送的红包别人是否可以领取 发的红包自己可不可以领取 24小时内没有领取的红包是否可以退回到原来的账户 超过24小时没有领取的红包,是否还可以领取 用户是否可以多次抢一个红包 红包的金额里的小数位数是否有限制 可以按返回键,取消发红包 断网时,无法抢红包 可不可以自己选择支付方式 余额不足时,会不会自动匹配支付方式 在发红包界面能否看到以前的收发红包的记录 红包记录里的信息与实际收发红包记录是否匹配 支付时可以密码支付也可以指纹支付 如果直接输入小数点,那么小数点之前应该有个0 支付成功后,退回聊天界面 .发红包金额和收到的红包金额应该匹配 是否可以连续多次发红包 输入钱数为0,"塞钱进红包"置灰
性能测试 弱网时抢红包,发红包的时间 不同网速时抢红包,发红包的时间 发红包和收红包成功后的跳转时间 收发红包的耗电量 退款到账的时间
兼容性测试 苹果,安卓是否都可以收发红包 电脑端可以抢红包 网络兼容性:2g/3g/4g,WiFi,热点,移动/联通/电信 不同分辨率
界面测试 发红包界面没有错别字 收红包界面没有错别字 收红包和发红包界面布置合理 发红包和收到红包界面颜色搭配合理
安全性测试 对方QQ号异地登录,是否会有提醒 2人 红包被领取以后,发送红包人的金额会减少,收红包金额会增加 发送红包失败,余额和银行卡里的钱数不会少 .红包发送成功,是否会收QQ支付的通知
易用性测试 红包描述,可以通过语音输入 可以指纹支付也可以密码支付 中断测试:前后台切换,网络异常,低电量,断电,来电,短信等
7.算法题
求:二叉树所有根到叶子路径组成的数字之和
描述信息
二叉树每个节点的value范围是1-9
1
2 3
4 5
从根到叶子共3条:1->2->4, 1->2->5, 1->3
构成的数字为124,125,13,求和124 + 125 + 13 = 262即为所求
1 | public List<List<Integer>> pathsum(node root) |
8.反问
我:请问我的面试有什么优缺点和注意的地方,可以指导一下吗?
面试官:你觉得自己的逻辑表达怎么样
我:有待加强
面试官:你自己准备过的部分回答得很有条理和逻辑,但是就像你自己说的缺点一样,项目经验不足,在某些实际应用的场景上还是稍有欠缺
我:谢谢!
结语
晚饭都没来得及吃就去准备某门选修课的答辩了……被老师蹂躏了一通之后晚上大概8:30接到了hr姐姐的电话约hr面。等待ing~
HR面(10min):
- 自己的优点和缺点
- 了解字节的大小周吗?能接受吗?
- 对之前的面试官感觉怎么样?
- 面试官有没有向你介绍字节跳动相关?
- 为什么想来测试开发,而不是纯开发呢?
- 之前看你是参加踢馆赛之后内推来面试的,那有没有别的offer?为什么想来字节?
- 之后是准备保研吗?什么时候能来实习?
- 反问:
- 入职之前要学习什么知识?
- 工作内容具体是什么?
- 什么时候能收到offer?
hr面试官说审批流程走完三天之后就能收到offer了~