近期好文:
秋招还没offer怎么办?面试指南 | 重磅来袭!!
阿里巴巴 CTO 线 Java一面,稳了 (50min)
百度一面,顶不住
得物一面,场景题问得有点多
MySQL索引结构,为何选用B+树,悟了
最近某客上一篇面经又炸啦,评论区网友直呼顶不住,今天主要分享这位网友的面经
面经原帖地址:https://www.nowcoder.com/discuss/542181239265013760
1.自我介绍一下?
2.你 Java是怎么学的?学校有讲吗?
3.进程的通信方式有哪些?
4.给你几个看看进程通信方式和几种场景,你看看它们是不是对应?
(这几个都是一一对应的,面试官跟我解释说这是一种抽象,不是具体编码实现,是那个思想)
5.网络五层协议是哪五层?上面三层常见的协议可以说一下吗?
6.网络层除了IP,还有哪些协议?
7.linux中,哪些命令与应用层的协议相关?哪些命令与传输层的协议相关?哪些命令与网络层的协议相关?你可以举几个例子吗?
候选人说:面试官在聊天框又写了 netstat,ss,lsof 这些命令,然后跟我讲解这些命令,包括还提到了 IP 等命令
8.Map map = new HashMap(100)
,往里面 put 多少个元素会扩容?
候选人说:这里跟他说这不好说,因为hash冲突也会引起扩容,他跟我说不考虑冲突;后来面完想了想,因为容量已经大于64了,hash冲突就不会引起扩容了
9.为什么是128*0.75?
10.为什么它要把长度调整到 2的n次幂 ?
11.ReentrantLock 的 lock 做了什么事情?概括一下?先考虑只有一个线程的情况?
12.只做了cas吗?
13.你可能漏了些什么;那我这个线程再次执行这个lock方法会怎么样?
14.那如果换一个线程过来执行这个lock呢?
15.那为啥原来的那个线程可以重入,新线程不能重入?
16.AQS有个当前线程的变量吗?怎么标记当前线程是否自己呀?
17.Java里面把线程挂起的方式有哪些?
面试官提示:sleep 和 join 不算吗?(明牌提示)
18.这里面有个 wait
方法比较特殊,你知道 wait
方法是要在什么条件下才能用吗?
19.为什么要在同步代码块里面才能用呢?有思考过什么原因吗?
候选人说:他说我说的没毛病,但是有点不完整,稍微完善一点的是这个意思,sync是个关键字,在编译的时候会编译成monitorenter和moniterexit指令,会生成一个辅助对象,也就是ObjectMonitor,来辅助sync的执行;
这个对象里面有个核心的类似aqs的结构体,也就是我说的waiter队列和state和owner,wait必须依靠这个waiter队列进行挂起,所以说它必须依赖ObjectMonitor这个对象,如果没有这个对象,那就会报错
20.NIO核心的三大组件是什么?
候选人说:我在说 selector 的时候说了个 epoll,他跟我说不一定是 epoll,还可能是 kqueue,甚至是 select 和 poll,要看具体的操作系统以及操作系统的版本
21.你在网络传输的时候,传输的字节,会出现黏包半包问题,你知道黏包半包问题有什么解决思路吗?
22.你在哪些中间件或者哪些地方见过黏包半包的这些解决思路吗?
23.除了netty还有吗?
候选人说:然后他说我刚才说的基于长度字段方式,让他想起了 kafka
的协议,然后讲了 kafka
里面的黏包半包问题
24.问下实习吧,你在实习的公司做过什么项目?
25.你觉得你的系统的用户量是百级千级还是万级呢?
26.你们项目部署的话,是部署多少台机器呢?
27.你可以说一下你做的一些的核心功能吗?
28.你觉得你这个预约系统的接口里面,最核心的接口是哪个接口?
29.你说你还有下游,你说说你跟下游通信的方式?Feign 还是 HttpClient?
30.我看你之前做过rpc,你应该很清楚模块与模块之间调用的方式吧?
我给你举个场景,我本地服务A,有个C接口,要调用M方法,这个M方法是在服务B上的,但是我现在服务A上面只有C这个接口,而且是没有任何实现类的,那它是怎么实现调用服务B的呢?
你是不是还漏了什么?服务A是直接跟服务B发起请求的吗?前面没有其他步骤了吗?
候选人说:然后他开始讲起来他对 rpc 实现的思路,从注册中心,协议,序列化,连接方式,代理模式等等;跟我说这个框架不难写,但如果要实现的像 dubbo 和 grpc 那样,还是要有点水平
然后就结束了,他说我是本科,看得出来我很热爱技术,对技术有自己的理解,在本科很少见,但是技术涉及的不是很多,也比较简单和基本;最后说要给我个建议,讲了十分钟。最后没有反问
可以看出来面试官是个真大佬,对很多东西都很熟悉,面试的时候边打哈欠边面,一边吃面包一边喝水,很自然但是很凌厉,一点不拘谨,随手在留言板上面写两句代码都能问很多东西,能从一个问题扩展到其他问题,也给了很多提示,补齐我说漏的东西;特别是最后赶时间还是跟我讲了好久的建议,是真的很感谢
1.自我介绍一下?
2.没有考虑过考研吗?考研和工作两方面是怎么考虑的?你的专业课有哪些?(作为双非本被问这些已经不惊讶了)
3.操作系统的进程和线程有什么样的联系和区别?
4.Java里面也有进程和线程,Java里面的进程和线程跟操作系统的进程和线程有什么区别?
5.我在一个jvm进程里面,最多能开多少个线程,原因是什么?
6.一个线程的创建需要消耗哪些资源呢?
7.http熟悉吗?1.0,1.1,2.0有什么区别?
8.3.0也出来啦,3.0解决了什么问题呢?
9.一个表有学生号,学生名,学科,学期,成绩;查出每个学期的每个学科的最高分学生名(没写出来。。。)
10.聊聊你毕业后的计划吧?
**候选人说:**二面也是没有反问,二面面试官非常和蔼可亲,很温柔,讲话也有点俏皮,SQL 写不出来都给我过了
但是也狠狠地批评我,说我有个问题至少犯了四次,还没看清题目,没听完要求就开始写了,他要我直接在代码里面初始化数组就好了,我还用了scanner,甚至那个算法题的题目都是有二义性的,我也没发现,也没问清楚,关键还是在于审题,而不是二分法;
听得我很慌,以为要挂了。然后告诉我以后工作也是,写代码也是,都要先听需求,先思考,写代码的时候永远是最短的
最后说没关系,说我面试能学到东西就好,其他的问题跟hr聊吧
常规HR面,介绍实习,实习收获,为什么不转正,毕业志向,个人优缺点
反问,11月左右反馈面试结果
没有提前实习的要求
建议
略
略
进程间通信(Inter-Process Communication,IPC)的方式有以下几种:
以上是常见的进程间通信方式,每种方式都有其适用的场景和特点。在面试中,我可以进一步详细介绍每种方式的原理、优缺点以及适用场景。
面试回答:是的,您提到的几个场景和对应的进程通信方式是相匹配的。
pub/sub
机制使用了消息队列的方式进行进程间通信。发布者(pub)将消息发布到特定的频道,订阅者(sub)可以通过订阅该频道来接收消息。kill
命令可以向指定进程发送信号,用于通知进程发生了某个事件。通过发送不同的信号,可以实现进程的终止、暂停、继续等操作。buffer pool
是一块共享内存区域,用于缓存数据库中的数据页。多个MySQL进程可以将该共享内存映射到各自的地址空间,实现数据的共享和高效访问。这些场景和对应的进程通信方式都是合理的,它们在实际应用中发挥了重要的作用。
网络五层协议是指OSI(Open Systems Interconnection)参考模型中的五层协议,包括以下五层:
常见的三层协议如下:
这些协议在网络通信中起着重要的作用,每个层级都有不同的功能和责任,共同构成了网络通信的基础。
除了IP协议,网络层还涉及到以下几个常见的协议:
这些协议在网络层起着重要的作用,用于实现网络间的路由、错误控制、安全性等功能。
在Linux中,以下是一些与应用层、传输层和网络层协议相关的命令:
与应用层协议相关的命令:
与传输层协议相关的命令:
与网络层协议相关的命令:
这些命令与不同层级的协议相关,可以帮助我们在Linux系统中进行网络调试、连接测试和配置管理等操作。
面经作者说:(面试官在聊天框又写了netstat,ss,lsof这些命令,然后跟我讲解这些命令,包括还提到了ip等命令)
在Java中,HashMap的扩容是基于负载因子(load factor)和初始容量(initial capacity)来确定的。负载因子是指HashMap在达到容量的百分比时进行扩容,默认为 0.75。
在给定初始容量的情况下,当HashMap中的元素数量超过负载因子乘以初始容量时,就会触发扩容操作。具体来说,在给定初始容量为100的 HashMap 中,当元素数量超过 75 个时,就会进行扩容。
需要注意的是,HashMap的扩容是一个相对耗时的操作,因为它需要重新计算哈希值、重新分配存储空间,并将原有的元素重新散列到新的存储位置。因此,在使用HashMap时,尽量提前估计元素数量,并设置合适的初始容量,以减少扩容操作的频率。
面经作者说:(这里跟他说这不好说,因为hash冲突也会引起扩容,他跟我说不考虑冲突;后来面完想了想,因为容量已经大于64了,hash冲突就不会引起扩容了)
HashMap在进行扩容时,会将容量调整为2的幂次方。这是因为 HashMap
使用的散列算法中,计算元素在数组中存储位置的方法是通过对元素的哈希值进行位运算。具体来说,HashMap会使用 (n - 1) & hash 的方式来计算元素在数组中的索引位置,其中 n 是数组的长度,hash 是元素的哈希值。
通过使用位运算 (n - 1) & hash
,可以保证元素的哈希值在数组中的存储位置均匀分布,减少哈希冲突的概率。而当数组的长度是2的幂次方时,(n - 1) 的二进制表示中所有位都是1,这样与任何哈希值进行位运算都能保证结果在数组索引范围内。这种方式可以提高散列算法的效率和均匀性。
另外,调整容量为 2 的幂次方还有一个好处是,在进行哈希值与数组长度进行位运算时,可以使用位运算符 & 代替取模运算符 %,位运算的效率更高。
因此,HashMap将容量调整为2的幂次方是为了提高散列算法的效率和均匀性,同时也能够使用位运算来代替取模运算,提高性能。
当HashMap进行扩容时,会将容量调整为2的幂次方,主要有以下几个原因:
综上所述,将HashMap的容量调整为2的幂次方可以提高散列性、减少碰撞、提高扩容效率和优化计算效率,从而提升HashMap的性能和效率。
ReentrantLock 是 Java 中的一个可重入锁,它提供了与 synchronized 关键字类似的功能,用于实现线程之间的互斥访问。
当只有一个线程调用 ReentrantLock 的 lock 方法时,它会尝试获取锁。如果锁当前没有被其他线程持有,那么该线程会成功获取锁,并继续执行后续的代码。同时,ReentrantLock会记录当前持有锁的线程和获取锁的次数。
在这种情况下,如果同一个线程再次调用lock方法,它会增加获取锁的次数,而不会被阻塞。这种机制称为可重入性,即同一个线程可以多次获取同一个锁,而不会产生死锁。
当线程执行完需要保护的代码块后,可以调用 ReentrantLock 的 unlock 方法释放锁。在释放锁的过程中,ReentrantLock会判断当前线程是否还持有锁,如果是,则减少获取锁的次数。只有当获取锁的次数减少到0时,锁才会完全释放,其他等待获取锁的线程才有机会获取到锁。
总结起来,ReentrantLock的lock方法在只有一个线程的情况下,会尝试获取锁并记录获取锁的次数,而unlock方法会释放锁并减少获取锁的次数,实现了可重入性。
当同一个线程再次执行 ReentrantLock
的 lock 方法时,会增加获取锁的次数。这种机制称为可重入性,即同一个线程可以多次获取同一个锁,而不会产生死锁。
具体来说,如果当前线程已经持有了锁,再次调用lock方法时,锁的持有计数会增加。这意味着线程在释放锁之前,可以多次获取锁,而不会被阻塞。
当线程执行完需要保护的代码块后,可以调用 ReentrantLock 的 unlock 方法释放锁。在释放锁的过程中,ReentrantLock 会判断当前线程是否还持有锁,如果是,则减少获取锁的次数。只有当获取锁的次数减少到0时,锁才会完全释放,其他等待获取锁的线程才有机会获取到锁。
总结起来,ReentrantLock 的 lock 方法在同一个线程多次执行时,会增加获取锁的次数,而 unlock 方法会释放锁并减少获取锁的次数,实现了可重入性。这种机制可以避免同一个线程因为重复获取锁而产生死锁,并且提供了更灵活的锁控制。
当另一个线程尝试执行ReentrantLock的lock方法时,如果锁已经被其他线程持有,情况如下:
总结起来,当另一个线程尝试执行ReentrantLock的lock方法时,如果锁已经被其他线程持有,尝试获取锁的线程会被阻塞,直到锁被释放。具体的等待和唤醒策略取决于锁的类型(公平锁或非公平锁)。
在Java中,有几种方式可以将线程挂起:
Thread.sleep(long millis)
:这个方法会使当前线程暂停执行指定的时间(以毫秒为单位)。线程在休眠期间不会占用CPU资源,可以用于实现简单的定时等待。Object.wait()
:这个方法会使当前线程进入等待状态,直到其他线程调用相同对象的notify()或notifyAll()方法来唤醒它。wait()方法必须在同步代码块或同步方法中调用。Thread.join()
:这个方法会使当前线程等待调用它的线程执行完毕。调用join()方法的线程会被挂起,直到目标线程执行完毕才会继续执行。LockSupport.park()
:这个方法可以使当前线程挂起,类似于Thread.sleep(),但是它没有指定时间参数。它需要与LockSupport.unpark()方法配合使用,unpark()方法可以唤醒指定线程。但线程挂起并不意味着线程的终止,只是暂时停止了线程的执行,等待某个条件满足或其他线程的唤醒。
是的,wait()方法需要在满足以下条件时才能使用:
这些条件的目的是确保wait()方法的正确使用,以避免死锁、竞态条件等问题。同时,这也是Java中实现线程间协作的一种常见方式。
我的理解:
在 Java 中,wait() 方法必须在同步代码块或同步方法中调用的原因是与对象的监视器(monitor)相关。
每个Java对象都有一个与之关联的监视器,也称为内置锁(intrinsic lock)或互斥锁(mutex lock)。同步代码块或同步方法使用这个锁来实现线程的同步和互斥访问。
当一个线程进入同步代码块时,它会尝试获取对象的锁。如果锁已经被其他线程持有,那么当前线程将被阻塞,直到锁被释放。这样可以确保同一时间只有一个线程能够执行同步代码块中的代码,避免了多个线程同时访问共享资源导致的数据不一致或竞态条件问题。
wait()方法需要在同步代码块或同步方法中调用的原因是,它依赖于对象的监视器(锁)。调用wait()方法会释放当前线程持有的锁,并使线程进入等待状态,直到其他线程调用相同对象的 notify()
或 notifyAll()
方法来唤醒它。
如果 wait() 方法不在同步代码块或同步方法中调用,那么当前线程就没有持有对象的锁,也就无法释放锁,也就无法让其他线程获得锁并唤醒等待的线程。这样就无法实现线程间的协作和同步。
因此,为了正确使用wait()方法,必须在同步代码块或同步方法中调用,以确保线程在调用wait()方法时持有对象的锁,并且能够在适当的时候被其他线程唤醒。
博主说:(他说我说的没毛病,但是有点不完整,稍微完善一点的是这个意思,sync是个关键字,在编译的时候会编译成monitorenter和moniterexit指令,会生成一个辅助对象,也就是ObjectMonitor,来辅助sync的执行,这个对象里面有个核心的类似aqs的结构体,也就是我说的waiter队列和state和owner,wait必须依靠这个waiter队列进行挂起,所以说它必须依赖ObjectMonitor这个对象,如果没有这个对象,那就会报错)
NIO(New I/O)是Java中提供的一种基于通道和缓冲区的I/O操作方式,相比传统的流式I/O,NIO具有更高的效率和灵活性。
NIO的核心组件包括:
这三个核心组件共同构成了 NIO 的基础架构,通过它们可以实现高效的非阻塞 I/O 操作,适用于处理大量的并发连接和高负载的网络应用。
面经作者说:(我在说selector的时候说了个 epoll,他跟我说不一定是 epoll,还可能是kqueue,甚至是select和poll,要看具体的操作系统以及操作系统的版本)
黏包和半包问题是在网络传输中常见的问题,主要是由于数据的传输和接收速度不一致导致的。解决黏包和半包问题可以采取以下几种思路:
以上是一些常见的解决思路,具体的选择可以根据实际情况和需求进行考虑。在实际应用中,可以根据具体的业务需求和网络环境选择合适的解决方案。
常见的中间件和框架,它们在处理黏包和半包问题时使用了相应的解决思路:
回答略
(然后他开始讲起来他对rpc实现的思路,从注册中心,协议,序列化,连接方式,代理模式等等;跟我说这个框架不难写,但如果要实现的像dubbo和grpc那样,还是要有点水平)
然后就结束了,他说我是本科,看得出来我很热爱技术,对技术有自己的理解,在本科很少见,但是技术涉及的不是很多,也比较简单和基本;最后说要给我个建议,讲了十分钟。最后没有反问
可以看出来面试官是个真大佬,对很多东西都很熟悉,面试的时候边打哈欠边面,一边吃面包一边喝水,很自然但是很凌厉,一点不拘谨,随手在留言板上面写两句代码都能问很多东西,能从一个问题扩展到其他问题,也给了很多提示,补齐我说漏的东西;特别是最后赶时间还是跟我讲了好久的建议,是真的很感谢
略
略
操作系统的进程和线程是操作系统中的两个重要概念,它们之间有联系,同时也有区别。
联系:
区别:
总结起来,进程是资源分配和调度的基本单位,拥有独立的地址空间和系统资源;线程是进程内的执行单元,共享进程的资源,可以实现并行执行。进程和线程在操作系统中相互配合,共同完成任务的执行和资源的管理。
在Java中,进程和线程的概念与操作系统的进程和线程有一些区别,主要体现在以下几个方面:
总的来说,Java中的进程和线程是基于Java虚拟机的概念,与操作系统的进程和线程有一些差异。Java中的线程是轻量级的,创建和销毁的开销较小,共享进程的资源,并通过Java虚拟机的线程调度器实现并发执行。这种跨平台的特性使得Java在多平台开发中具有很大的优势。
在一个JVM进程中,最多能够开启的线程数量是有限的,具体数量取决于操作系统和JVM的限制。主要的限制因素包括以下几个方面:
需要注意的是,开启过多的线程并不一定能提高程序的性能,反而可能会导致线程切换的开销增加,造成性能下降。因此,在设计和实现多线程程序时,需要合理评估和控制线程的数量,避免过多的线程带来的负面影响。
创建一个线程需要消耗一些资源,包括以下几个方面:
需要注意的是,线程的创建和销毁过程相对较为耗时,因此在设计多线程程序时,需要合理评估线程的创建和销毁频率,避免频繁创建和销毁线程带来的性能开销。同时,合理控制线程的数量,避免过多的线程占用过多的内存资源。
HTTP/1.0:
HTTP/1.1:
HTTP/2.0:
总的来说,HTTP/1.0是最早的版本,HTTP/1.1引入了持久连接和管道化技术,HTTP/2.0进一步提高了性能和效率,引入了二进制分帧层和多路复用等新特性。这些改进使得HTTP在传输效率、性能和用户体验方面有了显著的提升。
HTTP/3解决了一些HTTP/2存在的问题:
总的来说,HTTP/3通过使用QUIC协议解决了HTTP/2存在的队头阻塞问题,提高了连接的建立速度和网络切换的适应性。这些改进有助于提升HTTP在移动网络环境下的性能和用户体验。
大致看了下面经,考题其实都很常规普通,建议在实习和秋招提前批不断面试总结
、看书、研究面经、和面试官请教
、查资料查漏补缺,不断熟悉面试的基本套路
和问题模式
把每次的点点滴滴都总结记载了下来,每次面前复习预热
。
比如项目实习: 我提前便研究好了可能问的问题,对可能问的问题准备了几套回答,首先说基本回答,等面试官问优化思路再把准备的第二套方案拿出来,这样,一切在自己掌控之中!
又比如:MySQL知识,你可能问某个问题,我便把相关原理和场景都给你准备好啦,就等着面试官提 MySQL 这个词,然后一套装逼就来啦!!
然后遇到面试问到的什么基础,场景
,项目实习
,优化
啥的,都会总结记录,然后针对该问题查询专栏书籍等进行总结,下次面试就是一套装逼过去!就这样,大家口中的【面试笔记】可以点击查看就诞生啦!!
可能网络上的什么八股文资料很多,无可厚非,你能把那些资料都理解,随便进大厂大概率没问题。但是个人觉得,那些资料每个知识都是一大偏文章
,然后回答方式
还特官方
(我当时开始也背过网上的资料,但是随便一个问题都官方的一大片,不适合我这种懒记性差
,面试还紧张的一个字都挤不出来的人)
然后一个知识一个pdf,我遭不住,这咋记得住。而且没针对没对策
,没重点,不知道从何背起
,也不知道面试是否真的要考,我当时就是这个心态。收集了一大堆八股资料然后就没然后啦!!
而且对于一个问题,网上的帖子资料众说纷纭,你一个问题查大半天的资料甚至都还不知道哪个是对的哪个是错的
(别问我为啥知道,我都经历过)
而【面试笔记】可以点击查看上的总结归纳,我每次面试除了灵活一点的场景设计题和一些略偏的问题,基本每次面试我都按照我总结的回答的(回答是每次面试和无数次查询实践得出的结论,我觉得都是对的也很深入,逼格足够高啦
,反正我每次面试就这样回答的)
然后也每次回答的起飞
,除开算法和难一点的场景分析,我基本没有不会的(不是说简单的回答基本,而是BAT面试官喜欢的原理实质!)
比如:你问我事务是啥?我给你扯到ACID实现原理!牵扯到的离级别实现原理、顺带说一下幻读(自己亲自实验的过程)、再谈谈日志,扯到日志原理,不就又到了binlog,然后我再给你扯扯两阶段提交,主从原理之类的,这样时间也凑够了,逼格也高。【手动狗头】