您的位置 首页 java

java开发面试题(1-3年)

1.注册中心的原理

涉及到三大角色:服务提供者、服务消费者、注册中心

各个 微服务 在启动时,将自己的网络地址等信息注册到注册中心,注册中心存储这些数据。

服务消费者从注册中心查询服务提供者的地址,并通过该地址调用服务提供者的接口。

各个微服务与注册中心使用一定机制(例如心跳)通信。如果注册中心与某微服务长时间无法通信,就会注销该实例。

微服务网络地址发送变化(例如实例增加或IP变动等)时,会重新注册到注册中心。这样,服务消费者就无需人工修改提供者的网络地址了。

2. redis 用到了哪些数据类型

string、list、set、hash、sortedset

3.怎么考虑某个数据是否需要加过期时间的

像token这种考虑登录过期的数据;

作为临时加载到缓存中使用的数据;

4. Redis 的内存淘汰策略

noeviction:当内存使用超过配置的时候会返回错误,不会驱逐任何键

allkeys- LRU :加入键的时候,如果过限,首先通过LRU算法驱逐最久没有使用的键

volatile -lru:加入键的时候如果过限,首先从设置了过期时间的键集合中驱逐最久没有使用的键

allkeys-random:加入键的时候如果过限,从所有key随机删除

volatile-random:加入键的时候如果过限,从过期键的集合中随机驱逐

volatile-ttl:从配置了过期时间的键中驱逐马上就要过期的键

volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键

allkeys-lfu:从所有键中驱逐使用频率最少的键

(全局的键空间选择性移除

  • noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
  • allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。(这个是最常用的)
  • allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。

设置过期时间的键空间选择性移除

  • volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
  • volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
  • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。)

5.redis应用场景

缓存热点数据:

热点数据(经常会被查询,但是不经常被修改或者删除的数据),首选是使用 redis缓存 ,redis的性能非常优秀。

计数器

诸如统计点击数、访问数、点赞数、评论数、浏览数等应用,由于单线程,可以避免并发问题,保证数据的正确性,并且100%毫秒级性能,同时开启Redis持久化,以便于持久化数据。

单线程机制:

验证前端的重复请求,可以自由扩展类似情况),可以通过redis进行过滤,比如,每次请求将 Request IP、参数、接口等 hash 作为key存储redis(幂等性请求),设置多长时间有效期,然后下次请求过来的时候先在redis中检索有没有这个key,进而验证是不是一定时间内过来的重复提交;再比如,限制用户登录的次数,比如一天错误登录次数10次等。

秒杀系统,基于redis是单线程特征,防止出现数据库超卖;

全局增量ID生成等;

排行榜:

谁得分高谁排名在前,比如点击率最高、活跃度最高、销售数量最高、投票最高的前10名排行等等;

分布式锁:

使用redis可以实现分布式锁,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:

互斥性,在任意时刻,只有一个客户端能持有锁。

不会发生 死锁 ,即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。

具有容错性,只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。

解铃还须系铃人,加锁和解锁必须是同一个客户端,客户端不能解他人加的锁。

Session 存储:

使用Redis的进行会话缓存(session cache)是非常常见的一种场景。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化,目前大量的方案均采用了redis作为session的存储方案

6.redis架构

单机版

特点:简单

问题:内存容量有限、处理能力有限 、无法高可用。

主从复制

Redis 的复制(replication)功能允许用户根据一个 Redis 服务器来创建任意多个该服务器的复制品,其中被复制的服务器为主服务器(master),而通过复制创建出来的服务器复制品则为从服务器(slave)。 只要主从服务器之间的网络连接正常,主从服务器两者会具有相同的数据,主服务器就会一直将发生在自己身上的数据更新同步给从服务器,从而一直保证主从服务器的数据相同。可以把数据的修改添加等等操作在主服务器上进行,再从服务中查询数据。

特点:

1、master/slave 角色

2、master/slave 数据相同

3、降低 master 读压力在转交从库

问题:

无法保证高可用

没有解决 master 写的压力

哨兵

Redis Sentinel 是一个分布式系统中监控 redis 主从服务器,并在主服务器下线时自动进行故障转移。其中三个特性:

监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。

提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。

自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作。

特点:

1、保证高可用

2、监控各个节点

3、自动故障迁移

缺点:主从模式,切换需要时间丢数据

没有解决 master 写的压力

集群(proxy 型):

Twemproxy 是一个 Twitter 开源的一个 redis 和 memcache 快速/轻量级代理服务器; Twemproxy 是一个快速的单线程代理程序,支持 Memcached ASCII 协议和 redis 协议。

特点:1、多种 hash 算法:MD5、CRC16、 CRC32 、CRC32a、hsieh、murmur、 Jenkins

2、支持失败节点自动删除

3、后端 Sharding 分片逻辑对业务透明,业务方的读写方式和操作单个 Redis 一致

缺点:增加了新的 proxy,需要维护其高可用。

failover 逻辑需要自己实现,其本身不能支持故障的自动转移可扩展性差,进行扩缩容都需要手动干预

集群至少需要3主3从,且每个实例使用不同的配置文件,主从不用配置,集群会自己选。

集群(直连型):

从redis 3.0之后版本支持redis-cluster集群,Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。

特点:

1、无中心架构(不存在哪个节点影响性能瓶颈),少了 proxy 层。

2、数据按照 slot 存储分布在多个节点,节点间数据共享,可动态调整数据分布。

3、可扩展性,可线性扩展到 1000 个节点,节点可动态添加或删除。

4、高可用性,部分节点不可用时,集群仍可用。通过增加 Slave 做备份数据副本

5、实现故障自动 failover,节点之间通过 gossip 协议交换状态信息,用投票机制完成 Slave到 Master 的角色提升。

缺点:

