字节跳动面试题

Java

1.ThreadLocaljava

ThreadLocal实现线程本地存储的功能,同一个ThreadLocal所包含的对象,在不一样的Thread中有不一样的实例,获取ThreadLocal对象时实际上是在Thread类中的Map类型的threadLocals变量中经过ThreadLocal变量为键值进行获取。node

2.volatile的做用和原理mysql

被volatile修饰的变量保证Java内存模型中的可见性和有序性。linux

  • 可见性:当一个线程修改了一个被volatile修饰的变量的值,新值会当即被刷新到主内存中,其余线程能够当即得知新值。
  • 有序性:禁止进行指令重排序。

volaitle底层是经过内存屏障来实现可见性和有序性。内存屏障是一个CPU的指令,他的做用有两个,一是保证特定操做的执行顺序,二是保证某些变量的内存可见性。内存屏障告诉编译器和CPU,无论什么指令都不能和这条内存屏障指令重排序,另外一个做用是强制刷出各类CPU的缓存资源,所以任何CPU上的线程都能读取到这些数据的最新版本。redis

3.J.U.C中的锁算法

Java提供了两种锁机制来控制多个线程对共享资源的互斥访问。sql

  实现 公平锁 等待可中断 条件 性能
synchronized JVM 非公平 不可中断 / 大体
ReentrantLock JDK 非公平/公平 可中断 可绑定多个Condition 相同

除非要使用ReentrantLock的高级功能,不然优先使用synchronized,synchronized是JVM实现的一种锁机制,JVM原生的支持它,而ReentrantLock不是全部的JDK版本都支持。synchronized锁释放由JVM保证,ReentrantLock须要显式的释放。数据库

4.atomic包里的一些问题数组

atomic是使用volatile和CAS来实现的浏览器

5.HashMap的扩容

当HashMap的容量到达threshold时,须要进行动态扩容,将容量扩大为原来的两倍,而后将存储的数据进行rehash。

6.Semaphore信号量用来作什么?

Semaphore信号量相似于操做系统的信号量,能够控制对互斥资源的访问线程数。

7.Java内存模型

CPU和内存之间增长高速缓存。

全部的变量都存储在主内存中,每一个线程有本身的工做内存,工做内存存储在高速缓存中,保存了该线程使用变量的拷贝。

线程只能直接操做工做内存中的变量,不一样线程之间的变量值传递须要经过主内存来完成。

内存模型的三大特性:

  • 原子性:Java内存模型保证了read、load、use、assign、store、write、lock、unlock操做具备原子性
    • 实现:原子类、synchronized
  • 可见性:当一个线程修改了共享变量的值,其余线程可以当即得知这个修改。
    • Java内存模型是经过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值来实现可见性
    • 实现:volatile、synchronize、final
  • 有序性:在本线程内观察,全部操做都是有序的。在一个线程观察另外一个线程,全部操做都是无序的,无序是由于发生了指令重排序。
    • 实现:volatile、synchronized

8.Java内存空间是怎么分配的?

  • 对象优先在Eden区分配
  • 大对象直接进入老年代
  • 长期存活的对象进入老年代
  • 动态对象年龄断定
  • 空间分配担保

8.Full GC触发条件

  • System.gc()
  • 老年代空间不足
  • 空间分配担保失败

8.类加载机制

  • 加载:1.经过类文件加载二进制字节流 2.在方法区建立类的动态存储结构 3.在内存中建立class对象做为方法去的访问入口。
  • 验证:验证class文件的字节流是否符合虚拟机要求。
  • 准备:为类变量分配内存并设置初始值。
  • 解析:将常量池的符号引用替换为直接引用的过程。
  • 初始化:执行Java程序代码。

8.新生代和老年代能够转换吗?

对象优先分配在新生代的Eden区,经过长期存活(达到必定岁数)的对象进入老年代和动态对象年龄断定使对象重新生代进入老年代。

9.这些内存里面的垃圾怎么回收?
引用计数法和可达性分析法。回收算法包括:标记-清除、标记-整理、复制、分代收集算法。

10.怎么判断是垃圾?GCRoot能够为哪些?

可达性分析法中,从GC Root出发,不可达的是能够被回收的对象。

  • Java虚拟机栈局部变量表中引用对象。
  • 本地方法栈JNI中引用的对象。
  • 方法区中类静态变量引用的对象。
  • 方法去中常量引用的对象。

11.G1收集器

垃圾收集器都存在 Stop The World 的问题,G1对这个问题进行了优化,G1对整个新生代和老年代一块儿回收,把堆划分为多个大小相等的独立区域region,使得每一个region能够单独进行垃圾回收,经过记录每一个region垃圾回收时间以及回收所得到的空间(经过过去回收的经验得到),并维护一个优先列表,每次根据容许的收集时间,优先回收价值最大的region。

初始标记 -> 并发标记 -> 最终标记 -> 筛选回收

特色:

  • 空间整合:基于标记-整理和复制,不会产生内存空间碎片
  • 可预测的停顿:也能够并发执行

8.BIO、NIO、AIO

BIO,同步阻塞IO,一个线程处理一个链接,发起和处理IO请求都是同步的

NIO,同步非阻塞IO,一个线程处理多个连接,发起IO请求是非阻塞的,处理IO请求是同步的(轮询)

AIO,异步非阻塞IO,一个有效请求一个线程,发起和处理IO请求都是异步的。

