面试问题总结

免责声明

注意!本文档的回答仅供本人面试前回顾要点之用,不保证回答的完整性与正确性,若因本文档答案不正确而产生一切后果,请自行承担。

目录


数据库
计算机网络
高并发
Spring MVC
Spring
Redis
Linux
Java集合类
分布式
设计模式
排序算法
海量数据
剑指Offer

数据库

数据库三大范式是什么?


1NF 每一列属性不可再分
2NF 消除非主属性对键的部分依赖 / 非主属性完全依赖于主属性
3NF 消除非主属性对键的传递依赖

MySQL有关权限的表都有哪几个?


user权限表:记录允许连接到服务器的用户帐号信息,里面的权限是全局级的。
db权限表:记录各个帐号在各个数据库上的操作权限。
table_priv权限表:记录数据表级的操作权限。
columns_priv权限表:记录数据列级的操作权限。
host权限表:配合db权限表对给定主机上数据库级操作权限作更细致的控制。这个权限表不受GRANT和REVOKE语句的影响。

MySQL的binlog有有几种录入格式?分别有什么区别?


Statement:每一条会修改数据的SQL都会记录在binlog中。
Row:不记录sql语句,仅保存哪条记录被修改。
Mixed:一般的语句修改使用Statement,Statement无法完成主从复制的操作,则采用Row格式。

MySQL有哪些数据类型 ?


整形(BIGINT、INT、MEDIUINT、SMALLINT、TINYINT)、
浮点型(FLOAT、DOUBLE)、
日期时间(DATE、TIME、TIMESTAMP)、
字符串(CHAR、VARCHAR、LONGTEXT、MEDIUMTEXT、TEXT、TINYINT)。

MySQL存储引擎MyISAM与InnoDB区别?

InnoDB支持事务、外键、使用聚集索引、最细粒度支持行级锁。
MyISAM不支持事务、外键、使用非聚集索引、最细粒度支持表锁。

MyISAM索引与InnoDB索引的区别?

MyISAM索引文件和数据文件是分离的,索引文件仅保存记录所在页的指针(物理位置),通过这些地址来读取页,进而读取被索引的行。

存储引擎选择 ?

InnoDB需要维护MVCC以及聚簇索引所以比较慢。

索引有哪些优缺点?

加速查询、占用空间、修改时需要维护。

索引使用场景?

当数据多且字段值有相同的值得时候用普通索引。
当字段多且字段值没有重复的时候用唯一索引。
当有多个字段名都经常被查询的话用复合索引。

索引的数据结构(B/B- B+ Hash)?


Hash无法范围查询、一直需要回表、大量Hash相等。
B树非叶子结点也存数据指针、无法范围查询。
B+树只有叶子结点存数据指针,且兄弟节点之间存在指针构成双向链表,范围查询。

创建索引的原则?

对于查询频率高的字段创建索引。
索引的数目不宜太多。
尽量使用数据量少的索引。
尽量使用前缀来索引。

什么是索引覆盖?


select时where语句里的条件完全和联合索引一致覆盖索引,无需回表。

什么是最左前缀原则?

b+ 树的数据项是复合的数据结构,b+ 树是按照从左到右的顺序来建立搜索树的。

如何查看SQL语句是否走了索引?


explain + SQL语句,走了索引Extra会出现Using Index。

平衡二叉树和红黑树的区别?


AVL树是严格的平衡二叉树,必须满足所有节点的左右子树高度差不超过1,在修改时非常耗时,适合查找多的情况。
红黑树根节点是黑色的、红色节点下不能有红色节点、从根到任意一个节点经过的黑色节点数相同,维护起来比AVL树快捷。

B树和B+树的区别?

B+树中只有叶子节点会带有指向记录的指针,而B树则所有节点都带有。
B+树中兄弟节点都是通过指针连接在一起,而B树不会。

使用B树的好处?

不需要到叶子结点即可得到数据。

使用B+树的好处?

非叶子节点不会带上指向记录的指针,一个块中可以容纳更多的索引项。
可以进行范围查询。

Hash索引和B+树所有有什么区别或者说优劣呢?

Hash不能范围查询、需要回表。

什么是聚簇索引?

聚簇索引是按照索引的顺序分配内存。

非聚簇索引一定会回表查询吗?

不一定,如果查询是覆盖索引,也就是查询的字段全部命中了索引,那么就不需要回表查询。
若不是覆盖索引,需要回表检查其他属性值。

为什么需要注意联合索引中的顺序?

满足最左匹配、命中索引。

什么是数据库事务?

满足ACID的一系列操作。

事物的四大特性(ACID)介绍一下?