1、资源隔离性较差,容易出现相互影响的情况。

2、数据通过异步复制,不保证数据的强一致性

7.redis主从优缺点

优点:

  • 一个Master可以同步多个Slaves
  • Slave同样可以接受其它Slaves的连接和同步请求,这样可以有效的分载Master的同步压力。因此我们可以将Redis的Replication架构视为图结构
  • Master Server是以非阻塞的方式为Slaves提供服务。所以在Master-Slave同步期间,客户端仍然可以提交查询或修改请求
  • Slave Server同样是以非阻塞的方式完成数据同步。在同步期间,如果有客户端提交查询请求,Redis则返回同步之前的数据
  • 为了分载Master的读操作压力,Slave服务器可以为客户端提供只读操作的服务,写服务仍然必须由Master来完成。即便如此,系统的伸缩性还是得到了很大的提高
  • Master可以将数据保存操作交给Slaves完成,从而避免了在Master中要有独立的进程来完成此操作
  • 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离

缺点:

  • Redis不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复
  • 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性
  • Redis的主从复制采用全量复制,复制过程中主机会 fork 出一个子进程对内存做一份快照,并将子进程的内存快照保存为文件发送给从机,这一过程需要确保主机有足够多的空余内存。若快照文件较大,对集群的服务能力会产生较大的影响,而且复制过程是在从机新加入集群或者从机和主机网络断开重连时都会进行,也就是网络波动都会造成主机和从机间的一次全量的数据复制,这对实际的系统运营造成了不小的麻烦
  • Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费

8.有什么方式能够解决这些缺点

数据不需要高度一致时可以忽略;

数据需要高度一致:配置一台redis服务器,读写都用一台服务器,这种方式仅限于少量数据;

9.读写并发的时候,会出现缓存与数据库不一致的场景,怎么解决

懒加载模式缓存可采取双删+TTL失效来实现;

双删失败情况下可采取重试措施,重试有业务通过 mq 重试以及组件消费mysql的binlog再写入mq重试两种方式;

主动加载由于操作本身不具有幂等性,所以需要考虑加载的有序性问题,采取mq的分区机制实现串行化处理,实现缓存和mysql数据的最终一致,此时读和写操作的缓存加载事件是走的同一个mq。

canal+kafka+redis

canal缺点:

  1. canal只能同步增量数据。
  2. 不是实时同步,是准实时同步。
  3. 存在一些bug,不过社区活跃度较高,对于提出的bug能及时修复。
  4. MQ 顺序性问题。

10.什么是 分布式事务 ?分布式事务的类型有哪些?

事务 ACID :原子性、一致性、隔离性、持久性

涉及到多个数据库操作的事务即为分布式事务,目的是为保证 分布式系统 中的数据一致性.

分布式事务类型:

二阶段提交 2PC:第一步请求阶段通过协调者来统计表决结果,第二步执行表决后的结果,如果表决的结果是提交,那就提交执行,否则不执行提交.缺点是同步阻塞,而且万一协调者挂了就无法保证ACID.

三阶段提交3PC:在2PC的第一步拆分成了2步并且引入了超时机制,解决了 2PC 的痛点.第一步先向参与者发出一个信号,看看大家是否都能提交,如果可以就返回yes,否则返回no.第二步Pre commit 阶段,预提交一下,如果参与者可以完成commit,就返回ack进确认,如果不能则放弃提交本次事务.第三步doCommit阶段,进行真正的事务提交.

11.分布式事务解决方案

XA :XA是基于二阶段事务实现的一种标准协议,目前主流数据库都已支持该协议.由于是二阶段提交,缺点很明显,不适合高并发场景.

补偿机制TCC:try,commit,cancel的缩写,try阶段进行检测,commit提交执行,只要try阶段成功了commit就一定会被执行,cancel业务出现错误时执行,回滚事务,释放资源.

消息中间件MQ:可以通过 阿里 的消息中间件RocketMQ来进行解决.RocketMQ支持带事务的消息,具体思路大概是这样的:

第一步:消息生产者向消息集群发送prepared消息,获取消息地址.

第二步:本地提交事务,并向集群发送确认消息.

第三步:通过第一步获取到的地址,访问消息并改变消息状态.

(A系统先发送一个prepared消息到MQ,如果这个prepared消息发送失败,那么就直接取消操作,不执行了

  • 如果这个消息发送成功过了,那么接着执行本地事务,如果成功就告诉MQ发送确认消息,如果失败就告诉MQ回滚消息
  • 如果发送了确认消息,那么此时B系统会接收到确认消息,然后执行本地的事务
  • MQ会自动定时轮询所有prepared消息 回调 你的接口,问你这个消息是不是本地事务处理失败了,所有没发送确认的消息,是继续重试还是回滚?
  • 这里你就可以查下数据库看之前本地事务是否执行,如果回滚了,那么这里也回滚吧。这个就是避免可能本地事务执行成功了,别确认消息发送失败了。
  • 如果系统B的事务失败了就自动不断重试直到成功,如果实在是不行,要么就是针对重要的资金类业务进行回滚,比如B系统本地回滚后,想办法通知系统A也回滚;或者是发送报警由人工来手工回滚和补偿)

12.CAP理论

任何分布式系统都无法同时满足一致性(consistency),可用性(availibity),分区容错性(partition tolerance)这三项,最多只可同时满足其中的两项.

13.BASE理论

BASE是 Basically Available (基本可用) Soft state (软状态) Eventually consistent(最终一致性)这几个单词的缩写,

是从CAP理论发展而来的,其核心思想是:即使无法做到强一致性,但每个应用都可以根据自身特点,采取适当的方式来使系统达到最终一致性.

14.接口的幂等性问题