9.AQS

   描述
 FutureTask  用来封装Callable的返回值
 BlockingQueue  当队列中没有元素时take()被阻塞,当队列满时put()被阻塞
 ForkJoin  大的计算任务拆分红小任务,并行计算

10.JUC

 描述 CountDownLatch countDown()会使计数器减1,当计数器为0时,调用await()的线程会被唤醒 CyclicBarrier await()会使计数器减1,当计数器为0时,全部调用await()的方法会被唤醒 Semaphore 相似于操做系统的信号量,能够控制对互斥资源的访问线程数

11.实现线程安全的方法

  • 不可变
  • synchronized和ReentrantLock
  • CAS、AtomicInteger
  • TreadLocal

12.IO与NIO

  I/O NIO
数据打包和传输方式
是否阻塞 阻塞 非阻塞

13.NIO

  • 通道(Channel):对原I/O包中的流的模拟,能够经过它读取和写入数据,流是单向的,通道是双向的,能够同时用于读、写或者同时用于读写。
  • 缓冲区:不会直接对通道进行读写数据,而是要先通过缓冲区。
  • 选择器(Selector):在Socket NIO用于IO复用。

14.Class.forName()怎么执行的?

15.守护线程是什么?守护线程是怎么退出的?

守护线程是在程序运行时提供后台服务的线程,不属于程序运行中不可或缺的部分。

当程序中全部非守护线程结束时,程序也就终止,同时杀死全部的守护线程。

16.Stack与ArrayList的区别

Stack是用Vector实现的,Queue是用ArrayList实现的,因此比较Stack与ArrayList的区别就是比较这二者之间的区别。

  • 一个先进先出,一个后进先出
  • 一个线程不安全,一个线程安全

17.HashMap的rehash过程

HashMap中使用一个技巧,和将哈希值与旧容量进行&运算,若是位上为0则在原位置,若是为1则在下边。

18.hashcode和equals的区别

equals用来判断实体在逻辑上是否相等,当重写equals方法时要重写hashcode方法。

  • 若是两个对象经过equals断定相等,则hashcode相等。
  • hashcode相等,equals不必定相等。

19.equals和==的区别?我要比较内容呢?

  • equals:用来比较逻辑上是否相等
  • ==:用来判断两个对象地址是否相同,便是否是同一个对象。

20.Java代码编译过程

词法分析 -> 语法分析 -> 语义分析 -> 字节码生成

21.如何设计hash函数

22.经常使用的线程池

23.分段锁

JVM

1.运行时数据区域

  程序计数器 JVM栈 本地方法栈 方法区 运行时常量池
功能 记录正在执行的虚拟机字节码指令的地址 栈帧用于存储局部变量表、操做数栈、常量池引用等信息 与JVM栈相似,为本地方法服务 对象分配区域,垃圾收集的主要区域 用于存访加载的类信息、常量、静态变量、即时编译器编译后的代码 方法区的一部分,存放生成的字面量和符号引用
  线程私有 线程私有 线程私有 公有 公有 公有
垃圾收集 不须要 不须要 不须要 须要(垃圾回收的主要区域)

类的卸载:1.类实例被回收2.加载类的classloader被回收3.class对象没有被引用

方法区在jdk1.8之前放在永久代中,jdk1.8之后放在本地内存中,而不是JVM内存

须要

2.垃圾收集算法

  新生代 老年代    
垃圾收集算法 复制(Eden:Survivor) 标记-清除/标记-整理    
GC Minor GC Full GC    
触发条件 Eden空间满时

1.调用System.gc()

2.老年代空间不足

3.空间分配担保失败

   
         

3.类加载过程:

  • 加载:从各类渠道获取二进制字节流转化为方法区的运行时存储结构,在内存中生成一个class对象做为访问入口。
  • 验证:确保字节流符合当前虚拟机的要求
  • 准备:为类变量分配内存并设置初始值
  • 解析:将常量池的符号引用替换为直接引用的过程
  • 初始化:虚拟机执行类构造器clinit方法的过程。<clinit>()是由编译器自动收集类中全部类变量的赋值动做和静态语句块中的语句合并产生的

4.引用类型

  描述
强引用 不会被回收
软引用 只有在内存不够的状况下才会被回收
弱引用 必定会被回收,只能存活到下一次垃圾回收发生以前
虚引用 不会对其生存时间形成影响,惟一目的是在这个对象被回收时收到一个系统通知

5.垃圾收集算法

  描述 不足
标记-清除 标记要收集的对象,而后清除 标记和清除效率都不高,形成内存碎片
标记-整理 让全部存活的对象都向一端移动,而后直接清理掉端边界之外的内存 对标记-清除算法的补充
复制 将内存划分为相等的两块,每次只使用其中的一块,当这一块内存用完了就将还存活的对象复制到另外一块上面,而后把使用过的内存空间进行一次清理 只使用了内存的一半
分代收集

他根据对象存活周期将内存划分为几块,不一样块采用适当的收集算法。

通常将堆分为新生代和老年代。

  • 新生代使用:复制算法
  • 老年代使用:标记-清除 或者 标记-整理 算法
 

6.垃圾收集器

  多线程与单线程 串行与并行 描述 适用场景
