java面试整理
IO和NIO的区别
这是一个很常见的问题,如果单纯的只回答IO和NIO的区别,只能算及格。我个人觉得应该从以下几个方面回答:
1)、IO简介,
2)、TCP的三次握手,因为这也是两者的区别之一,
3)、NIO简介,
IO:(Block-IO)是一种阻塞同步的通信模式。是一个比较传统的通信方式,模式简单,使用简单。但并发处理能力低(每次连接都会创建新的线程进行链路处理),通信耗时(TCP三次握手),依赖网速。
TCP三次握手:
第一次握手:建立连接,客户端发送syn包到服务器端,等待服务器确认
第二次握手:服务器收到客户端传来的syn包,给客户端返回ack和syn包,等待客户端确认
第三次握手:客户端收到服务器传来的ack+ayn包,向服务器发送ack包,连接建立成功
NIO:(New IO / Non-Block IO)是一种非阻塞同步的通信模式。客户端和服务器之间通过Channel通信(避免TCP建立连接使用三次握手带来的开销)。NIO在Channel进行读写操作。这些Channel都会被注册在Selector多路复用器上。Selector通过一个线程不停的轮询这些Channel。找出已经准备就绪的Channel执行IO操作。
参考文档
Java堆和栈的区别
1)、堆和栈的共同点,
2)、堆和栈的区别,
3)、线程之间数据共享,
Java内存分两类,一类是堆内存,一类是栈内存
堆:主要用于存储实例化的对象,数组。由JVM动态分配内存空间。一个JVM只有一个堆内存,线程是可以共享数据的。
栈:主要用于存储局部变量和对象的引用变量,每个线程都会有一个独立的栈空间,所以线程之间是不共享数据的。
既然回答了内存,其实可以把内存溢出的情况一并回答。
谈谈内存溢出
1)、先回答什么是内存溢出,
2)、再谈谈内存溢出的原因,
3)、最后提出几个内存溢出的解决方案,
内存溢出:程序运行实际使用的内存大于虚拟机设置的内存
溢出原因:
1)、虚拟机配置参数设置不合理
2)、代码中出现死循环或者产生大量的重复对象实体
3)、数据流如果没有闭关也容易导致
4)、内存中一次性加载的数据量过于庞大
5)、大量的垃圾不能被JVM回收
解决方案:
1)、最直接,最不负责的方案就是修改JVM启动参数
2)、正确流程是通过检查错误日志,查找OutOfMemory错误原因,然后修改bug
JVM垃圾回收机制
1)、先回答什么是JVM认为的垃圾,
2)、都有那些常用的垃圾回收算法,
3)、都有那些常用的垃圾回收器,
JVM认为那些不被使用的对象就是垃圾,需要从内存中除掉。
常用的垃圾回收算法有:引用计算法,标记清除法,标记压缩法,复制算法,分代,分区思想
引用计算法:古老的算法,对象被引用时加一,引用断开时减一,若为零则被当成垃圾回收。频繁加减操作性能低。
标记清除法:先把垃圾"标记"(遍历所有的GC Roots,然后将所有GC Roots可达的对象标记为存活的对象),后统一"清理"。清理后的内存空间不连续,性能不高。
标记压缩法:在标记清除法的基础上做一个压缩功能。
复制算法:内存被分为两个大小相同的A,B两块,使用A内存时,将把A内存中的活对象复制到B,然后清空内存A中的所有对象。使用B内存也是一样的。缺点是内存变小了。
分代思想:新生代,老年代(新生代中的对象经过频繁的GC中存活下来的对象)。新生代因为存活率低,需要复制的对象少,建议用复制算法。老年代存活率高,需要清理的对象少,建议用标记压缩法。
分区思想:将整个内存分为多个独立空间。在每个独立空间进行垃圾回收,提高系统性能。
串行垃圾回收器:使用单线程进行垃圾回收
并行垃圾回收器:使用多线程进行垃圾回收,对性能要求比较高
CMS收回器:并发回收器,比并行更快,当占用的资源更多,可以尽可能减少系统停顿时间
G1回收器:JDK1.7之后提供的新的收集器,是基于标记压缩的算法,特点是针对整个java堆进行
参考文档:
JVM系列博客:https://blog.csdn.net/column/details/javavirtualmachine.html
lock和synchronized区别
1)、简单介绍两者的区别
2)、工作中建议使用synchronized的原因
3)、JDK1.6之后对synchronized的优化
synchronized:关键字,在jvm层面上,线程正常执行完后会释放锁,若线程发生异常则由jvm释放锁,jdk1.5之前没有锁竞争机制,容易出现线程一直阻塞的情况,只适合少并发量使用。
Lock:类,必须在finally中释放锁,因为添加了锁竞争机制,不会出现线程一直等待的问题,适合高并发量使用。
工作中建议使用synchronized关键字,原因很简单,jdk1.6之后对synchronized做了大量的优化。为了减少获得锁和释放锁所带来的性能消耗,引入了“轻量级锁”和“偏向锁”来提高性能。
一、适应性自旋
降低了线程在等待锁的过程中的开销。因为线程从挂起到恢复是很耗时的,有些线程在挂起后很短的时间内就可以获取锁,通过漫无目的的循环让线程暂不挂起。适应性自旋表现在自选的循环次数是根据实际情况来定。
二、锁消除
删除一些没必要的加锁操作。
三、锁粗化
虽然减小锁的粒度,可以提高性能。但有些代码明明只需要加一把锁,你非要加多个??锁粗化可以把多次加锁和解锁合并成一次,用以提高效率。
四、轻量级锁
轻量级锁所适应的场景是线程交替执行同步块的情况,如果存在同一时间访问同一锁的情况,就会导致轻量级锁膨胀为重量级锁。
五、偏向锁
引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行路径,因为轻量级锁的获取及释放依赖多次CAS原子指令,而偏向锁则是在只有一个线程执行同步块时进一步提高性能。
参考文档:
lock和synchronized区别:https://blog.csdn.net/wangtaomtk/article/details/52264043
lock和synchronized区别:https://blog.csdn.net/u012403290/article/details/64910926?locationNum=11&fps=1
HashMap底层实现
1)、简单谈谈HashMap的底层实现
2)、HashMap和HashTable的区别
3)、工作中线程安全的HashMap使用
HashMap的底层是链表散列的数据结构,即数组和链表的结构。通过计算key的hash值确定数组的位置,若该位置已经有值,则往该元素后面添加,形成一个链表。每个Entity存储hash,key,value,next四个值。在检索中,通过hash找到数组的位置,然后在通过key遍历链表,直到找到为止。在jdk1.8以后,若链表长度超过阙值后,会将该链表转为红黑树,以提高检索效率。红黑树是自平衡查找二叉树,解决了二叉树多次插入新节点导致的不平衡。当红黑树插入新节点时会通过[变色]和[选择]来满足自身规则,详情可以参考文档连接。
HashMap,HashTable,LinkedHashMap区别
1)、HashMap不是线程安全的,HashTable是线程安全的,内部通过synchronized修改加锁,其性能较差
2)、HashMap允许一个key为null,HashTable不允许
3)、HashMap的初始容量是16,而HashTable的初始容量是11
4)、工作中HashMap的使用频率较高,若希望值有序则使用LinkedHashMap,若考虑线程安全则使用ConcurrentHashMap,ConcurrentHashMap使用分段式锁性能比HashTable好。
参考文档:
ArrayList和LinkedList的区别,Java 常用List集合使用场景分析
HashMap的实现原理:https://blog.csdn.net/tuke_tuke/article/details/51588156
hashMap和hashTable的区别:https://www.cnblogs.com/aspirant/p/6856487.html
什么是红黑树:http://www.sohu.com/a/201923614_466939
Java单例模式实现
1)、什么是单例模式
2)、常见的单例模式有那些
3)、写出其中的一种
单例模式:某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
常见的单例模式有:懒汉模式,饿汉模式,静态类内部加载
// 线程安全的静态类内部加载
public class SingletonDemo{
private static class SingletonHolder{
private static SingletonDemo instance = new SingletonDemo();
}
public static SingletonDemo getInstance() {
return SingletonHolder.instance;
}
}
参考文档:
https://www.cnblogs.com/cielosun/p/6582333.html
谈谈MVC模式
听到这个问题时,我是窃喜的,多么简单的问题,可是面试的时候,却不知道该怎么回答。
MVC:model,view,controller。
Model:模型层,负责实现业务逻辑,操作数据库
View:视图层,负责页面展示
Controller:控制层,负责处理请求
我们通过实际的框架来了解,
SpringMVC,它实现了mvc设计模式的web框架。用户发送的请求会通过前端控制器(DispatcherServlet),根据URL映射到对应的Handler中。而这个Handler可以是用注解@RequestMapping修饰的方法,而这个方法所在的类可以用注解Controller修饰表明该类是一个控制层。可以在方法里面调用接口完成业务逻辑的操作和数据修改,将返回的结果放在ModelAndView变量中。DispatcherServlet会通过视图解析器拼接返回页面的路径,并将数据放到作用域中,渲染给用户。实现业务逻辑的接口可以理解为Model层,处理请求的类可以理解为Controller层,页面就是View层。
谈到SpringMVC,就不会少了Struts2,
Struts2也是一个基于MVC设计模式的Web应用框架,在web.xml文件中将符合要求的请求交给Servlet处理,这个 Servlet再参考struts-config.xml文件找到对应的action方法,执行完成后关联到对应的页面。SpringMVC比Struts2简单了很多。
我们再简单谈谈Hibernate,Mybatis,SpringData
我们还可以再谈谈Spring
还有设计原理
偏应用的问题
因为时间和精力的情况,这里只做简单整理的问题。
一、redis事务是怎么做的?
答:redis的事务主要是通过multi(开启事务),exec(提交事务),watch(监控版本号)几个命令完成的。
二、如何实现抢购防超卖功能?
答:抢购,秒杀的场景下既要保证用户体验, 又要防超卖。其实很简单,减少数据库的调用,1000个商品限时抢购,就是1000个数字减一。通过加锁保证线程安全。使用缓存提高效率。
三、如何设计数据查询接口保证数据的安全和性能?
答:安全:服务器接口令牌验证;查询参数长度校验;查询参数格式校验(避免sql注入);
性能:索引优化;Ehcache本地缓存;redis缓存预热;
四、Nginx如何做权限拦截?
答:Nginx和lua可以实现权限拦截
五、2*16如何最快计算结果?
答:向左移动4位
六、架构如何用一个字段设置10个Boolean型权限?
答:用int型字段,分别用0,1表示false和true
七:为什么不用mysql做分布式锁?
答:我会从性能方面回答,用redis做分布式锁,是判断key值是否存在,存在则表示有锁。用Zookeeper做分布式锁,是判断临时节点是否存在,存在则表示有锁。而mysql做分布式锁在并发量高的情况下出现死锁。
八:分布式系统如何避免消息重复消费?
答:从生产者避免重复发送角度:将生产者发送的消息持久化到数据库中,如有相同的信息则不再保存;当生存者服务的事务完成后,消息服务会将数据库中的消息发送给消费者。
从消费者避免重复消费角度:消费者服务可能是集群,要考虑幂等性。查询和删除是天然的幂操作,所以我们要在更新和创建之前做判断,是否已经存在,是否已经更新。
九、你对我们公司的了解么?
答:.......