幂等的意思是重复操作,接口的幂等性也就是接口被重复调用了,在前端不进行限制的情况下,同一个接口可能重复调用多次,为了避免类似重复下单的问题,可以通过以下几种方式来解决幂等性问题:

全局唯一ID,根据业务操作和内容生成全局唯一的ID,然后在执行操作前先判断是否已经存在该ID,如果不存在则将该ID进行持久化(存在数据库或者redis中),如果已经存在则证明该接口已经被调用过了.比如下单时可以生产一个流水号来作为该订单的唯一标识.

可以使用select+insert来进行判断,因为一般订单的ID都是唯一索引,在高并发场景下不推荐.

可以使用 乐观锁 解决,在表中可以添加一个version字段.

token机制,将token放在redis中.

15.消息中间件如何解决消息丢失问题

可以在消息发送者这里发送一个id,接收端收到消息后将该ID存储于DB中,然后可以通过判断DB是否存在该ID来确定消息是否成功发送.

当然现在的消息中间件都比较强大,已经考虑并完善了这块内容,所以你可以直接借助消息中间件提供的方法来解决.

比如 rabbitMQ ,提供了事务机制,你可以在消息发送前提交事务,如果发送失败会自动回滚.

但这样会比较耗性能,所以更推荐的做法是使用 rabbitmq 提供的confirm机制.confirm机制是异步的,在消费者收到生产者发送的消息后会回调ack,如果消费者接收失败会回调nack接口.

要想真正做到万无一失,我们不仅需要对Message进行持久化,还需要对Exchange,Queue也进行持久化,而rabbitMQ提供了这些持久化机制,因此使用一款好的消息中间件就可以很好的解决消息丢失问题.

当然除了rabbitMQ, kafka 也有相应的解决方案

(生产者端:采用发送确认机制,如果发送到消息 中间件 报错,记录异常或者重试发送。

消息中间件本身:要保证消息队列是持久化的,消息是持久化的。

消费者端:取消自动确认机制,手动应答)

16.Redis的持久化方式有哪些?各有何利弊?

Redis的持久化有两种方式,AOF和RDB.

AOF:以独立日志记录写命令方式存储日志,重启时再重新执行日志中记录的数据即可完成恢复.优势是数据安全,不容易丢数据,而且aof中的内容可以读懂,如果不小心敲了flushall命令,还可以将aof文件尾部的flushall命令删掉,然后就可以恢复数据了.缺点是占用存储空间大,数据恢复较慢.

RDB:将内存中的数据以快照的方式写进二进制文件中.优势是数据恢复快,方便备份,缺点是比较耗内存,可能会造成数据丢失.

17.aof文件过大怎么处理?

可以重复执行aof命令,bgwriteaof,执行后会触发重写aof文件,会将aof中的重复命令进行压缩

18.redis的事务

redis的事务先以MULTI开启事务,将多个命令入队到事务中,然后通过EXEC命令触发执行队伍中的所有命令,如果想取消事务可以执行discard命令.

Redis事务的三个阶段

  1. 事务开始 MULTI
  2. 命令入队
  3. 事务执行 EXEC

事务执行过程中,如果服务端收到有EXEC、DISCARD、WATCH、MULTI之外的请求,将会把请求放入队列中排队

19.如何保证消息不被重复消费(幂等性问题)

rabbitmq并没有提供防止消息重复消费的功能,只能在业务端去实现,如果业务是做了集群的,我们可以用redis帮助解决,具体做法是将消息的msgid和消费状态用redis存储起来,每次消费前先查看一下本次消费的消息状态,如果已经被消费过了,就可以不用消费.如果尚未消费,就可以进行消费,消费完把对应的状态改为已消费即可.

20.如何保证消息的消费顺序?

同一个业务的消息由同一个生产者发送到消息队列,并且保证由同一个消费者进行消费,例如:同一个订单号的消息会发送到同一个队列,由同一个消费者进行消费

21.如何解决消息队列的延时及过期失效问题?

批量重导,自己写程序把失效的数据查出来然后重新导入队里中.

22.消息队列满了怎么处理?当消息过度积压怎么处理?

应当在设计上尽量避免出现这种问题,如果确实已经碰到了,可以采取服务降级策略,同时临时增加一些消费能力更强劲的消费者,以X倍速率消费队列中积压的消息.

23.说说RocketMQ实现分布式事务的优缺点

优点:

消息的时效性比较高;

从应用设计的角度实现了消息数据的可靠性,消息数据的可靠性不依赖于MQ中间件,弱化了对MQ中间件特性的依赖;

方案轻量级,容易实现。

缺点:

与具体的业务场景绑定,耦合性强,不可以共用;

消息数据与业务数据同步,占用业务系统资源;

业务系统在使用 关系型数据库 的情况下消息服务性能会受到关系型数据库的并发性能限制。

24.消息中间件是如何选型

25.Mysql的存储引擎有哪些,以及它们之间的区别

26.设计索引上,你会怎么考虑

1.最适合 索引 的列是出现在 WHERE子句中的列,或连接子句中指定的列,而不是出现在 SELECT 关键字后的选择列表中的列。使用惟一索引。考虑某列中值的分布。索引的列的基数越大,索引的效果越好。

例如,存放出生日期的列具有不同值,很容易区分各行。而用来记录性别的列,只含有“ M”和“F”,则对此列进行索引没有多大用处,因为不管搜索哪个值,都会得出大约一半的行。

2.如果对字符串列进行索引,应该指定一个前缀长度,只要有可能就应该这样做。

例如,如果有一个 CHAR (200)列,如果在前 10 个或 20 个字符内,多数值是惟一的,那么就不要对整个列进行索引。对前 10 个或 20 个字符进行索引能够节省大量索引空间,也可能会使查询更快。较小的索引涉及的磁盘 IO 较少,较短的值比较起来更快。更为重要的是,对于较短的键值,索引 高速缓存 中的块能容纳更多的键值,因此,MySQL 也可以在内存中容纳更多的值。这样就增加了找到行而不用读取索引中较多块的可能性。