Serial收集器 单线程 串行 简单高效 Client
ParNew收集器 多线程 串行 Serial的多线程版本 Server
Parallel Scavenge收集器 多线程 串行 动态调整以提供最合适的停顿时间或者最大吞吐量 注重吞吐量以及CPU资源敏感场合
Serial Old收集器 单线程 串行 Serial的老年代版本 Client
Parallel Old收集器 多线程 串行 Parallel Scavenge的老年代版本 注重吞吐量以及CPU资源敏感场合
CMS收集器 多线程 串行/并行

吞吐量低

没法处理浮动垃圾

标记-清除致使碎片

 
G1收集器 多线程 串行/并行

空间整合:基于 标记-整理

可预测的停顿

 

7.内存分配与回收

  描述 特色 触发条件  
Minor GC 回收新生代 频繁,快 Eden区空间不够时  
Full GC 回收老年代和新生代 少,慢

1. System.gc()

2. 老年代不足(大对象、长期存活的对象进入老年代)

3. 空间分配担保失败

 

内存分配策略

  • 对象优先在Eden分配
  • 大对象直接进入老年代
  • 长期存活的对象进入老年代:年龄计数器
  • 动态对象年龄断定
  • 空间分配担保

计算机网络

1.简述TCP的三次握手、四次挥手,为何要三次握手?为何client会进入TIME_WAIT?

TCP的三次握手:

三次握手过程当中主要对序号(seq)、确认序号(ack)、标志位(ACK、SYN)进行操做。

(1)client端发送链接请求:SYN=1(创建新链接),seq=x

(2)server端接收请求并返回确认报文:SYN=1(创建新链接),ACK=1(ack有效),ack=x+1,seq=y

(3)client接收到确认报文,再次发送确认消息:ACK=1(ack有效),seq=x+1(client上一条请求seq+1),ack=y+1

(4)server端收到确认后,链接创建

TCP的四次挥手:

(1)client端发送链接释放报文:FIN=1,seq=u

(2)server收到以后发出确认,此时TCP属于半关闭状态,server能向client发送数据反之不能:ACK=1,seq=v ack=u+1

(3)当server处理完毕后,发送链接释放报文:FIN=1,ACK=1,seq=w,ack=u+1

(4)client收到后发出确认,进入TIME-WAIT状态,等来2MSL(最大报文存活时间)后释放链接:ACK=1,seq=u+1,ack=w+1

(5)server收到client的确认后释放链接

为何要进行三次握手?

第三次握手时为了防止失效的链接请求到达服务器,让服务器错误打开链接。

客户端发送的链接请求若是在网络中滞留,那么就会隔很长时间才能收到服务器的链接确认。客户端等待一个超时重传时间后,就会从新发起请求。可是这个滞留的链接请求最后仍是会到达服务器,若是不进行第三次握手,那么服务器就会打开两个链接。若是有第三次握手,客户端会忽略服务器发送的对滞留链接请求的链接确认,不进行第三次握手,所以就不会再次打开链接。

为何会有TIME_WAIT?

客户端接收到服务器的FIN报文后进入TIME_WAIT状态而不是CLOSED,还须要等待2MSL,理由:

确保最后一个确认报文可以到达。若是server端没收到client端发来的确认报文,那么就会从新发送链接释放请求报文。

为了让本链接持续时间内所产生的全部报文都从网络中消失,使得下一个新的链接不会出现旧的链接请求报文。

2.TCP的拥塞控制

慢开始:最初,发送方只能发送一个报文段(假设),当收到确认后,将拥塞窗口(cwnd)加倍,呈指数型增加

拥塞避免:设置一个慢开始门限ssthresh,当cwnd>=ssthresh,进入拥塞避免,每一个轮次只将cwnd加1

快重传:在接收方,要求每次接收到报文段都应该对最后一个已收到的有序报文段进行确认。例如已经接收到M1和M2,此时收到M4,应该发送对M2的确认。在发送方,若是收到三个重复确认,那么能够知道下一个报文段丢失,此时执行快重传,当即重传下一个报文段。

快恢复:在这种状况下,只是丢失个别报文段,不是网络拥塞,所以执行快恢复,令ssthresh=cwnd/2,cwnd=ssthresh,此时直接进入拥塞避免。

3.浏览器输入url请求服务器的过程,分析其中哪些部分用到缓存。

输入url

浏览器查找浏览器缓存

若浏览器缓存中未找到,查找本机host文件

若本机host文件中未找到,则查找路由器、ISP缓存

若路由器、ISP缓存中未找到,则向配置的DNS服务器发起请求查找(若本地域名服务器未找到,会向根域名服务器->顶级域名服务器->主域名服务器)

获取到url对应的ip后,发起TCP三次握手

发送http请求,将响应显示在浏览器页面中

四次挥手结束

4.ARP(地址解析协议)

ARP实现由IP地址获得MAC地址。

主机A知道主机B的IP地址,可是ARP高速缓存中没有该IP地址到MAC地址的映射,此时主机A经过广播的方式发送ARP请求分组,主机B收到该请求后会发送ARP响应分组给主机A告知其MAC地址,随后主机A向其高速缓存中写入主机B的IP地址到MAC地址的映射。

5.HTTP的流量控制,具体的控制算法

流量控制是为了控制发送方发送速率,保证接收方来得及接收。

接收方发送的确认报文中的窗口字段能够用来控制发送方窗口大小,从而影响发送方的发送速率。

6.计算机网络体系结构

  设备 地址 通讯方式 数据单位 协议 描述