原子性:从结果上看事务要么全部成功要么全部失败。
一致性:事务前后不破坏数据的完整性约束。
隔离性:两个事务之间互不干扰,防止多个事务并发执行时由于交叉执行而导致数据的不一致。
持久性:事务一旦提交就会被持久化,即使故障也不应丢失。

什么是脏读?不可重复读?幻读?

脏读:事务A修改a未提交,事务B读取a,事务A回滚。
不可重复读:事务A读取a,事务B修改a,事务A再次读取a不一致。
幻读:事务A读取最后10行,事务B增加一行,事务A再次读取数据不一致。

什么是事务的隔离级别?MySQL的默认隔离级别是什么?

读未提交、读已提交、可重复读、序列化。可重复读。

如何解决幻读?

间隙锁,但是不能完全保证。使用序列化。

MVCC是什么?

MVCC多版本并发控制,维护两个列:开始版本、删除版本。

分布式事务存在的问题?解决方法有哪些?

TCC,全局事务管理。

按照锁的粒度分数据库锁有哪些?锁机制与InnoDB锁算法?

InnoDB行锁是通过索引上的索引项来实现的。
只有通过索引条件检索数据,InnoDB才会使用行级锁,否则,InnoDB将使用表锁。

InnoDB存储引擎的锁的算法有三种?

行锁、next-key锁(查询防止幻读)、间隙锁(写入防止幻读)。

什么是死锁?怎么解决?

互斥、占有且等待、不可抢占、循环等待。

数据库的乐观锁和悲观锁是什么?怎么实现的?

乐观锁MVCC,悲观锁行锁、表锁。

什么是游标?

可以把游标当作一个指针,它可以指定结果中的任何位置,然后允许用户对指定位置的数据进行处理。

MySQL的主从复制原理及流程?


前提,Master必须开启bin-log记录功能。
首先,Master开启IO线程,Slave开启IO线程和SQL线程。
Slave向Master请求bin-log某一位置后的日志内容,然后Master的IO线程传输到Slave线程的IO线程。
Slave的IO线程写到relay-log中。
Slave的SQL线程会监听relay-log是否有更新,然后解析并执行。

如何找到语句运行很慢的原因?

索引相关、未命中索引、不满足最左前缀匹配。
执行的时候,遇到锁,如表锁、行锁。

回到目录

计算机网络

OSI七层模型及每一层的协议?






物理层 Ethernet
数据链路层 ARP
网络层 IP
传输层 TCP UDP
会话层 RPC
表示层 HTML
应用层 FTP Telnet SSH

端口号在哪一层?


传输层。

DNS查询的三种方式?


本地Host、根域名递归、迭代向上。

为什么TCP连接的时候是3次?2次不可以吗?


为了防止历史链接初始化造成混乱,3次握手会将连接建立与否的决定权交由发送方,因为此时只有发送方能判断序列号是否过期,若过期第三次握手会发送RST。

为什么TCP连接的时候是3次、关闭的时候却是4次?


握手时把2、3次合并了。

TCP与UDP的区别?


面向连接、有序、可靠性。

TCP如何保证可靠性传输?


确认应答、超时重答、拥塞控制、流量控制。

什么是超时重答机制?


TCP每发送一个报文段,就会对这个报文段设置一次计时器,只要计时器设置的 重传时间到,但发送端还没有收到接收端发来的确认,此时就会重传此报文段。

说说TCP的流量控制?


接收端将自己可以接受的缓冲区大小放入TCP首部中的“窗口大小”字段,通过ACK段通知发送端;
窗口大小字段越大,说明网络的吞吐量越高
接收端一旦发现自己的缓冲区快满了就会将窗口大小设置成一个更小的值通知发送端。
发送端接收到窗口大小以后,就会减慢自己的发送速度。
如果接收缓冲区满了,就会将窗口置为0,这时发送方不再发送数据。

说说TCP的拥塞控制算法?


慢启动算法 每次乘以2
拥塞避免算法 超过慢启动门限则每次加1

为什么客户端发出第四次挥手的确认报文后要等2MSL的时间才能释放TCP连接?


主动挥手方Time-Wait,确保对面收到了ACK,否则对面会再次发送FIN。

如果已经建立了连接,但是客户端突然出现故障了怎么办?


TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

路由器和交换机的区别?



交换机主要工作在数据链路层、路由器工作在网络层。

交换机根据MAC地址转发、路由器根据IP地址转发。

路由谋短,交换求快。

HTTP的幂等性是什么?



任意多次执行对资源本身所产生的影响均与一次执行的影响相同

幂等使用的情况是第一次请求不知道结果(比如超时)或者失败的异常情况下,发起多次请求,目的是多次确认第一次请求成功,却不会因多次请求而出现多次的状态变化。