3.利用最左前缀。

在创建一个 n 列的索引时,实际是创建了 MySQL 可利用的 n 个索引。多列索引可起几个索引的作用,因为可利用索引中最左边的列集来匹配行。这样的列集称为最左前缀。

4.不要过度索引。

不要以为索引“越多越好”,什么东西都用索引是错误的。每个额外的索引都要占用额外的磁盘空间,并降低写操作的性能。在修改表的内容时,索引必须进行更新,有时可能需要重构,因此,索引越多,所花的时间越长。如果有一个索引很少利用或从不使用,那么会不必要地减缓表的修改速度。此外,MySQL 在生成一个执行计划时,要考虑各个索引,这也要花费时间。创建多余的索引给查询优化带来了更多的工作。索引太多,也可能会使 MySQL 选择不到所要使用的最好索引。只保持所需的索引有利于查询优化。

5.对于 InnoDB 存储引擎的表,记录默认会按照一定的顺序保存,如果有明确定义的 主键 ,则按照主键顺序保存。

如果没有主键,但是有唯一索引,那么就是按照唯一索引的顺序保存。如果既没有主键又没有唯一索引,那么表中会自动生成一个内部列,按照这个列的顺序保存。按照主键或者内部列进行的访问是最快的,所以 InnoDB 表尽量自己指定主键,当表中同时有几个列都是唯一的,都可以作为主键的时候,要选择最常作为访问条件的列作为主键,提高查询的效率。

另外,还需要注意,InnoDB 表的普通索引都会保存主键的键值,所以主键要尽可能选择较短的数据类型,可以有效地减少索引的磁盘占用,提高索引的缓存效果。

27.Redis的过期策略有哪些?

惰性删除:当读/写一个已经过期的key时,会触发惰性删除策略,直接删除掉这个过期key,并按照key不存在去处理。惰性删除,对内存不太好,已经过期的key会占用太多的内存。

定期删除:每隔一段时间,就会对Redis进行检查,主动删除一批已过期的key

28.为什么Redis不使用定时删除?

定时删除,就是在设置key的过期时间的同时,创建一个定时器,让定时器在过期时间来临时,立即执行对key的删除操作。

定时删除,会占用CPU,影响服务器的响应时间和性能

29.Redis集群数据分片的原理是什么?

Redis数据分片原理是哈希槽(hash slot)。Redis 集群有 16384 个哈希槽。 每一个 Redis 集群中的节点都承担一个哈希槽的子集。哈希槽让在集群中添加和移除节点非常容易。例如,如果我想添加一个新节点 D,我需要从节点 A,B, C 移动一些哈希槽到节点 D。同样地,如果我想从集群中移除节点 A,我只需要移动 A 的哈希槽到 B 和 C。 当节点 A 变成空的以后,我就可以从集群中彻底删除它。 因为从一个节点向另一个节点移动哈希槽并不需要停止操作,所以添加和移除节点,或者改变节点持有的哈希槽百分比,都不需要任何停机时间(downtime)。

30.为什么Redis Cluster分片不使用Redis一致性Hash算法?

一致性 哈希算法 也有一个严重的问题,就是数据倾斜。

如果在分片的集群中,节点太少,并且分布不均,一致性哈希算法就会出现部分节点数据太多,部分节点数据太少。也就是说无法控制节点存储数据的分配。

31.集群的 拓扑结构 有没有了解过?集群是怎么连接的?

无中心结构。Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接

32.Redis如何实现分布式锁?

使用set key value ex nx 命令。当key不存在时,将 key 的值设为 value ,返回1。若给定的 key 已经存在,则setnx不做任何动作,返回0。当setnx返回1时,表示获取锁,做完操作以后del key,表示释放锁,如果setnx返回0表示获取锁失败。

33.为什么不先set nx,然后再使用expire设置超时时间?

我们需要保证setnx命令和expire命令以原子的方式执行,否则如果客户端执行setnx获得锁后,这时客户端宕机了,那么这把锁没有设置过期时间,导致其他客户端永远无法获得锁了。

34.如果加锁和解锁之间的业务逻辑执行的时间比较长,超过了锁过期的时间,执行完了,又删除了锁,就会把别人的锁给删了。怎么办?

这两个属于锁超时的问题。可以将锁的value设置为Json字符串,在其中加入线程的id或者请求的id,在删除之前,get一下这个key,判断key对应的value是不是当前线程的。只有是当前线程获取的锁,当前线程才可以删除。

35.Redis分布式锁,怎么保证可重入性?

可以将锁的value设置为Json字符串,在其中加入线程的id和count变量。当count变量的值为0时,表示当前分布式锁没有被线程占用。如果count变量的值大于0,线程id不是当前线程,表示当前分布式锁已经被其他线程占用。如果count变量的值大于0,线程id是当前线程的id,表示当前线程已经拿到了锁,不必阻塞,可以直接重入,并将count变量的值加一即可。这种思路,其实就是参考了ReentrantLock可重入锁的机制。

36.Redis做分布式锁,Redis做了主从,如果设置锁之后,主机在传输到从机的时候挂掉了,从机还没有加锁信息,如何处理?

可以使用开源框架Redisson,采用了redLock。

37.假如现在有一条慢查询,你会怎么进行排查

查看服务器性能,cpu内存,磁盘情况等,查看mysql慢查询日志。

38.可以说一下JUC吗

java.util.concurrent包名的简写,是关于并发编程的API。与JUC相关的有三个包:java.util.concurrent、java.util.concurrent.atomic、java.util.concurrent.locks