应用层       报文 HTTP、DNS、FTP、DHCP、SMTP(邮件发送)、POP3和IMAP(邮件接收) 为特定应用程序提供数据传输服务
传输层       报文段

TCP

 

为进程提供数据传输服务
   

用户数据报

UDP

 

网络层

路由器(路由选择和分组转发)

路由协议选择:RIP/OSPF(内部)

BGP(外部)

IP地址   分组

IP协议(分类、子网划分、无分类)

NAT:将本地IP转换为全球IP

为主机提供数据传输服务

地址解析协议(ARP):由IP地址获得MAC地址

 

网际控制报文协议(ICMP):封装在IP数据包中,但不属于高层协议,是为了更有效地转发IP数据包和提升交付成功的机会。

 

网际组管理协议(IGMP)

 
数据链路层 交换机(自学习交换表:MAC地址到接口的映射) 一台主机有多少个网络适配器就有多少个MAC地址 广播信道(星型、环形、直线型) 信道复用技术

频分复用

时分复用

统计时分复用

波分复用

码分复用

为同一链路的主机提供数据传输服务
点对点信道 CSMA/CD  
物理层 集线器  

单工通讯

半双工通讯

全双工通讯

比特   在传输媒体上传输数据比特流

在向下的过程当中,须要添加下层协议所须要的首部或者尾部,而在向上的过程当中不断拆开首部和尾部。

7.路由选择协议

 

RIP

OSPF BGP
名称   开放最短路径优先 边界网关协议
使用范围 内部 内部 外部
描述 基于距离向量的路由选择协议 洪泛法 每一个自治系统必须配置BGP发言人,发言人之间经过TCP链接来交换路由信息
特色

实现简单开销小

最大距离为15,限制了网络规模

故障传播时间长

更新过程收敛快 只能寻找一条比较好的路由,而不是最佳路由

8.UDP和TCP比较

 

  UDP TCP
链接 无链接 面向链接
可靠 尽最大能力交付 可靠交付
拥塞控制
面向 面向报文 面向字节流
通讯 一对1、一对多、多对一和多对多 一对一,全双工通讯

HTTP

1.HTTP的过程

相似于浏览器输入url请求服务器的过程?

2.HTTPS怎么创建请求

HTTPS = HTTP + SSL(Secure Sockets Layer, 安全套接字层)

HTTPS 能够防窃听(非对称密钥加密)、防假装、防篡改(加密和认证)

客户端发送请求到服务器端

服务器端返回证书和公开密钥,公开密钥做为证书的一部分而存在

客户端验证证书和公开密钥的有效性,若是有效,则生成共享密钥并使用公开密钥加密发送到服务器端

服务器端使用私有密钥解密数据,并使用收到的共享密钥加密数据,发送到客户端

客户端使用共享密钥解密数据

SSL加密创建...

 3.GET和POST比较

  GET POST
做用 获取资源 传输实体
参数 查询字符串 request body
安全(不会改变服务器状态) 安全 不安全
幂等性 知足 不知足
缓存 可缓存 多数状况下不能够

 

MySQL

1.mysql的索引,最左匹配原则

索引能够加快对数据的检索。常见的有B+Tree索引,哈希索引。

最左匹配原则:

当索引是联合索引,在查询条件中,mysql是从最左边开始命中的,若是出现了范围查询(>、<、between、like),就不能进一步命中了,后续退化为线性查找,列的排列顺序决定了可命中索引的列数。

2.mysql的主从复制

mysql为了保持高可用,会采用一主多从的结构,一个master节点,多个slave节点,master节点能够进行写操做,而slave节点只能进行读操做。

binlog线程:将主服务器上的数据更改写入二进制日志中

I/O线程:从主服务器上读取二进制日志,并写入从服务器的重放日志中

SQL线程:读取重放日志并重放其中的SQL语句

3.mysql的汇集索引、非汇集索引

汇集索引:以主键建立的索引,在叶子结点上存储的是表中的数据

非汇集索引:以非主键建立的索引,叶子结点上存储的是主键和索引列

使用非汇集索引查询出数据时,拿到叶子上的主键再去查到想要查找的数据。(回表)

4.mysql联合索引,要注意什么?

联合索引即索引由多个列(a,b,c,d)组成,要注意索引的命中,最左匹配原则,从左开始命中,遇到范围查询就不能进一步匹配。

5.为何数据库要使用B+树来实现索引?

更少的查找次数(B+树相比红黑树更矮胖)

利用磁盘预读特性(一次IO能彻底载入一个节点)

6.MySQL索引

  描述 特色 使用场景
B+ Tree索引

使用B+ Tree做为底层实现

对树进行搜索,查找速度快

分为聚簇索引和非聚簇索引

查找、排序、分组
哈希索引

使用哈希做为底层实现

没法用于排序与分组

只支持精确查找,时间复杂度为O(1)

当索引值使用的频繁时,会在B+ Tree索引之上再建立一个哈希索引
全文索引 全文索引使用倒排索引实现,记录着关键词到其所在文档的映射   查找文本中的关键词
空间数据索引   从全部维度来索引数据 用于地理数据存储

索引优化:

  • 独立的列:索引列不能是表达式的一部分,也不能是函数的参数。
  • 多列索引:多个列为条件查询时,使用多列索引。
  • 索引的顺序:让选择性最强的索引放在最前面。
  • 前缀索引:对于BLOB、TEXT、VARCHAR类型的列,必须使用前缀索引,只索引开始的部分字符。
  • 覆盖索引:索引包含全部须要查询的字段的值。