这种方式分成两个阶段:申请token阶段和支付阶段。 第一阶段,在进入到提交订单页面之前,需要订单系统根据用户信息向支付系统发起一次申请token的请求,支付系统将token保存到Redis缓存中,为第二阶段支付使用。 第二阶段,订单系统拿着申请到的token发起支付请求,支付系统会检查Redis中是否存在该token,如果存在,表示第一次发起支付请求,删除缓存中token后开始支付逻辑处理;如果缓存中不存在,表示非法请求。 实际上这里的token是一个信物,支付系统根据token确认,

HTTP 建立连接的过程?


DNS、TCP、客户端请求、Web响应、Connection:keep-alive 控制是否立即关闭、客户端解析资源渲染页面。

HTTP 的长链接?



短链接指客户端每进行一次HTTP请求就建立一次连接,任务结束连接就中断,比如访问一个网页或者其他web资源,每遇到一个web资源就要建立一次连接。

HTTP1.1之后默认使用长链接,就是说TCP连接在完成任务后继续保持连接不进行四次挥手,保活机制,2小时,75秒,10次。

HTTPS 建立连接的过程?


DNS、TCP、客户端请求、Web响应SSL证书以及公钥、客户端验证SSL、客户端生成对称加密用公钥加密给Web、Web私钥解密、用对称加密传输、Connection:keep-alive 控制是否立即关闭、客户端解析资源渲染页面。

常用HTTP状态码?


100 继续发送请求
200 成功
301 永久移动
302 临时移动
307 临时重定向
404 NotFound
500 服务器内部错误
502 BadGateWay
503 服务不可用

HTTP头包括什么?


请求头
GET /newcoder/hello.html HTTP/1.1
Accept:
Accept-Language: en-us
Connection: Keep-Alive
Host:
Referer:
User-Agent: Mozilla/4.0
Accept-Encoding: gzip, deflate
Cookie
Date: Tue, 11 Jul 2000 00:00:00 GMT

响应头
Content-Encoding: gzip
Content-Length: 80
Content-Language: zh-cn
Content-Type: text/html; charset=GB2312

GET和POST区别?


GET产生一个TCP数据包,POST产生两个TCP数据包。
对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

什么是HTTP2.0?


HTTP 2.0 的所有帧都采用二进制编码
HTTP 2.0多个请求可同时在一个连接上并行执行。通过分帧,某个请求任务耗时严重,不会影响到其它连接的正常执行。

Session、Cookie和Token的主要区别?


服务器使用session把用户的信息临时保存在了服务器上,用户离开网站后session会被销毁。这种用户信息存储方式相对cookie来说更安全,可是session有一个缺陷:如果web服务器做了负载均衡,那么下一个操作请求到了另一台服务器的时候session会丢失。
cookie由服务器生成,发送给浏览器,浏览器把cookie以kv形式保存到某个目录下的文本文件内,下一次请求同一网站时会把该cookie发送给服务器。

Servlet是线程安全的吗?


Servlet不是线程安全的。
因为Servlet是单例模式的。

Servlet生命周期?


Servlet 通过调用 init () 方法进行初始化。
Servlet 调用 service() 方法来处理客户端的请求。
Servlet 通过调用 destroy() 方法终止(结束)。
最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。

网络IO模型有哪些?


阻塞IO、非阻塞IO、多路复用IO、信号驱动IO、异步IO。

I/O多路复用中select/poll/epoll的区别?

select的三个缺点?


大量文件句柄拷贝从用户态到内核态。
轮询大量文件句柄。
限制1024个。

epoll怎么克服这三个缺点的?


epoll结构体、红黑树注册文件句柄、双向链表保存响应的文件句柄、callback注册进双向链表。
水平触发只要有内容则触发。
边缘触发只提醒一次。

回到目录

高并发

并发编程三要素是什么?



原子性:是一个或多个操作要么全部执行成功要么全部执行失败。

有序性:程序执行的顺序按照代码的先后顺序执行。(处理器可能会对指令进行重排序)

可见性:一个线程对共享变量的修改,另一个线程能够立刻看到。

在 Java 程序中怎么保证多线程的运行安全?



JDK Atomic开头的原子类、synchronized、LOCK,可以解决原子性问题。

synchronized、volatile、LOCK,可以解决可见性问题。

Happens-Before 规则可以解决有序性问题。

创建线程有哪几种方式?



继承Thread、实现runnable、实现callable、线程池。

说一下 runnable 和 callable 有什么区别?



callable可以返回执行结果、一般与FutureTask共同使用。

说说线程的生命周期及五种基本状态?



新建态、就绪态、运行态、阻塞态、终止态。