39.Eureka作为注册中心,怎么判断服务是否可用

服务定时向注册中心发送心跳,表明自己还活着

40.zuul作为网关,实现了哪些功能

路由,反向代理,日志记录,权限控制,限流

41.JDK内置锁

  • synchronized同步机制
  • ReentrantLock重入锁
  • ReadWriteLock读写分离锁

42.类实例化顺序

父类静态成员和静态代码块->子类静态成员和静态代码块->父类非静态成员和非静态代码块->父类构造方法->子类非静态成员和非静态代码块->子类构造方法

43.为什么使用MQ?MQ的优点、缺点

  • 异步处理 – 相比于传统的串行、并行方式,提高了系统吞吐量。
  • 应用解耦 – 系统间通过消息通信,不用关心其他系统的处理。
  • 流量削锋 – 可以通过消息队列长度控制请求量;可以缓解短时间内的高并发请求。
  • 日志处理 – 解决大量日志传输。
  • 消息通讯 – 消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。

缺点:

系统可用性降低、系统复杂度提高、一致性问题

44.Redis key的过期时间和永久有效分别怎么设置?

EXPIRE和PERSIST命令。

45.redis主从复制

过程原理

  1. 当从库和主库建立MS关系后,会向主数据库发送SYNC命令
  2. 主库接收到SYNC命令后会开始在后台保存快照(RDB持久化过程),并将期间接收到的写命令缓存起来
  3. 当快照完成后,主Redis会将快照文件和所有缓存的写命令发送给从Redis
  4. 从Redis接收到后,会载入快照文件并且执行收到的缓存的命令
  5. 之后,主Redis每当接收到写命令时就会将命令发送从Redis,从而保证数据的一致

缺点

所有的slave节点数据的复制和同步都由master节点来处理,会照成master节点压力太大,使用主从从结构来解决

46.Redis集群会有写操作丢失吗?

Redis并不能保证数据的强一致性,这意味这在实际中集群在特定的条件下可能会丢失写操作。

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

异步复制

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

16384个

49.Redis集群如何选择数据库?

Redis集群目前无法做数据库选择,默认在0数据库。

50.索引有哪几种类型?

主键索引: 数据列不允许重复,不允许为NULL,一个表只能有一个主键。

唯一索引: 数据列不允许重复,允许为NULL值,一个表允许多个列创建唯一索引。

  • 可以通过 ALTER TABLE table_name ADD UNIQUE (column); 创建唯一索引
  • 可以通过 ALTER TABLE table_name ADD UNIQUE (column1,column2); 创建唯一组合索引

普通索引: 基本的索引类型,没有唯一性的限制,允许为NULL值。

  • 可以通过ALTER TABLE table_name ADD INDEX index_name (column);创建普通索引
  • 可以通过ALTER TABLE table_name ADD INDEX index_name(column1, column2, column3);创建组合索引

全文索引: 是目前搜索引擎使用的一种关键技术。

  • 可以通过ALTER TABLE table_name ADD FULLTEXT (column);创建全文索引

51.SQL 标准定义了四个隔离级别:

  • READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
  • READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
  • REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
  • SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。

这里需要注意的是:Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别

52.如何定位及优化SQL语句的性能问题?创建的索引有没有被使用到?或者说怎么才可以知道这条语句运行很慢的原因?

对于低性能的SQL语句的定位,最重要也是最有效的方法就是使用执行计划,MySQL提供了explain命令来查看语句的执行计划。 我们知道,不管是哪种数据库,或者是哪种数据库引擎,在对一条SQL语句进行执行的过程中都会做很多相关的优化,对于查询语句,最重要的优化方式就是使用索引。 而执行计划,就是显示数据库引擎对于SQL语句的执行的详细情况,其中包含了是否使用索引,使用什么索引,使用的索引的相关信息等。

53.描述一下JVM加载Class文件的原理机制

Java中的所有类,都需要由类加载器装载到JVM中才能运行。类加载器本身也是一个类,而它的工作就是把class文件从硬盘读取到内存中。在写程序的时候,我们几乎不需要关心类的加载,因为这些都是隐式装载的,除非我们有特殊的用法,像是反射,就需要显式的加载所需要的类。

类装载方式,有两种 :

1.隐式装载, 程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中,

2.显式装载, 通过class.forname()等方法,显式加载需要的类

Java类的加载是动态的,它并不会一次性将所有类全部加载后再运行,而是保证程序运行的基础类(像是基类)完全加载到jvm中,至于其他类,则在需要的时候才加载。这当然就是为了节省内存开销。

54.什么是类加载器,类加载器有哪些?

实现通过类的权限定名获取该类的二进制字节流的代码块叫做类加载器。

主要有一下四种类加载器:

  1. 启动类加载器(Bootstrap ClassLoader)用来加载java核心类库,无法被java程序直接引用。
  2. 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
  3. 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。
  4. 用户自定义类加载器,通过继承 java.lang.ClassLoader类的方式实现。

55.说一下类装载的执行过程?

类装载分为以下 5 个步骤:

  • 加载:根据查找路径找到相应的 class 文件然后导入;
  • 验证:检查加载的 class 文件的正确性;
  • 准备:给类中的静态变量分配内存空间;
  • 解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址;
  • 初始化:对静态变量和静态代码块执行初始化工作。

56.JVM 调优的工具?

JDK 自带了很多监控工具,都位于 JDK 的 bin 目录下,其中最常用的是 jconsole 和 jvisualvm 这两款视图监控工具。

  • jconsole:用于对 JVM 中的内存、线程和类等进行监控;
  • jvisualvm:JDK 自带的全能分析工具,可以分析:内存快照、线程快照、程序死锁、监控内存的变化、gc 变化等。

57.MyBatis编程步骤是什么样的?