索引的优势:

  • 大大减少了服务器须要扫描的行数。
  • 帮助服务器避免排序和分组。
  • 将随机I/O变为顺序I/O。

7.InnoDB和MyISAM比较

  InnoDB MyISAM
默认
隔离级别 四个隔离级别  
事务 支持 不支持
行级/表级 表级
外键 支持 不支持
备份 在线热备份  
崩溃恢复   几率高,恢复慢
特性   压缩表和空间数据索引
使用场景   读写分离的读表

8.切分

  • 水平切分:将同一个表中的记录拆分到多个结构相同的表中。
    • 切分策略:
      • 哈希取模
      • 范围:ID或者时间
      • 映射表:使用单独的一个数据库来存储映射关系
  • 垂直切分:将一个表按列切分红多个表,一般按关系紧密度或者使用频率来切分。

9.MySQL数据库是怎么插入的?

10.事务怎么回滚?里面有什么日志?

11.一百万条数据记录,如何分页显示最后一条?

设一个列从1开始自增,并设为索引,以这个列为条件进行查询。

12.数据库事务隔离级别,可重复度和可串行化实现的原理

隔离级别:读未提交、读已提交、可重复度、串行化

  • 可重复度:MVCC(多版本并发控制)
  • 串行化:MVCC + Next-Key Locks(Record Locks(锁定索引) + Gap Locks(锁定索引间的间隙))

数据库

1.数据库并发一致性问题

数据库并发一致性问题是因为隔离性致使的。

  • 丢失修改:新的修改覆盖了老的修改。
  • 读脏数据:读取其余线程rollback了的数据。
  • 不可重复读:数据的值被修改。
  • 幻影读:整条数据的插入和删除。

2.封锁

  • 封锁粒度:表级锁 行级锁
  • 封锁类型:读写锁 意向锁
  • 封锁协议:三级封锁协议 两段锁协议

3.多版本并发控制

  基于 描述
系统版本号 系统 没开始一个事务,系统版本号+1
事务版本号 事务 事务开始时的系统版本号
建立版本号 行数据 数据建立时的系统版本号
删除版本号 行数据 数据删除时的系统版本号

4.异常和数据库范式

  描述
1NF 属性不可分
2NF 每一个非主属性彻底依赖于键码
3NF 每一个非主属性不传递函数依赖于键码

5.链接

  关键字 描述
内连接 INNER JOIN 等值链接
自链接 INNER JOIN 本身链接本身
天然链接 MATURAL JOIN 全部同名列的等值链接
外链接 LEFT OUTER JOIN 保留左表没有关联的行
RIGHT OUTER JOIN 保留右表没有关联的行
OUTER JOIN 保留全部没有关联的行

数据结构

1.B+树和B树的区别

B+树的数据都在叶子结点上,而B树的非根非叶节点也是数据节点,因此B+树的查询更稳定。

B+树有两个指针,一个指向root节点,一个指向叶子节点的最左侧,所以其范围查询效率更高,而B树则须要中序遍历B树。

同阶的状况下,B+树节点中的关键字要比B树多一个,而且B+树的中间节点不保存数据,因此磁盘也可以容纳更多结点元素,所以B+树更加矮胖,查询效率也更高。

2.红黑树

红黑树是一个自平衡二叉查找树。时间复杂度O(log n)

  • 节点颜色为红或者黑
  • 根结点是黑色
  • 叶节点(NIL结点,空结点)为黑
  • 红节点的孩子为黑(路径上不能有两个连续的红节点)
  • 从根到叶子节点路径中的黑节点数相等

3.红黑树和平衡二叉树的区别

平衡二叉树和高度相关,保持平衡的代价更高(屡次旋转),所以适用于插入、删除较少,查询较多的场景。

红黑树和高度无关,旋转次数较少,所以适用于插入、删除较多的场景。

框架

1.Mybatis动态代理

2.Spring IOC是什么?怎么实现的?

3.Spring IOC里面的反射机制怎么实现的?

Redis

1.redis分片,客户端请求怎么处理?

  Redis的分片是指将数据分散到多个Redis实例中的方法,分片以后,每一个redis拥有一部分原数据集的子集。在数据量很是大时,分片能将数据量分散到若干主机的redis实例上,进而减轻单台redis实例的压力。

  • 范围分片
  • 哈希分片

分片的位置:

  • 客户端分片
  • 代理分片
  • 服务器分片

 2.redis的zset底层实现

  跳跃表来实现。

  跳跃表相比于红黑树的优势:

  • 存取速度快,节点不须要进行旋转
  • 易于实现
  • 支持无锁操做

3.redis和mysql的区别

  • redis是key-value非关系型数据库,MySQL是关系型数据库
  • redis基于内存,也可持久化,MySQL是基于磁盘
  • redis读写比MySQL快的多
  • redis通常用于热点数据的缓存,MySQL是存储

4.redis加锁

redis为单进程单线程模式,采用队列模式将并发访问变为串行访问,redis自己没有锁的概念,但能够用redis实现分布式锁。

  • INCR
Redis Incr 命令将 key 中储存的数字值增一。
若是 key 不存在,那么 key 的值会先被初始化为 0 ,而后再执行 INCR 操做。
  • SETNX
  • SET:以上两种都没有设置超时时间,SET能够实现超时时间