Java 中用到的线程调度算法是什么?



抢占式线程调度(Preemptive Threads-Scheduling)。

Java 中用到的线程模型是什么?



使用一对一的线程模型实现的,一条Java线程就映射到一条轻量级进程之中,交由操作系统调度。

sleep() 和 wait() 有什么区别?



sleep的类是Thread,会自动唤醒,释放锁。

wait的类是Object,必须被唤醒,不释放锁。

你是如何调用 wait() 方法的?使用 if 块还是循环?为什么?



wait() 方法应该在循环调用,因为当线程获取到 CPU 开始执行的时候,其他条件可能还没有满足,所以在处理前,循环检测条件是否满足会更好。

为什么线程通信的方法 wait(), notify()和 notifyAll()被定义在 Object 类里?



任何对象都可以作为锁,而所有类都继承于Object。

为什么 wait(), notify()和 notifyAll()必须在同步方法或者同步块中被调用?



调用wait()就是释放锁,释放锁的前提是必须要先获得锁,先获得锁才能释放锁。

Thread 类中的 yield 方法有什么作用?



它让掉当前线程 CPU 的时间片,使正在运行中的线程重新变成就绪状态,并重新竞争 CPU 的调度权。它可能会获取到,也有可能被其他线程获取到。

为什么 Thread 类的 sleep()和 yield ()方法是静态的?



Thread类的sleep()和yield()方法将在当前正在执行的线程上运行。

线程的 sleep()方法和 yield()方法有什么区别?



sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会。

yield()方法只会给相同优先级或更高优先级的线程以运行的机会。

如何在两个线程间共享数据?



同一个Runnable用内部变量。

不同封装一个类。

Java 如何实现多线程之间的通讯和协作?



锁。

synchronized 的作用?



能够保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果。

说一下 synchronized 底层实现原理?



同步代码块是通过 monitorenter 和 monitorexit 指令获取线程的执行权

同步方法通过加 ACC_SYNCHRONIZED 标识实现线程的执行权的控制

多线程中 synchronized 锁升级的原理是什么?

当一个线程进入一个对象的 synchronized 方法 A 之后,其它线程是否可进入此对象的 synchronized 方法 B?



不能,要进行同步方法必须持有他的对象锁。

synchronized 和 Lock 有什么区别?



源码级别、API方法。

Lock可知道是否获取锁。

synchronized不需要手动释放锁。

volatile 关键字的作用?



指明该变量不稳定,保证可见性。

Java 中能创建 volatile 数组吗?



能,java中可以创建volatile数组,不过只是一个指向数组的引用,而不是整个数组,如果改变引用指向的数组,将会受到volatile的保护,但是如果多个线程同时改变数组的元素,volatile 标示符就不能起到之前的保护作用了。

volatile 变量和 atomic 变量有什么不同?



volatile保证可见性,atomic保证可见性和原子性。

synchronized 和 volatile 的区别是什么?

什么是 CAS?

CAS 的会产生什么问题?如何解决?



ABA问题,引入版本。

ThreadLocal 是什么?有哪些使用场景?



线程本地变量,经典的使用场景是为每个线程分配一个 JDBC 连接 Connection。这样就可以保证每个线程的都在各自的 Connection 上进行数据库的操作,不会出现 A 线程关了 B线程正在使用的 Connection、还有 Session 管理 等问题。

ThreadLocal造成内存泄漏的原因?

线程池有哪几种创建方式?



newCachedThreadPool(),它是用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置时间超过60秒,则被终止并移除缓存;长时间闲置时,这种线程池,不会消耗什么资源。其内部使SynchronousQueue作为工作队列。

newFixedThreadPool(int nThreads),重用指定数目(nThreads)的线程,其背后使用的是无界的工作队列,任何时候最多有nThreads个工作线程是活动的。这意味着,如果任务数量超过了活动线程数目,将在工作队列中等待空闲线程出现;如果工作线程退出,将会有新的工作线程被创建,以补足指定数目nThreads。

newSingleThreadExecutor(),它的特点在于工作线程数目限制为1,操作一个无界的工作队列,所以它保证了所有的任务都是被顺序执行,最多会有一个任务处于活动状态,并且不予许使用者改动线程池实例,因此可以避免改变线程数目。

newScheduledThreadPool(int corePoolSize),创建的是个ScheduledExecutorService,可以进行定时或周期性的工作调度。

线程池参数?



corePoolSize:核心线程数,核心线程会一直存活,即使没有任务需要执行。

maxPoolSize:最大线程数。

keepAliveTime:线程空闲时间。

可重入锁如何实现可重入的?



volatile int state 计数。