1、 创建SqlSessionFactory

2、 通过SqlSessionFactory创建SqlSession

3、 通过sqlsession执行数据库操作

4、 调用session.commit()提交事务

5、 调用session.close()关闭会话

58.MyBatis的工作原理

1)读取 MyBatis 配置文件:mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。

2)加载映射文件。映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。

3)构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。

4)创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。

5)Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。

6)MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。

7)输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。

8)输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程。

59.Spring 框架中都用到了哪些设计模式?

  1. 工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
  2. 单例模式:Bean默认为单例模式。
  3. 代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
  4. 模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
  5. 观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现–ApplicationListener。

60.spring为什么把bean设计成单例?

单例bean的优势:

由于不会每次都新创建新对象所以有一下几个性能上的优势:

1.减少了新生成实例的消耗

新生成实例消耗包括两方面,第一,spring会通过反射或者cglib来生成bean实例这都是耗性能的操作,其次给对象分配内存也会涉及复杂算法。

2.减少jvm垃圾回收

由于不会给每个请求都新生成bean实例,所以自然回收的对象少了。

3.可以快速获取到bean

因为单例的获取bean操作除了第一次生成之外其余的都是从缓存里获取的所以很快。

单例bean的劣势:

单例的bean一个很大的劣势就是他不能做到线程安全!由于所有请求都共享一个bean实例,所以这个bean要是有状态的一个bean的话可能在并发场景下出现问题,而原型的bean则不会有这样问题(但也有例外,比如他被单例bean依赖),因为给每个请求都新创建实例。

61.ApplicationContext通常的实现是什么?

FileSystemXmlApplicationContext :此容器从一个XML文件中加载beans的定义,XML Bean 配置文件的全路径名必须提供给它的构造函数。

ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,这里,你需要正确设置classpath因为这个容器将在classpath里找bean配置。

WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。

62.你怎样定义类的作用域?

当定义一个 在Spring里,我们还能给这个bean声明一个作用域。它可以通过bean 定义中的scope属性来定义。如,当Spring要在需要的时候每次生产一个新的bean实例,bean的scope属性被指定为prototype。另一方面,一个bean每次使用的时候必须返回同一个实例,这个bean的scope 属性 必须设为 singleton。

63.bean的生命周期

Spring对bean进行实例化;

Spring将值和bean的引用注入到bean对应的属性中;

如果bean实现了BeanNameAware接口,Spring将bean的ID传递给setBean-Name()方法;

如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入;

如果bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()方法,将bean所在的应用上下文的引用传入进来;

如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessBeforeInitialization()方法;

如果bean实现了InitializingBean接口,Spring将调用它们的after-PropertiesSet()方法。类似地,如果bean使用initmethod声明了初始化方法,该方法也会被调用;

如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessAfterInitialization()方法;

此时,bean已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到该应用上下文被销毁;

如果bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法。同样,如果bean使用destroy-method声明了销毁方法,该方法也会被调用。

64.什么是DispatcherServlet

Spring的MVC框架是围绕DispatcherServlet来设计的,它用来处理所有的HTTP请求和响应。

65.请描述Spring MVC的工作流程?描述一下 DispatcherServlet 的工作流程?

(1)用户发送请求至前端控制器DispatcherServlet;

(2) DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handler;

(3)处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet;

(4)DispatcherServlet 调用 HandlerAdapter处理器适配器;

(5)HandlerAdapter 经过适配调用 具体处理器(Handler,也叫后端控制器);

(6)Handler执行完成返回ModelAndView;

(7)HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;

(8)DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;

(9)ViewResolver解析后返回具体View;

(10)DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)

(11)DispatcherServlet响应用户。

66.redis发布/订阅模式:

  • subscribe: 订阅一个或者多个频道;
  • unsubscribe: 退订一个或者多个频道;
  • publish: 向通道发送消息;
  • psubscribe: 订阅给定模式相匹配的所有频道;
  • punsubscribe: 退订 给定模式所有的频道,若未指定模式,退订所有频道;

发布订阅模式 是阻塞模式

缺点:

PubSub 的生产者来一个消息会直接传递给消费者。如果没有消费者,消息会直接丢弃。如果有多个消费者,一个消费者突然挂掉,生产者会继续发送消息,另外的消费者可以持续收到消息。但是挂掉的消费者重新连上后,断连期间的消息会彻底丢失;

如果 Redis 停机重启,PubSub 的消息是不会持久化

应用场景:

构建实时消息系统

消息通知

67.java多态原理

在Java中,当你调用一个方法时,可能会在编译时期(compile time)解析(resolve),也可能实在运行时期(runtime) 解析,这全取决于到底是一个静态方法(static method )还是一个虚方法(virtual method)。如果是在编译时期解析,那么就称之为静态绑定(staticbinding)[换句话说静态方法就是在编译期解析的],如果方法的调用是在运行时期解析,那就是动态绑定(dynamic binding)或者延迟绑定(late binding)。

Java是一门面向对象的编程语言,优势就在于支持多态( Polymorphism)。多态使得父类型的引用变量可以引用子类型的对象。如果调用子类型对象的一个虛方法(非private,final or static),编译器将无法在子类中找到真正需要调用的方法,因为它可能是定义在父类型中的方法(从父类继承过来) ,也可能是在子类型中被重写(override) 的方法,这种情形,只能在运行时进行解析(说白了就是不能在编译期间确定要调用的方法在哪儿),因为只有在运行时期,才能明确具体的对象.到底是什么。这也是我们俗称的运行时或动态绑定( runtime or dynamic binding)。

另一方面,privatestatic和final方法将在编译时解析,因为编译器知道它们不能被重写,所有可能的方法都被定义在了一个类中,这些方法只能通过此类的引用变量进行调用。这叫做静态绑定或编译时绑定(static or compile time binding)。动态绑定只有在重写可能存在时才会用到,而重载的方法在编译时期即可确定( 因为重载的方法是在一个类中出现的)