分布式锁的核心思想是将设置锁和超时时间、删除锁分别做为一个原子操做进行。

5.redis的淘汰策略

  • volatile-lru:在设置超时时间的数据中进行lru
  • volatile-ttl:在设置超时时间的数据中挑选即将过时
  • volatile-random:在设置超时时间的数据中随机挑选
  • allkeys-lru:全部数据的lru
  • allkeys-random:全部数据的随机
  • noeviction:禁止驱逐数据

6.redis没法被命中怎么办?会出现什么问题?

没法被命中:没法直接经过缓存获得想要的数据

解决方案:

  • 缓存尽量聚焦在高频访问且时效性不高的业务热点上。
  • 将缓存容量设置为热点数据的容量。
  • 缓存预加载。
  • 设置合适的缓存淘汰策略。

7.Redis和MySQL复制和分片

  复制 分片
MySQL 三个线程(binlog线程、I/O线程、SQL线程),目的是实现读写分离 水平切分、垂直切分
Redis 使用RDB快照进行复制,发送期间使用缓冲区记录执行的写命令,在RDB快照发送完毕后,发送缓冲区中的写命令 水平切分

 8.Redis是什么?Sorted List是什么?skiplist是什么?怎么实现的?怎么插入一个值?怎么进行查询?和其余数据结构进行对比?

9.Redis的hash和Java的map的区别

消息队列

JVM

1.四种引用类型

  • 强引用:如用new关键字建立,不会进行回收。
  • 软引用:在内存不足的状况下会进行回收。
  • 弱引用:只能存活到下一次垃圾回收。
  • 虚引用:不影响其生存周期,只是在回收的时候收到一个系统通知。

2.可达性分析算法的root

可达性分析算法是从GC root出发,只有经过GC root可达的对象是被引用的对象,不可达的对象属于能够回收的对象。

操做系统

1.进程和线程的区别

  • 拥有资源:进程是资源分配的基本单位,可是线程不拥有资源,线程能够访问隶属进程的资源。
  • 调度:线程是独立调度的基本单位,在同一进程中,线程的切换不会引发进程切换,从一个进程中的线程切换到另外一个进程中的线程,会引发进程切换。
  • 系统开销:建立、撤销或切换进程,系统都要为之分配、回收资源或保存环境,开销远比线程大。
  • 通讯方面:线程间能够经过直接读取统一进程中的数据进行通讯,可是进程通讯须要借助IPC。

主线程是什么?

2.操做系统的内存管理

  • 分页地址映射:分页是一种动态重定位技术,经过页表将逻辑地址映射为物理地址。
  • 段式存储:分页有一个不可避免的问题就是用户视角的内存和实际物理内存的分离,分段则是将逻辑地址空间分红一组段,每一个段长度能够不一样,而且能够动态增加,由段表来维护。
  • 段页式存储:段内分页。

3.分页式的页表放在哪

进程控制块(PCB)中。

4.进程的PCB里还有哪些东西?

  • 进程状态
  • 程序计数器
  • CPU寄存器
  • CPU调度信息
  • 内存管理信息
  • 记帐信息
  • I/O状态信息

5.MMU(内存管理单元)

内存管理单元(MMU)管理着地址空间和物理内存的转换,根据其内存管理方式的不一样,其中包括基地址寄存器、界限地址寄存器的值以及段表和页表。

6.进程通讯

  • 管道(父子进程间通讯)
  • 命名管道FIFO(去除管道只能在父子进程间进行通讯,经常使用于客户-服务器应用程序中)
  • 信号量
  • 消息队列
  • 共享内存(生产者消费者的缓冲池)
  • 套接字(可用于不一样机器间的进程通讯)

7.共享内存

采用共享内存的进程间通讯须要通讯进程创建共享内存区域。一般一块共享内存区域驻留在生成共享内存段的进程的地址空间。须要使用信号量用来同步对通向存储的访问。

8.Inode

9.应用程序是如何读取文件的?

LINUX

1.linux脚本,杀掉包含一个关键字的全部进程

ps -ef | grep 关键字 | awk '{print $2}' | xargs kill -9

2.自旋锁和互斥锁

都属于linux内核中的内核锁。

互斥锁经过对共享资源的锁定和互斥解决利用资源冲突问题,互斥锁是选择睡眠的方式来对共享工做中止访问的。

自旋锁不会引发调度者睡眠,而是一直循环。

Socket

1.linux I/O模型,说说select和epoll的区别

  • Socket等待数据到达
  • 复制到内核缓冲区中
  • 从内核缓冲区复制到应用进程缓冲区中
  描述 特色
阻塞式I/O 应用进程被阻塞,知道数据从内核缓冲区复制到应用进程缓冲区才返回 阻塞期间,其余进程继续执行,CPU利用率高
非阻塞式I/O 轮询I/O是否完成 屡次执行系统调用,CPU利用率低
I/O复用 select poll epoll 单个线程具备处理多个I/O时间的能力
信号驱动I/O 执行系统调用后当即返回,内核在数据到达时向应用进程发送SIGIO信号,应用进程收到后将数据从内核复制到应用进程 CPU利用率比非阻塞I/O高
异步I/O 系统调用当即返回,不会阻塞,内核完成全部操做后向应用进程发送信号

异步I/O通知应用进程I/O完成

信号驱动I/O是通知应用进程能够开始I/O

 

  select poll epoll