Synchronized 使用对象监视器。会在对象头部有个区域,专门记录锁信息。包括持有锁的线程,锁的计数器,锁的状态这些。 线程在尝试获取对象锁时,先看看锁计数器是不是为0,为零就说明锁还在,于是获取锁,计数器变成1,并记录下持有锁的线程,当有线程再来请求同步方法时,先看看是不是当前持有锁的线程,是的,那就直接访问,锁计数器+1,如果不是,对不起,你阻塞吧。当退出同步块时,计数器-1,变成0时,释放锁。

什么是MarkWord?



存在于对象头中用于指示锁的状态、hashCode、GC年龄等信息。

在 JUC 中有哪些原子类?



AtomicInteger、AtomicLong、AtomicBoolean。

说一下原子类的原理?



volatile修饰、CAS操作。

回到目录

Spring MVC

请求处理的整个过程?



DispatcherServlet前端控制器接收发过来的请求,把URL交给HandlerMapping处理器映射器。

HandlerMapping处理器映射器,根据调用链找到相应的HandlerAdapter处理器适配器。

HandlerAdapter处理器适配器,调用Controller,返回一个ModelAndView对象。

ViewResolver视图解析器,先根据ModelAndView中设置的View解析具体视图,然后再将Model模

型中的数据渲染到View上,返回给前端。

回到目录

Spring

Spring IoC 的实现机制?



通过反射机制实例化Bean,若为单例模式则在容器创建时实例化Bean,若为多例模式则在使用时实例化Bean。

BeanFactory 和 ApplicationContext有什么区别?



BeanFactory用来懒加载创建多例模式的多个Bean实例。

ApplicationContext是BeanFactory的子接口用于创建单例模式Bean实例。

构造器依赖注入和 Setter方法注入的区别?



构造器注入采用有参构造函数进行属性设置,Setter方法注入采用Setter进行属性设置。

bean的作用域?



singleton、prototype、request、session、globalSession。

Spring框架中的单例bean是线程安全的吗?



不是,要保证线程安全要使用prototype。

bean的生命周期?

初始化Bean的过程?





解决Bean间循环依赖?





什么是AOP?如何实现?



面向切面编程,通过动态代理实现。

JDK动态代理和CGLIB动态代理的区别?



JDK动态代理是通过接口代理的,需要被代理类有实现接口。利用拦截器加上反射机制生成一个实现代理接口的匿名类, 在调用具体方法前调用InvokeHandler来处理。

CGLIB动态代理通过子类进行代理,对指定的类生成一个子类,修改其字节码,覆盖其中的方法。

回到目录

Redis

Redis为什么这么快?

Redis有哪些数据类型?底层实现?

Redis的应用场景?

Redis 的持久化机制是什么?各自的优缺点?

过期键的删除策略?

MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据?

Redis的内存淘汰策略有哪些?

Redis线程模型?



展示了文件事件处理器的四个组成部分, 它们分别是套接字、 I/O 多路复用程序、 文件事件分派器(dispatcher)、 以及事件处理器。

Redis事务的三个阶段?



MULTI、DISCARD、EXEC。

语法错误全部失效、运行时错误单个报错其他执行。

哨兵模式?

Redis集群的主从复制模型是怎样的?



基于分布式一致性算法RAFT,但是Master宕机选举使用哨兵集群。

说说Redis哈希槽的概念?



Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。

Redis集群会有写操作丢失吗?为什么?



有,若Master在接受写操作后未进行主从复制即宕机,则丢失数据。

Redis集群之间是如何复制的?



Redis可以使用主从同步,从从同步。第一次同步时,主节点做一次bgsave,并同时将后续修改操作记录到内存buffer,待完成后将rdb文件全量同步到复制节点,复制节点接受完成后将rdb镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。

Redis集群最大节点个数是多少?



在redis节点发送心跳包时需要把所有的槽放到这个心跳包里,以便让节点知道当前集群信息,16384=16k,在发送心跳包时使用char进行bitmap压缩后是2k(2 * 8 (8 bit) * 1024(1k) = 2K),也就是说使用2k的空间创建了16k的槽数。

虽然使用CRC16算法最多可以分配65535(2^16-1)个槽位,65535=65k,压缩后就是8k(8 * 8 (8 bit) * 1024(1k) = 8K),也就是说需要需要8k的心跳包,作者认为这样做不太值得;并且一般情况下一个redis集群不会有超过1000个master节点,所以16k的槽位是个比较合适的选择。

Redis实现分布式锁?

如何解决 Redis 的并发竞争 Key 问题?



消息队列、分布式锁+时间戳(时间戳是为了防止要求有序性)。

缓存雪崩、缓存穿透、缓存击穿、缓存预热、缓存降级?

