阿里电话面试题(附答案)

嗯,首先接到阿里电话面试的不是我(伤心),其次这个是我看到别人分享的面试题,但是我觉得挺有意义的,所以这里我自己作答了下,顺便分享一下,与诸君共勉,如果有错误的地方请指正,谢谢。

线程问题

问题一:多线程的实现方式有哪些?
回答:多线程的实现方式主要有两种,一种是继承Thread类,一种是实现Runnable接口,不论是继承还是实现,线程业务代码都是写在run方法里,然后在运行处new出线程,然后调用start方法,启动线程。这里要注意一下,不能直接调用run方法,这种相当于是直接调用对象的方法,不是启动多线程;然后调用start方法,启动多线程,相当于是线程处于就绪状态,当CPU分配的时间片给它时,它才运行。
还有四种变形实现方式:实现内部类的方式,定时器方式,带返回值的线程实现,基于线程池的实现。当然这里的四种方式都是基于上面两种的变形

问题二:线程死锁的原因?
回答
造成死锁的主要原因:
1、系统资源不足
2、进程运行的推进顺序不合适
3、资源分配不当
造成死锁的四个条件:
1、互斥条件
2、占有且申请
3、不可强占
4、循环等待条件
解决办法:
1、死锁预防:通过设置某些限制条件,去破坏死锁的四个条件中的一个或几个条件,来预防发生死锁。但由于所施加的限制条件往往太严格,因而导致系统资源利用率和系统吞吐量降低。
2、死锁避免:在资源分配过程中若预测有发生死锁的可能性,则加以避免。这种方法的关键是确定资源分配的安全性。
3、死锁检测与恢复:不须实现采取任何限制性措施,而是允许系统在运行过程发生死锁,但可通过系统设置的检测机构及时检测出死锁的发生,并精确地确定于死锁相关的进程和资源,然后采取适当的措施,从系统中将已发生的死锁清除掉。

问题三:线程池的工作原理?底层方法的参数分别是什么意思?
回答
工作原理:
1、当有新任务来时,线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则执行第二步。
2、线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这个工作队列里进行等待。如果工作队列满了,则执行第三步
3、线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务
底层参数:
1、 corePoolSize 核心线程池大小,活动线程小于corePoolSize则直接创建,大于等于则先加到workQueue中,队列满了才创建新的线程。当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程。
2、 maximumPoolSize 线程池允许创建的最大线程数,线程池中的当前线程数目不会超过该值。如果队列满了,并且当前线程个数小于maximumPoolSize,那么会创建新的线程来执行任务
3、 keepAliveTime 线程池的工作线程空闲后,保持存活的时间。所以,如果任务很多,并且每个任务执行的时间比较短,可以调大时间,提高线程的利用率
4、 unit 线程活动保持时间的单位:可选的单位有天(DAYS)、小时(HOURS)、分钟(MINUTES)等
5、 workQueue 工作队列,线程池中的工作线程都是从这个工作队列源源不断的获取任务进行执行

Mybatis

问题一:mybatis 的 $ 与 # 的区别?
回答
1、#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:order by #user_id#,如果传入的值是111,那么解析成sql时的值为order by “111”, 如果传入的值是id,则解析成的sql为order by “id”.
2.、$ 将传入的数据直接显示生成在sql中。如:order by $ user_id $ ,如果传入的值是111,那么解析成sql时的值为order by user_id, 如果传入的值是id,则解析成的sql为order by id.
3、# 方式能够很大程度防止sql注入。
4、$ 方式无法防止Sql注入。
5.、一般能用#的就别用$

问题二:$ 跟 # 的使用场景 ?
回答:$方式一般用于传入数据库对象,例如传入表名;#一般是用于传具体的参数

问题三:mybatis 的 dao 接口跟 xml 文件里面的sql 是如何建立关系的?
回答:Mybatis在初始化SqlSessionFactoryBean的时候,找到mapperLocations路径去解析里面所有的XML文件,我们执行Mybatis方法的时候,就通过全限定类名+方法名找到MappedStatement对象,然后通过执行器Executor去执行具体SQL并返回

数据库

问题一:mysql 锁机制 ?
回答
表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般

问题二:排它锁 & 共享锁?
回答
排他锁和共享锁主要是针对的MySQL的InnoDB引擎的
共享锁【S锁】
又称读锁,若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。
排他锁【X锁】
又称写锁。若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。这保证了其他事务在T释放A上的锁之前不能再读取和修改A
共享锁与排它锁区别
1、 加了共享锁的对象,可以继续加共享锁,不能再加排他锁。加了排他锁后,不能再加任何锁。
2、 比如一个DML操作,就要对受影响的行加排他锁,这样就不允许再加别的锁,也就是说别的会话不能修改这些行。同时为了避免在做这个DML操作的时候,有别的会话执行DDL,修改表的定义,所以要在表上加共享锁,这样就阻止了DDL的操作。
3、 当执行DDL操作时,就需要在全表上加排他锁