68.redis主从同步

Redis是由一个Master作为写的操作,多个slave作为读的操作。Master和slave代表一个个的Redis实例。定期的数据备份操作是单独的一个slave完成的。主从数据应满足最终一致性:使用主从同步或从从同步实现。

主从同步的弊端在于:不具备高可用性,当master挂掉时,redis将不能对外提供写入操作。

Redis Sentinel(哨兵)

69.mysql主从复制

主从复制:是用来建立一个和主数据库完全一样的数据库环境,称为从数据库;主数据库一般是准实时的业务数据库。

70.主从复制的作用(好处或者说为什么要做主从)

1、做数据的热备,作为后备数据库,主数据库服务器故障后,可切换到从数据库继续工作,避免数据丢失。

2、架构的扩展。业务量越来越大,I/O访问频率过高,单机无法满足,此时做多库的存储,降低磁盘I/O访问的频率,提高单个机器的I/O性能。

3、读写分离,使数据库能支撑更大的并发。在报表中尤其重要。由于部分报表sql语句非常的慢,导致锁表,影响前台服务。如果前台使用master,报表使用slave,那么报表sql将不会造成前台锁,保证了前台速度。

71.主从复制的原理:

1.数据库有个bin-log二进制文件,记录了所有sql语句。

2.我们的目标就是把主数据库的bin-log文件的sql语句复制过来。

3.让其在从数据的relay-log重做日志文件中再执行一次这些sql语句即可。

4.下面的主从配置就是围绕这个原理配置

5.具体需要三个线程来操作:

binlog输出线程。每当有从库连接到主库的时候,主库都会创建一个线程然后发送binlog内容到从库。

在从库里,当复制开始的时候,从库就会创建两个线程进行处理:

从库I/O线程。当START SLAVE语句在从库开始执行之后,从库创建一个I/O线程,该线程连接到主库并请求主库发送binlog里面的更新记录到从库上。从库I/O线程读取主库的binlog输出线程发送的更新并拷贝这些更新到本地文件,其中包括relay log文件。

从库的SQL线程。从库创建一个SQL线程,这个线程读取从库I/O线程写到relay log的更新事件并执行。

可以知道,对于每一个主从复制的连接,都有三个线程。拥有多个从库的主库为每一个连接到主库的从库创建一个binlog输出线程,每一个从库都有它自己的I/O线程和SQL线程。

72.主从复制存在问题:

  1. 负载均衡,由于复制的时间差,不能保证同步读,而且写仍然单点,没法多点写,我对这个理解就是半吊子的读写均衡。
  2. 容灾,基本都是有损容灾,因为数据不同步,谁用谁知道,半吊子的容灾。

73.从数据库的读的延迟问题了解吗?如何解决?

原因:当主库的TPS并发较高时,产生的DDL数量超过slave一个sql线程所能承受的范围,那么延时就产生了,当然还有就是可能与slave的大型query语句产生了锁等待,还有网络延迟。

解决方法一 :最简单的减少slave同步延时的方案就是在架构上做优化,尽量让主库的DDL快速执行。还有就是主库是写,对数据安全性较高,比如sync_binlog=1,innodb_flush_log_at_trx_commit = 1 之类的设置,而slave则不需要这么高的数据安全,完全可以讲sync_binlog设置为0或者关闭binlog,innodb_flushlog也可以设置为0来提高sql的执行效率。另外就是使用比主库更好的硬件设备作为slave。

解决方法二 :数据放入缓存中,更新数据库后,在预期可能马上用到的情况下,主动刷新缓存->(Redis的角度)

解决办法三:对于比较重要且必须实时的数据,比如用户刚换密码(密码写入 Master),然后用新密码登录(从 Slaves 读取密码),会造成密码不一致,导致用户短时间内登录出错。所以在这种需要读取实时数据的时候最好从 Master 直接读取,避免 Slaves 数据滞后现象发生。

74.做主从后主服务器挂了怎么办?

Mysql主库宕机情况分类:

1)硬件问题,(服务器、ecs、虚拟主机等等)宕机

2)service问题,Mysql宕机,服务异常,端口异常等

解决思路:应该围绕bin log日志和从服务器上的relay log 等文件还有就是数据的备份。所以说做好数据备份才是王道,这样能最大程度上保证数据不会丢失。

75.什么是ES,为什么要用ES?

Elasticsearch是一个基于Lucene的搜索引擎。它提供了具有HTTP Web界面和无架构JSON文档的分布式,多租户能力的全文搜索引擎。Elasticsearch是用Java开发的,根据Apache许可条款作为开源发布。

用ES做一个全文索引

76.ElasticSearch中的分片是什么?

因为Elasticsearch是一个分布式搜索引擎,所以索引通常被分割成分布在多个节点上的被称为分片的元素

77.Elasticsearch中的倒排索引是什么? 这个问题很重要

倒排索引是搜索引擎的核心。搜索引擎的主要目标是在查找发生搜索条件的文档时提供快速搜索。倒排索引是一种像数据结构一样的散列图,可将用户从单词导向文档或网页。它是搜索引擎的核心。其主要目标是快速搜索从数百万文件中查找数据。

传统的检索方式是通过文章,逐个遍历找到对应关键词的位置。倒排索引,是通过分词策略,形成了词和文章的映射关系表,也称倒排表,这种词典 + 映射表即为 倒排索引

其中词典中存储词元,倒排表中存储该词元在哪些文中出现的位置。有了倒排索引,就能实现 O(1) 时间复杂度的效率检索文章了,极大的提高了检索效率。

加分项: 倒排索引的底层实现是基于:FST(Finite State Transducer)数据结构。

