java面试题:2018年10月-1

好不容易从传统行业跳出来,选了一个互联网的坑,没想到公司融资失败,业务又不好,到试用期快结束的时候才劝退,还是当天就让你走的,这种公司我也只能自认倒霉了,在此提醒大家要在要看好公司,否则太影响心情了;接下来整理下我后面几次面试的一些问题:

有赞:

先从业务上去问:

1:请先讲讲你之前几个项目经验中对你帮助最大的项目是什么?

      这个需要自己准备下,我个人觉的要把项目的架构模块,业务模型讲讲,其中用了些什么技术【个人想法不知道有没有更好思路推荐】

2:你们项目中的哪些地方使用到了redis?

      我接触到的是作为session缓存,和mybatis的二级缓存

3:假如session发生变化时,redis的主库中挂了,此时没有同步到从库,要怎么设计防止数据不一致?

      这个问题我有点把握不住,因为redis是有持久化的,比如快照,或者aof;当redis主数据库挂掉感觉可以从aof文件中去重新读取之前的信息,不过这样貌似保证不了session的实时性(自己猜测,请大家指教,后续修改)

    redis持久化:

       rdb:在指定时间间隔内,将内存中的数据集快照写入磁盘。fork(复制)一个子进程,先将完整的数据集写入临时文件;写入成功后在替换直起爱的额文件,并用2进制压缩存储

     优点:1:数据只包含一个文件,容灾备份非常方便,只用将文件压缩后转移到其他磁盘上

                2:性能最大化:使用单独子进程来进行持久化,主进程不会进行任何IO操作,保证了redis的高性能 

                3:在数据量很大的情况下,相比aof方式,rdb的启动效率更高

     缺点:RDB是间隔一段时间进行持久化,如果持久化之间redis发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候

      aof:以日志的形式记录服务器处理的每一个写、删除操作,查询操作不记录,以文本方式记录

      优点:1:可以保持更高的数据完整性,如果设置追加file的时间是1s,如果redis发生故障,最多会丢失1s的数据;

                2:如果日志写入不完整支持redis-check-aof来进行日志修复【比如写入一半时发生宕机】;

                3:AOF文件没被rewrite之前(文件过大时会对命令进行合并重写),可以删除其中的某些命令(比如误操作的flushall)。 
      缺点:AOF文件比RDB文件大,且恢复速度慢。

我还是猜想aof的方式:

     日志文件进行同步,可以把错误率降到最低,但是还是无法完全避免数据丢失的情况

4:synchroniz和lock的区别是什么?

     1) lock是一个接口,而synchronized是java的一个关键字,synchronized是内置的语言实现

     2)synchronized在发生异常时候会自动释放占有的锁,因此不会出现死锁;而lock发生异常时候,不会主动释放占有的锁,必须手动unlock来释放锁,可能引起死锁的发生。

     3) lock等待锁过程中可以用interrupt来终端等待,而synchronized只能等待锁的释放,不能响应中断;

     4)lock可以通过trylock来知道有没有获取锁,而synchronized不能;

     这个参考网址:https://blog.csdn.net/u011307997/article/details/77444110

开始从java基础:

1:假如你想保证线程安全,你要用什么集合呢?

     concurrentHashmap

2:你知道concurrentHashmap在1.7和1.8中安全的区别在哪里体现?

      所机制会有所不同

     jdk1.7采用的分段所机制,简单讲就是讲一个大table分割成多个小table进行加锁;他是先定义一个segment数组和多个hashEntry组成,它这里sgment就是继承了ConncurrentLock类,通过trylock()方法获取锁;锁粒度是segment

   jdk1.8采用的是node数组+链表+红黑树的数据结构,摒弃的所分段,采用的synchronize和cas来进行的保证数据一致,锁力度更小,速度更快,结构也会更清晰;

    这里是简单讲解下,最好去看下他们的里面的详细结构和机制

3:你有了解过concurrent包里的方法么?

4:有了解过栅栏和闭包么?

      栅栏:类似于闭锁,它能阻塞一组线程直到某个事件发生。栅栏与闭锁的关键区别在于,所有线程必须同时到达栅栏位置;才能继续执行。闭锁用于等待事件,而栅栏用于等待其他线程,且能够重复执行。

      给一篇参考文章:https://blog.csdn.net/u012572955/article/details/54971072?utm_source=blogxgwz2

     下面是我用里面代码运行的效果图:

     

      就是让线程到都达到之后,才能进行下一次事情

