• Java高频面试题


    网络相关

    1.http1.0和http1.1的区别?

            http1.0每次请求都需要和服务器建立一个TCP连接,服务器完成请求后立即断开TCP连接。

      http1.1支持长连接,客户端在http请求头上加上Connection=Keep-Alive时,告诉服务器处理完本次请求后保持连接。Connection=close时告诉服务器返回本次请求结果后关闭连接。

      http2.0:只用于https://网址,HTTP2.0大幅度的提高了web性能,在HTTP1.1完全语意兼容的基础上,进一步减少了网络的延迟。实现低延迟高吞吐量  

      与HTTP 1.1相比,主要区别包括
        HTTP/2采用二进制格式而非文本格式
        HTTP/2是完全多路复用的,而非有序并阻塞的——只需一个连接即可实现并行
        使用报头压缩,HTTP/2降低了开销
        HTTP/2让服务器可以将响应主动“推送”到客户端缓存中

    2.http和https的区别?

      1.http数据传输是明文的,https是在http的技术上增加了SSL协议使数据传输加密

      2.http默认端口是80,https是443

      3.http是无状态的,https ssl基于证书验证服务器身份

    3.TCP三次握手和四次握手的流程,为什么断开连接是四次,如果握手只有两次会出现什么?

      三次握手简洁版:

      1.客户端发送syn(同步确认包)包到服务器,等待服务器确认

      2.服务器收到syn包,必须确认sync包然后发送syn+ack报文给客户端

      3.客户端收到服务端的syn+ack,向服务器发送ack包,建立连接

      三次握手详细版:

      1、A机器发出一个数据包并将SYN置为1,seq=x,表示希望建立连接

      2、B机器收到A机器发过来的数据包后,发送一个响应包并将SYN和ACK都置为1,并将seq置为y,确认序列号ack=x+1

      3、A机器收到B的响应后需进行确认,确认包中将ACK置为1,并将确认序列号置为y+1,

     为什么需要三次握手?

      主要有两个目的,信息对等和防止超时

      信息对等:在第三次握手时,B机器才能确认自己的发报能力和对方的收报能力是正常的。

      防止超时导致脏连接:TTL网络报文的生存时间往往都会超过TCP请求超时时间,如果两次握手就可以建立连接,传输数据并释放连接后,第一个超时的连接请求才到达B机器的话

        B机器会以为是A创建新的连接,然后确认同意创建连接。因为A机器的状态不是SYN_SENT,所以直接丢弃了B的确认,就导致了B建立了脏连接。

      四次挥手:

      1.客户端发送一个FIN报文请求关闭连接

      2.服务器收到这个FIN,然后发送一个ACK确认收到

      3.服务器在数据传输完成之后,发送一个FIN给客户端

      4.客户端收到FIN,发送ACK确认

      为什么是四次:服务器收到客户端的FIN报文时,仅仅表示客户端没有数据再发送给服务端了,但是服务端可能还有数据没有完整发送给了客户端,所以不会马上关闭socket连接,等数据发送完毕后再发送FIN报文给客户端,

      告诉客户端同意关闭连接。

      如果握手只有两次,可能出现因为网络问题客户端没有收到服务端的syn+ack包,导致连接没有建立成功。

      

    三次握手:
      A:“喂,你听得到吗?”A->SYN_SEND

      B:“我听得到呀,你听得到我吗?”应答与请求同时发出 B->SYN_RCVD | A->ESTABLISHED

      A:“我能听到你,今天balabala……”B->ESTABLISHED

    四次挥手:
      A:“喂,我不说了。”A->FIN_WAIT1

      B:“我知道了。等下,上一句还没说完。Balabala…..”B->CLOSE_WAIT | A->FIN_WAIT2

      B:”好了,说完了,我也不说了。”B->LAST_ACK

      A:”我知道了。”A->TIME_WAIT | B->CLOSED

      A等待2MSL,保证B收到了消息,否则重说一次”我知道了”,A->CLOSED

    4.TCP/IP如何保证可靠性,数据包有哪些数据组成?

      1.校验和

      发送的数据包的二进制相加然后取反,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段。

      2.确认应答+序列号

      TCP给发送的每一个包进行编号,接收方对数据包进行排序,把有序数据传送给应用层。

      3.超时重传

      当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。

      4.流量控制

      TCP连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP使用的流量控制协议是可变大小

      的滑动窗口协议。

      接收方有即时窗口(滑动窗口),随ACK报文发送

      5.拥塞控制

      当网络拥塞时,减少数据的发送。

      发送方有拥塞窗口,发送数据前比对接收方发过来的即使窗口,取小

      慢启动、拥塞避免、拥塞发送、快速恢复

    5.http get请求和post请求的区别?以及数据包格式?

      1.get请求是向服务端请求数据,post是向服务端提交数据

      2.get请求参数拼装在url上只支持url编码,post请求参数在请求报文中

      3.get请求发送一个数据包,post发送两个数据包

    6.长连接和短连接?

      1.1.0客户端向服务端发送请求建立连接,请求结束就关闭连接,1.1支持设置connection:keep-alive,还有超时时间建立长连接,下次请求继续用当前连接,不用新建连接

      

    7.TIME_WAIT和CLOSE_WAIT?

      TIME_WAIT:主动要求关闭的机器表示收到了对方的FIN报文,并发送出了ACK报文,进入TIME_WAIT状态,等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。

      CLOSE_WAIT:被动要求关闭的机器收到对方请求关闭连接的FIN报文,在第一次ACK应答后,马上进入CLOSE_WAIT状态。这种状态其实表示在等待关闭,并且通知应用程序发送剩余数据,处理现场信息,关闭相关资源。

      如果服务器上大量出现这两种状态,就会加重机器负载,也会影响有效连接的创建,因此需要针对性的进行调优处理。

    在TIME_WAIT等待的2MSL是报文在网络上生存的最长时间,超过阀值便将报文丢弃。在RFC793规定MSL为2分钟,为什么需要TIME_WAIT呢?

      1、确认被动关闭方能够顺利进入CLOSED状态,如果最后一个ACK因为网络原因没有到达B机器,那么B机器就会认为A机器没有收到自己的ACK+FIN报文,所以会重发,A机器再次收到B机器的ACK+FIN报文会再次发送ACK报文,如果没有TIME_WAIT,A机器发送ACK后直接CLOSE,可能导致B机器收不到A的ACK报文一直等待。

       2、防止失效请求。

    8.说出你知道的几种http响应码?

      200 ok

      302重定向

      404没找到请求资源

      406请求无法接受

      500服务器内部错误

      502 网关错误

    缓存相关

    1.redis持久化的几种方式,优缺点是什么,怎么实现的?

      RDB和AOF

      RDB:一次全量备份

      AOF:收集指令,指令重放

    2.redis的缓存失效策略?

      定期删除+惰性删除

      1.定期删除,每100ms起一个线程扫描全部的key,发现过期,删除

      2.查询到这个key的时候先判断有没有设置有效期有没有过期,过期的话删除。

    3.缓存穿透和缓存雪崩的解决办法?

      1.缓存穿透:指查询一个数据库中一定不存在的数据。比如传入商品编号去查询数据库,数据库中不存在。解决方案是查询到的即使为空的对象也设置缓存,有效期设置短一点,比如60秒。

        使用布隆过滤器,将所有可能存在数据的id放在缓存中,查询前先去布隆过滤器中查询是否有记录。

      2.缓存雪崩:在某个时间段,缓存集中过期失效。解决方案:

        1.使用互斥锁,在缓存失效后通过加锁或者队列来控制数据库写缓存的 线程数量。使用mutex, 在缓存失效的时候,不是去立即查询数据库而是用SETNX设置一个mutex key,当返回key设置成功后再去查询数据库并回设缓存,设置失败可能是其他线程已经查询数据库重设了缓存就重试get缓存的方法

        2.数据预热,通过缓存reload机制,预先去更新缓存,在即将大并发访问前手动触发加载缓存不同的key,再设置不同的过期时间,让缓存失效的时间点尽量均匀

        3.做二级缓存:a1为原始缓存,a2为备份缓存,a1失效时可以访问a2,a1缓存的有效时间设置为短期,a2为长期

        4.缓存永远不过期:1.不设置过期时间  2.把过期时间存在key的value中,发现要过期了,通过后台线程进行缓存的更新。

      3.缓存击穿:key是个热点数据,扛着高并发请求集中对这个key进行访问,在这个key失效的瞬间,持续的高并发就穿破缓存直接请求数据库,解决方案:

        1.使用互斥锁(mutex key)
        业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,

        而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,

        当操作返回成功时再进行load db的操作并回设缓存;否则,设置失败可能是其他线程已经查询数据库重设了缓存, 就重试整个get

        缓存的方法。
        2.缓存永远不过期:1.不设置过期时间  2.把过期时间存在key的value中,发现要过期了,通过后台线程进行缓存的更新

    4.redis集群,高可用,原理?

    5.mysql里有20万条数据,如何保证redis中的数据都是热点数据?

      使用allkeys-lru淘汰策略

    6.redis的数据淘汰策略?

      1.allkeys-lru:删除最近最少使用的一部分key(LRU less recentley used )

      2.allkeys-random:随机删除一部分key

      3.volatile-lru:在设置了过期时间的key中,删除最近最少使用的一部分key

      4.volatile-random:在设置了过期时间的key中,随机删除一部分key

      5.noeviction:不删除策略,达到最大内存直接返回错误信息

      6.volatile-ttl:在设置了过期时间的key中,优先删除剩余时间(time to live)短的key

    7.Redis的并发竞争问题如何解决?了解redis事务的CAS操作吗?

       Redis的并发竞争问题主要发生在并发写竞争,如两个连接同时对一个数据进行修改,需要先取出现有的数据值,然后修改后,再更新到Redis中。由于取出和修改保存是两个操作,不是原子操作。可能会出现修改覆盖的问题,解决方案:

      1、如果是自增,使用Redis的incr命令

      2、使用互斥锁的方式(实现相对复杂)

      3、使用乐观锁(实现简单,性能高) watch

      4、使用redis的setnx实现内置的锁

      5、在客户端中进行同步顺序控制,如Lock或Syncronized

    数据库相关

    1.MySql InnDB存储的文件结构?

    MySql使用使用文件系统的目录和文件来保存数据库和表的定义。innodb的数据存储在表空间中,表空间是由innodb管理的一个黑盒子,由一系列的数据文件组成。

    2.索引树是如何维护的?

      每个索引都是一个B+树,索引分为一级索引和二级索引。InnoDB使用主键聚集索引,主键索引作为一级索引,叶子节点存放所有的数据值。其他索引都是二级索引,

        叶子节点存放的是主键值

    3.数据库自增主键可能的问题?

      并发量大导致的性能问题

    4.MySql的几种优化?

      建表时

    5.MySql为什么使用B+树?

      1.B+树的每个叶子节点都包含指向下一个节点的指针,从而方便叶子节点的范围遍历

      2.B+树对索引列是顺序组织存储的,所以很适合查找范围数据

      3.B+树的节点只保存键值数据不保存对应的数据记录,因此节点可以有更多的子节点,B+树的索引的高度大大降低,磁盘IO次数最多只需要两次,提升了性能。

      4.B+树的叶子节点存放了数据记录信息

    6.数据库锁表的相关处理?

    7.索引失效的场景?

      1.查询中的列不是独立的,则mysql就不会使用索引。如对列使用了表达式和函数

      2.联合索引不符合最左查询原则

      3.

    8.高并发下如何做到安全的修改同一行数据,乐观锁和悲观锁是什么?InnDB的行级锁有哪两种,解释其含义?

      通过行锁来实现,inndb默认行锁。

      乐观锁:持乐观态度,读取数据认为不会用其他线程修改本条数据,因此不加锁。修改数据通过版本号或者cvs来控制。

      悲观锁:持悲观态度,读和写都加锁。

    9.数据库会死锁吗,举一个例子,mysql如何解决死锁?

      会死锁,如:两个事物同时修改同样的两行数据,只是修改顺序相反,可能出现都执行了第一条update语句,同时也锁定了该行数据,接着每个事物都去尝试执行第二条update语句,发现已被对方锁定,则出现了死锁。

    解决方案:数据库系统实现了各种死锁检测和死锁超时机制,如inndb检测到死锁依赖,并立即返回一个错误。inndb目前解决方案是,将持有最少行级排它锁的事物进行回滚。

     10.为什么select *会影响性能

      1.首先数据库要知道*是什么,先查询数据字典(记录数据库和应用程序元数据的目录),增大开销

      2.多出一些不用的列,可能不在索引的覆盖范围,同时增加传输时间

      3.影响数据库自动重写优化sql

     11.索引的优点,即索引为什么能加快查询

      1.索引大大减少了服务器需要扫描的数据量

      2.索引可以帮助服务器避免排序和临时表

      3.索引可以讲随机io变为顺序io

    分布式相关

    1.接口的幂等性概念?

      一个接口对其连续请求多次返回的结果都相同

    2.消息中间件如何解决消息丢失的问题?

    3.对分布式事务的理解?

      一个原子性的事务操作涉及到多个系统并分布在不同的机器上对应不同的数据库,TCC、最终一致性

    4.如何实现负载均衡,有哪些算法可以实现?

      https://i.cnblogs.com/EditPosts.aspx?postid=10578883

    5.zookeeper的用途,选举的原理是什么?选举的时候能提供服务吗?

      用途:数据发布订阅、集群管理、分布式锁、Master选举、分布式协调通知、负载均衡、命名服务

      选举的原理:超半数机器达成一致即可选举出leader,当leader无法通信,则follower将状态修改为LOOKING,进入选举状态

        1、每台机器将自己的(sid,zxid)作为投票信息发送给集群中其他所有机器

        2、收到其他机器发来的投票(vsid,vzxid)后,与依次自己的投票信息相比较,如果vzxid>zxid或者vzxid=zxid且vsid>sid,那么修改自己的投票为(vsid,vzxid),否则保持自己的投票信息不变,再一次将投票信息发送给集群中的其他机器

        3、过半数的机器的投票信息相同,那么投票信息中的sid机器称为leader

      选举的时候没有leader,因此不能对外提供服务  

    6.zookeeper的原理和适用场景?

      注册中心

    7.zookeeper的watch机制?

      1、客户端可以向节点注册Watcher监听,当节点发生了变更,会触发通知回调客户端告知通知的变更

      2、Watcher监听具有一次性,触发一次通知之后,客户端想要继续监听节点变更,需再次注册监听

    8.redis/zk节点宕机如何处理?

      Redis哨兵模式,主从切换,AP保证可用性

      zk,若是leader,则进行选举,若是follower,重启即可,然后加入到zk集群中,重新作为follower并和leader进行数据同步

    9.分布式集群下如何做到唯一序列号?

      ZK创建顺序节点会自动在其子节点上添加一个序号并返回节点的名称,节点名加上父节点等前缀作为唯一的ID

    10.如何做一个分布式锁?

      1、Redis分布式锁

      2、ZK分布式锁

    11.用过哪些MQ,怎么用的?和其他MQ比有什么优缺点,MQ的连接是线程安全的吗?

      rabbitMQ,kafka

    12.MQ系统的数据如何保证不丢失?

    RabbitMQ有传输确认和接收确认机制以及消息持久化

      传输确认:将信道改成确认模式,在该信道上发布的所有消息都会分配一个唯一的ID,一旦消息被成功的投递到所有匹配的队列中,

        该信道就会向生产者发送确认消息,在确认消息中包含了之前的唯一ID,从而让生产者知道消息已发送到目的队列。且是异步

      消息者回执:Broker将消息发送给消息者后不会立即将此消息删除,而是等待消费者回执的确认消息后才会删除。消费者收到消息并处理

        完成后需要向Broker显示发送ACK指令,如果Broker没有收到ACK消息,那么Broker就会把消息发送给其他消费者。

    Kafka

      消息发送确认:有三种方式,一般收到leader的确认消息就认为是成功的

      消息失败重发:遇到可恢复错误时,生产者会向Broker重发消息

      消息消费提交:成功处理后异步方式提交

      

       

    如何处理消息不被重复消费呢?

      首先要知道消息为什么会被重复消费,大多是由于网络不通导致,消费者的确认消息没有传送到消息队列,导致消息队列不知道消息已经被消费了,再次

    将该消息分发给其他消费者。所以解决的思路有下面几种:

      1):如果消息是做数据库的插入操作,给这个消息做一个唯一的主键,那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。

      2):判重表,将消费过处理成功的消息存入判重表中,每次消费处理前先去判重表查询是否已消费过

      2):如果你拿到这个消息做Redis的set操作,不用解决,因为无论你set几次结果都是一样的,set操作本来就算幂等操作

      3):如果上面两种情况都不行,准备一个第三方服务方来做消费记录。以Redis为例,给消息分配一个全局id,只要消费过该消息,将<id,Message>以KV

        形式写入Redis。那消费者开始消费前,先去Redis中查询 有没有消费记录即可。

      总之,解决思路就是,如果消息重复消费不会带来问题,那大可不用理会,如果有问题,要对消费过的消息做记录(数据库或者缓存),再次消费前查询是否已经被消费。

    或者两次操作做互斥操作,使只有一次操作能成功执行。有些情况需要考虑使用分布式锁

    13.列举你能想到的数据库分库分表策略;分库分表后,如何解决全表查询的问题?

    14.什么是线程死锁,如何解决 ?

    产生死锁的条件有四个:

    1. 互斥条件:所谓互斥就是进程在某一时间内独占资源。 

    2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 

    3. 不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺。 

    4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

      线程死锁是因为多线程访问共享资源,由于访问的顺序不当所造成的,通常是一个线程锁定了一个资源A,而又想去锁定资源B;在另一个线程中,锁定了资源B,而又想去锁定资源A以完成自身的操作,两个线程都想得到对方的资源,而不愿释放自己的资源,造成两个线程都在等待,而无法执行的情况。

    要解决死锁,可以从死锁的四个条件出发,只要破坏了一个必要条件,那么我们的死锁就解决了。在java中使用多线程的时候一定要考虑是否有死锁的问题哦。

      1、按顺序加锁

      上个例子线程间加锁的顺序各不一致,导致死锁,如果每个线程都按同一个的加锁顺序这样就不会出现死锁。

      2、获取锁时限

      每个获取锁的时候加上个时限,如果超过某个时间就放弃获取锁之类的。

      3、死锁检测

      按线程间获取锁的关系检测线程间是否发生死锁,如果发生死锁就执行一定的策略,如终断线程或回滚操作等。

    END

  • 相关阅读:
    LockSupport的用法及原理
    ReentrantReadWriteLock读写锁详解
    Java的CountDownLatch和CyclicBarrier的理解和区别
    AbstractQueuedSynchronizer超详细原理解析
    走进JVM
    【题解】Hanoi塔问题
    【题解】二的幂次方
    【题解】桐桐的递归函数
    【题解】数的计数
    【题解】极品飞车
  • 原文地址:https://www.cnblogs.com/yangyongjie/p/10579130.html
Copyright © 2020-2023  润新知