1线程池中提交一个任务得流程是怎样的
源代码
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
2 线程池中有几种状态?分别是如何变化的?
状态:
1 RUNNING: 会接收新任务并且会处理队列中的任务
2 SHUTDOWN:不会接收新任务并且会处理队列中的任务,任务处理完后会中断所有线程
3 STOP: 不会接收新任务并且不会处理队列中的任务,并且会直接中断所有线程
4 TIDYING:所有线程都停止之后,线程池的状态会转为TIDYING,一旦达到此状态,就会调用线程池的terminated()
5 TERMINATED: terminated()执行完之后就会转变为TERMINATED
源码:
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
3 如何优雅的停止一个线程
stop():stop()会释放线程占用的synchronized锁,而不会自动释放ReentranLock锁
interrupt(): 该方法只是在目标线程中设置一个标志,表示它已经被中断,线程是否中断,取决于线程本身,
4 线程池的核心线程数,最大线程数如何设置?
对于cpu密集型任务: cpu核心数+1
io型任务: 2*cpu核心数
cpu核心数 * (1 + 线程等待时间 / 线程运行总时间 )
可以使用jvisualvm来估算这两个时间
corePoolSize: 核心线程数,表示线程中的常驻线程个数
maximumPoolSize: 最大线程数,表示线程池总能开辟的最大线程个数
核心应用():核心线程数 = maximumPoolSize = 压测出来的个数,
非核心线程:核心线程数设置少点,maximumPoolSize=压测个数
5 如何理解java并发中的可见性
java 并发可见性是指多线程并发访问共享变量时,对变量的更改能够被其他线程及时感知
可以使用volatile关键字来保证变量的可见性
6 如何理解Java并发中的原子性?
线程在执行一段代码的时候,要么全部执行成功,要么全部不执行,不能执行到一半就结束了
java里面通过各种锁机制来保证原子性
7 如何理解Java并发中的有序性
java并发有序性指的是多个线程执行的指令和操作,按照开发者编写程序的顺序或预定的顺序进行执行。多线程并发执行时,可能会发生指令的重排,导致程序的执行顺序与预期不一致,从而出现数据竞争和线程安全问题
8JDK JRE JVMM之间的关系
JDK : java标准开发包,他提供了编译,运行Java程序所需的各种工具和资源,包括java编译器, java运行时环境,以及常用的java类库等
JRE:java运行环境,用于运行java的字节码文件
JVM:JAVA虚拟机,是jre的一部分,他是整个java实现跨平最核心的部分,负责运行字节码文件
9 hashCode()和 equals()之间的关系
在java的一些集合类的实现中,在比较两个对象是否相等时,会先调用对象的hashCode()方法得到hashCode进行比较,如果hashCode不相同,就可以认为这两个对象不相同,如果hashcode相同,就会调用equals方法进行比较
10 String StringBuffer StringBuilder的区别
1 String是常量不可变,StringBuilder StringBuffer是可变的
2 StringBuilder线程不安全,StringBuffer线程安全
11 泛型中extends和super的区别
12==和equals的方法区别
13ArrayList和LinkList有哪些区别
14CopyOnWriteArrayList底层原理
15HashMap的扩容机制
16ThreadLocal的底层原理
17如何理解volatile关键字
保证了并发领域的可见性和有序性(内存屏障),对于加了volatile关键字的属性,会直接将cpu高级缓存中的数据写回主内存,对于这个变量的读取,也会从主内存中读取
18ReentrantLock中的公平锁和非公平锁的底层实现
底层使用AQS进行排队
公平锁 加锁时会检查AQS队列中是否存在线程在排队,如果有线程在排队,则当前线程也进行排队
非公平锁: 不会检查是否有排队,而是直接竞争锁
ReentranLock是可重入锁
19ReenTrantLock中tryLock()和lock()方法的区别
tryLock():尝试加锁,有返回值,非阻塞,可以用来设计自选锁
lock:没加到锁会阻塞,无返回值
自旋锁:比较消耗cpu
20 CountDownLatch和Semaphore的区别和底层原理
CountDownLatch表示计数器,可以给CountDownLatch设置一个数字,一个线程调用CountDownLatch的await()将会阻塞,其他线程可以调用CountDown()方法对CountDownLatch中的数字减一,当数字减少到0后,所有await的线程都将被唤醒。
Semaphoore表示信号量,可以设置许可的个数,表示同时允许最多多个线程使用该信号量,通过acquire()来获取许可,如果没有许可可用线程阻塞,并通过AQS来排队,可以通过release()方法来释放许可,当某个线程释放了某个许可后,会从AQS中正在排队的第一个线程开始依次唤醒,知道没有空闲许可。
21 Sychronized的偏向锁,轻量级锁,重量级锁
1 偏向锁: 在锁对象的对象头中记录下当前获取到该锁的线程ID,该线程下次如果又来获取该锁就可以直接获取到了
2 轻量级锁: 由偏向锁升级而来,当一个线程获取到锁后,此时这把锁是偏向锁,此时如果第二个线程来竞争锁,偏向锁就会升级为轻量级锁,之所以叫轻量级锁,是为了和重量级锁区分开,轻量级锁底层是通过自旋实现的,并不会阻塞线程
3 如果自旋次数过多任然没有获取到锁,则会升级称为重量i就锁,重量级锁会导致线程阻塞
4 自旋锁 自旋锁是线程在获取锁的过程中,不会去阻塞线程,也就无所谓唤醒线程,阻塞和唤醒这两个步骤都是需要操作系统中进行的,比较消耗时间,自旋锁是线程通过CAS获取预期的一个标记,如果没有获取到,则会继续循环后去,如果获取到了则表示获取到了锁,这个过程线程一致在运行中,想对而言没有使用泰索的操作系统资源,比较轻量
22 Sychronized和ReentLock的区别
1 Sychronized是关键字,ReentLock是一个类
2 sychronized会自动加锁和释放锁,ReentrantLock需要程序员手动加锁与释放锁
3 sychronized的底层是JVM层面的锁,ReentranLock是API层面的锁
4 sychronized是公平锁,ReentrantLock可以选择公平锁或非公平锁
5 sychronized锁是对象,锁幸喜保存在对象头中,ReentrantLock通过代码中的int类型的state标识来标识锁的状态
6 sychronized底层有一个锁升级的过程
23线程池的工作原理
线程池内部是通过队列+线程实现的,当我们利用线程池执行任务时:
1 如果此时线程池中线程的数量小于corePoolSize,即使线程池中的线程都础语空闲的状态,也要创建新的线程来处理被添加的任务
2 如果此时线程池中的线程数等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放入缓冲队列中
3 如果此时线程池中的线程数量大于等于corepoolSize缓冲队列workQueue满,并且线程池中的数量小于maxmumPoolSize,建新的线程来处理被添加的任务
4 如果此时线程池中的线程数量德语corePoolSize,缓冲队列满了,并且线程池中的数量等于maximumPoolSize,那么通过handler所指定的策略来吃力此任务
5 当线程池的线程数量大于corePoolSize时,如果某线程的空闲时间超过keepAliveTime,线程将被停止,这样线程池可以动态地调整池中地线程数
24 JVM中哪些是线程共享区
堆区和方法区是共享的,栈,本地方法栈,程序计数器是每个线程独有的
25 JVM中哪些可以作为gc root
26你们的项目如何排查JVM问题
27 说说类加载器双亲委派模型
28 Tomcat中为什么要使用自定义类加载器
29 Tomcat如何进行优化
30 浏览器发出一个请求到收到响应经历了哪些阶段
31 跨域请求是什么?有什么问题如何解决
跨域是指浏览器在发起网络请求的时候,会检查该请求所对应的协议,域名端口和当前网页是否一致,不一致浏览器就会进行限制
解决:
1 response添加header,比如 resp.setHeader("Access-Control-Allow-Origin","*")表示可以访问所有网站,不受是否同源的限制
2 jsonp 的方式,script标签是可以跨域的
3 后台自己控制,先访问同域名下的接口,然后再接口中再去使用HttPClient工具去调用目标接口
4 网关,交给后台服务来进行跨域访问
32 Spring中Bean创建的生命周期有哪些
1 推断构造方法
2 实例化
3 填充属性,也就是依赖注入
4 处理aware回调
5 初始化前 ,处理@PostConstruct注解
6初始化,处理InitializingBean接口
7初始化后,进行AOP
33 Spring中Bean是线程安全的吗
Spring本身并没有针对Bean做线程安全的处理,所以:
1.如果Bean是无状态的,那么Bean则是线程安全的
2 如果Bean是有状态的,那么Bean则不是线程安全的
bean是不是线程安全跟作用域没有关系,Bean的作用域只是表示Bean的生命周期范围
34 ApplicationContext和BeanFactory有什么区别
BeanFactory是Spring中非常核心的组件,表示Bean工厂,可以生成Bean,ApplicationContext继承BeanFactory,拥有BeanFactory的所欲特点,也是一个Bean工厂,但是Applicationcontext还继承了EnvironmentCapable,MessageSource,ApplicationEventPulisher等接口,从而Applicationcontext还有获取环境变量,国际化,时间发布等功能
35 Spring中的额事务是如何实现的
1 spring事务底层时基于数据库事务和AOP机制实现的
2 首先对于使用了@Transational注解的Bean,Spring会创建一个代理对象作为Bean
3 当调用代理对象的方法是,会判断该方法上是否加了@Transactional注解
4 如果加了,那么则利用事务管理器创建一个数据库连接
5并且蟹盖数据库连接的autocommit属性为false,禁止此连接自动提交
6 然后执行当前方法,方法中会执行sql
7 执行完当前方法后,如果没出现异常就直接条事务
8如果出现了异常,并且这个异常时需要回滚的就会回滚事务,否则任然提交事务
9 Spring事务的隔离级别对应的就是数据库的隔离级别
10 Spring事务的传播机制是Spring事务自己实现的,也是Spring事务中最复杂的
11 Spring事务的传播机制是基于书库连接来做的,一个是数据库连接一个事务,入股偶传播机制配置为需要新开一个事务,那么实际上就是先及案例一个数据库连接,再次新数据库连接上执行sql
36 spring中什么时候@Transactional会失效
Spring中的事务是基于代理实现的,所以某个加了@Transactional 的方法只有是被代理对象调用时,那么欸这个注解才会生效,如果是被代理对象来调用这个方法,@Transactional是不会失效的
同时某个方法是private,那么@Transactional也会失效,应为底层cglib是基于父子类来实现的,子类是不能重载父类的private方法的,所以无法很好的利用代理,也会导致@Transactional失效
37Spring容器启动流程是怎样的
1 在创建Spring容器,也就是启动Spring 时:
2 首先会进行扫描,扫描所得到的所有BeanDefinition对象,并存在一个Map中
3 然后筛选出非懒加载的单例BeanDefinition进行创建Bean,对于多礼Bean不需要再启动过程中去进行创建,对于多礼Bean会在每次获取Bean时利用BeanDefinition去创建
4 利用BeanDefinition创建Bean的创建生命周期,这期间包括了合并BeanDefinition推断构造方法,实例化,属性填充,初始化前,初始化,初始化后等步骤,其中AOP就是发生载初始化后这一步骤中
5 单例Bean创建完了之后,Spring会发布一个容器启动事件
6 Spring启动结束
7 在源码中会跟复杂,源码中提供了一些模板方法,让子类实现
8 在Spring启动过程中还会去处理@Import等注解
38 Spring用到了哪些设计模式
工厂模式:BeanFactory FactoryBean
适配器模式: AdvisionAdapter 接口,对Advision进行了适配
访问者模式:PropertyAccessor接口,属性访问问题,用来访问和设置莫格对象的某个属性
装饰器模式:BeanWrapper
代理模式:AOP
观察者模式:事件监听
策略模式:InstantiationStrategy
模板模式:JdbcTemplate
委派模式:BeanDefinitioonParserDelegate
责任链模式:BeanPostProcessor
39 SpringMVC的底层工作原理
1 用户发送请求至前端控制器DispatcherServlet
2 DispatcherServlet收到请求调用HandlerMapping处理器映射器
3 处理器映射器找到具体的处理器,生成处理器及拦截器一并返回给DispatcherServlet
4 DispatcherServlet调用HandlerAdapter处理器适配器
5 HandlerAdapter经过适配调用具体的处理器(C哦那troller,也叫后端控制器)
6 Controller执行完后返回ModelAndView
7 HandlerAdapter将controller 执行结果ModelAndView返回给DispatcherServlet
8. DispatcherServlet将 ModelAndView传给ViewReslover视图解析器。
9.ViewReslover解析后返回具体View。
10.DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。11.DispatcherServlet响应用户。
40 Springboot中常用的注解及底层实现
1 @SprigBootApplication注解:这个注解标识了一个Springboot工程,由三个注解组合
a.@SpringBootConfiguration:这个注解实际就是一个@Configuration,表示启动类也是一个配置类
b.@EnableAutoConfiguration:向Spring容器中导入了一个Selector,用来加载ClassPath下的SpringFactories中所定义的自动配置类,将这些自动加载为配置Bean
c.@ComponentScan:标识扫描路径
2 @Bean注解:用来定义Bean类似于XML中的<bean>标签,Spring在启动时,会对加了@Bean注解的方法进行解析,将方法的名字作为beanName, 并通过执行方法得到Bean对象
3 @Controller @Service @ResponseBody @Autowired
41 SpringBoot是如何启动Tomcat的
1 首先,SpringBoot在启动时会先创建一个Spring容器
2 在创建Spring容器过程中,会利用@ConditionalOnClass技术判断当前classpath中是否存在Tomcat依赖,如果存在则会生成一个启动Tomcat的Bean
3 Spring容器创建完,就会创建Tomcat 的Bean,然后创建Tomcat对象,绑定端口等,启动Tomcat
42 SpringBoot中配置文件的加载顺序是怎么样的?
1 命令行参数
2 Java 系统属性
3 操作系统环境变量
4 先jar包外部后jar包内部,先带profile,后不带profile
5 @Configuration注解类上的@PropertySource
43 Mybatis存在哪些优缺点
1 相当灵活,基于SQL语句编程,不会对应用程序和数据库设计造成影响,解耦,接触程序代码和sql语句的耦合
2 于jdbc相比,代码量减少50%以上,不需要手动关闭连接
3 很好的与各种数据库兼容
4 能够与spring很好的集成
5 提供映射和关系映射标签
44 Mybatis中#{} ${}的区别是什么?
1 #{}是占位符, ${}是字符串替换,拼接符
2 Mybatis会将#{}替换为?,调用PrepareStatement来赋值
3 Mybatis在处理${}时,就是把${}替换成变量的值,调用Statement来赋值
4 使用#{}可以用小的防止SQL注入,提高系统的安全性
45 什么是CAP理论
分布式领域的一个重要论
C:表示强一致性
A:表示可用性
p:分区容错性
一个分布式系统是必须要保证分区容错性的,分布式系统要么cp,要么ap
分区容错性表示:系统虽然是分布式的,但对外看上去应该是一个整体,
可用性:一个分布式系统要保证对外可用
强一致性表示;一个分布式系统中各个节点之间能及时同步数据,在同步数据的过程中,是不能对外提供服务的
46 什么是BASE理论
1 BA: 表示机泵可用,允许在一定程度上不可用
2 S:Soft state:表示分布式系统处于一种中间状态
3 E: 表示最终一致,不要求数据实时一致允许数据经过一段时间后达到一致,在达到一致的过程中,系统是可用的
47什么是RPC
RPC,表示远程过程调用,对于java这种面向对象的语言也可以理解为远程方法调用,是一种远程调用方法的方式
RPC可以使用Http协议实现也可以使用TCP实现
RPC协议是HTTP之上的一种协议
48 分布式ID是什么?有哪些解决方案?
分布式系统中标识数据的唯ID,单体系统中可以使用数据库自增id,内存中自增数字作为唯一id ,但分布式系统中不行
解决方案:
1 uuid,复杂度低,但会影响存储空间和性能
2 使用单机数据库的自增主键,复杂适中,id较uuid短,不适合并发量大的时候
3 使用redis的自增命令,zookeeper的自增节点,较2性能有所提升,适当选用
4 雪花算法,底层是某台机器在某一毫秒内对某个数字自增
49 分布式锁的应用场景?有哪些实现方案?
应用场景:不同进程的线程并发执行时遇到资源竞争,需要一个分布式锁生成器生成锁,达到多个进程中的线程使用同一把锁
实现方案:
zookeeper: 利用zookeeper的临时节点,顺序节点,watch机制实现,保证了cp
redis: 利用redis的setnx,lua脚本,消费订阅机制实现,保证了AP,缺点:可能出现数据不一致的问题
50什么是分布式事务,有哪些实现方案?
在分布式系统中,一次业务可能需要多个应用实现
实现方案:
1 本地消息
2 消息队列
3 Seata
51 什么是ZAB 协议
是zookeeper实现一致性的原子广播协议,描述了zookeeper如何实现一致性的:
实现分为三个阶段:
1 领导者选举阶段:在zookeeper集群中选举一个leader,所有写请 由leader处理
2 数据同步阶段:集群中所有节点要和leader保持一致
3 请求广播阶段:当leader节点收到写请求时,广播请求,达到节点上的数据一实时一致性
zookeeper只是尽量达到数据一致性,但任是最终一致性
52 为什么Zookeeper可以用来作为注册中心
利用Zookeeper的临时节点和watch机制实现自动注册和发现,数据存在内存中,再用nio,多线程模型,性能高,zookeeper使用的是cp
53 Zookeeper中领导者选举流程是怎么样的
1 观望投给自己
2 交换投票比较,先比较zxid,相等再比较myid
3 输 投给更大的节点,把选票投到自己节点和发送给其他节点
平局 接收到的选票放入自己投票箱中
赢 不接受收到选票
4 收到选票,统计,看超过一半节点和自己所投节点是否一致,一致,自己所投节点是leader
5 zookeeper集群中节点之间数据是如何同步的
54 Zookeeper集群中节点之间的数据是如何同步的
1 集群启动,领导者选举,确定哪个是leader,follower observer
2 leader和其他节点进行数据同步,采用发送快照和diff日志的方式
3 leader处理写请求,从节点处理读请求
4Leader节点收到一个写请求时,会通过两阶段机制处理
5 leader将写请求日志发送给Follower节点,等待Follower节点持久化日志成功
6 Follower节点持久化日志成功会发送一个ack给leader
7 leader收到半数以上的ACK 后,先更新本地内存数据再更新Follower,
8 同时leader 在收到写请求时,会把写请求发发送给observe,observe收到写请求,会更新自己的内存和数据
10 最后leader向客户端返回响应成功
11 通过同步机制和两阶段提交机制来达到集群中节点数据一致
55 Doubbo支持的负载均衡策略
1 随机: 从多个服务提供者中选择一个来处理当前情趣,调用量越大,分布越均匀,并支持按权 重 设置随机概率
2 轮询: 依次选服务提供者来处理请求,支持权重轮询,底层采用平滑加权算法
3 最小活跃调用次数: 统计服务提供者当前正在处理的请求,下次请求交给活跃数最小的服务器处理
4 一致性哈希: 相同参数的请求总是发送到同一个服务提供者
56 Doubbo是如何完成服务导出的
56 Doubbo如何完成服务导出
1 解析@Doubboservice和@Service 得到一个ServiceBean
2 然后调用ServiceBean的export导出
3 将服务信息注册到注册中兴
4 绑定监听器
5 启动对应的web服务框架,tomcat,netty等
57 Doubbo是如何完成服务引入的?
1 @Reference来引入一个服务,Doubbo会将注解和服务信息解析出来,得到所引用的服务名,服务接口
2 在注册中心查询服务信息,得到服务的提供者信息,并存到消费端
3 绑定监听器来动态监听配置中心得变更
4 更据查询得到得服务提供者信息生成一个服务接口代理对象,并放入Spring容器中作为Bean
58 Doubbo的架构设计是怎样的
架构有很多层,每层都是可扩展的:
1 Proxy服务代理层
2 Register注册中心层
3 Protocol远程调用层
4 Transport网络传输层
5 Seralize数据序列化层
59 Spring Cloud有哪些些常用组件,作用是什么
1 Eureka:注册中心
2 Nacos: 注册中心,配置中心
3 Consul: 注册中心,配置中心
4 Spring Cloud Config:配置中心
5 Feign/OpenFeign:RPC调用
6 Kong: 服务网关
7 Zuul:服务网关
8 Spring Cloud Getway: 服务网关
9 Ribbon:负载均衡
10 Spring Cloud Sleuth:链路追踪
11 Zipkin: 链路追踪
12 分布式事务
13 Doubbo: RPC调用
14 Sentinel:服务熔断
15 Hystrix:服务熔断
60 Spring Cloud和Doubbo有哪些区别
Spring Coud是一个大而全的微服务框架
Doubbo 是一个RPC调用框架,侧重调用
Doubbo和spring cloud并不冲突,可以结合在一起使用
61 什么是雪崩?什么是服务限流?
1 A请求调用B请求,B请求调用C请求,当大量请求过来,A能抗住,C不能抗住,导致c请求大量堆积,b请求堆积,从而导致A请求不可用,这就是服务雪崩,解决方式是服务降级和服务熔断
2 服务限流是指在高并发的请求下,为了保护系统,可以对访问服务器的请求进行限制,保护系统不会被大量请求压垮
62 什么是服务熔断?什么是服务降级?区别是什么?
1 服务熔断:当a调用b,b不可用,a保证自己不受影响,不在调用b,直接返回一个结果,减轻a和b的压力,直到b可用
2 服务降级: 当系统压力过载时,就会关闭或限流某个服务,减轻系统压力
63 SOA,分布式,微服务之间的关系和联系?
1 分布式架构是将单体架构的各个部分拆分,部署到不同的机器或线程上去,SOA和微服务都是分布式架构
2 SOA是一种面向服务的架构
3 微服务是一种更加彻底的面向服务的架构
64 BIO NIO AIO分别是什么
BIO:同步阻塞IO,BIO读取数据时线程会阻塞,需要线程主动查询是否有数据可读,处理完一个soket后才能处理下一个socket
NIO:同步非阻塞io,使用NIO读取数据时线程不会阻塞,但需要去查询是否欧io时间
AIO:异步非阻塞,读取数据时不会阻塞,当有可读数据时会通知给线程
65 零拷贝是什么
用户在把内核的一块数据区域转移到另外一块区域时,不需要经过赋值到用户空间,再转移到目标内核区域,而直接实现转移
66 Netty是什么,n和tomcat有什么区别,特点是什么?
Netty是基于NIO的异步网络通信框架,性能高,用来开发各种网络服务器
Tomcat是一个网络服务器,内部只运行servlet,并处理HTTP请求,而Netty封装的是底层IO模型,关注的是网络数据传输,不关注具体协议
netty的特点是什么:
1 异步,NIO 网络通信框架
2 高性能
3 高扩展,高定制性
4 易用性
67 Netty的线程模型是怎么样的
Netty同时支持Reactor单线程模型,Reactor多线程模型,Reactor主从多线程模型,用户可以根据启动参数配置在这三种模型之间切换
68 Netty的高性能体现在哪些方面
1 NIO模型,用更少的资源做更多的事情
2 内存零拷贝
3 内存池设计
4 串行化处理读写
5 高性能序列化协议
6 高效并发编程体现
69 Redis有哪些数据结构?分别有哪些典型应用场景?
1 字符串:分布式锁 计数器 Session共享 分布式id
2 哈希表:存储kv键值对
3 列表:可以当栈也可以当队列,缓存消息流数据
4 集合:不能重复,实现共同关注,朋友圈点赞
5 有序集合:有顺序,可以实现排行榜
70 Redis分布式锁是如何实现的
1 利用setnx来办证:key不存在才能获取到锁
2 利用lua脚本来保证多个redis操作的原子性
3 考虑锁过期,需要额外的定时任务来监听是否需要续约
71 Redis主从复制的核心原理
1 集群启动时主从库会建立连接,为全量复制做准备
2 主库将所有数据同步给从库,从库收到数据后,完成本地数据加载,依赖内存快照RDB
3 主库将数据同步给从库的过程中不会阻塞,任然可以接收请求
4 第三阶段,主库把收到的写命令发送给从库
5 后续主库和从库都可以处理客户端读操作,写操作只能交给主库处理
72 缓存穿透,缓存击穿,缓存学蹦分别是什么
1 缓存雪崩:如果缓存中某一时刻大批量热点数据同时过期,那么就可能导致大量请求直接访问Mysql,解决办法是在过期时间上增加一点随值,搭建一个高可用Redis集群也是防止缓存雪崩的有效手段
2 缓存击穿:某一个热点key突然失效,导致大量请求直接访问Mysql,解决,热点key不设置过期时间
3 缓存穿透:假如某一时刻访问redis的大量key都在redis中不存在,会给数据造成大量压力,使用布隆过滤器来拦截不存在的key
72 Redis和Mysql如何保证数据一致
延迟双删:先删除Redis缓存数据,再更新Mysql,延迟几百秒再删除缓存
73 Explain语句中各个字段分别表示什么意思
id: 查询语句每出现一个select,Mysql就会为其分配一个唯一ID,某些子查询会被优化为join查询,那么出现的id会一样
select_type:SELECT对应的那个查询类型
table: 表名
partitions:匹配的分区信息
type:争对单表的查询方式(全表扫描,索引)
possible_keys:可能用到的索引
key: 实际上用到的索引
key_len:实际使用到的缩影长度
ref: 当使用索引列查询时,与索引列进行等值匹配的对象信息
rows: 预估需要的去的记录条数
filtered: 某个表经过搜过条件过滤后剩余记录条数的百分比
Extra:一些额外信息,比如排序
74 索引覆盖是什么
索引覆盖是指sql执行时,利用索引快速查找,,并且此sql要查询的字段在当前索引对应的字段都包含了,此sql走完索引就不用回表了,所需的字段都在当前索引的叶子节点存在,可以直接作为返回结果
75 最左前缀原则是什么
当一个sql 想要利用索引时,那么就一定要提供该索引所对应字段中的最左边字段
76 Innodb是如何实现事务的
Innod通过Buffer Pool log,Buffer ,redo log,undo log来实现事务的
77B树和B+树的区别,为什么说Mysql使用B+树
B树的特点:
1 节点排序
2 一个节点可以存多个元素,多个元素也排序了
B+树的特点:
1 拥有B树的特点
2 叶子节点之间有指针
3 非叶子节点上的元素在叶子节点上冗余了,也就是叶子节点中存储了所有的元素,并且排好顺。
Mysql索引使用的是B+树,可以加快查询,一个节点可以存多个元素,B+树高度不会太高,一个Innodb页就是一个B+树节点,一个 Innodb 页默认16kb,所以一般情况下两层的B+树可以存2000万行数据
可以支持全表扫描,范围查询等Sql 语句
78 Mysql锁有哪些,如何理解
按锁力度分:
1 行锁,锁某行数据,锁粒度小,并发高
2 表锁: 锁整张表,锁粒度最大,并发低
3 间隙锁:锁某个区间
还可以分为:
1 共享锁:也就是读锁,给某行数据加了读锁,其他事务也可以读,但不能写
2 排他锁: 写锁,给某行数据加了写锁,其他事务不能读,也不能写
还可以分为:
1 乐观锁: 并不会真正的锁某行数据,而是通过某一个版本号实现
2 悲观锁:行锁,表锁都是悲观锁
79 Mysql慢查询如何优化?
1 检查是否走了索引,如果没有则优化SQL利用索引
2 检擦利用的索引是否是最优索引
3 检查所查字段是否都是必须的
4 检查表中数据是否过多,是否应该进行分库分表
5检查数据库所在机器的配置性能,适当增加资源
80 消息队列有哪些作用
1 解耦:使用消息队列来作为两个系统直接的通讯方式,两个系统不需要相互依赖
2 异步:系统A给消费队列发送完消息之后,就可以做其他事情了
3 流量削峰:人如果使用消息队列的方式调用某个系统,那么消息在某个队列中排队,由消费者自己控制消费速度
81 死信队列是什么,延时队列是什么?
1 死信队列也是一个消息队列,存放没用消费成功的消息,通常可以用作消息重试
2 延时队列就是用来存放在指定时间需要被处理的元素的队列,处理一些具有过期性操作的业务
82 Kafka为什么比RocketMq的吞吐量高
kafka生产者采用异步发送消息的机制,消息发送先缓存起来,达到一定数量,再批量发送给Broker,减少了网络io,生产者宕机会导致数据丢失,这样做提高了性能,降低了可靠性
83 Kaflka的push和Push分别什么优缺点
1 push表示消费者主动拉去,可以批量拉去,也可以单条拉取,但有可能拉去到空消息
2 push表示Broker给消费者推送消息,所以肯定是有消息才推送,推送多少消息,消费者消费多少消息,可能会造成网络堵塞
84 RocketMq的底层实现
RocketMQ由NameServer集群,Producer集群,Conssumer集群,Broker集群组成
1 Broker在启动的时候向所有NameServer注册,保持长连接
2 Producer在发送消息的时候从NameServer获取Broker服务地址,根据负载均衡算法向一台服务器发送一条信息
3 Consumer消费信息的时候同样从NameServer中获取Broker地址,然后主动拉取信息消费
85 消息队列如何保证消息可靠传输
消息可靠传输代表:既不能多也不能少
1 生产者不能重复生产消息,消费者不能重复消费消息,保证消息不多
2保证消息不多发,需要在消费端做控制
3 避免不重复消费,消费者实现幂等性
4 消息不能少,生产者发送消息时,确认broker收到并持久化了这条消息(RabbitMQ的confirm机制,Kafka ack机制),broker要等消费者收到了消息才删除调消息,通过消费端的ack 机制实现
86 TCP的三次握手和四次挥手
TCP是7层网络协议中的传输层协议,负责数据的可靠传输
在建立tcp连接时,需要通过三次握手建立:
1 客服端向服务端发送一个SYN
2 服务端接收到SYN后,向客服端发送一个SYN_ACK
3 客服端接收到SYN_ACk 后向服务端发送一个ACK
在断开TCP连接时需要通过四次挥手来断开:
1 客服端向服务器发送FIN
2 服务端接收到FIN后向客服端发送ack,表示我接受到断开连接的请求,客户端可以不发送数据了,不过服务端可能还有数据在处理
3 服务端处理完所有数据后,向客户端发送FIN,表示服务端现在可以断开连接了
4 客户端收到服务端哦的fin,向服务端发送ACK,表示客服端断开连接了
87 java中创建线程的几种方式
1 继承Thread 类,重写run方法
2 实现了Runnable接口,实现run方法
集合篇
java 提供的集合:
Collection:
list(有序可重复):
Vector-----数组结构,线程安全
ArrayList----数组结构,非线程安全
LinkList----链表结构,非线程安全
set(无序,唯一)
HashSet-----哈希表结构
TreeSet ----红黑树结构
Map:
map(双列集合)
HashTable: 哈希表结构,线程安全----Properties
HashMap: 哈希表结构,非线程安全----LinkedHashMap 哈希表和链表结构
ConcurrentHashMap 哈希表结构,线程安全
TreeMap 红黑树结构
1 算法复杂度分析
时间复杂度:代码的执行耗时
大O表示法:不具体表示代码的真正执行时间,而是表示代码执行时间随数据规模增长趋势的变化
、
空间复杂度;代码运行所需要占用的内存空间
2 List相关面试题
1 数据结构-数组
添加删除:o(n)
查询:
已知索引:o(1)
未知索引o(n)
2 ArrayList源码分析
成员变量:
构造函数:
关键方法:
添加:
第一次添加数据:
扩容:
3 ArrayList底层实现原理
底层数组实现,每次扩容是以前的1.5倍,每次扩容会复制原来的数组,初始容量为0,第一次添加数据时容量为10,添加数据时,检查使用长度+1是否能存下一个数据,就会先计算容量,容量不够,调用grow方法进行扩容,新增数据有地方存了之后,放到size这个地方,放回成功布尔值
4 如何使用数组和List之间的转换
aslist是对象的应勇,tolist是数组的拷贝
5 链表操作数据的时间复杂度
6 ArrayList和LinkList的区别是什么
1 底层数据结构
Arraylist是动态数组实现的
LinkList是双向链表实现的
2 操作效率
ArrayList按照下标查询的时间复杂度0(1)
未知索引:
Arraylist和linkList都是 o(n)
新增和删除
Arraylist尾部删除和插入,时间复杂度是O(1),其他是O(n)
LinkList头尾节点增删是o(1),其他o(n)
3 内存空间的占用:
arrayList:内存连续,节省内存
LinkList: 双向链表,有两个指针,更占用内存
4 线程安全
都是线程不安全的·
解决:
方法内使用,局部变量则是线程安全
使用线程安全的ArrayList和Linklist
使用Collection.synchronizedList()方法包装
3 HashMap相关面试题
1 二叉树:每个节点最多有两个子节点
1二叉搜索树:左节点都要小于又节点,插入删除查询o(log(n))
2 红黑树 :节点要么是红色,要么是黑色,叶子节点是黑色空节点,红色节点的子节点是黑色,任意节点到子节点经过的黑色节点相同
1红黑树的时间复杂度,查找删除添加是o(log(n))
2 散列表
概念:是更据键访问内存存储位置值的数据结构,由数组演变而来
散列函数:将key映射为数组下标的函数叫散列函数,可以表示为hashValue=hash(key)
要求:散列函数必须是大于0的正整数,key相同散列值相同,key不相同,散列值也不相同
hash冲突:多个key 存映射到了相同位置,解决办法:
拉链法:将链表法中的改造改造为红黑树效率更高,查找和删除o(log(n))
3 HashMap的实现原理
数据结构:hash表的数据结构,数组加链表或红黑树
放元素时:
1 利用key的HashCode重新hash计算出当前对象的元素在数组的下标。
如果hash值相同:
key 相同:覆盖
key不同:key-value放入链表或红黑树(当链表长度大于8,数组长度大于64,转为红黑树)
2 获取值,找到hash值对应的下标,进一步判断key是否相同,从而找到对应值
4 HashMap的jDK1.7和jdk1.8有什么区别
jdk1.8之前采用的是拉链法:将链表和数组结合。遇到hash冲突直接将冲突的值加到链表中
jdk1.8之后遇到hash冲突,当数组长度大于64,链表长度大于8,转化为红黑树,减少搜索空间。扩容时,红黑树拆分程的树节点小于等于6,退化为链表
5 Hash的put方法的具体流程
默认容量值为16 ,加载因子为0.75,扩容阈值==数组容量*加载因子
6 HashMap的扩容机制
7 hashMap的寻址算法
8 hashmap在1.7情况下的多线程死循环问题
数据库篇
Mysql-优化
1 定位慢查询
方案一:
调试工具:Arthas
运维工具: Promethus,Skywalking
方案二:Mysql 自带慢日志
慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志,如果要开启慢查询日志吗,需要在Mysql的配置文件(/etc/my.cnf)中配置如下信息
Redis篇
缓存穿透:
查询一个不存在的数据,mysql查询不到数据也不会直接写入缓存,就会导致每次请求都查询数据库
框架篇
Spring框架中的bean是线程安全的吗?
spring框架中的bean默认是单例的
不是线程安全的
Sping框架中有一个@Scope注解,默认值就是singleton,单例的
因为一般在spring中的bean都是注入无状态的对象,没有线程安全问题,如果在bean中定义了可修改的成员变量,是要考录线程安全的
可以使用多例或加锁解决
什么是aop?
aop称为面向切面编程,用于将哪些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为切面(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性
常见的aop的使用场景?
1 记录操作日志
2 缓存处理
3 spring中内置的事务处理
spring中事务失效的场景?
异常捕获处理
抛出检查异常
非public方法
Spring的bean的生命周期?
1 通过BeanDefinition获取bean的定义信息
2 调用构造函数实例化bean
3 bean 的依赖注入
4 处理Aware接口(BeanNameAware,BeanFactory,ApplicationAware)
5 Bean的后置处理器BeanPostProcessor-前置
6 初始化方法(InitalizingBean,init-method)
7 Bean的后置处理器BeanPostProcessor-后置
8 销毁bean
Spring中的循环引用
Spring解决循环依赖是通过三级缓存
SpringMvc的执行流程
1 视图版本
2前后端接口开发
Springboot的自动配置原理
@SpringbootApplication包括:
@SpringBootConfiguration:该注解与@Configuration注解作用相同,来声名当前也是一个配置类
@ComponentScan:组件扫描,默认扫描当前引导类所在的包及其子包
@EnableAutoConfiguration:SpringBoot实现自动化配置核心注解
Spring 框架常见的注解
SpringMVC常见的注解有哪些?
Springboot常见的注解有哪些?
Mybatis执行流程
Mybatis是否支持延迟加载?
Mybatis的一级,二级缓存用过吗?
一级缓存:基于PrepetualCache的hashMap本地缓存,其存储域为Session,当Session进行flush或close后,该Sessio中所有Cache将清空,默认打开一级缓存
JVM篇
1 jvm是什么
Java virtual Machine java程序运行时环境(java二进制的运行时环境)
2 JVM由哪些部分组成,运行流程是什么
3 什么是程序计数器?
线程私有的,每个线程一份,内部保存的字节码的行号,用于记录正在执行的字节码指令地址
4 java堆
线程共享区域:主要用来保存对象实例,数组,当堆没有内存空间可分配给实例,也无法再扩展时,则抛出OutofMermoryError异常
组成:年轻代,老年代
年轻代:Eden区 和两个幸存者区
老年代;保存生命周期长的对象
5 什么是虚拟机栈
1 每个线程运行时所需要的内存,称为虚拟机栈
2 每个栈由多个栈帧组成,对应每次方法调用时所占内存
3 每个线程只有一个活动栈帧,对应正在执行的哪个方法
6 垃圾回收是否涉及栈内存
垃圾回收主要指的是堆内存,当栈帧弹栈以后,内存就会释放
7 栈内存分配越大越好吗?
栈帧过大会导致线程数变少
8 方法的局部变量是否线程安全
如果方法内的局部变量没有逃离方法的作用范围,是线程安全的
如果局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全
9 什么情况下会导致栈内存溢出
栈帧过多会导致栈内存溢出
10 堆栈的区别是什么
栈内存一般会用来存储局部变量和方法调用,但堆内存是用来存储Java对象和数组的,堆会GC垃圾回收,而栈不会
栈内存是私有的,而堆内存是线程共享的
两者异常错误不同,但是如果栈内存或者堆内存不足,都会抛出异常
实战八股文
创新思路:
1 问设计模式有哪些?你最常用哪些?
在Java后端开发中,设计模式是一种重要的编程范式,用于解决常见的软件设计问题。以下是一些常见的设计模式:
1. 创建型模式:
- 单例模式(Singleton Pattern):确保一个类只有一个实例。
- 工厂模式(Factory Pattern):根据条件创建不同类型的对象。
- 建造者模式(Builder Pattern):通过逐步构建复杂对象来创建对象。
- 原型模式(Prototype Pattern):通过复制现有对象来创建新对象。
2. 结构型模式:
- 适配器模式(Adapter Pattern):将一个类的接口转换为客户端所期望的另一种接口。
- 装饰器模式(Decorator Pattern):动态地给对象添加额外的职责。
- 代理模式(Proxy Pattern):控制对其他对象的访问。
- 桥接模式(Bridge Pattern):把抽象和实现解耦,使它们可以独立变化。
3. 行为型模式:
- 观察者模式(Observer Pattern):定义对象间的一种一对多的依赖关系。
- 策略模式(Strategy Pattern):定义一系列算法,将每个算法封装起来,并使它们可以互换。
- 命令模式(Command Pattern):将请求封装成对象,以便使用不同的请求、队列或日志来参数化其他对象。
- 模板方法模式(Template Method Pattern):定义一个操作中的算法骨架,将一些步骤延迟到子类中实现。
对于我个人而言,在实际开发中,我最常用的设计模式是单例模式、工厂模式和观察者模式。这三种设计模式在很多场景下都能帮助解决常见的问题,提高代码的重用性和灵活性。但实际使用的设计模式还要根据具体的项目需求和场景进行选择。
2 谈谈对于线程的一些理解
线程是计算机中的一种基本的执行单位,它可以独立地运行,并且能够与其他线程共享内存空间。在Java中,线程是通过Thread类来实现的。
以下是我对线程的一些理解:
1. 线程的优点:线程可以提高程序的并发性,充分利用CPU资源,提高程序的响应速度和处理能力。通过线程,我们可以将耗时的操作放到后台进行,避免阻塞主线程。
2. 线程的状态:线程在运行过程中会有不同的状态,包括新建状态、就绪状态、运行状态、阻塞状态和死亡状态。不同的状态对应着线程生命周期中的不同阶段,也需要不同的操作来进行处理。
3. 线程的同步:线程之间的并发执行可能会出现一些问题,如竞态条件、死锁等。为了解决这些问题,我们需要使用同步机制,如synchronized关键字、Lock接口、Atomic变量等,来保证线程之间的协作和数据的一致性。
4. 线程的通信:线程之间需要相互通信,以实现协作完成任务。Java提供了多种方式来实现线程通信,如wait/notify机制、Condition接口、管道等。
5. 线程的创建:在Java中,我们可以通过继承Thread类或实现Runnable接口来创建线程。同时,使用线程池可以更好地管理和控制线程的数量和生命周期。
总之,线程是Java中非常重要的概念,了解线程的基本原理和使用方法对于编写高效、稳定的并发程序非常重要。
3 单点登录原理图
4 查看linux内核版本该用哪些命令
uname -r 这个命令会显示当前正在运行的内核版本号
cat /proc/version 个命令会显示包括内核版本在内的系统版本信息。
cat /proc/sys/kernel/osrelease 这个命令会显示当前正在运行的内核版本号。
dmesg | grep "Linux version" 这个命令会显示内核启动时的信息,其中包括内核版本号。
4 ack怎么用
Awk是一种强大的文本处理工具,它可以用于对结构化文本文件进行处理和分析。下面是使用Awk的基本用法:
1. 执行Awk脚本:
```
awk '脚本' 文件名
```
2. 打印文件的每一行:
```
awk '{print}' 文件名
```
3. 根据字段进行操作:
- 打印指定字段:使用$符号加上字段编号,从1开始计数。
```
awk '{print $1, $2}' 文件名
```
- 打印最后一个字段:
```
awk '{print $NF}' 文件名
```
4. 使用条件进行筛选:
```
awk '条件 {print}' 文件名
```
5. 使用分隔符进行字段划分:
- 使用空格作为分隔符:
```
awk -F" " '{print $1, $2}' 文件名
```
- 使用逗号作为分隔符:
```
awk -F"," '{print $1, $2}' 文件名
```
6. 自定义变量:
```
awk 'BEGIN{变量=值} 条件 {print 变量}' 文件名
```
7. 使用Awk内置函数:
- length:获取字符串长度。
- substr:获取子串。
- toupper:将字符串转换为大写。
- tolower:将字符串转换为小写。
以上是Awk的基本用法,你可以根据具体需求使用更多的Awk特性和函数来进行文本处理和分析。
5 想要看进程树该怎么操作
要查看进程树结构,可以使用`pstree`命令。以下是一些常用的参数:
1. `-p`:显示进程ID。
2. `-u`:显示进程所属的用户名。
3. `-n`:以数字排序方式显示进程树。
4. `-a`:显示命令行参数。
5. `-h`:显示帮助信息。
例如,要显示进程树结构以及每个进程的PID和命令行参数,可以使用以下命令:
```
pstree -ap
```
这将以树状结构显示所有进程,并显示每个进程的PID和命令行参数。
注意:`pstree`命令在不同的Linux发行版中可能会有些许差异,你可以使用`man pstree`命令来查看特定发行版下的帮助文档,了解更多参数和用法。
6 问 spring中bean的作用域;
在Spring框架中,Bean的作用域指定了在应用程序中创建和管理Bean实例的方式。Spring提供了以下常见的Bean作用域:
1. Singleton(单例):默认的作用域,每个Spring容器中只有一个Bean实例。无论是通过依赖注入还是通过程序获取Bean,都将返回同一个实例。
2. Prototype(原型):每次请求Bean时,Spring容器都会创建一个新的Bean实例。每个获取Bean的地方都会得到不同的实例。
3. Request(请求):每个HTTP请求都会创建一个新的Bean实例,该Bean仅在当前HTTP请求内有效。在同一个请求中,多次获取该Bean将得到同一个实例。
4. Session(会话):每个用户会话(Session)都会创建一个新的Bean实例,该Bean仅在当前会话内有效。在同一个会话中,多次获取该Bean将得到同一个实例。
5. Global Session(全局会话):仅适用于基于Portlet的Web应用程序。每个全局会话都会创建一个新的Bean实例,该Bean仅在当前全局会话内有效。
6. Custom(自定义):可以通过实现`org.springframework.beans.factory.config.Scope`接口来定义自己的作用域。
通过在Spring配置文件或使用注解的方式,您可以为每个Bean定义所需的作用域。选择适当的作用域可以根据应用程序的需求,实现对Bean实例的合理管理和控制。