毕竟没有实际场景中用过。这里也就简单的说下      

      闭包:闭包能够将一个方法作为一个变量去存储,这个方法有能力去访问所在类的自由变量。

关键点:

如何用变量去存储方法?

        java中能够保存方法的变量指的就是普通的对象

如何让这个普通对象能够访问所在类的自由变量?

        纯天然的解决办法是:内部类。内部类能够访问外部类的所有属性及方法。

隐藏具体实现是内部类的作用之一,如何保证隐藏具体实现的同时还能将闭包传递到外部使用?

        让内部类实现通用接口,然后将内部类对象向上转型为接口类型。

因此,Java最常用的闭包实现办法(内部类+接口)

      参考博客:https://blog.csdn.net/icannotdebug/article/details/78594033

      这里说明了闭包和lambada的区别:https://www.jianshu.com/p/c22db2a91989      

5:你了解threadLocal的使用么?

spring问题:

1:请讲下springBean的加载过程,简单讲下

<bean id="demoBean"  class="xxx.xx.xx"  init-method="initMethod[方法]"  destroy-method="destroyMethod[方法]">

感觉就是上面的属性被定义了或者关联了,然后就会在初始化的时候会调用各自的方法

参考文章:https://www.cnblogs.com/kenshinobiy/p/4652008.html

    实例化一个bean(new)

    给这个bean添加属性

    调用setBeanName,传id

    调用setBeanFactory,传入自身工程

    调用ProcessBeforeInitialization()方法

    调用配置属性方法

    调用ProcessAfterInitialization()方法

    调用destroy()调用自己的销毁方法

    调用配置中写销毁方法

2:请说下spingbean的作用域:

     5中作用域:

  • singleton : bean在每个Spring ioc 容器中只有一个实例。
  • prototype:一个bean的定义可以有多个实例。
  • request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。
  • session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
  • global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。    

3:spring常用的注解有哪些?

     @controller @service @resource @transition

4:假如A是一个类,其中有方法B和方法C,B方法有注解,c方法没有注解,C会调用B的方法,然后外面调用C方法,此时有发生事物么?请分析下【这个问题可能我记录的有问题】

jvm问题:

1:请讲下cms收集器和G1收集器的区别?

       cms垃圾收集器:

             以获取最短时间为目标【标记-清除】

             执行过程:初始标记(stop)->并发标记 -> 重新标记(stop) -> 并发清除

             缺点:cpu资源敏感,无法清除浮动碎片,他的算法机制会导致有空间碎片

     G1垃圾收集器:

           并行并发的,分代收集,空间整合:整体是标记-整理,局部是2个regin的复制算法,可预测停顿时间的

           它是将java堆分成多个大小的独立区域,新生代和老年代不再是物理隔离

           执行过程:初始标记->并发标记 -> 重新标记 -> 筛选回收 【这个我在看下】       

2:讲下cms的收集器会发生几次stop事件?

        2次,请看上题       