如何保证缓存与数据库双写时的数据一致性?

假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如果将它们全部找出来?



使用keys指令可以扫出指定模式的key列表。

对方接着追问:如果这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?

这个时候你要回答redis关键的一个特性:redis的单线程的。keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长。

Redis回收使用的是什么算法?



引用计数器和LRU。

回到目录

Linux

进程间通信方式?哪种最快?



管道,命名管道和非命名管道。非命名管道只能用于父子进程通讯,命名管道可用于非父子进程,命名管道就是FIFO,只允许数据的单向流动。每个FIFO都有一个名字,允许不相关的进程访问同一个FIFO。

消息队列:是用于两个进程之间的通讯,首先在一个进程中创建一个消息队列,然后再往消息队列中写数据,而另一个进程则从那个消息队列中取数据。

信号量, 不能传递复杂消息,只能用来同步。

共享内存最快,只要首先创建一个共享内存区,其它进程按照一定的步骤就能访问到这个共享内存区中的数据,当然可读可写。

线程间通信方式?



锁、信号量。

Linux 上查找哪个线程cpu利用率最高?



top -H

Linux 进程调度算法?



FCFS 先来先服务 按顺序

RR 时间片轮转 时间片轮转

HPF 优先级调度 按优先级调度

多级反馈调度 RR+HPF 对同一优先级采用时间片轮转

高响应比优先 等待时间+运行时间/运行时间

Linux 线程模型?



Linux 并不存在单独的线程这样的结构,所谓线程不过是一些轻量级的进程,之所以满足线程的定义的是因为他们之间共享进程资源。

Linux buffer内存和cache内存?



两者都是RAM中的数据。简单来说,buffer是即将要被写入磁盘的,而cache是被从磁盘中读出来的。

缓存(cached)是把读取过的数据保存起来,重新读取时若命中(找到需要的数据)就不要去读硬盘了,若没有命中就读硬盘。

缓冲(buffers)是根据磁盘的读写设计的,把分散的写操作集中进行,减少磁盘碎片和硬盘的反复寻道,从而提高系统性能。

操作系统内存管理机制?



分页式、分段式、段页式。

用户态切换到内核态?



系统调用、中断、异常。

Ping命令?



使用ICMP,在TCP/IP层。

cat、chmod 、chown、cp、find 、head 、less 、ln 、locate、more 、mv、rm 、tail 、touch、vim、whereis 、which 、grep、wc、cd 、df 、du 、lsmkdir、 pwd 、rmdir 、ifconfig 、iptables 、netstat 、ping 、telnet、 date 、free 、kill 、ps 、rpm 、top 、yum 、bzip2 、gzip 、tar 、unzip?

回到目录

Java

接口和抽象类的区别?



接口需要被实现、抽象类需要被集成。

接口只能声明方法、抽象类可以实现方法。

Set接口与List有什么区别?



有序、无序。

重复、不重复。

Java集合的快速失败机制 “fail-fast”?



当modCount != expectedModCount时,就会抛出该异常。但是在一开始的时候expectedModCount初始值默认等于modCount,为什么会出现modCount != expectedModCount,很明显expectedModCount在整个迭代过程除了一开始赋予初始值modCount外,并没有再发生改变,所以可能发生改变的就只有modCount,在前面关于ArrayList扩容机制的分析中,可以知道在ArrayList进行add,remove,clear等涉及到修改集合中的元素个数的操作时,modCount就会发生改变(modCount ++),所以当另一个线程(并发修改)或者同一个线程遍历过程中,调用相关方法使集合的个数发生改变,就会使modCount发生变化,这样在checkForComodification方法中就会抛出ConcurrentModificationException异常。

类似的,hashMap中发生的原理也是一样的。

Iterator 怎么使用?有什么特点?



iterator.hasNext()

iterator.next()

iterator.remove()

如何边遍历边移除 Collection 中的元素?



iterator.remove()

如何实现数组和 List 之间的转换?



List<String> list=Arrays.asList(a);

.toArray()

ArrayList 和 LinkedList 的区别是什么?



底层数组、双向链表。

ArrayList 和 Vector 的区别是什么?



线程安全。

多线程场景下如何使用 ArrayList?



Synchronized、CopyOnWrite。

在往集合中添加数据的时候,先拷贝存储的数组,然后添加元素到拷贝好的数组中,然后用现在的数组去替换成员变量的数组,写入时加锁。

为什么 ArrayList 的 elementData 加上 transient 修饰?



transient阻止实例中那些用此关键字声明的变量持久化

elementData里面不是所有的元素都有数据,因为容量的问题,elementData里面有一些元素是空的,这种是没有必要序列化的ArrayList的序列化和反序列化依赖writeObject和readObject方法来实现

