首先自我介绍。
1.一道笔试题
判断是否匹配:
aabb 匹配 “杭州 杭州 北京 北京”,返回true。
abab 匹配 “杭州 北京 杭州 北京”,返回true。
abab 匹配 “杭州 杭州 北京 北京”,返回false。
2.说一下hashmap的底层数据结构
我自己对map这块知识点的总结 : 传送门
3.ArrayList和LinkedList的区别
简单来说就是数组和链表的区别
ArrayList底层使用的是数组,因此它具有数组的优点和缺点。优点是检索效率高,缺点是定容,在增加或删除数据时开销大。
LinkedList底层使用的是双端链表,它具有链表的优点和缺点。优点是增加和删除时只需要修改引用,缺点是检索时只能通过遍历解决,开销较大。
4.想做一个线程安全的计数器,怎么做
用AtomicInteger做,它底层使用的是Unsafe包中的方法,使用的是CAS操作来对数据进行修改。CAS是Java中一种轻量级锁的实现,它在操作之前要判断目标对象是否处于一个理想值,如果是则进行操作,如果不是就不会进行操作。在多线程的环境下,如果线程B对计数器做了修改,线程A在修改计数器时发现计数器已经不是自己理想的状态了,就不会再把错误的值写入了,这样就能保证计数器计数的准确性。
5.说到锁,在方法上使用synchronized和在代码块中使用synchronized有什么区别
在方法上使用synchronized会把当前对象或当前类作为互斥量对象,在代码块中使用synchronized则需要显式的指定互斥量对象。还有就是在方法上使用synchronized会把整个方法中的代码作为临界区代码,这样可能会包括一些线程安全的代码,这样就会造成资源浪费,使得线程执行临界区代码的时间增长,等待线程等待的时间增加,整体并发效率降低。用synchronized修饰一个代码块能灵活的修改临界区代码数量,把真正需要通过synchronized去保证线程安全的代码保护起来,一来能提高整体整体的并发效率,二来能自定义互斥量对象,能够实现更加复杂的业务。
6.你知道双重检查锁吗?
知道,双重检查锁就是两个if条件来过滤请求线程,并且用synchronized关键字来修饰第二个if,保证线程过滤的正确性。
7.为什么要有两个if条件呢?为什么第二个if要在synchronized中
这一块我举例来说,比如我用双重检查锁来防止缓存穿透。有多个线程来查询数据时首先要查缓存,缓存中没有值再去查询数据库。
现在有线程A和线程B同时来查询数据,线程A在第一个if判断处发现缓存中没有值,于是他决定去查询数据库,这是线程A的CPU时间片结束。线程B也来到第一个if处进行判断,也发现缓存中没有值,也要去查询数据库。
接着线程A和线程B要进入synchronized代码块中,线程A进入,在第二个if条件处判断结果为查询数据库,然后查询数据库,并把值写到缓存。线程B进入synchronized代码块中,在第二个if处发现缓存中有值了,就不去查询数据库了。
第一个if条件用来过滤大部分的线程,第二个被synchronized修饰的if就是用来过滤高并发情况下多个线程穿透第一个if的线程的,能够通过synchronized的排他性准确控制查询数据库的线程只有1个。
8.说一下你对SpringIOC的理解
ioc表示一个依赖倒置的思想,让被依赖对象给出依赖,颠覆了传统的由依赖方获取被依赖方的编程思想。
Spring主要是通过依赖注入,也就是DI的方法实现依赖倒置。通过Spring的IOC拿到所有被依赖方的依赖,然后注入到需要这个依赖的对象中,实现特点是侵入型低,使用方便,高拓展性。
9.AOP思想也说说吧
aop是一种面向切面编程思想,由aop联盟提出。Spring框架中也集成了aop编程的模块,实现方式是通过动态代理的方式去把代码织入到方法周围,给方法进行一个逻辑加强。动态代理的实现分为两种,一个是jdk中自带的动态代理,这需要我们去面向接口编程,因为它是基于接口的,另外一个是使用CGlib框架去做动态代理,CGlib底层是通过操作java字节码的方式在jvm中生成代理类,是一种基于继承的动态代理,在代理类中回调被代理类的方法并进行逻辑加强。
我在开发中使用aop主要是把一些业务代码和系统服务代码进行分离,达到解藕的目标。
小结
这次面试还是比较顺利的,因为是简历面,所以面试官问的问题也都比较简单,希望自己能够再接再厉,一路通关。