3:每隔10分钟,系统里的元空间就会发生一次fullGC,请分析下可能出现的问题?

        首先需要讲明一下,在jdk1.8以后,永久带已经被metaspace(元空间)取代。metaspace是保存在本地内存中,是没有上限的;在jvm中有个参数MetaspaceSize的默认值约20.8m左右。jdl1.8以后,metaspace主要用来保存方法和类的,字符串常量的保存已经移动到了堆内存(堆内存会有个String常量池,这个String池保存的是的字符串的对象,这里不展开说);

      如果metaSpace总是发生fullgc,首先我们会想到的是该区域的设置初始值(MetaSpaceSize)是不是太小了,因为他的触发fullgc是由于类加载的容量已经超过该阈值;

      其次,我们会想metaSpace主要存放的是类,会不会是有关类加载的代码出现了什么问题,比如classloader不断创建,classloader不断加载class,之前的classloader和class在fullgc的时候没有回收掉。

      以上仅是个人猜测,如有不对请指导,万分感谢,同时对于jvm的参数相关知识,我这里推荐一个大神(你假笨:http://lovestblog.cn/)的博客,可以关注他的公众号还有他的一个jvm参数小程序,可以直接去查看参数的初始值什么的;同时推荐一篇文章:https://www.jianshu.com/p/1a0b4bf8d498

元空间相关补充:
       在查资料的时候发现(https://www.aliyun.com/jiaocheng/801203.html),jdk1.8的有限版本使用MetaSpaceSize设定初始值大小失败,需要用InitialBootClassLoaderMetaspaceSize这个参数来设定,因为metaspace是由两块构成,刚好是这个参数来设定总和(这里有介绍参数:http://lovestblog.cn/blog/2016/10/29/metaspace/

       MetaSpaceSize:Sets the size of the allocated class metadata space that will trigger a garbage collection the first time it is exceeded. This threshold for a garbage collection is increased or decreased depending on the amount of metadata used. The default size depends on the platform.(相关参数:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

扩展下:

      除了初始值大小的设定,每次meta增加的容量是否也要设定,还有最大值默认是物理内存大小这里也需要设定下

数据库:

1:mysql的查询的时候怎么用到行锁

    select * from table  for update;

2:mysql的索引结构知道么?

     b+树

3:mysql的索引在InnoDB和MyISAM下的区别是什么?

      myisam:非聚集索引,叶子节点存放的数据地址,索引和数据分开。辅助索引页也直接指向数据

      innodb:聚集索引,叶子节点会存放数据;辅助索引的叶子节点存放的是主键索引的键值,因此我们的主键的字段不要很大,否则辅助索引存放的信息也很大;查询的时候我们会从辅助索引去查,然后在到主键索引上去查;然后在叶子节点中,其实还有很多指针,比如回表指针,事物id指针,回滚指针,这些指针也就是他可以执行事务,回滚的原理;不过这些指针具体怎么用的我还没有查到

     先甩个连接,后面我再简化下https://blog.csdn.net/ljfphp/article/details/80029968

     这个博客写的很好,从树的结构,以及树优化后的作用,说的清晰,通过文章知道了范围索引是怎么回事

     https://www.cnblogs.com/boothsun/p/8970952.html

4:sql优化问题1:假如分页查询时,limit偏移量超过4w的时候,我们对数据查询有什么可以优化的?

      代码示例: select * from user limit 10000,10;   这条sql语句多次运行,时间保持在0.0187左右  

      代码示例: select * from user where uid >=( select uid from user order by uid limit 10000,1 ) limit 10; 这条sql语句多次运行,时间保持在0.0061左右,只有前者的1/3。可以预计offset越大,后者越优。

     这个主要需要去了解mysql的索引机制

      参考网址:

       https://blog.csdn.net/fdipzone/article/details/72793837  这个讲述了原理

5:sql优化问题2:一张表建立一个聚合索引a,b,c(有顺序的),此时我查询时where条件下a=xxx and b=xxx,此时会走索引么?假如走索引走什么的索引?

      会走索引,走索引ab索引;

     abc顺序的索引可以搜索,a,ab,abc;如果是bc的话,则不走索引

分布式问题:

6:你了解分布式锁么?

      多个系统在竞争同一个资源的时候,我们需要添加分布式锁,来维持住数据的一致性;多系统不能像java一样使用synchronize和Lock来保证数据的一致性,因此我们需要自己想办法创建这个锁;

     我刚开始想这个问题的时候,最开始想到的是不是利用数据库,比如某张表有了一条数据,那么再插入时这个数据不允许插入,或者说使用方法是,先去取这个数据,我们看这个数据的某些条件是不是被用,我们就不能用了-- 

   后面有个zookeeper是另一个方法

     方法1:数据库锁

               1基于MySQL的锁表

                  获取锁:插入数据

                  释放锁:删除数据

                  该数据的某个字段是唯一索引

            问题:1:无效时间:假如我解锁失败,这条数据一一直在,我永远无法获得锁了

                       2:非阻塞锁:insert失败的时候,会直接报错,没有等待时间

                       3:不可重入:A方法里有B方法,A方法调用插入锁,B方法里面还有个方法也要插入锁,虽然是同一个操作,但是没法继续【自己想的,不知道这样的描述是否有问题,请大家指正】

          2:我们可以把他改成乐观锁,给每个方法加一个版本号,版本号比他大1就可以操作,否则不能操作,可以避免死锁问题,先不详细讲

      方法2:缓存锁(这里主要利用的是rediss)

              1:基于setnx和expire两个命令实现

                   获取锁:setnx lock:codehole true:当缓存key不存在时,才会set数据,否则返回false;

                   释放锁:del lock:codehole; 删除

                                 expire lock:codehole 5;设置有效时间为5秒,方式死锁

                  老版setnx 和 expire命令是分开的,在这之间发生问题,同样会死锁,后来2.8中添加了一个原子命令如下

                  set lock:codehole true ex 5 nx;   ---这个就是组合写法

                 缺点:主从模式下,主库(a)获取锁,从库(b,c)没有同步,a挂了,在哨兵模式下,b变成主库,此时别的地方要获取锁就能获取成功,这样就有问题了

            2:利用redis的readlock算法

                   就是n(默认5)个独立的redis节点,同时setnx和expire,如果多个节点数获取成功,就拿到锁

        方法三:利用zookeeper来实现分布式锁

                  zookeeper的特点: 1:有序节点,/mylock/lock-0000000000; 2:临时节点,3:事件监听   

         1)创建一个目录mylock; 
         2)线程A想获取锁就在mylock目录下创建临时顺序节点; 
         3)获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁; 
         4)线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点; 
         5)线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。   