timeout精度 1ns 1ms 1ms
描述符数据结构 数组(所以有最大限制) 链表 链表
复制描述符 每次调用都须要复制 每次调用都须要复制 第一次复制、修改
返回结果 不返回准备好的描述符 不返回准备好的描述符 准备好的描述符加入到一个链表中管理
支持 几乎全部系统 较新的系统 Linux系统
适用场景 实时性高,全部平台 实时性低,描述符适中 描述符多,描述符变化小

select和epoll的区别

  • select、poll每次调用都要把fd集合从用户态往内核态拷贝一次,而且要把当前进程往设备等待队列中挂一次,而epoll只要一次拷贝,并且把当前进程往等待队列上挂也只挂一次(在epoll_waitd的开始,注意这里的等待队列并非设备等待队列,只是一个epoll内部定义的等待队列)。
  • select、poll实现须要本身不断轮询全部fd集合,直到设备就绪,期间可能要睡眠和唤醒屡次交替。而epoll是在设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进行睡眠的进程。select和poll要遍历整个fd集合,epoll只要判断一下就绪链表是否为空就好了。

2.多路复用模型

分布式

1.分布式事务

CAP定理:

  • 一致性(Consistency):多个数据副本是否能保持一致的特性。
  • 可用性(Availability):分布式系统在面对各类异常时能够提供正常服务的能力。
  • 分区容忍性(Partition tolerance):分布式系统在遇到任何网络分区故障的时候,仍然须要能对外提供一致性和可用性的服务。

在分布式系统中,分区容忍性必不可少,由于须要老是假设网络是不可靠的。所以,CAP理论其实是要在可用性和一致性作权衡。

BASE:

BASE是基本可用(Basically Available)、软状态(Soft State)和最终一致性(Eventually Consistent)三个短语的缩写。

BASE理论是对CAP中一致性和可用性权衡的结果,它的核心思想是:即便没法作到强一致性,但每一个应用均可以根据自身业务特色,采用适当的方式来使系统达到最终一致性。

ACID要求强一致性,一般运用在传统的数据库系统上。而BASE要求最终一致性,经过牺牲强一致性来达到可用性,一般运用于大型分布式系统中。

解决方案:

(1)两阶段提交(2PC)

基于XA协议实现

存在问题:1.同步阻塞 2.单点问题 3.数据不一致 4.太过保守

(2)TCC

针对每一个操做,都要注册一个与其对应的确认和补偿操做

Try/Confirm/Cancel

(3)本地消息表(异步确保)

将业务操做和本地消息表放在一个事务中。业界使用最多。

(4)Sagas事务模型

事件溯源,至关于将分布式系统中的操做都记录到数据库日志表中,要得到最新的状态,则须要从新执行一遍日志表的操做。而且能够dump某一时刻的数据表,在此基础上执行在这以后的操做。

手写代码

1.二叉树的先序遍历,层序遍历的实现

private static class Node{
    int value;
    Node left;
    Node right;

    public Node(int value) {
        this.value = value;
    }
}

/**
 * 先序遍历非递归版
 * @param root
 */
public void preOrder1(Node root){
    if(root == null) return;
    Stack<Node> stack = new Stack<>();
    stack.push(root);
    while(!stack.isEmpty()){
        Node node = stack.pop();
        System.out.println(node.value);
        if(node.right != null) stack.push(node.right);
        if(node.left != null) stack.push(node.left);
    }
}

/**
 * 先序遍历非递归版回溯法
 * @param root
 */
public void preOrderBack(Node root){
    if(root == null) return;
    Stack<Node> stack = new Stack<>();
    while(root != null || !stack.isEmpty()){
        if(root != null){
            System.out.println(root.value);
            stack.push(root);
            root = root.left;
        }else{
            Node upNode = stack.pop();
            root = upNode.right;
        }
    }
}
  public void preOrderTrans(Node root){
        if(root == null) return;
        System.out.println(root.value);
        preOrderTrans(root.left);
        preOrderTrans(root.right);
    }

    public void bfs(Node root){
        Queue<Node> queue = new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()){
            Node top = queue.remove();
            System.out.println(top.value);
            if(top.left != null) queue.add(top.left);
            if(top.right != null) queue.add(top.right);
        }
    }
View Code

2.用栈实现队列

import java.util.Stack;

public class Queue {
    private Stack<Integer> stack1 = new Stack<>();
    private Stack<Integer> stack2 = new Stack<>();

    public void push(Integer value){
        if(value == null){
            throw new RuntimeException("value is null");
        }
        stack1.push(value);
    }
    public Integer pop(){
        if(stack1.size() == 0){
            return null;
        }
        while(!stack1.empty()){
            stack2.push(stack1.pop());
        }
        Integer value = stack2.pop();
        while(!stack2.empty()){
            stack1.push(stack2.pop());
        }
        return value;
    }

    public static void main(String[] args) {
        Queue queue = new Queue();
        queue.push(1);
        queue.push(3);
        queue.push(5);
        queue.pop();
        queue.pop();
        queue.pop();
    }
}
View Code

3.包括max函数的栈

import java.util.Stack;

public class StackMax {

    private Stack<Integer> stack = new Stack<>();
    private Stack<Integer> s = new Stack<>();

    public void push(Integer value) {
        stack.push(value);
        if (s.size() == 0 || value >= s.peek()) {
            s.push(value);
        }
    }

