Java SPI是什么
Java提供了标准的接口,然后第三方可以实现这个接口来完成功能的拓展和实现。把装配的控制权移到程序之外,完成标准和实现的解耦
MyISAM和InnoDB的区别
MyISAM不支持事务,只支持表锁。不支持外键,索引和数据分开存储。
InnoDB支持事务,支持表锁行锁间隙锁,支持外键,索引和数据一起存储。
单例
私有化构造方法,防止被外部实例化造成多实例的问题。提供一个静态方法作为全局访问点获取唯一的实例对象。
1.双重检查锁。
2.静态内部类。
3.枚举类。可以防止反序列化破坏单例
ConcurrentHashMap中key不允许为null
避免多线程环境下出现的歧义问题,即如果key/value为null,我们无法判断值本身就是null还是key本身就不存在。
程序出现死锁
1.破坏请求与保持。第一次执行一次性申请所有的资源
2.非剥夺。抢不到所需资源主动放弃自己持有的资源。
3.循环等待。按照顺序去申请锁资源。
RabbitMQ高可用
1.普通集群。一个Queue只会存储在集群的一个节点上,集群其他节点会同步Queue所在节点的元数据。收到请求最终都会转发到Queue上,无法保证Queue的高可用,但是可以提高消息吞吐能力。
2.镜像集群。集群里每个节点都会存储Queue的数据副本。每次生产消息都需要把消息同步给集群的其他节点。
Redis的理解
Redis是一个基于内存的Key-Value型Nosql开源内存数据库。提供了物种创建的数据类型。像String,Map,set,Zset,List,可以覆盖应用开发的大部分场景。
链路追踪
链路追踪是一种针对分布式架构下实现请求链路可视化监控的一种技术。核心目的是了解分布式系统中请求调用的一些行为,从而去从整体到局部去详细展示各个系统的一个指标,去实现故障的快速定位,缩短故障的排除时间。Zipkin
为什么使用元空间去替换永久代
永久代内存有上限,容易出现OOM问题。永久代是通过FullGC进行垃圾回收的,替换成元空间简化了垃圾回收的过程。Oracle要合并HotSpot和JRockit的代码,JRockit里没有永久代。
令牌桶算法
限流是在高并发场景下保护系统稳定性的一种策略。针对什么资源去限流,比如接口,连接。阈值达到多少限流。触发限流后的一个行为,比如熔断降级。令牌桶算法是系统以一定的速率去生成令牌,然后所有的客户端请求需要从令牌桶获取令牌,成功获取表示可以正常访问
finally语句块什么时候不会执行
还未进入try语句块程序因为异常终止。try或者catch中执行了System.exit(0)。
空的Object对象占用多少字节
在开启了压缩指针的情况下默认占用12个字节,但是为了避免伪共享的问题,会进行对齐填充,按照8字节的倍数,所以是16字节,不开启压缩指针正好就是16个字节。
http和rpc协议的区别
http是一个应用层的超文本传输协议,主要服务于网页端和服务端的数据传输,有标准报文格式的规定。rpc是远程过程调用协议,主要用于不同计算机应用之间的数据通信,本身是一种规范,并没有具体的实现,只要按照rpc通信协议规范去实现就可以。
SimpleDataFormat
不是线程安全的,内部维护了一个Calendar的对象引用。可以使用DataTimeFormater。
Redis的内存淘汰策略
LRU:不经常使用的key直接淘汰掉。
LFU:在LRU的基础上增加了频次
随机策略
ttl策略:从设置了过期时间的key里面,挑选出最近过期的清理。
直接报错
SynchronousQueue可以避免任务进入阻塞队列,而是直接启动最大线程数量去处理。
阻塞队列被异步消费怎么保证顺序
阻塞队列首先是符合FIFO特性的队列。阻塞队列已经满了,启动多个线程消费需要加锁。如果是阻塞队列为空,消费者线程会按照FIFO存储在一个条件等待队列里。
线程池如何实现线程复用
分布式id设计方案
分布式id首先要满足唯一性,然后是满足有序性,安全性,可用性。像Mysql的全局表,Zookeeper的有序节点,Redis的自增id这些可以满足唯一性,但是其他的性能不好。推特推出过雪花算法,或者是美团的leaf算法等。
Happens-Before的理解
Happens-Before内存可见性模型,保证一个操作对另一个操作可见。volatile写操作Happens-Before读操作。线程join,start之前的操作Happens-Before之后的操作。
Java几种文件拷贝方式
java.io包下的FileInputStream和FileOutputStream。拷贝的时候需要调用操作系统底层的read write,涉及内核态到用户态的切换,已经从内核空间拷贝数据至用户空间。
java.nio下面的transferTo或transFrom。利用了零拷贝。
IO多路复用的原理
服务端使用一个线程去处理客户端连接请求,在对系统性能消耗比较小的情况下去提升服务端连接的处理数量。客户端请求会注册到一个Selector上,然后服务端启动一个线程去执行selector.select()去轮询就绪的channel。select,poll是基于轮询的方式。epoll基于事件驱动的方式,性能较高。像Redis,Netty等。
缓存击穿
某一热点Key失效大量请求打到数据库,可以设置续期。或者失效后先获得锁,然后把数据写会缓存。
一致性Hash算法
一致性hash算法是一种比较特殊的hash算法,它的核心是为了解决分布式环境下hash表可能存在的动态扩容和缩容的问题。通过hash取模的方式来决定把当前的key存储到哪一个节点。一致性hash是通过hash环来实现的。hash算出后通过顺时针找到最近的节点。如果节点性能不同,可以采用hash倾斜,如果希望避免hash倾斜,可以采用虚拟节点的方式。
SpringBean的作用域
1.singleton。整个Spring容器中只会存在一个bean实例。
2.prototype。每次从IOC容器去获取指定Bean的时候,都会返回一个新的实例对象。
request:针对每一次http请求都会创建一个新的Bean
session:同一个session共享,不同session使用不同的Bean实例。
globalSessin:全局共享。
Dubbo如何感知服务下线
Dubbo默认采用了Zookeeper来实现服务注册和服务发现。Zookeeper是通过心跳机制来来决定是否剔除某一结点,而Dubbo是通过Zookeeper的watch机制来针对Zookeeper的provider节点注册监听,如果节点发生变化,
Zookeeper会发送一个事件去通知Dubbo Client端。
Mybatis缓存机制
Mybatis里设计了二级缓存来提升数据的检索效率。一级缓存是sqlsession级别,在同一个会话下可用。二级缓存可以是跨sqlsession。一级缓存通过Executor查询Local Cache来实现二级缓存通过CacheingExecutor来实现,并且只要有一个会话查询到结果,可以共享。
伪共享
伪共享是说多线程多缓存行的修改导致其他线程读取的缓存行失效,影响程序执行效率。解决办法可以使用对其填充,在Java中可以使用@Contented注解。在Netty中有大量使用。
ThreadLocal原理
每个线程都保存了共享变量的副本,每个线程只操作自己的副本,这样就实现了数据的隔离。比如数据库连接,客户端请求会话。
wait和notify
实现多线程之间的协调,线程等待锁资源释放就可以用wait,唤醒其他等待锁的线程就是用notify。实现多线程下的条件等待和条件唤醒。
Kafka如何保证消息不丢失
对于producer:可以同步发送消息。或者添加异步回调来监听消息发送的结果。
broker:可以通过持久化到磁盘,但是kafka是通过异步批量刷盘。可以采用broker下leader和follower的同步机制,ack=-1,即leader等待follower数据同步完成后
volatile
保证多线程环境下共享变量的可见性。JVM虚拟机会自动添加一个lock指令,加上缓存锁。通过MESI缓存一致性协议实现
2.增加内存屏障防止指令重排序。加入内存屏障。
Netty中的Reactor模型
在NIO多路IO复用的基础上提出的一个高性能IO设计模式。它的核心是把响应io事件和业务处理进行分离,通过一个或者多个线程去处理io时间,然后再把就绪的事件分发给业务线程进行异步处理。
分为Reactor,Acceptor,Handler。
Cookies和Session的区别
Cookies是客户端浏览器用来保存服务端数据的一张机制。下次访问服务器就可以携带这些状态数据,服务端可以根据这些状态数据来识别。Session表示一个会话,他可以在服务器端存储一些会话时的状态数据。
RabbitMQ如何保证消息可靠传输
生产者把消息发送到 RabbitMQ Server端,Server持久化消息前宕机,消费端收到消息还没来得及处理。前两种可以用ack确认机制,最后一种可以使用手动确认可能造成重复消费,需要考虑幂等设计。
生产环境服务器变慢,如何排查处理
涉及三个方面,CPU的利用率,磁盘IO的效率,内存。CPU的利用率:可以使用top命令查看占用CPU过高的进程,以及定位到这个进程里比较活跃的线程.在通过jstack命令打印当前虚拟机的线程快照,排查有问题的代码。
可以使用iostat命令查看磁盘负载。借助缓存系统,减少磁盘IO次数,顺序写代替随机写入,减少寻址开销。可以dump出当前JVM的堆内存进行分析,进行JVM调优。
Thread和Runnable的区别
Thread是一个类,Runnable是一个借口,类只能单一继承,在已经存在继承关系,接口表示一种标准或者规范,实现类表示对这个标准或者规范的实现。
SpringBoot为什么默认使用CGLib不再使用JDK动态代理
1,性能和速度:Cgb动态代理在性能上通常比标准的DK动态代理更快。Cgib直接通过字节码生成子类来实现代理,避免了一些反射燥作,因此在方法调用等方面通常更加高效。
2,无需接口:JDK动态代理要求目标类必须实现一个接口,而Cgb动态代理不需要。这使得Cgib更适用于那些没有接口的类,从而扩展了动态代理的适用范围。
3,无侵入性:Spring Boot选择Cglib动态代理可以使你的类无需实现任何接口或继承特定的类,从而减少了对源代码的侵入性。这对于集成第三方库或需要代理的现有类特别有用。
4,方便集成:Spring Boot默认提供了Cglib相关的依赖,因此在应用程序中使用Cgib动态代理非常方便。
BLOCK和WAITING的区别
block表示线程阻塞状态,只有竞争Synchronized锁失败才会被动进入这个状态,waiting是人为主动触发的状态。block的唤醒是自动触发的,而waiting状态是必须要通过特定方法来唤醒。
Nacos配置更新
首先是采用长轮询的方式想nacos server端去发起配置更新查询,当服务端配置没有变更时这个连接会一直打开,直到服务端配置有变化时返回。其次在更新配置的时候一次最多发送3000片,也就是最多更新3000个配置。客户端把这3000个配置的key以及对应value值得md5拼接成一个字符串,发送到nacos server端进行比较,服务端接收到后会去比较key和value的值,把存在更新的key返回给客户端。客户端拿到这些变更的key,循环逐个去调用服务端获取这些key的value值。核心是减少网络通信数据包的大小
MQ是如何保证消息顺序消费的
Kafka可以自定义路由策略使得消息都路由到一个partition里,消费端有可能是异步消费,那就可以把消息先放到阻塞队列里,消费端去阻塞队列进行消费。
行锁、间隙锁、临界锁
行锁、间隙锁、临界锁都是Mysql里面InnoDBj引擎下去解决事务隔离性的一系列排它锁。行锁只锁定该行数据,间隙锁锁定左右开的一个区间,临界锁锁定左开右闭的一个区间。
DCL单例模式为什么需要使用volatile关键字
当我们使用new Instance()创建对象时,它会被编译成三条指令,第一条指令为对象分配内存空间,第二条是初始化对象,然后把实例对象赋值给instance引用。在单线程的情况下在不影响程序最终结果的前提下允许指令重排序,素以可能拿到一个不完整的对象
线程池的线程回收
线程池里的线程分为核心线程和非核心线程。核心线程一般不会被回收,而非核心线程可以需要从阻塞队列中去那获取任务,所以只要阻塞队列一段时间内没有任务,非核心线程就可以结束了。当前线程在指定的时间没有从阻塞队列中获取到任务,poll方法就返回null,从而终止当前线程,完成线程回收 。
RabbitMQ的消息如何实现路由
Exchange是一个路由交换机,里面定义了消息的路由规则,把消息路由到消息队列里。消息的路由规则是由ExchangType和Binding来决定的。Binding表示建立Queues和Exchange的绑定关系,每一个绑定关系会存在一个bindingkey,生产者发送消息是带一个RoutingKey,然后和bindingkey进行匹配,匹配的方式是根据ExchangType来决定的。direct表示完整匹配。fanout表示广播机制。topic表示正则表达式匹配。
时间轮
时间轮是用来存储一系列定时任务的环形数组,由环状数组和遍历它的指针组成。环状数组上的任务可以用双向链表来存储。优点:减少定时任务添加和删除的时间复杂度,提升性能。可以保证每次执行定时器任务都是O(1)的时间复杂度,在定时任务密集的情况下,性能优势非常明显。时间轮算法的精度取决于最小时间单元,例如一秒为一个时间刻痕,小于一秒的任务就无法被时间轮所管理。
深拷贝和浅拷贝
深拷贝或者浅拷贝是用来描述对象或者对象数组这种引用数据类型的一个复制场景的。浅拷贝就是只复制某一个对象的指针而不复制这个对象本身。深拷贝则是指完全创建一个一模一样的对象。两者都需要趋势线Cloneable接口的clone()方法。深拷贝可以通过序列化再反序列化的形式实现。
AQS为什么使用双向链表
AQS有公平锁的实现,就需要有一个阻塞队列。唤醒等待的线程需要从前往后,添加没有竞争到锁的现成需要尾插法。另一个就是如果需要从队列中移除线程,需要从后往前,如果从前往后跟唤醒又存在并发竞争。
Mysql如何解决幻读问题
在RR的隔离级别下,InnoDB采用了MVCC来解决幻读问题。MVCC是乐观锁的机制,针对不同的事物生成不同的快照版本,然后通过UNDO版本链来进行管理。当前事务只能看到事务id小于等于自己的快照,从而解决了幻读问题。但是如果采用当前读(直接读取内存的数据)还是会存在幻读。
Spring如何解决循环依赖
Spring采用三级缓存来解决循环依赖。第一级缓存存储完整的bean实例,这些实例是可以直接被使用的。第二级缓存存放着实例化以后但是还没有依赖注入的Bean实例。第三级缓存是用来存放bean工厂,他主要是用来生成原始bean对象,并且放入二级缓存。
ThreadLocal内存泄漏问题
线程私有成员变量ThreadLocalMap中的key是弱引用,当成员变量ThreadLocal没有其他强引用关系时,就有可能被垃圾回收,导致key变为null,使得这块内存永远无法被访问。可以通过每次使用完就调用remove移除对应的数据。
Mybatis如何实现分页
1.可以通过limit。使用Mybatis提供的RowBounds对象,实现内存级别的分页。使用 Interceptor拦截器,在select语句执行之前拼接分页关键字。
如何破坏双亲委派模型
集成ClassLoader,重写loadClass方法。使用线程上下文加载器,java.lang.Thread的setContextClassLoader().
Redis哨兵和集群模式的区别
Redis哨兵是通过主从复制来实现的,可以实现读写分离,分担Redis读操作的压力。而Redis Cluster集群里只是实现冷备的机制,只有在master宕机后才会工作。Redis哨兵无法实现在线扩容,并发压力受限于单个服务器资源的一个配置。RedisCluster提供了基于Slot槽的一个数据分片机制,他可以实现在线扩容,去提升读写的性能。Redis哨兵是一主多从,集群模式是多主多从。
Conditional注解的作用
为bean的装载提供一个条件判断。只有在满足条件的情况下,Spring才会把当前bean装载到IOC容器里,增加了bean装载的灵活性。shenm
Redis中key过期内存未释放
Redis中的key过期是逻辑上的过期,并不会立即释放对应的内存空间,Redis采用了定期删除和惰性删除的方式来清理key,Redis中key的清理并不是实时的,只有在定期删除或者访问的时候才会执行删除操作。
ThreadLocal的使用场景
ThreadLocal基于副本隔离机制来保证共享变量的安全性。1.线程上下文传递,在跨线程调用的场景中,可以使用ThreadLocal在不修改方法签名的亲光下传递线程上下文信息,例如用户信息。2.数据库连接管理。Mybatis的SqlSesson对象使用了ThreadLocal来存储当前线程的数据库会话信息。3.事务管理。存储事务上下文信息。使用时要注意内存泄漏问题,可以手动remove。
什么情况下不需要建索引
1.数据量比较小2.区分度不高的列3.使用函数操作的列。4频繁变更的表。经常需要更新删除或插入记录。
多线程异步和MQ有什么区别
首先数据维度不同。多线程是在一个进程的内部,MQ是把消息发送到不同节点的进程处理。然后数据的可靠性不同。多线程异步是基于共享内存,有数据丢失的风险,MQ可以把消息持久化。分布式能力不同,MQ可以把新消息发送到不同节点去消费,而多线程只能在一个线程中处理任务。
对接第三方接口需要考虑哪些问题
1.数据传输安全性问题,使用https协议或者数字签名。2.接口的稳定性和可靠性。3.支持最大并发量及费用问题
CompletableFuture
CompletableFuture是Java8引入的组件用来处理异步任务以及异步任务的结果。解决了Future阻塞等待获异步任务结果的问题。CompletableFuture可以把耗时的任务提交给线程池异步处理,等到异步任务完成后调用回调方法返,可以在该任务中处理异步任务的执行结果,同时也提供了thenApply ,thenAccept,thenRun等链式处理异步任务的执行结果。
过滤器和拦截器区别
运行顺序不同,Filter在请求发送到Servlet之前,Interceptor在Servlet被调用后,响应发送给客户端之前。Filter依赖Servlet容器,Interceptor不依赖。Filter只能操作Request和Response。Interceptor还可以操作handler,modelAndView,exception。
为什么使用包装类型定义属性
使用基本数据类型会赋默认值,像int赋值0,包装类型则不会。基本数据类型在使用时可能需要装箱拆箱,包装类型提供了有equals(),toString(),hashCode()等这些方法。
不推荐使用join操作
每个join操作都需要对两个个或者多个表进行关联操作,非常消耗资源,并导致sql执行效率下降。2.代码可读性下降,难以理解和维护。
不推荐使用存储过程
存储过程所有逻辑都是在数据库层面,这会导致代码后续的可维护性下降。存储过程可能包含复杂的逻辑,会导致数据库负载增加,进而影响系统性能。
拆包和粘包
拆包是指把一个完整数据包拆成多个小包发送,接收端可能无法一次接收所有小包,接收到的数据可能不完整。粘包是指把多个数据包合成一个发送,接收端可能无法正确区分每个数据包,可能导致数据包出现错位或者混乱。解决问题时可以约定发送固定长度的数据,或者在数据包开始结束添加特殊字符去识别。
如何解决跨域问题
跨域是指浏览器执行JS代码的时候,由于浏览器同源策略的限制,只能访问同源的资源,而不能访问其他资源。使用WebMvcConfigurer的addCorsMapping接口配置允许跨域的请求源。
Mysql update是行锁还是表锁
如果更新列走索引则是行锁,否则是表锁。
Redis实现延时队列。
使用Zset这样一个有序,使用ZADD命令把消息添加到sorted set中,并将当前时间作为score。启动消费者线程,使用ZRANGEBYSCORE命令获取定时从Zset中获取当前时间之前的所有消息。
服务端挂了,客户端链接还在么?
正常情况下需要通过四次挥手来关闭全双工的TCP连接,但是服务端如果挂了,客户端发送数据包迟迟收不到应答这样就会导致客户端进行重传,如果六次还没有收到应答客户端就会尝试关闭这个连接,同样是进行最大六次的重试,收不到应答就直接关闭连接。
SpringMVC执行流程
DispatcherServlet拦截请求,通过handlerMapping去寻找对应的控制器,然后把请求交给对一个控制器进行处理,对应控制器处理完成后返回i一个modelAndView对象,通过视图解析器进行渲染成html对象返回。
binlog和redolog
binlog记录的是数据库的逻辑变化,在执行sql语句的时候,在主线程中生成逻辑变化,写入到磁盘,是语句级别的语句,主要用于数据备份,数据恢复,主从同步。redolog是在Mysql后台线程中去生成 ,并且写入到磁盘中的,记录物理变化,即数据页的变化结果,保证数据的持久化作用,属于sql语句层面,主要是在数据库宕机时进行未提交数据的回滚,或者是已提交数据的持久化。
会员批量过期
1.使用搜索引擎Solr或者Elasticsearch把会员表里的会员id和过期时间存储一份到搜索引擎中。
2.把会员id和过期时间存入Redis,通过Redis过期机制来进行过期,监听这个过期事件进行邮件提醒。
3.或者是存入一个延时队列,延时的时间即会员过期的时间。
select和epoll的区别
select是轮询的机制,需要遍历整个监听集合,并且集合长度有限制。epoll基于事件通知机制,只需要遍历当前就绪的集合。并且数据传输过程中select需要拷贝传输整个集合,epoll只需要传续就绪的集合。
如何处理消息队列中的消息积压
采用异步,或者批量处理来处理消息。或者水平扩容consumer。
多线程存在缓存一致性
Dubbo的服务请求失败怎么处理
Dubbo会做重试,默认是两次。
快速失败。失败了直接把错误抛出去。
失败安全。出现异常吞掉。
失败自动恢复。记录失败请求,通过定时任务重发。
并行调用服务列表,有一个返回表示请求成功。
如何理解双亲委派
1.安全性。核心类库中的类优先交给bootstrap ClassLoader,防止被破坏。
2.避免重复加载。如果父类已经加载过了,子类就没有必要去加载
聚集索引和非聚集索引
聚集索引是基于主键创建的索引,非聚集索引是除了主键索引外的其他索引也叫二级索引。
Redis和Mysql数据一致性
先更新数据库在更新缓存,可以用RocketMQ去监听更新成功与否,如果更新失败则通过异步确保更新成功。也可以采用canal组件监控binlog日志,把更新的数据同步到Redis里。
wait和notify为什么要放在synchronized代码块里
为了实现多个通信线程的互斥,实现条件等待和条件唤醒。例如实现生产者消费者模型。
Spring中事务传播行为
多个声明了事物的方法互相调用时,存在的事务嵌套的问题。
REQUIRED:默认的。如果存在事务,则加入。不存在则新建。
REQUIRED_NEW:不管是否存在事务,都会新建一个事务。
NESTED:存在事务则嵌套到当前事务,不存在则新建。
SUPPORTS:表示支持当前事务,如果不存在事务,则以非事务的方式执行
NOT_SUPPORTS:以非事务的方式执行,如果存在事务,则把它挂起。
强制事务执行,不存在事务则抛异常。
NEVER:以非事务的方式去执行,如果当前存在事务,则抛异常。