常见的面试问题和面试 遇到的一些没答上来的问题总结。有些问题可能平时学习时容易忽略,面试被问到了才后悔没有仔细研究。希望这篇文章对所有读者有所帮助,有所启发。面试的准备不在于能猜到面试官要问什么,其实猜到了也没有意义,面试的准备过程和面试的过程最大的意义在于让自己知道还需要学什么,还缺什么。
1、意义最大的项目
2、项目中遇到的最困难的问题
3、项目中用到的设计模式
责任链模式,策略
4、hashmap和concurrenthashmap的区别和优缺点
hashmap线程不安全,chashmap线程安全
5、熟悉的设计模式
策略,责任链,builder,工厂,适配器,观察者,模板方法,装饰器,代理,
创建型:工厂,抽象工厂,单例,建造者,原型
结构型:适配器,装饰器,代理,外观,桥接,组合,享元
行为型:策略,模板方法,责任链,观察者,迭代子模式,命令,状态,备忘录,访问者,中介者,解释器
6、Java的垃圾回收机制
1、Java内存分为新生代,老年代,再细分可以分为eden,两个survivor区,新创建的对象被分配在eden区,当分配不下时,触发新生代GC,将存活的对象复制到其中一个survivor区,判断对象是否应该被回收常见的有两种算法,一种是引用计数,一种是可达性分析,引用计数是为对象添加一个引用计数器,如果引用计数器是0则可以被回收,这种方法在某些情况下会导致内存泄漏,比如说循环引用,可达性分析是指定一些对象作为GC Roots,可以作为gcroots的对象一般是本地变量表中的对象,静态类属性,常量引用的对象等,从这些对象出发的调用链到达不了的对象,会被认定为是需要回收的对象。值得注意的是,虚拟机在分析对象的引用关系时,为了防止引用关系发生变化,会暂停所有线程,就是常说的stoptheworld。当对象被认定为是需要回收的对象,则虚拟机会将其回收,回收算法有很多种,新生代使用以空间换时间的复制清除,除此之外还有标记清除,标记整理等,例如在老年代这种不需要额外空间来争取效率的场景就会使用标记清除或者标记整理。
常见的收集器:
G1:全区域的垃圾收集,
7、数据库的范式
1、第一范式:每一个字段保证原子性
2、第二范式:表字段与主键有依赖关系
3、第三范式:表字段与主键的依赖要求直接依赖
8、项目中如何解决高并发的问题
1、在高频率读的数据加入缓存
2、注意并发的安全问题,在需要的地方使用同步容器或加锁
3、加机器
4、避免循环查库等消耗性能的操作
9、分布式环境下如何保证订单号唯一
服务唯一编码+时间戳+oracle自增序列
10、微服务和SOA
11、Spring bean的生命周期
- Spring容器 从XML 文件中读取bean的定义,并实例化bean。
- Spring根据bean的定义填充所有的属性。
- 如果bean实现了BeanNameAware 接口,Spring 传递bean 的ID 到 setBeanName方法。
- 如果Bean 实现了 BeanFactoryAware 接口, Spring传递beanfactory 给setBeanFactory 方法。
- 如果有任何与bean相关联的BeanPostProcessors,Spring会在postProcesserBeforeInitialization()方法内调用它们。
- 如果bean实现IntializingBean了,调用它的afterPropertySet方法,如果bean声明了初始化方法,调用此初始化方法。
- 如果有BeanPostProcessors 和bean 关联,这些bean的postProcessAfterInitialization() 方法将被调用。
- 如果bean实现了 DisposableBean,它将调用destroy()方法。
12、TCP和UDP的区别
13、xml 中配置的 init、destroy 方法怎么可以做到调用具体的方法
14、自增的原子性
15、volatile (long类型的原子性)http://www.cnblogs.com/dolphin0520/p/3920373.html
cpu存在各自的高速缓存,两个线程同时操作自己cpu的高速缓存中的值,再写入内存,可能会出现和预期结果不一致的情况,这就是缓存一致性问题。解决缓存一致性问题,主要有两种方式:
1、在总线加LOCK#指令
2、缓存一致性协议
起初通过总线加锁实现,但是在加锁期间,其他CPU不能访问内存,效率有一丝低下。所以缓存一致性协议应运而生。核心思想是,当CPU在操作一个共享变量的时候,发出信号通知其他CPU将自己缓存里的变量设置为无效状态,等下次其他CPU再去操作这个变量的时候,发现缓存中变量状态是无效,就会去内存里读取,这样值就保证了一致
。
16、lock的实现
lock的锁实现是基于一个内部类Sync实现的,内部类Sync的实现则是基于JUC包中的并发组件的基础框架AQS,Sync有两个子类,分别的公平和不公平,对应了公平锁和不公平锁的两种实现方式。默认是不公平锁,在不公平锁的lock方法被调用时,线程会直接尝试acs的方式设置state的值为1,也就是说直接获取锁而不排队,所以叫不公平锁。如果值设置成功,也就是说当前线程已经获取了锁了,那将当前持有线程的变量值设置为当前线程,ownerThread这个变量是AQS的父类定义的一个变量。如果acs没有设值成功,则进入acquire方法尝试获取锁,首先调用的是tryAcquire方法,这个方法AQS是没有给出实现的,而是将实现留给了子类,意味着子类可以根据公平或不公平,或者其它原则来实现这个获取锁的过程。非公平锁的话首先还是ACS得方式将state值设置为1,如果成功则意味着成功获取了锁,那接下来的操作和之前一样,将持有锁的线程设置为当前线程,然后返回,如果没有成功,则说明锁被其它线程正持有,那就接着判断持有锁的线程和当前获取锁的线程是不是同一个线程,如果是的话,就将state+1,返回,这意思就是说ReetrantLock是可重入的,当前线程可以重复持有多次锁,只要使用完成后释放相应多的次数即可。AQS的state在不同的子类里代表不同的意思,在ReetrantLock中就表示了当前线程的锁的次数。两次尝试都没有成功的话,tryAcquire方法返回false,AQS调用acquireQueued方法,将当前线程加入到等待队列中,然后调用当前请求锁的线程的interrupt方法将线程阻塞。
17、AQS的实现
AQS通过模板方法模式为子类的实现定下基调。AQS中规范了acquire和release的流程,但是tryAcquire和tryRelease留给子类自己实现。AQS中维护了一个int类型的变量status,这个变量的使用很自由,子类可以任意定义变量所表示的含义,例如最常见的ReentrantLock中status就用来表示锁被线程持有的次数。AQS中维护了一个队列来放置等待获取锁的被阻塞的线程,每个线程被构造成一个Node对象。
18、spring aop的实现原理,动态代理
aop中的动态代理:
1、JDK动态代理:代理对象与被代理对象实现同一接口
Proxy.newProxyInstance JDK反射包提供的工厂,Class对象在运行时以 参数形式传入,也就是说代理关系是在运行时指定的,不是在编译时指定的,因此是动态代理,和静态的有区分,字节码级别的代理对象
2、CGLIB:被代理类不需要强制实现接口,通过实现MethodInterceptor ,重写intercept方法,
所有的动态代理,都是在运行时生成被代理的子类。spring aop根据被代理的子类是否实现了接口,选择使用哪一种代理方式,【问题:如何知道是否实现了接口呢】
19、jvm调优的参数
20、线程池的实现原理,参数的用处 https://www.cnblogs.com/KingJack/p/9595621.html
execute方法:
workerCountOf(c) 获取当前线程池的线程数,如果小于指定容量,则addWorker
如果addWorker失败,或者当前线程数b不小于线程池容量
如果线程池还在工作而且向工作队列中添加任务成功,则进行一次recheck检查线程池当前的线程数是否小于线程池的容量,如果线程池没有在running,将任务从队列中移除,并且调用reject。如果线程池还在running,但是当前线程池数量为0,则新建任务
如果线程池没有在工作或者向工作队列中添加任务线程不成功,那么尝试直接创建新线程来执行任务,如果失败,reject方法调用
addWorker方法:
21、分布式缓存的一致性hash
https://www.cnblogs.com/akaneblog/p/6736386.html
22、项目的结构框图(支付系统的流程)
23、链表反转
1.就地反转法
public ListNode reverseList1(ListNode head) {
if (head == null)
return head;
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode prev = dummy.next;
ListNode pCur = prev.next;
while (pCur != null) {
prev.next = pCur.next;
pCur.next = dummy.next;
dummy.next = pCur;
pCur = prev.next;
}
return dummy.next;
}
24、数据库索引为什么用树
25、N个数字找K个最大值
1、小顶堆 时间复杂度O(NlogK)
2、分成很多份,每份再做小顶堆或者直接快排,可以发挥多核优势
3、bitmap去重
26、volatile能使得非原子操作变成原子的吗
能。long和double
27、什么是线程局部变量
28、双亲委派机制,哪些情况违反了,双亲委派的好处
回答:哪些情况违反:
1、jdk1.2之前就已经实现的类加载器
2、基础类需要回调用户代码的时候,线程上下文类加载器,可以使父加载器去请求子类加载器,实现逆向的加载
3、OSGI模块化热部署,实现的类加载器是网状结构,而非双亲委派模型的树状结构,类加载器可以委派同级别的其它类加载器完成
···双亲委派的好处:具备天生的优先级关系,使得同一个根类在不同的类加载环境中加载出来的都是一样的对象。例如Object类无论何时加载都得到一样的对象。
29、线程通信的方式
1、最容易想到的,共享变量,用synchronized,volatile等修饰的变量,可以用作线程之间的通信
2、队列,生产者消费者不同的线程之间用队列来共享内容
3、Condition
4、notify()
30、Happen Before
如果一个操作的结果需要对另一个操作可见,这两个操作就是happens-before关系
包括:
书写操作按照代码的顺序发生
unlock发生在下一个lock之前
读操作发生在写操作之前
线程的start发生在线程的其他动作之前
总的来说就是指令执行按照代码的顺序执行,如果前后代码存在关联关系
这些原则都是原生Java就满足的happens-before原则,是Java语言天生具备的有序性
31、HTTP协议
32、dubbo实现原理 https://blog.csdn.net/u013076044/article/list/2?
是一个分布式框架,分为服务消费者,服务提供者,注册中心等几个角色。注册中心会对服务的提供者进行实时的心跳检测,发现变化之后将最近的serverList推送给消费者,消费者在本地会缓存一份。一个服务新注册的时候,会主动向注册中心发送心跳。
dubbo是基于spring的命名空间加载的,spring在装配bean 的时候,遇上dubbo的命名空间,就会调用DubboNamespaceHandler来加载对象。
Dubbo会在Spring实例化完bean之后,在刷新容器最后一步发布ContextRefreshEvent事件的时候,通知实现了ApplicationListener的ServiceBean类进行回调onApplicationEvent 事件方法,dubbo会在这个方法中调用ServiceBean父类ServiceConfig的export方法,而该方法真正实现了服务的(异步或者非异步)发布。
https://blog.csdn.net/yanpenglei/article/details/80261762
export的步骤简介
首先会检查各种配置信息,填充各种属性,总之就是保证我在开始暴露服务之前,所有的东西都准备好了,并且是正确的。
加载所有的注册中心,因为我们暴露服务需要注册到注册中心中去。
根据配置的所有协议和注册中心url分别进行导出。
导出的具体操作:
1、loadRegistries方法构造url的列表
2、url的对象存储的是注册中心的消息,包括注册中心的host地址,协议,application,类型(常用的是zk)等信息
3、循环列表,调用doExportUrlsFor1Protocol方法将同一种协议发布到每一个注册中心上
4、doExportUrlsFor1Protocol方法:
1)、InetAddress.getLocalHost().getHostAddress()
2)、获取port等信息,没有配置则获取随机可用端口
3)、将application,side,method等参数解析到一个map里,application通过ApplicationConfig对象解析获得,method通过反射获得
4)、将host,port,map等信息构造成一个url对象
5)、如果配置的不是remote,则做本地导出。如果配置的不是local,则暴露为远程服务。
6)、proxyFactory构造Invoker
7)、获取完Invoker之后,转换成对外的Exporter,缓存起来。
所有的provider都被解析成ServiceBean。ServiceBean实现了InitializingBean接口,继承了ServiceConfig类。在afterProperties方法中对注册中心等进行配置,配置完成后调用了export方法,export方法中设置了延迟发布等配置,最后最核心的逻辑是调用doExport方法,将ip,端口,方法,类名等拼成url暴露给注册中心解析。针对每一个注册中心,分别组装url,进入RegistoryProtocol类的export方法,打开一个netty Server侦听服务,并接受客户端的各种请求,向注册中心暴露服务。
doExport方法的核心逻辑,校验application,校验注册中心等,最后调用doExportUrls。doExportUrls方法的逻辑是将同一种协议发布到不同的注册中心上。发布的方法是doExportUrlsFor1Protocol。
doExportUrlsFor1Protocol步骤:获取provider的host,
服务的提供者和消费者之间的调用是不经过注册中心的,而是直接的调用,因此消费者也可以直接在配置中指定提供者的ip地址。
消费者调用过程:RegistryProtocol对象的refer()方法,根据url和服务接口构造出invoker对象,
33、MQ重复消费
业务上幂等
34、stop the world是咋回事
回答:虚拟机在GC之前需要判断对象是否存活,常见的有两种算法,引用计数和可达性分析。HotSpot的基于可达性分析算法实现的,可达性分析算法必须要求定义一系列的GCRoots节点,并且在进行分析工作时这些节点的引用关系不能动态变化,不然分析的准确性得不到保证,因此在整个分析的过程中,所有的Java线程都将被停顿,sun将这件事称为stop the world。
35、AtomicInteger等原子类
36、动态规划 https://blog.csdn.net/rock_joker/article/details/68928150
37、ReentrantLock和synchronized的区别
1、Reen可以控制公平性,syn是天生非公平的。
2、Reen提供中断等待,超时等操作,syn不可以。
38、spring拦截器
39、spring事务
7大传播特性
有事务,加入,没有就新建
有事务,加入,没有就非事务
有事务,加入,没有抛出异常
有事务,嵌套(等于还是新建了一个)
有事务,挂起当前,新建一个
有事务,挂起当前,非事务执行
有事务,抛出异常,没有就非事务
5种隔离级别:
默认和数据库的一样
未提交读:没提交就能读
已提交读:提交了才能读
可重复读:
串行化
40、countdownlatch源码
41、NIO
[https://blog.csdn.net/chen8238065/article/details/48315085#%E7%9B%B8%E5%85%B3%E8%B5%84%E6%96%99
(https://blog.csdn.net/chen8238065/article/details/48315085#%E7%9B%B8%E5%85%B3%E8%B5%84%E6%96%99)
https://segmentfault.com/a/1190000003063859
https://blog.csdn.net/u014507083/article/details/73784898
41、死锁发生的条件和解决方案
死锁产生的条件:
1、互斥条件:资源不能被共享,只能由一个进程使用
2、请求与保持条件:进程已获得了一些资源,但因请求其它资源被阻塞时,对已获得的资源保持不放。
3、不可抢占条件(No pre-emption) :有些系统资源是不可抢占的,当某个进程已获得这种资源后,系统不能强行收回,只能由进程使用完时自己释放。
4、循环等待条件(Circular wait) :若干个进程形成环形链,每个都占用对方申请的下一个资源。
处理死锁的方法:
1、鸵鸟算法:
2、银行家算法:
对待死锁的策略主要有:
(1) 死锁预防:破坏导致死锁必要条件中的任意一个就可以预防死锁。例如,要求用户申请资源时一次性申请所需要的全部资源,这就破坏了保持和等待条件;将资源分层,得到上一层资源后,才能够申请下一层资源,它破坏了环路等待条件。预防通常会降低系统的效率。
(2) 死锁避免:避免是指进程在每次申请资源时判断这些操作是否安全,例如,使用银行家算法。死锁避免算法的执行会增加系统的开销。
(3) 死锁检测:死锁预防和避免都是事前措施,而死锁的检测则是判断系统是否处于死锁状态,如果是,则执行死锁解除策略。
(4) 死锁解除:这是与死锁检测结合使用的,它使用的方式就是剥夺。即将某进程所拥有的资源强行收回,分配给其他的进程。
42、线程池的参数
43、避免重复创建线程
https://blog.csdn.net/MingHuang2017/article/details/79571529
44、多线程的同步和互斥有哪几种方法
1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。
2、互斥量:为协调共同对一个共享资源的单独访问而设计的。
3、信号量:为控制一个具有有限数量用户资源而设计。
4、事 件:用来通知线程有一些事件已发生,从而启动后继任务的开始。
45、批量执行异步任务
46、索引使用B+树的好处,索引的原理
主键是唯一的聚集索引,覆盖了真实的数据,对其它字段加索引都是非聚集索引,非聚集索引是先找到主键,再通过主键查询数据。如果两个字段组成一个联合索引,那么以一个字段为条件查询另一个字段就不需要经过表,查询效率也会很高
B+树:每一个节点的叶子上限是2d。只有叶节点存储data。
https://www.cnblogs.com/wuchanming/p/6886020.html
mysql联合索引的最左前缀机制
https://www.jianshu.com/p/e1dce41a6b2b
47、spring的事务传播机制和隔离机制
7大传播特性
有事务,加入,没有就新建
有事务,加入,没有就非事务
有事务,加入,没有抛出异常
有事务,嵌套(等于还是新建了一个)
有事务,挂起当前,新建一个
有事务,挂起当前,非事务执行
有事务,抛出异常,没有就非事务
5种隔离级别:
默认和数据库的一样
未提交读:没提交就能读
已提交读:提交了才能读
可重复读:
串行化
48、sql语法
49、数据库乐观锁悲观锁
https://blog.csdn.net/xiaokang123456kao/article/details/75268240
50、springMVC dispatcherServlet源码
51、加解密算法,RSA,jdk的一些加密api
52、mq重发机制,nameServer挂了之后还能不能接着发消息
53、基本的UML图
54、redis线程模型
55、redis的持久化机制
RDB,AOF
56、netty框架
57、mysql主从复制和redis主从复制的区别
58、redis数据持久化 https://www.cnblogs.com/chenliangcl/p/7240350.html
59、