    public Integer pop() {
        Integer value = stack.pop();
        if(value.equals(s.peek())){
            s.pop();
        }
        return value;
    }

    public Integer max() {
        return s.peek();
    }

    public static void main(String[] args) {
        StackMax stackMax = new StackMax();
        stackMax.push(1);
        System.out.println(stackMax.max());
        stackMax.push(3);
        System.out.println(stackMax.max());
        stackMax.push(4);
        System.out.println(stackMax.max());
        stackMax.push(2);
        System.out.println(stackMax.max());
        stackMax.pop();
        System.out.println(stackMax.max());
        stackMax.pop();
        System.out.println(stackMax.max());
        stackMax.pop();
        System.out.println(stackMax.max());
    }
}
View Code

4.找一个n*n矩阵的最长上升序列

5.快速排序,何时复杂度最大

  public void quickSort(int[] num, int st, int ed) {
        if (st >= ed) return;
        int left = st;
        int right = ed;
        int value = num[left];
        while (left < right) {
            while(left < right && num[right] >= value){
                right--;
            }
            num[left] = num[right];
            while(left < right && num[left] < value){
                left++;
            }
            num[right] = num[left];
        }
        int mid = left;
        num[mid] = value;
        quickSort(num, st, mid - 1);
        quickSort(num, mid + 1, ed);
    }

    public static void main(String[] args) {
        QuickSort quickSort = new QuickSort();
        int[] num = {3, 7, 4, 2, 5, 8, 1};
        quickSort.quickSort(num, 0, 6);
        for (int t : num) {
            System.out.println(t);
        }
    }
View Code

6.归并排序

import java.util.Arrays;

public class MergeSort {

    public int[] mergeSort(int[] num) {
        if (num.length <= 1) return num;
        int mid = num.length / 2;
        int[] left = Arrays.copyOfRange(num, 0, mid);
        int[] right = Arrays.copyOfRange(num, mid, num.length);
        return mergeArrays(mergeSort(left), mergeSort(right));
    }

    private int[] mergeArrays(int[] mergeSort1, int[] mergeSort2) {
        int[] result = new int[mergeSort1.length + mergeSort2.length];
        int i = 0, j = 0, k = 0;
        while (i < mergeSort1.length && j < mergeSort2.length) {
            if (mergeSort1[i] < mergeSort2[j]) {
                result[k++] = mergeSort1[i++];
            } else {
                result[k++] = mergeSort2[j++];
            }
        }
        while (i < mergeSort1.length) {
            result[k++] = mergeSort1[i++];
        }
        while (j < mergeSort2.length) {
            result[k++] = mergeSort2[j++];
        }
        return result;
    }

    public static void main(String[] args) {
        MergeSort mergeSort = new MergeSort();
        int[] num = {3, 7, 4, 2, 5, 8, 1};
        num = mergeSort.mergeSort(num);
        for (int t : num) {
            System.out.println(t);
        }
    }
}
View Code

7.手写一个LRU

8.给你一个数组,数组长度为n。请找出数组中第k大的数

public class Solution {
    public int findK(int[] num, int k) {
        return quickSort(num, 0, num.length - 1, k - 1);
    }

    public int quickSort(int[] num, int st, int ed, int k) {
        if (st >= ed) return num[st];
        int value = num[st];
        int left = st;
        int right = ed;
        while (left < right) {
            while (left < right && num[right] >= value) {
                right--;
            }
            num[left] = num[right];
            while (left < right && num[left] < value) {
                left++;
            }
            num[right] = num[left];
        }
        num[left] = value;
        if (left == k) return value;
        else if (left < k) {
            return quickSort(num, left + 1, ed, k);
        } else {
            return quickSort(num, st, left, k);
        }
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        int[] num = {1,8,8,7,4,1,5,1,5,7};
        System.out.println(solution.findK(num, 1));
        System.out.println(solution.findK(num, 2));
        System.out.println(solution.findK(num, 3));
        System.out.println(solution.findK(num, 4));
        System.out.println(solution.findK(num, 5));
        System.out.println(solution.findK(num, 6));
        System.out.println(solution.findK(num, 7));
        System.out.println(solution.findK(num, 8));
        System.out.println(solution.findK(num, 9));
        System.out.println(solution.findK(num, 10));
    }
}
View Code

附加条件:不容许改变元素在数组中的位置

在int范围内去中位数,算出其在数组中是第几大的元素(遍历数组O(n)),与k比较不断二分。

 9.找到数据流中的中位数

使用大小顶堆,若是放入的是奇数个,则取大顶堆的根结点,若是是偶数个则取大小顶堆根结点的平均值。

  • 若是是奇数,放入小顶堆,而后取根结点加入大顶堆。
  • 若是是偶数,放入大顶堆,而后取根结点加入小顶堆。

10.删除链表中重复节点

HashSet

11.给定一个排序链表,删除全部重复的元素,使得每一个元素只出现一次。

12.给定过一个二叉树,原地将它展开为链表

13.给定一个二叉树,想象本身站在他的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

11.判断是不是二叉搜索树

中序遍历

12.合并两个链表,用递归和非递归实现

13.字符串是否为给定字符串的子串

14.查找两个链表的公共节点

15.小顶堆

16.一个数x,一个数n,x中删n位使得剩下的数最大

17.给定一颗二叉树,求其中root的最长路径。所谓路径是指,联通两个节点的最小边数。

18.二叉树的序列化与反序列化

相关文章
相关标签/搜索