问题三:Mysql 索引是怎么实现的?
回答:Mysql索引是通过B+tree算法实现的,索引分为聚簇索引与非聚簇索引,InnoDB是聚簇索引数据,MyISAM是非聚簇索引数据,InnoDB将通过主键聚集数据。如果没有定义主键,InnoDB会选择一个唯一的非空索引代替。如果没有这样的索引,InnoDB会隐式定义一个主键来作为聚簇索引。非聚簇索引也是根据主键来作为索引。
聚簇索引叶子节点保存的是具体的数据,根据B+tree的搜索规则可以直接查到值;而非聚簇索引叶子节点保存的是数据所在的物理地址,根据B+tree的搜索规则可以直接查到值的物理地址,然后根据地址进行一次I/O就能够查到值。

缓存

问题一:缓存可能会遇到的问题?
回答
缓存穿透:
现象:查询一条缓存和数据库都不存在的数据,直接穿透缓存查询数据库,造成数据库压力。
解决:查询不到数据,返回设置一个该数据的缓存,同时设置过期时间;或者采用在缓存之前在加一层 BloomFilter ,在查询的时候先去 BloomFilter 去查询 key 是否存在,如果不存在就直接返回。

缓存击穿:
现象:大量的请求同时查询一个 key 时,此时这个key正好失效了,就会导致大量的请求都打到数据库上面去
解决:我们可以在第一个查询数据的请求上使用一个 互斥锁来锁住它。其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。

缓存雪崩:
现象:缓存雪崩的情况是说,当某一时刻发生大规模的缓存失效的情况,比如你的缓存服务宕机了,会有大量的请求进来直接打到DB上面。
解决:事前:使用集群缓存,保证缓存服务的高可用;事中:使用本地缓存 +限流&降级,避免MySQL被打死;事后:尽快恢复缓存集群

热点数据失效:
现象:对于一些热点的数据来说,当缓存失效以后会存在大量的请求过来,然后打到数据库去,从而可能导致数据库崩溃的情况。
解决:设置不同的失效时间;加互斥锁

缓存一致性问题
现象:更新数据库数据的时候,缓存没来得及更新,用户查询的数据为脏数据,缓存数据不一致。
解决:先淘汰缓存再更新数据库,再生成缓存。

问题二:先删缓存还是先更新数据库,为什么?
回答:建议是先删除缓存再更新数据库,最多会导致以此cache miss.

Kafka

问题一:kafka 的架构包含了哪些角色?
回答:主要包含了producer(生产者),broker(中间商),consumer(消费者)

问题二:kafka 的最小工作单元?
回答:Kafka由producer生产消息,发送给topic,topic中进行partitions(区),然后一个partition对应多个segment,然后再由consumer进行消费,所以最小的工作单元应该是segment 。

问题三:kafka 消息重复消费原因?幂等怎么做的?
回答
kafka 消息重复消费是怎么产生的:
消费者fetch消息,然后处理消息,然后保存offset。如果消息处理成功之后,但是在保存offset阶段zookeeper异常导致保存操作未能执行成功,这就导致接下来消费者再次fetch时可能获得上次已经处理过的消息,原因offset没有及时的提交给zookeeper,zookeeper恢复正常还是之前offset状态。
幂等实现原理:
1、为每个producer设置唯一的PID;
2、引入seq number以及broker端seq number缓存更新机制来去重。

问题四:kafka ack 机制?集群中的ack 是怎么实现的?
回答:ack机制是一个确认机制,确认produce发送过来的消息broker是否需要回复,这个是为了预防producer发送的消息是否会丢失。
ack实现:
acks = 0,发就发了,不需要 ack,无论成功与否 ;
acks = 1,当写 leader replica 成功后就返回,其他的 replica 都是通过fetcher去异步更新的,当然这样会有数据丢失的风险,如果leader的数据没有来得及同步,leader挂了,那么会丢失数据;
acks = –1, 要等待所有的replicas都成功后,才能返回;这种纯同步写的延迟会比较高。
一般设置成1就可以了,极端情况下可能会丢失;如果对延迟没什么要求的话,可以设置成-1

最后还问到了redis的一些问题和是否查看了源码,redis我们项目中没用到,我也仅仅是了解下,没有深入去用过,关于源码,也只是了解了以下,如果大家要更深入还是最好多读点源码,有助于进步,谢谢。

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值