说一下 HashSet 的实现原理?



HashSet是基于HashMap实现的,Value全部为同一个final修饰的Object。

HashSet如何检查重复?HashSet是如何保证数据不可重复的?



hashCode()+equals()

BlockingQueue是什么?



当队列中没有数据的情况下,消费者端的所有线程都会被自动阻塞(挂起),直到有数据放入队列。

当队列中填满数据的情况下,生产者端的所有线程都会被自动阻塞(挂起),直到队列中有空的位置,线程被自动唤醒。

在 Queue 中 offer()/poll()/peek()和 add()/remove()/element()有什么区别?



但是如果想在一个满的队列中加入一个新元素,调用 add() 方法就会抛出一个 unchecked 异常,而调用 offer() 方法会返回 false。

但是peek()方法在队列为空时返回null,调用element()方法会抛出NoSuchElementException异常。

poll()和remove()都将移除并且返回对头,但是在poll()在队列为空时返回null,而remove()会抛出NoSuchElementException异常。

说一下 HashMap 的实现原理?



底层数组加链表、线程不安全引发环形链表、可为null、初始化长度为16、8/64扩容、6缩小。

hashCode() hash()。

HashMap在JDK1.7和JDK1.8中有哪些不同?HashMap的底层实现?



1.7 数组加单链表,1.8 8/64红黑树。

1.7 头插法,1.8 尾插法。

1.7 扩容rehash,1.8 扩容看最高位。

HashMap在jdk1.7中采用头插入法,在扩容时会改变链表中元素原本的顺序,以至于在并发场景下导致链表成环的问题。而在jdk1.8中采用尾插入法,在扩容时会保持链表元素原本的顺序,就不会出现链表成环的问题了。

HashMap头插法有什么问题?



HashMap在jdk1.7中采用头插入法,在扩容时会改变链表中元素原本的顺序,以至于在并发场景下导致链表成环的问题。而在jdk1.8中采用尾插入法,在扩容时会保持链表元素原本的顺序,就不会出现链表成环的问题了。

HashMap的put方法的具体流程?

HashMap的扩容操作是怎么实现的?

HashMap是怎么解决哈希冲突的?

HashMap在多线程情况下会发生吗什么问题?如何发生的?

HashMap为什么不直接使用hashCode()处理后的哈希值直接作为table的下标?

HashMap 的长度为什么是2的幂次方?

HashMap 和 ConcurrentHashMap 的区别?

ConcurrentHashMap 和 Hashtable 的区别?



ConcurrentHashMap:1.7 Segment锁、1.8 CAS + Synchronized。

Hashtable:表锁。

ConcurrentHashMap 底层具体实现知道吗?实现原理是什么?



1.8之前是Segment分段锁,1.8之后是Synchronized+CAS,Synchronized锁不为空Node头。

JDK1.8 对方法去做了哪些改变?



1.7 采用永久代实现方法区。

1.8 之后移除了永久代(PermGen),替换为元空间(Metaspace);
永久代中的 class metadata 转移到了 native memory(本地内存,而不是虚拟机);
永久代中的 interned Strings 和 class static variables 转移到了 Java heap;

回到目录

分布式

CAP理论?



C 一致性、A 可用性、P 分区容错性。

如果保证 G2 的一致性,那么 G1 必须在写操作时,锁定 G2 的读操作和写操作。只有数据同步后,才能重新开放读写。锁定期间,G2 不能读写,没有可用性不。

如果保证 G2 的可用性,那么势必不能锁定 G2,所以一致性不成立。

综上所述,G2 无法同时做到一致性和可用性。系统设计时只能选择一个目标。如果追求一致性,那么无法保证所有节点的可用性;如果追求所有节点的可用性,那就没法做到一致性。

缓存和数据库一致性解决方案?



延时删除Redis、更新数据库,确保最终一致性。

分布式一致性算法?



Paxos、Raft。

分布式事务如何解决?



TCC,全局事务管理。

一致性哈希是什么?



首先求出服务器(节点)的哈希值,并将其配置到圆上。

然后采用同样的方法求出存储数据的键的哈希值,并映射到相同的圆上。

然后从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上。

RPC是什么?



远程过程调用。

回到目录

必会设计模式

工厂模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//接口
public interface Terminal {
void use();
}
//实现1
public class Phone implements Terminal{
@Override
public void use() {
System.out.println("Use Phone");
}
}
//实现2
public class Computer implements Terminal {
@Override
public void use() {
System.out.println("Computer Use");
}
}
//工厂
public class Factory {
public Terminal create(String s){
if(s.equals("Phone")){
return new Phone();
}
else if(s.equals("Computer")){
return new Computer();
}
return null;
}
}

