java面试题整理!
CMS与G1?
CMS包括四个过程:初始标记 并发标记 重新标记 并发清除(步骤一和三stop the word)
CMS收集器:一款以获取最短回收停顿时间为目标的收集器,是基于“标记-清除”算法实现的
G1收集器:面向服务端应用的垃圾收集器,过程:初始标记;并发标记;最终标记;筛选回收。整体上看是“标记-整理”,局部看是“复制”,不会产生内存碎片。
Spring容器?
Sping的容器可以分为两种类型
1. BeanFactory:(org.springframework.beans.factory.BeanFactory接口定义)是最简答的容器,提供了基本的DI支持。最常用的BeanFactory实现就是XmlBeanFactory类,它根据XML文件中的定义加载beans,该容器从XML文件读取配置元数据并用它去创建一个完全配置的系统或应用。
2. ApplicationContext应用上下文:(org.springframework.context.ApplicationContext)基于BeanFactory之上构建,并提供面向应用的服务。
ApplicationContext的实现?
ClassPathXmlApplicationContext:从类路径下的XML配置文件中加载上下文定义,把应用上下文定义文件当做类资源。
FileSystemXmlApplicationContext:读取文件系统下的XML配置文件并加载上下文定义。
XmlWebApplicationContext:读取Web应用下的XML配置文件并装载上下文定义。
什么是类加载器?
负责读取Java字节代码,并转换成java.lang.Class类的一个实例
类加载器种类?
启动类加载器 Bootstrap ClassLoader JAVA_HOME/lib
扩展类加载器 Extension ClassLoader JAVA_HOME/lib/ext
应用程序类加载器 Application ClassLoader ClassPath库
自定义类加载器 通过继承ClassLoader实现,一般是加载自定义的类
类加载的过程?
加载
根据一个类的全限定名(如cn.edu.hdu.test.HelloWorld.class)来读取此类的二进制字节流到JVM内部;将字节流所代表的静态存储结构转换为方法区的运行时数据结构(hotspot选择将Class对象存储在方法区中,Java虚拟机规范并没有明确要求一定要存储在方法区或堆区中)转换为一个与目标类型对应的java.lang.Class对象;
连接(包括验证 准备 解析)
验证
验证阶段主要包括四个检验过程:文件格式验证、元数据验证、字节码验证和符号引用验证;
准备
为类中的所有静态变量分配内存空间,并为其设置一个初始值(由于还没有产生对象,实例变量将不再此操作范围内);
解析
将常量池中所有的符号引用转为直接引用(得到类或者字段、方法在内存中的指针或者偏移量,以便直接调用该方法)。这个阶段可以在初始化之后再执行。
初始化
在连接的准备阶段,类变量已赋过一次系统要求的初始值,而在初始化阶段,则是根据程序员自己写的逻辑去初始化类变量和其他资源
Java多线程实现的三种方式?
继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的
Redis的高并发和快的原因?
redis是基于内存的,内存的读写速度非常快;
2.redis是单线程的,省去了很多上下文切换线程的时间;
3.redis使用多路复用技术,可以处理并发的连接。非阻塞IO 内部实现采用epoll,采用了epoll+自己实现的简单的事件框架。epoll中的读、写、关闭、连接都转化成了事件,然后利用epoll的多路复用特性,绝不在io上浪费一点时间。这里“多路”指的是多个socke网络连接,“复用”指的是复用同一个线程
MySQL limit offset?
mysql查询时,offset过大影响性能的原因是多次通过主键索引访问数据块的I/O操作
消息队列的应用场景?
解耦 最终一致性 广播 错峰和流控
乐观锁和悲观锁?
悲观锁:共享资源每次只给一个线程适用,其他线程阻塞synchronized和ReentrantLock
乐观锁:版本号和CAS算法,适用于多读的场景,可以提高吞吐量juc.atomic的实现方式
乐观锁适用于写比较少的情况,即冲突真的很少发生,省去了锁的开销,加大了系统的吞吐量。多写的情况,经常会产生冲突,导致上层不断重试,反而降低了性能,所以一般多写的情况下用悲观锁比较合适
ThreadPoolExecutor线程池?
SynchronousQueue LinkedBlockingQueue ArrayBlockingQueue DelayQueue
当超过最大的线程数时,且队列已满,就会跑出异常
new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue
这种线程池内部没有核心线程,线程的数量是有没限制的。
在创建任务时,若有空闲的线程时则复用空闲的线程,若没有则新建线程。
没有工作的线程(闲置状态)在超过了60S还不做事,就会销毁。
new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue
该线程池的最大线程数等于核心线程数,所以在默认情况下,该线程池的线程不会因为闲置状态超时而被销毁。
如果当前线程数小于核心线程数,并且也有闲置线程的时候提交了任务,这时也不会去复用之前的闲置线程,会创建新的线程去执行任务。如果当前执行任务数大于了核心线程数,大于的部分就会进入队列等待。等着有闲置的线程来执行这个任务。
ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue
有且仅有一个工作线程执行任务
所有任务按照指定顺序执行,即遵循队列的入队出队规则
new ScheduledThreadPoolExecutor(corePoolSize);
不仅设置了核心线程数,最大线程数也是Integer.MAX_VALUE。
这个线程池是上述4个中为唯一个有延迟执行和周期执行任务的线程池。
防止一条数据更新两次?
触发器 唯一索引
分布式锁的三种实现方式?
数据库排他锁
某个属性唯一性约束 for update 行锁
基于ZooKeeper的分布式锁
每个客户端对某个方法加锁时,在zookeeper上的与该方法对应的指定节点的目录下,生成一个唯一的瞬时有序节点。判断是否获取锁的方式很简单,只需要判断有序阶段中序号最小的一个。当释放锁的时候,只需将这个瞬时节点删除即可。
基于Redis的分布式锁
Set(key,value,nxxxmexpx,time)第一个参数是锁;第二个参数加锁的请求;第三个参数是NX,set if not exist,key存在不做操作,不存在时set操作;第四个是PX,设置过期时间
Redis如何保证数据一致性?
方案一:写库前后都进行redis.del(key),并且设置合理的超时时间。
方案二:异步更新缓存:MySql binlog增量订阅消费+消息队列+增量数据更新到redis
Redis哈希槽?
Redis集群有16384(2^14)个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。
使用过Redis做异步队列么,你是怎么用的?有什么缺点?
一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。缺点:在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。
什么是缓存穿透?如何避免?什么是缓存雪崩?如何避免?
缓存穿透
一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB)。一些恶意的请求会故意查询不存在的key,请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。
如何避免?
1:对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存。
2:对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该bitmap过滤。
缓存雪崩
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,会给后端系统带来很大压力。导致系统崩溃。
如何避免?
1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
2:做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期
3:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
Redis适用场景?
会话缓存 全页缓存 队列(list&set) 排行榜/计数器(set/sorted set)
Redis三种架构模式?
主从复制 哨兵模式 集群模式
Redis分布式锁实现?
先用setnx来争抢锁,抢到之后,再用expire给锁添加一个过期时间防止锁忘记释放
RPC框架
Object类的九大方法?
clone(): 创建并返回此对象的一个副本
equals(): 表示某个其他对象是否与此对象“相等”
Finalize(): 当垃圾回收期确定不存在对该对象的更多引用时,由对象垃圾回收器调用方法
getClass(): 返回一个对象的运行时类
hashCode(): 返回该对象的哈希码值
Notify(): 唤醒在此对象监控器上等待的单个线程
notigyAll(): 唤醒在此对象监视器上等待的所有线程
toString(): 返回该对象的字符串表示
Wait(): 导致当前的线程等待,直到其他线程调用该对象的notify()方法或者notifyAll()
java的wait()和notify()等方法必须在同步方法中?
每个对象都有一个“监视器monitor”,包括独占锁、入口队列、等待队列
调用wait()就是释放锁,释放锁的前提是必须要先获取锁;调用notify()方法是唤醒wait()方法的线程,让其继续执行下去
Wait()和sleep()的区别?
Sleep()方法是Thread类的静态方法,不涉及线程间同步概念,该过程包含了同步锁的获取和释放,调用wait方法将会将调用者的线程挂起,直到其他线程调用同一个对象的notify()方法才会重新激活调用者。
注意:线程调用notify()之后,只有该线程完全从 synchronized代码里面执行完毕后,monitor才会被释放,被唤醒线程才可以真正得到执行权。
单例模式??
静态内部类的方式:
Public class Singleton{
private static class SingletonHolder{ public static Singleton instance = new Singleton(); } private Singleton(){}
Public static Singleton newInstance{ return SingletonHolder.instance; }
}
负载均衡策略:
随机 轮询 最少活跃调用数 一致性Hash
Kafka如何保证消息不丢失和不重复?
消息丢失是什么造成的,从生产端和消费端两个角度来考虑;消息重复是什么造成的,从生产端和消费端两个角度来考虑;如何保证消息有序;如果保证消息不重不漏,损失的是什么
消费端重复消费:很容易解决,建立去重表
消费端丢失数据:也容易解决,关闭自动提交offset,处理完之后受到移位
生产端重复发送:这个不重要,消费端消费之前从去重表中判重就可以
生产端丢失数据:这个是最麻烦的情况
解决策略:
1.异步方式缓冲区满了,就阻塞在那,等着缓冲区可用,不能清空缓冲区
2.发送消息之后回调函数,发送成功就发送下一条,发送失败就记在日志中,等着定时脚本来扫描
Spring的bean初始化?
调用 BeanNameAware 的 setBeanName 方法
调用 BeanFactoryAware 的 setBeanFactory 方法
调用 ApplicationContextAware 的 setApplicationContext
调用 InitializingBean 的 afterPropertiesSet 或者没有实现这个接口,但指定了@Bean(initMethod="不加括号的方法名"),会执行这个方法
调用 BeanPostProcessor 的 postProcessBeforeInitialization 方法
调用 BeanPostProcessor 的 postProcessAfterInitialization 方法
Bean 初始化完成,可以被使用
容器关闭前,调用 DisposableBean 的 destroy 方法
树的广度遍历
快排