Lucene 从 4+ 版本后开始大量使用的数据结构是 FST。FST 有两个优点:1)空间占用小。通过对词典中单词前缀和后缀的重复利用,压缩了存储空间;2)查询速度快。O(len(str)) 的查询时间复杂度。

78.ES是如何实现master选举的?

前置条件: 1)只有是候选主节点(master:true)的节点才能成为主节点。2)最小主节点数(min_master_nodes)的目的是防止脑裂。

Elasticsearch 的选主是 ZenDiscovery 模块负责的,主要包含 Ping(节点之间通过这个RPC来发现彼此)和 Unicast(单播模块包含一个主机列表以控制哪些节点需要 ping 通)这两部分;获取主节点的核心入口为 findMaster,选择主节点成功返回对应 Master,否则返回 null。

选举流程大致描述如下: 第一步:确认候选主节点数达标,elasticsearch.yml 设置的值 discovery.zen.minimum_master_nodes;第二步:对所有候选主节点根据nodeId字典排序,每次选举每个节点都把自己所知道节点排一次序,然后选出第一个(第0位)节点,暂且认为它是master节点。第三步:如果对某个节点的投票数达到一定的值(候选主节点数n/2+1)并且该节点自己也选举自己,那这个节点就是master。否则重新选举一直到满足上述条件。

  • 补充:
    • 这里的 id 为 string 类型。
    • master 节点的职责主要包括集群、节点和索引的管理,不负责文档级别的管理;data 节点可以关闭 http 功能。

79.如何解决ES集群的脑裂问题

所谓集群脑裂,是指 Elasticsearch 集群中的节点(比如共 20 个),其中的 10 个选了一个 master,另外 10 个选了另一个 master 的情况。

当集群 master 候选数量不小于 3 个时,可以通过设置最少投票通过数量(discovery.zen.minimum_master_nodes)超过所有候选节点一半以上来解决脑裂问题;当候选数量为两个时,只能修改为唯一的一个 master 候选,其他作为 data 节点,避免脑裂问题。

80.上下文切换

多线程编程中一般线程的个数都大于 CPU 核心的个数,而一个 CPU 核心在任意时刻只能被一个线程使用,为了让这些线程都能得到有效执行,CPU 采取的策略是为每个线程分配时间片并轮转的形式。当一个线程的时间片用完的时候就会重新处于就绪状态让给其他线程使用,这个过程就属于一次上下文切换。

概括来说就是:当前任务在执行完 CPU 时间片切换到另一个任务之前会先保存自己的状态,以便下次再切换回这个任务时,可以再加载这个任务的状态。任务从保存到再加载的过程就是一次上下文切换。

81.创建线程的几种方式

1.继承thread类

定义一个Thread类的子类,重写run方法,将相关逻辑实现,run()方法就是线程要执行的业务逻辑方法

创建自定义的线程子类对象

调用子类实例的start()方法来启动线程

2.实现runnable接口

定义Runnable接口实现类MyRunnable,并重写run()方法

创建MyRunnable实例myRunnable,以myRunnable作为target创建Thead对象,该Thread对象才是真正的线程对象

调用线程对象的start()方法

3.实现callable接口

创建实现Callable接口的类myCallable

以myCallable为参数创建FutureTask对象

将FutureTask作为参数创建Thread对象

调用线程对象的start()方法

4.使用executors工具类创建线程池

Executors提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。

主要有newFixedThreadPool,newCachedThreadPool,newSingleThreadExecutor,newScheduledThreadPool

82.runnable 和 callable 有什么区别?

相同点

  • 都是接口
  • 都可以编写多线程程序
  • 都采用Thread.start()启动线程

主要区别

  • Runnable 接口 run 方法无返回值;Callable 接口 call 方法有返回值,是个泛型,和Future、FutureTask配合可以用来获取异步执行的结果
  • Runnable 接口 run 方法只能抛出运行时异常,且无法捕获处理;Callable 接口 call 方法允许抛出异常,可以获取异常信息

注:Callalbe接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。

83.为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?

调用 start 方法方可启动线程并使线程进入就绪状态,而 run 方法只是 thread 的一个普通方法调用,还是在主线程里执行。

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

有两种调度模型:分时调度模型和抢占式调度模型。

分时调度模型是指让所有的线程轮流获得 cpu 的使用权,并且平均分配每个线程占用的 CPU 的时间片这个也比较好理解。

Java虚拟机采用抢占式调度模型,是指优先让可运行池中优先级高的线程占用CPU,如果可运行池中的线程优先级相同,那么就随机选择一个线程,使其占用CPU。处于运行状态的线程会一直运行,直至它不得不放弃 CPU。

85.深拷贝和浅拷贝

浅拷贝(shallowCopy)只是增加了一个指针指向已存在的内存地址,

深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存,

86.JVM调优

对JVM内存的系统级的调优主要的目的是减少GC的频率和Full GC的次数。

1.Full GC

会对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个堆进行回收,所以比较慢,因此应该尽可能减少Full GC的次数。

2.导致Full GC的原因

1) 年老代(Tenured)被写满

调优时尽量让对象在新生代GC时被回收、让对象在新生代多存活一段时间和不要创建过大的对象及数组避免直接在旧生代创建对象 。

2)持久代Pemanet Generation空间不足

增大Perm Gen空间,避免太多静态对象 , 控制好新生代和旧生代的比例

3)System.gc()被显示调用

垃圾回收不要手动触发,尽量依靠JVM自身的机制

在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节,下面详细介绍对应JVM调优的方法和步骤。

文章来源:智云一二三科技

文章标题:java开发面试题(1-3年)

文章地址:https://www.zhihuclub.com/191482.shtml

关于作者: 智云科技

热门文章

网站地图