代理模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//接口
public interface Outer {
void out();
}
//实现
public class Printer implements Outer {
@Override
public void out() {
System.out.println("Print!");
}
}
//代理类
public class OuterProxy implements Outer {
private Outer outer;
public OuterProxy(Outer outer) {
this.outer = outer;
}
private void pre(){
System.out.println("pre");
}
private void after(){
System.out.println("after");
}
@Override
public void out() {
pre();
outer.out();
after();
}
}

单例模式/Double Check

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Singleton {
//静态内部不稳定变量
private volatile static Singleton singleton;
//隐藏构造函数
private Singleton(){}
//double check
public Singleton getInstance(){
if(singleton == null){
synchronized (Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}

单例模式/静态内部类

1
2
3
4
5
6
7
8
9
10
11
public class Singleton {
//隐藏构造函数
private Singleton(){}
//静态内部类
private static class InstanceHolder{
private static Singleton Instance = new Singleton();
}
public Singleton getInstance(){
return InstanceHolder.Instance;
}
}

观察者模式

1
2
3
被观察者维护一个观察者列表,提供方法attach()将观察者添加到观察者列表。
观察者拥有被观察者属性,生成时在构造函数里调用attach()注册到观察者列表。
观察者拥有update()方法,每次被观察者状态改变时在set()方法中遍历列表调用他们的update()方法。

回到目录

必会手撕排序算法

冒泡排序 平均N^2 / 最好N / 最差N^2 / 空间 1

1
2
3
4
5
6
7
8
9
10
11
private static void bubbleSort(int[] nums) {
for (int i = 0; i < nums.length; i++) {
for (int j = 0; j < nums.length - i - 1; j++) {
if (nums[j] < nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
}
}

选择排序 平均N^2 / 最好N^2 / 最差N^2 / 空间 1

1
2
3
4
5
6
7
8
9
10
11
private static void selectSort(int[] nums) {
for (int i = 0; i < nums.length; i++) {
int mini = i;
for (int j = i; j < nums.length; j++) {
if (nums[j] < nums[mini]) mini = j;
}
int temp = nums[i];
nums[i] = nums[mini];
nums[mini] = temp;
}
}

插入排序 平均N^2 / 最好N / 最差N^2 / 空间 1

1
2
3
4
5
6
7
8
9
10
11
12
13
private static void insertionSort(int[] nums) {
for (int i = 1; i < nums.length; i++) {
for (int j = i - 1; j >= 0; j--) {
if (nums[j] > nums[j + 1]) swap(nums, j, j + 1);
}
}
}

private static void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}

快速排序 平均NlogN / 最好NlogN / 最差N^2 / 空间 logN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private static void quickSort(int[] nums, int start, int end) {
if (start >= end) return;

int mid = partition(nums, start, end);
quickSort(nums, start, mid - 1);
quickSort(nums, mid + 1, end);
}

private static int partition(int[] nums, int low, int high) {
int key = nums[low];

while (low < high) {
while (low < high && key <= nums[high]) high--;//注意等号 注意--
nums[low] = nums[high];
while (low < high && key >= nums[low]) low++;
nums[high] = nums[low];
}

nums[low] = key;
return low;
}

归并排序 平均NlogN / 最好NlogN / 最差NlogN / 空间 N

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private static void mergeSort(int[] nums, int low, int high) {
if (low >= high) return;

int mid = (low + high) / 2;
mergeSort(nums, low, mid);
mergeSort(nums, mid + 1, high);
merge(nums, low, mid, high);
}

private static void merge(int[] nums, int low, int mid, int high) {
int[] temp = new int[nums.length];
int i = low, j = mid + 1, t = 0;
while (i <= mid && j <= high) {
if (nums[i] < nums[j]) {
temp[t++] = nums[i++];
} else {
temp[t++] = nums[j++];
}
}

while (i <= mid) {
temp[t++] = nums[i++];
}

while (j <= high) {
temp[t++] = nums[j++];
}
t = 0;
while (low <= high) {
nums[low++] = temp[t++];
}
}

堆排序 平均NlogN / 最好NlogN / 最差NlogN / 空间 1

回到目录

海量数据场景题

海量日志数据,提取出某日访问百度次数最多的那个IP。



IP是32位的,最多有个2^32个IP,4G内存。

Hash(IP)%1024分为1024个文件。

4G数据,2G内存如何排序?



归并排序,外部排序。

搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。



Top K算法。

在2.5亿个整数中找出不重复的整数,注,内存不足以容纳这2.5亿个整数。



2bit位图。

给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中?



按位分类,位图判断。

回到目录

必刷剑指Offer + Leetcode100