1:二叉树中序遍历
中序遍历的顺序是左根右,可以采用递归的方式进行遍历,先遍历根节点判断是否有左子树,若存在左子树则递归遍历左子树,若不存在则输出左叶子节点,输出父节点,然后递归遍历右子树。
2:springAop,在项目中有哪些实现。
Aop是面向切面编程,是对面向对象编程的一个补充。将与业务代码无关,并对多个类产生影响的公共行为抽取出来并封装成一个公共可重用的模块,减少重复代码,降低了模块的耦合度提高了系统的可维护性。主要用于日志记录、权限、事务等。
(1)切面(Aspect):被抽取的公共模块,可能会横切多个对象。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @AspectJ 注解来实现。
(2)连接点(Join point):指方法,在Spring AOP中,一个连接点 总是 代表一个方法的执行。
(3)通知(Advice):在切面的某个特定的连接点(Join point)上执行的动作。通知有各种类型,其中包括“around”、“before”和“after”等通知。许多AOP框架,包括Spring,都是以拦截器做通知模型, 并维护一个以连接点为中心的拦截器链。
(4)切入点(Pointcut):切入点是指 我们要对哪些Join point进行拦截的定义。通过切入点表达式,指定拦截的方法,比如指定拦截add*、search*。
(5)引入(Introduction):(也被称为内部类型声明(inter-type declaration))。声明额外的方法或者某个类型的字段。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可以使用一个引入来使bean实现 IsModified 接口,以便简化缓存机制。
(6)目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。也有人把它叫做 被通知(adviced) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象。
(7)织入(Weaving):指把增强应用到目标对象来创建新的代理对象的过程。Spring是在运行时完成织入。
切入点(pointcut)和连接点(join point)匹配的概念是AOP的关键,这使得AOP不同于其它仅仅提供拦截功能的旧技术。 切入点使得定位通知(advice)可独立于OO层次。 例如,一个提供声明式事务管理的around通知可以被应用到一组横跨多个对象中的方法上(例如服务层的所有业务操作)。
在项目中主要用于日志记录
在类上添加@Aspect和@Component注解
通过@pointcut来定义切点,拦截我们想要代理的类。
通知方式before after around,其中around 必须采用proceedingJoinPoint类,别的可传入JoinPoint类。可获取方法名,入参出参。
3:什么是幂等性
同一个事件无论执行多少次结果前后保持一致。
4:策略模式,在项目中为什么用策略模式。
策略接口、策略接口实现、上下文对象
对支付宝支付和微信支付的相关支付接口进行了封装,上下文对象根据调用方的入参来确定具体策略的执行者。在确定具体执行对象时,通过spring的上下文ApplicationContext,获取策略接口实现类的map集合,然后通过入参获取具体策略。作用减少了上下文对象在确定具体策略执行时的if else 判断。若新增一种策略不需要修改上下文对象的内容。可以理解为一个支付业务的微服务。
5:项目部署外网是否可以访问到,对外网攻击做了什么处理。
可以访问到,没做处理。
6:事务在什么情况下会失效。
a:方法不属于public类型
b:spring的事务是基于数据库事务实现的,数据库不支持事务spring的事务也会失效。
c:一个类中非事务方法A调用事务方法B失效。
d:方法在执行中抛出非事务的异常,事务无法回滚。
e:当采用spring和springMvc的架构时配置文件重复扫描service层会导致事务失效。在启动时默认先加载springMvc的配置,若加载配置时注册了service层,而此时事务还没有加载则会导致事务失效。所以要把service扫描放在spring的配置文件中。
7:线程同步和线程协作,口述线程协作的代码。
线程同步:在多线程运行的情况下,程序运行结果与单线程一致。实现线程同步的话需要加锁。
线程协作:通过线程调度,程序在多线程运行下结果与我们期待的一致。
8:谈一谈对线程池的理解。
线程池主要是帮助我们管理线程,提高响应速度,可以重复使用线程,减少了新建线程和销毁线程所带来的资源的损耗。
对任务的处理策略:接受任务判断核心线程数是否等于最大值,若小于则创建核心线程执行任务。否则将任务交给阻塞队列。判断阻塞队列是否已满,若未满则将任务加入到对列中,若已满则判断线程池线程数是否超过最大线程数,若不超过则创建一条非核心线程进行执行,若超过则执行饱和策略。
线程池类ThreadPoolExecutor可以创建线程核心参数
CoreThreadSize 核心线程数的最大值。
MaximunPoolSize 线程池最大线程数。
KeepAliveTime 线程池非核心线程存活时间大小。
Unit 存活时间单位。
workQueue存放任务的阻塞队列。
ThreadFactory 用于生产线程的工厂,我们可以给线程赋予有意义的名字,帮助我们调试。
Hander 线程饱和策略处理方式 主要有四种:抛出异常、放弃任务、放弃队列中最老的任务将任务交给线程池、教给线程池所在的线程进行处理。
线程池异常处理
1:使用try catch来捕获异常。
2:submit执行,使用Fature.get方法声明异常。
3:继承ThreadPoolExecutor类重写afterExecute方法来处理异常的应用。
4:实例化时传入ThreadFactory类,设置Thread的uncountExceptionHander方法来处理异常。
线程常用的阻塞队列
1:ArrayBlockingQueue 有界队列基于数组实现按照FIFO排序量。
2:LinkedBlockingQueue 基于链表实现的阻塞队列,可以设置边界,若不设置则是无界队列,newFixedThreadPool采用的就是这种队列。
3:DelayQueue 延迟队列,是一个任务定时周期的执行队列,根据指定执行时间从小到大排序,否则根据插入到队列的先后排序执行。newScheduledThreadPool线程池使用了这个队列。
4:priorityBlockingQueue优先级队列。
5:SynchronousQueue 同步队列,队列不储存元素,一个元素插入操作必须等到另一个队列移除。
几种线程池
1:newFixedThreadPool固定数目线程的线程池
核心线程数等于最大线程数,采用无界的阻塞队列,不存在过期时间。
执行流程:提交任务若线程数小于核心线程则创建线程执行任务否则将任务添加到阻塞队列。若执行完毕则去阻塞队列去任务继续执行。
若单个任务执行的时间较长则会向阻塞队列之中一直添加任务,因为是无界队列可能会导致内存溢出。适用于处理cpu密集型的任务,确保cpu长期被工作线程使用的情况下尽可能的少分配线程,适用于长期执行任务。
2:newCachedThreadPool可缓存线程的线程池
核心线程数目为0,最大线程数目为Integer.MAX_VALUE,阻塞队列为SynchronousQueue 同步队列,过期时间为60秒。
执行流程:是否有空闲的线程,有则执行任务,没有新建线程去执行任务,任务执行完毕判断是否到达过期时间,到达过期时间进行清除。
当提交任务的速度大于处理速度时,每提交一个任务就必然会创建一个线程,极端情况下会创建很多线程耗尽cpu资源。当任务空闲时活跃线程数为0不占用cpu资源,适合于并发量大执行时间断的情况。
3:newSingleThreadExecutor 单线程的线程池
核心线程数为1,最大线程数为1,使用linkedBlockingQueue队列过期时间为0
执行策略:提交任务判断队列中是否有一条线程,若不存在则新建一条线程执行任务,若存在则判断线程是否空闲,若空闲则执行任务,否则加入对列,任务被一条一条的执行,适合于串行执行的场景。
4:newScheduledThreadPool 定时及周期执行的线程池
最大线程数为Integer.MAX_VALUE,阻塞队列是DelayedWorkQueue,存活时间为0,scheduleAtFixedRate() :按某种速率周期执行scheduleWithFixedDelay():在某个延迟后执行。
执行流程:添加一个任务线程池中的线程从 DelayQueue 中取任务,线程从 DelayQueue 中获取 time 大于等于当前时间的task,执行完后修改这个 task 的 time 为下次被执行的时间这个 task 放回DelayQueue队列中。适用于周期执行任务的场景,需要现在线程的数量。
线程池的状态
Running:线程池会接收新任务,处理阻塞队列中的任务,调用shutdown方法切换到shutdown状态,调用shutdownNow方法切换到stop状态。
Shutdown:线程池不会接收任务,但是会继续处理队列中的任务,当队列为空,且线程池中的任务也为空时进入Tidying状态。
Stop:不接受新任务任务,也不处理队列中的任务,中断正在执行的任务。线程池中的任务为空进入Tidying状态。
Tidying:该状态表明线程池中的任务中止,记录的任务数量为0。terminated()执行完毕,进入TERMINATED状态。
TERMINATED:线程池已完全终止
9:java的内存模型。
内存模型:在特定操作协议下对特定的内存或高速缓存进行读写访问的过程的抽象。
Java内存模型:屏蔽各种硬件和操作系统的内存访问差异,让java程序在各个系统都能达到一致的内存访问效果。定义程序中各个变量的访问规则,即在虚拟机中将变量存入内存和在内存中取出变量这样的细节。
主要分为两部分:主内存。所有变量都保存在主内存中。工作内存:是一种抽象概念,包括写缓冲区、缓存、寄存器等。每条线程都有自己的工作内存,每个工作内存之间通信必须通过主内存。
内存模型并发的两个问题
1:内存数据一致性
各个线程数据会保存主内存变量的副本,当多个线程操作同一个变量时,将导致变量副本不一致,存入内存中以哪个线程中的变量为主?
2:指令重排序优化
处理器运行程序时会对代码的执行顺序进行优化,提高处理器效率,在单线程的情况下能过保证执行结果前后一致,但是在多线程情况下可能因执行顺序不同出现不同的结果。
内存交互的基本操作
Luck:作用于主内存变量,对内存中的变量加锁
Unluck:作用于主内存变量,将对象从锁定的状态释放,可以被其他内存访问。
Read:作用于主内存变量,将变量值从主内存传递到工作内存,方便load使用
Load:作用于工作内存,将read操作获取的值放入到工作内存的变量副本中。
Use:作用于工作内存,将工作内存中变量的值给执行引擎,当遇到获取值字节码指令时都会执行这个操作。
Assign:作用于工作内存变量,将执行引擎的值给变量,当遇到赋值的字节码指令时就会执行这个操作。
Store:存储,作用于工作内存变量,将工作内存中的变量存到主内存中,以便后续的white操作使用。
White:作用于主内存变量,将store中的变量存入主内存。
内存交互的三个特性
1:原子性,一个或多个操作要么不执行,要么全部执行,且在执行过程中不可被打断。
2:可见性:当多个线程访问一个变量时,一个线程对变量的做出改变,对其他线程是立即可见的。
3:有序性:主要表现在线程内和线程间
从线程内看,指令按照串行的方式执行。
从线程间看由于指令重排序优化,任何代码都可能交叉执行。可以通过同步方法或者volatile关键字来维持相对一致。
Java内存模型的Happen-before
1:程序次序原则:单个线程内前面的操作对后面的操作。
2:锁定原则lock对unlock。
3:传递性原则happen-before具有传递性。
4:volatile变量原则,对volatile修饰变量的写操作发生在对变量读操作之前。
5:对对象的初始化操作发生在对对象执行finalie方法之前。
6:线程启动原则:线程start方法发生在线程其他操作之前。
7:线程中断原则:调用interrupt方法发生在线程检测到中断事件的发生。
8:线程终结原则:线程中所有操作都发生在中止操作之前。
内存屏障
LoadLoad屏障:
对于这样的语句 Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。
StoreStore屏障:
对于这样的语句 Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。
LoadStore屏障:
对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被执行前,保证Load1要读取的数据被读取完毕。
StoreLoad屏障:
对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的(冲刷写缓冲器,清空无效化队列)。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能。
volatile关键字
1:保证可见性
线程写volatile变量的过程:
改变线程工作内存中volatile变量副本的值
将改变后的副本的值从工作内存刷新到主内存
线程读volatile变量的过程:
从主内存中读取volatile变量的最新值到线程的工作内存中
从工作内存中读取volatile变量的副本
2:禁止指令重排序
当程序执行到 volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;
在进行指令优化时,不能将在对 volatile 变量访问的语句放在其后面执行,也不能把 volatile 变量后面的语句放到其前面执行。
10:设计表对表进行拆分,是否通过外键关联,若是通过逻辑关联怎么避免脏数据产生。
数据库三大范式
第一范式:数据列表的每一列都是原子项不可再分。
第二范式:非码属性必须完全依赖于码。
第三范式:任何非码属性不依赖其他码。不存在传递依赖。
--
修改:PlutoKey FROM 223.104.68.*
FROM 223.104.68.*