zookepper的分布式锁参考博客:https://blog.csdn.net/qiangcuo6087/article/details/79067136    

分布式锁参考博客:https://blog.csdn.net/xlgen157387/article/details/79036337

7:你了解分布式事物么?

     首先我们先讲讲cap: consistency:一致性  availabilty:可用性  partition tolerance:分区容错性

任何一个web应用最多支持上面的两个特性;

     base原理:basically available(基本可用) soft state(软状态)  eventually consistant(最终一致性)

    acid:原子性,一致性,独立性,持久性

     多个节点下保保证事物的acid;

方法1:2阶段提交(2pc)

         a:投票阶段:参与者将通知结果告诉协调者

         b:提交阶段:收到参与者通知后,协调者在发通知,根据反馈情况决定各参与者是否要提交还是回滚

         缺点:若有节点处于阻塞,所有节点都阻塞

方法2:补偿事物(ttc)

       核心:对每个曹锁,都要有一个注册,与其对应的确认和补偿(撤销)操作

      a:try阶段:对业务系统做检测及资源预留

      b:confirm阶段:主要对业务系统做提交

      c:cancel阶段:业务出错,回滚状态下

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

     消息生产方:创建一个表(消息表)记录消息发送的状态,消息表和业务表在同一个事物里,之后通过MQ发送消息。

     消息消费方:处理消息,若业务处理成功,返回成功,若失败,重新执行,或者给生产方发送补偿消息。

     特点:生产方和消费方定时扫描本地消息表,把没处理完或者失败的重新发送

方法4:mq事物消息(RocketMQ)

    第三方MQ支持消息事物,比如rocketMQ

    rocketmq的事物本质也2段提交的方式

方法5:Sagas事物模型(这个我还没搞懂,先放着)

8:请讲讲rabbitMQ和kafaka的区别

    Kafka:严格保证了消息队列的顺序,就是一个topic下面的一个分区内只能给一个消费者消费;对于一个分区来说,kafka是不支持并发,但是可以通过扩大分区实现并发

   Rabbitmq 不承诺消息的顺序性,因此可以并发多线程处理。在队列中不必排队。如果对处理的顺序没有要求,就可以用Rabbitmq教容易的实现并发。

9:rabbit场景问题:假如我发送聊天消息,必须按顺序执行,你怎么处理?

    当时被问的有点蒙,回过来来想,是不是可以从应用层的角度去考虑,比如每条消息我保存到数据库中,并记录他执行的顺顺序,当该步执行完毕,我再去发送下一条的消息;【感觉这个就是分布式事物】

    其他方法稍后再总结

10:zookeeper是如何实现服务发现和服务管理的?

11:线程池有了解过么?

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值