- 一.集合
- 二.JVM
- 三.spring
- 四.springMVC
- 五.网络
- www.changgou.com是如何到达服务器的(网络传输是怎么做的)">用户发送www.changgou.com是如何到达服务器的(网络传输是怎么做的)
- 公司怎么保证网络安全
- 电商项目复习
- 六.Mybatis
- {} 会通过预编译的形式设置占位符,在调用的时候传入参数并执行SQL语句
${} 会直接把参数拼接进SQL语句,有SQL注入的风险
${}的用处,数据量比较大的系统,比如需要记录大量的日志,而这些日志不会存在一张表里,存多个表,以时间为表名比如log_6_9 select * from ${}
所以在为表名传值的时候用${}; - 七.spirngCloud
- 八.springBoot
- 九.线程池
- 十一.redis
- 十二.事务
- 十三.Mysql
- 十四.RabbitMQ
- ==和equals方法的区别
- Java中基本数据类型和包装类型有什么区别?
- linux常用命令
- 十五.薪资构成
- 十六.项目问题
- 【面试题】-java分布式及微服务面试题汇总
- SpringCloud 与 SpringBoot 微服务 架构 | 面试题及答案详解
- 面试日志
- 一.集合
- 二.JVM
- 三.spring
- 四.springMVC
- 五.网络
- 电商项目复习
- 六.Mybatis
- {} 会通过预编译的形式设置占位符,在调用的时候传入参数并执行SQL语句
- 七.spirngCloud
- 八.springBoot
- 九.线程池
- 十一.redis
- 十二.事务
- 十三.Mysql
- 十四.RabbitMQ
- ==和equals方法的区别
- Java中基本数据类型和包装类型有什么区别?
- linux常用命令
- 十五.薪资构成
- 十六.项目问题
- 【面试题】-java分布式及微服务面试题汇总
- SpringCloud 与 SpringBoot 微服务 架构 | 面试题及答案详解
- 面试日志
一.集合1.arrayList1.arrayList 存储特性2.arrayList 初始容量3.arrayList 如果存储满后,如何进行扩容4.为什么arrayList 会引发并发修改异常2.linkedList 链表集合 1.linkedList 特性3.hashSet存储特性4.hashMap1.hashMap的存储结构是什么样子的(jdk1.7)2.已知需要存储1000个长度,初始化hashMap时 初始化大小应该设置为多少.3.HashMap(jdk1.8)原理二叉树二叉树存在的问题红黑树(平衡二叉树,解决某一节点某一节点过长问题)5.HashTable1.hashMap线程不安全,如何保证hashMap线程安全?2.结构3.简单分析源码6.ConcurrentHashMap(1.7)1.如何保证线程安全,并且性能比hashTable高二.JVM三.springIOC控制的是什么,反转的是什么.bean对象的生命周期回答模板bean实例化三种方式bean的三种注入方式AOP为什么有接口用jdk,没接口用cglibSpring 框架中都用到了哪些设计模式? spring的bean对象,时单例还是多例?怎么能既保证线程安全,有使用线程不安全的类?四.springMVC1.springMVC 0配置加载实现的补充2.springMVC源码(执行流程)springMVC加载流程springMVC请求流程(用户发送请求controller如何处理的)springMVC执行流程五.网络用户发送www.changgou.com是如何到达服务器的(网络传输是怎么做的)公司怎么保证网络安全防火墙物理防火墙逻辑防火墙 电商项目复习线上问题用了多少台服务器搭建:如果当前用户第一次访问提供者服务,注册中心宕机,问当前提供者还能访问嘛?六.Mybatis一级缓存和二级缓存#{}和${}的区别七.spirngCloud五大组件八.springBootspring和springBoot的区别关系Spring Boot有哪些优点?springboot自动配置原理(聊的越深,越有谈资)springBoot 0配置springBoot如何实现jar工程发布九.线程池线程池好处线程池的运行机制线程池状态共有七个参数,每个参数含义如下:四种常见的线程池FixedThreadPool正规线程SingleThreadExecutor单线程线程池CachedThreadPool缓存线程池ScheduledThreadPool延迟线程池实现多线程的方式解决现线程安全问题synchronizedLock锁死锁线程状态线程并发三大特性java内存模型(JMM) ,jmm如何保证可见性解决可见性synchronized多线程中提供的锁机制自旋锁适应自旋锁volatile实现原理使用volatile会出现原子性问题,解决方案:volatile适合使用场景volatile和synchronized的区别十.JUCsynchronized性能低的原因CAS原理AQS锁机制ReentrantLock可重入锁ReentrantLock与synchronized的区别JUC下的并发工具类CyclicBarrier 同步屏障CountDownLatch 计数闭锁Semaphore(信号量)十一.redis1.数据类型 5个 String hash set zset list 应用场景2.redis中一个字符串的最大容量是多少:512M默认存储最大值3.redis常用命令4.redis的使用场景 满足后续几个条件都可以使用redis 5.为什么单线程(单线程模型)?这个问题现在不对6.redis提供的持久化方案7.RDB与AOF如何选择8.什么是缓存穿透,缓存雪崩,缓存击穿,如何避免.9.redis分布式锁代码redis如何删除设置了过期时间的key(redis的过期策略)redis的内存淘汰机制十二.事务事务的基本要素(ACID)事务的并发问题MySQL事务隔离级别事务的传播行为十三.Mysql1.查询优化Mysql执行流程慢查询2.索引优化(使用频率高,面试问的多)索引的类型索引存储结构BTree哈希索引Full-text全文索引 spring事务传播机制????mysql调优回答模板十四.RabbitMQ好处死信队列消息成为死信的三种情况延迟队列消息队列如何保证消息不丢失==和equals方法的区别Java中基本数据类型和包装类型有什么区别?linux常用命令十五.薪资构成十六.项目问题1.开发流程二面的一些问题1.如果你审查代码 你审查别人代码的标准是什么,2.工作模式3.工作中遇到的困难,怎么解决【面试题】-java分布式及微服务面试题汇总SpringCloud 与 SpringBoot 微服务 架构 | 面试题及答案详解面试日志2022/04/07 杭州市民卡(外包一面)== 和equals的区别String能被继承吗ArrayList和LinkedList区别redis持久化几种方式2022/04/08中软外包银行5.redis使用场景2022/04/10中软银行外包
一.集合
1.arrayList
1.arrayList 存储特性
有序且可重复 为什么?
底层是object数组,数组不会对元素进行判断,所以可以重复,基于数组下标得连续存储,所以有序
增删改慢,遍历查询快
2.arrayList 初始容量
默认情况下arrayList 初始大小是多少
初始容量是0,在第一次调用add 方法后,进行第一次扩容,大小为10,
3.arrayList 如果存储满后,如何进行扩容
扩容大小是多少
用户第一次调用add方法
arrayList 会进行第一次扩容
扩容后大小为10
何时扩容?存储11个元素时,扩容
扩容大小是原来得1.5倍(原来大小+原来大小右移一位)
4.为什么arrayList 会引发并发修改异常
arrayList 的remove方法,是把下标移动,将最后一个元素置为null,
当我们遍历arraylist时 在使用下标,同使用remove方法会改动这个下标(改变下标原有的位置),这是不可能的 ,就会出现并发修改异常.
解决方法:
- 不动用迭代器,使用普通for循环
CopyOnWriteArrayList使用这个,为什么 因为加了lock
2.linkedList 链表集合
1.linkedList 特性
3.hashSet
存储特性
无序且不可重复
HashSet的构造 是new 了一个hashMap
add()方法是把值存在了hashMap的key中,value存的是一个不变的object4.hashMap
1.hashMap的存储结构是什么样子的(jdk1.7)
Entry对象数组+单向链表(Entry就是一个对象)
为什么无序:通过key计算hash,通过hash和数组长度计算当前元素存储在数组的小标位置,所以无序.
不可重复:一旦重复会进行替换.
初始化的时候默认长度为0 数组长度16
hashMap什么时候第一次扩容:长度大于等于12并且存在hash冲突.
hashMap构造器
put方法2.已知需要存储1000个长度,初始化hashMap时 初始化大小应该设置为多少.
扩容机制很损耗性能,
初始大小应该时2048,因为长度要是2的幂次方, 1024*0.75=700多的时候要进行扩容,所以不是1024.3.HashMap(jdk1.8)
node数组+链表+红黑树
扩容机制:不会计算hash冲突(与1.7不一样) 数组长度只要>=12就直接扩容
为什么hashMap1.8要引入红黑树,因为1.7的扩容机制是:长度大于12并且出现hash冲突进行扩容,容易导致某一个链表变得特别长.
在多线程高并发情况下,容易出现环链(尾节点指向头节点)原理
链表转红黑树的阈值
在put方法 链表(节点node)长度>8转换成红黑树(treenode)
在delete方法还原链表结构 节点<6 红黑树转变链表
值<=6使用链表
扩容:默认存储第13个值(12是阈值)必定进行扩容机制.数组长度增长为162=32
阈值122=24二叉树
1.每个节点下,有两个叶子节点.
2.左叶子节点比根节点小,右叶子节点比根节点大
3.二叉树的查询最大次数是二叉树的高度.二叉树存在的问题
红黑树(平衡二叉树,解决某一节点某一节点过长问题)
5.HashTable
1.hashMap线程不安全,如何保证hashMap线程安全?
2.结构
3.简单分析源码
hashTable默认在构造的时候,底层entry数组就已经初始化了,初始大小是11.
- hashMap value可以为null ————hashTable value不能为null
- hashTable key也不能为null,因为null没有hashcode方法
hashTable 所有方法上加了synchronized(淘汰原因使用一把大锁,锁住了所有方法,导致性能及其低)
6.ConcurrentHashMap(1.7)
1.如何保证线程安全,并且性能比hashTable高
1.7引入了一个概念 分段锁机制
问题:默认底层entry数组的长度是2 总长度是多少? 总长度是32。
问题2:Segment中的entry数组 默认情况,什么时候扩容.
Segment是不能扩容的,entry可以是原来的2倍
存储第二个值得时候就应该扩容了.
1.8是synchnized+cas二.JVM
JVM通用内存模型
- 每个区的作用
- 虚拟机栈的流程
- java堆 eden,from,to,old 轻GC fullGC
- jvm调优,调的是什么 fullGC执行频率,执行时间.
- jvm调优工具,jconsole(jdk自带),jvisualvm.exe
- System.gc();手动执行GC,执行的fullGC 执行也没有什么用
- finally代码,在什么情况下不会执行. System.exit(0)//结束jvm
- String ccc=”aaa”+”bbb”;创建了几个String对象 1.6之后 答案是1个或者2个引用对象算对象就是2个ccc,aaabbb 一个就是aaabbb.简易对象
三.spring
创建对象?
生命周期
作用域
aop具体能做什么事情IOC
控制反转控制的是什么,反转的是什么.
控制的是bean对象
反转的是bean对象的获取方式以及对象之间的依赖关系.
以前是要什么对象就new什么对象,自己new
现在我们要什么对象就从spring容器中要,创建对象的过程交给spring,我们把创建对象的权力做了反转.
原来是自己new 现在是由spring去创建 bean对象之间的依赖关系也做了反转.bean对象的生命周期
单例对象
出生:当容器创建对象出生
活着:只要容器还在,对象一直活着
死亡:容器销毁,对象消亡
总结:单例对象的生命周期和容器相同
多例对象
出生:当我们使用对象时spring框架为我们创建
活着:对象只要是在使用过程中就一直活着
死亡:当对象长时间不用,且没有别的对象引用时,由java的垃圾回收器回收
多利对象的销毁不是spring控制的
init-method:指定对象初始化之前调用的方法,方法名称任意
destroy-method:指定对象在销毁之前调用的方法,方法名称任意回答模板
找工作的时候有些人会被问道Spring中Bean的生命周期,其实也就是考察一下对Spring是否熟悉,工作中很少用到其中的内容,那我们简单看一下。
在说明前可以思考一下Servlet的生命周期:实例化,初始init,接收请求service,销毁destroy;
Spring上下文中的Bean也类似,如下
1、实例化一个Bean--也就是我们常说的new;
2、按照Spring上下文对实例化的Bean进行配置--也就是IOC注入;
3、如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,此处传递的就是Spring配置文件中Bean的id值
4、如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(setBeanFactory(BeanFactory)传递的是Spring工厂自身(可以用这个方式来获取其它Bean,只需在Spring配置文件中配置一个普通的Bean就可以);
5、如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文(同样这个方式也可以实现步骤4的内容,但比4更好,因为ApplicationContext是BeanFactory的子接口,有更多的实现方法);
6、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;
7、如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。
8、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法、;
注:以上工作完成以后就可以应用这个Bean了,那这个Bean是一个Singleton的,所以一般情况下我们调用同一个id的Bean会是在内容地址相同的实例,当然在Spring配置文件中也可以配置非Singleton,这里我们不做赘述。
9、当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法;
10、最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。
以上10步骤可以作为面试或者笔试的模板,另外我们这里描述的是应用Spring上下文Bean的生命周期,如果应用Spring的工厂也就是BeanFactory的话去掉第5步就Ok了。bean实例化三种方式
无参构造方法实例化
- 在spring的配置文件中配置bean标签,配置id,class属性
工厂实例方法实例化
- 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)
工厂静态方法实例化
使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)
bean的三种注入方式
使用构造函数提供
- 使用set方法提供
- 使用注解提供@atuowired@qualifier@Resource
AOP
目标(target):别增强的类
连接点(joinpoint):目标类中的所有方法
切入点(pointcut):目标类中的真正被增强的方法
通知/增强(advice):增强类
切面(aspect):切入点+通知
织入(weaving):把通知类动态的加入到切入点位置的过程
目标类,增强类需要自己写,配置aop切面(spring配置文件中写)
- 告诉spring我的目标类是谁
- 我的增强类是谁
-
为什么有接口用jdk,没接口用cglib
jdk动态代理的大致逻辑即是
传入代理类 类加载器,与接口数组和自定义的InvocationHandler,然后通过分析接口信息生成java文件的字节码数据,然后调用本地方法将类加载到内存中,最后返回构造参数为InvocationHandler的代理类,该类实现代理接口,并继承Proxy类(所以jdk动态代理只能代理接口,java单继承),我们调用方法实际上是调用代理类的方法,代理类则可以通过我们传入的InvocationHandler反射调用原本的方法来实现无侵入的修改原有方法逻辑
https://www.cnblogs.com/hetutu-5238/p/11988946.htmlSpring 框架中都用到了哪些设计模式?
(1)工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
(2)单例模式:Bean默认为单例模式。
(3)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
(4)模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
(5)观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现—ApplicationListener。spring的bean对象,时单例还是多例?
是单例,会不会引起线程安全问题,spring controller bean 对象存不存在线程安全问题.
单例: 容器初始化的时候创建一个对象 让所有线程使用, 资源不会浪费,性能较好.线程不安全.
多例: 用户请求一次,创建一个当前对象.存在资源浪费,损耗性能.线程安全(因为你每个请求进来都会构建一个对象,不公用一个对象)
所以尽量不要再spring的bean 中使用成员属性.怎么能既保证线程安全,有使用线程不安全的类?
解决方案:ThreadLocal
ThreadLocal 存储空间,存储数值,变量
- 线程之间的隔离存储
ThreadLocal其实就是一个map,key是系统自己维护的,是当前线程
四.springMVC
1.springMVC 0配置加载实现的补充
spring提供了实现0配置,实现webApplicationInitialiazer重写了onStartup方法(tomact服务器启动时调用)
servlet3.0 引入了一个技术SPI(接口发现服务)是一种服务发现机制,他通过ClassPath路径下META-INF/services文件夹查找文件,自动加载文件里所定义的类
所以tomcat启动时会加载META-INF/services文件
加载SpringServletContainerInitializer这个类 实现了ServletContainerInitializer
ServletContainerInitializer这个接口有onstartup方法
2.springMVC源码(执行流程)
springMVC加载流程
dispatherServlet加载的时候,就会初始化很多处理器
dispatherServlet就是一个servlet,会有一个init方法.会初始化很多处理器
springMVC请求流程(用户发送请求controller如何处理的)
在处理用户请求的时候,(dispatherServlet会处理请求),会调用service doget dopost某一个方法
会调用doDispatch(req,res)方法
springMVC执行流程
1、 用户发送请求至前端控制器DispatcherServlet。
2、 DispatcherServlet收到请求调用HandlerMapping处理器映射器。(找资源)
3、 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4、 DispatcherServlet调用HandlerAdapter处理器适配器。(执行对应的资源)[əˈdæptər]
5、 HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
6、 Controller执行完成返回ModelAndView。
7、 HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
8、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器。(解析ModelAndView)
9、 ViewReslover解析后返回具体View。
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11、 DispatcherServlet响应用户。
五.网络
用户发送www.changgou.com是如何到达服务器的(网络传输是怎么做的)
DNS域名解析(这个域名解析器会查找本地host文件解析,host有对应得配置文件,就走当前对应得配置信息,没有就通过域名解析器,解析当前地址)
一般是对应的厂商(电信,移动)
用户再杭州,服务器再北京
用户发送请求给电信杭州域名解析服务器
电信杭州域名解析服务器会把www.changgou.com解析成服务器ip地址
中间会通过很多路由服务器,转发转发到对应的北京电信服务器
北京电信服务器再发送到畅购中,再响应回去
公司怎么保证网络安全
防火墙
物理防火墙
通常做一些流量访问的限制(物理限流)
买一个设备,这个设备可以做一个请求的拦截
逻辑防火墙
windows电脑的防火墙
我们学的限流,再nginx中配置的一些限流都是逻辑限流(逻辑限流)
电商项目复习
线上问题
redis出现过什么问题.
为什么redis单线程,为什么不使用多线程,读写速度快为什么
Dubbo超时时长.
feign接口超时时间.
Linux命令,看日志排查问题
mq出现数据丢失.怎么解决
线程池必问
ThreadLocal可能引发内存泄漏
抛出亮点别展开
项目亮点
用了多少台服务器搭建:
如果当前用户第一次访问提供者服务,注册中心宕机,问当前提供者还能访问嘛?
消费者可以访问提供者,因为数据不是通过注册中心传过去的,注册中心只是把当前提供者的服务器地址端口,给消费者,消费者会有缓存,拿到地址直接访问提供者(中间件技术基本都是这样)
六.Mybatis
一级缓存和二级缓存
一级缓存:默认开启,也叫sqlSession缓存,作用于同一个sqlSession.用户执行select操作会存入到一级缓存中,如果下次执行同样的操作,那么会直接从缓存中获取,不会执行mysql查询,mybatis在整合spring框架后,1级缓存,其实是没有任何作用的.因为ssm.我们会配置sqlSessionFactory,sqlSession是通过sqlSessionFactory来构建的,每次请求都用的是不一样的sqlSession.执行增删改操作的时候会清空一级缓存.
二级缓存:会作用在不同的sqlSession中,如果进行多表外连接查询时修改某一表中的数据,就会出现,数据不一致的问题,所以对数据实时性不高的可以使用二级缓存.
#{}和${}的区别
{} 会通过预编译的形式设置占位符,在调用的时候传入参数并执行SQL语句
${} 会直接把参数拼接进SQL语句,有SQL注入的风险
${}的用处,数据量比较大的系统,比如需要记录大量的日志,而这些日志不会存在一张表里,存多个表,以时间为表名比如log_6_9 select * from ${}
所以在为表名传值的时候用${};
七.spirngCloud
五大组件
Eureka,Ribbon,Hystrix,网关,config配置中心
八.springBoot
spring和springBoot的区别关系
简答:Spring Boot 并不是用来替代 Spring 的解决方案,它是基于Spring 框架用于快速构建spring应用的工具。通过自动化配置减少了很多重复性的配置信息。
Spring Boot有哪些优点?
简答:
- 快速创建独立运行的spring项目与主流框架集成
- 项目可独立运行,无需外部依赖 Servlet 容器
- 使用了Starter(起步依赖包)管理依赖并版本控制
- 大量的自动配置,简化开发,方便集成第三方
- 提供准生产环境运行时的监控,如指标,健康,外部配置等
springboot自动配置原理(聊的越深,越有谈资)
简答:@EnableAutoConfiguration注解 通过@Import会导入一个EnableAutoConfigurationImportSelector的类,并执行其selectImports方法。selectImports方法最终调用SpringFactoriesLoader.loadFactoryNames,加载META-INF/spring.factories文件,将配置文件载入到内存中。然后过滤出key为org.springframework.boot.autoconfigure.EnableAutoConfiguration全限定名对应的配置类,spring最终将解析出的类注册到spring容器中。
在b站看的:
ioc容器会,实例化bean首先把所有的bean定义解析出来,存放到beanDefinitionMap中,然后从这个当中挨个取出来进行实例化
@SpringBootApplication注解是一个组合注解里面有:
@SpringBootConfiguration配置类
@EnableAutoConfiguration
@ComponentScan包扫描
自动装配,装配了什么?
根据你maven依赖的情况导入了多个自动配置类
装配了你导入的maven依赖的自动配置类@configruation
装配了bean定义加载装配到了beanDefinitionMap
原理:
通过import(importSelector.class)注解
读取meta-inf里面的spring.factories
把里面的配置类解析成bean定义导入到beanDefinitionMap
springBoot 0配置
springBoot不是增强spring只是快速搭建spring项目
spring容器是父容器,springMVC是子容器,父容器不能访问子容器任何对象,子容器可以访问父容器的任何对象
web.xml里contextLoaderListenr,初始化spring容器并且存放到servletContext对象中
dispatcherServlet.java实现了servlet肯定有初始化方法,里面设置spring为父容器
springBoot 0配置和spring实现0配置原理是一样的,使用config来替换xml
springBoot如何实现jar工程发布
springBoot内置了tomcat,
new 了一个tomcat
tomcat.start();
tomcat.addwebapp(“虚拟路径”,”项目发布路径”);
tomcat.getServer().await();让tomcat一直开着
九.线程池
为什么需要线程池,创建线程池的方式有哪些
线程的生命周期,什么时候会出现线程僵死 (新建,就绪,运行,阻塞以及死亡)
什么是线程安全,如何实现线程安全(Synchronized ,volatile,原子类cas实现,lock)
线程池的几个重要参数,如何合理配置线程池的大小
分析线程池的实现原理和线程的调度过程,
ThreadLocal,volatile的实现原理和使用场景
ThreadLocal什么时候出现oom的情况,为什么
volatile,sychronized区别sychronized锁粒度,
模拟死锁场景,
原子性与可见性
线程池
好处
- 降低资源消耗:通过重用已经创建的线程来降低线程创建和销毁的消耗
- 提高响应速度:任务到达时不需要等待线程创建就可以立即执行。
- 提高线程的可管理性:线程池可以统一管理、分配、调优和监控。
java的线程池支持主要通过ThreadPoolExecutor来实现,我们使用的ExecutorService的各种线程池策略都是基于ThreadPoolExecutor实现的
线程池的运行机制
运行机制首先,线程池它是有一个核心线程数,当你线程启动的时候,如果你没有设置成预启动加载,它的首发线程为0,当你提交一个新任务的时候,它会首先建立一个核心线程去执行任务,此时如果你一直来任务,它之前的又没有执行完,那么就会一直建立核心线程,当达到最大核心线程数时,如果还都在忙, 那么此时就会放到BlockingQueue(阻塞队列)里面作为节点,如果BlockingQueue也放满了, 核心线程也都在忙,那他就会建立新线程,叫非核心线程,那如果一直创建,数量达到非核心线程数max access,就会触发一个拒绝策略,JDK内置了四种拒绝策略,第一种是AbortPolicy,直接抛出异常来解决,第二种是DiscardPolicy,就是悄无声息的丢弃你的任务,第三种是DiscardOldestPolicy,丢弃你最早未执行的任务。最后一种是,CallerRunsPolicy ,就是谁调用我的这个线程去之执行你的这个任务,它这种方式而是会影响你新任务的提交速度。关于阻塞队列的话,JDK提供了两种,一个是SynchronousQueue,它是不保存任务的那种,这个是jkd提供的new catch线程池使用的这个队列,第二种的话,他是一个有界队列,ArrayBlockingQueue,你指定数量,如果它要是超了,它可能会OOM的,第三种是无界队列,LinkedBlockingQueue,他是一个可能因超出上下文而OOM的,最后是优先级队列,这个我没怎么去用过它。关于线程池中还有一个比较重要的参数,我觉得开发中比较重要,就是线程构造,在创建一个线程池的时候,你要提供一个线程的threadFactory,你一定要指定它的名称,这是很重要的一个点,并且你也可以设置它为守护线程,当你的BM关闭的时候,你可以让线程跟着它一块消亡。(这些队列的底层使用AQS做的)
线程池状态
- running:处于RUNNING状态的线程池能够接受新任务,以及对新添加的任务进行处理。
- shutdown:处于SHUTDOWN状态的线程池不可以接受新任务,但是可以对已添加的任务进行处理。
- stop:处于STOP状态的线程池不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
- tidying:当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
-
共有七个参数,每个参数含义如下:
corePoolSize线程池中核心线程的数量(也称为线程池的基本大小)。当提交一个任务时,线程池会新建一个线程来执行任务,直到当前线程数等于corePoolSize。如果调用了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程。
- maximumPoolSize线程池中允许的最大线程数。线程池的阻塞队列满了之后,如果还有任务提交,如果当前的线程数小于maximumPoolSize,则会新建线程来执行任务。注意,如果使用的是无界队列,该参数也就没有什么效果了。
- keepAliveTime线程空闲的时间。线程的创建和销毁是需要代价的。线程执行完任务后不会立即销毁,而是继续存活一段时间:keepAliveTime。默认情况下,该参数只有在线程数大于corePoolSize时才会生效。
- unitkeepAliveTime的单位。TimeUnit
- workQueue用来保存等待执行的任务的BlockQueue阻塞队列,等待的任务必须实现Runnable接口。选择如下:ArrayBlockingQueue:基于数组结构的有界阻塞队列,FIFO。 LinkedBlockingQueue:基于链表结构的有界阻塞队列,FIFO。 PriorityBlockingQueue:具有优先级别的阻塞队列。 SynchronousQueue:不存储元素的阻塞队列,每个插入操作都必须等待一个移出操作。
- threadFactory用于设置创建线程的工厂。ThreadFactory的作用就是提供创建线程的功能的线程工厂。他是通过newThread()方法提供创建线程的功能,newThread()方法创建的线程都是“非守护线程”而且“线程优先级都是默认优先级”。
- handlerRejectedExecutionHandler,线程池的拒绝策略。所谓拒绝策略,是指将任务添加到线程池中时,线程池拒绝该任务所采取的相应策略。当向线程池中提交任务时,如果此时线程池中的线程已经饱和了,而且阻塞队列也已经满了,则线程池会选择一种拒绝策略来处理该任务。线程池提供了四种拒绝策略:AbortPolicy:直接抛出异常,默认策略; CallerRunsPolicy:用调用者所在的线程来执行任务;DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务; DiscardPolicy:直接丢弃任务; 当然我们也可以实现自己的拒绝策略,例如记录日志等等,实现RejectedExecutionHandler接口即可。
如何判断线程池的任务都执行完成了
https://blog.csdn.net/qq_39221436/article/details/123863354四种常见的线程池
FixedThreadPool正规线程
我的理解这是一个有指定的线程数的线程池,有核心的线程,里面有固定的线程数量,响应速度快.正规的并发线程,多用于服务器.固定的线程数由系统资源设置.SingleThreadExecutor单线程线程池
作为单一worker线程的线程池,它把corePool和maximumPoolSize均被设置为1,和FixedThreadPool一样使用的是无界队列LinkedBlockingQueue,所以带来的影响和FixedThreadPool一样.CachedThreadPool缓存线程池
这个线程池在执行大量短生命周期的异步任务时,可以显著提高程序性能.它把corePool设置为0,maximumPoolSize设置为integer.MaxValue.
(执行短操作,并发量过高不适合)ScheduledThreadPool延迟线程池
可以实现线程池的周期和延迟调度.实现多线程的方式
继承Thead类的实现 不支持线程池
实现Runable接口,重写run方法,run没有返回值
实现Callable接口 重写run方法,有返回值,返回了一个Object解决现线程安全问题
synchronized
同步代码块:用的是对象锁
同步方法 :用的是this锁
synchronized原理
被synchronized 加锁对象的对象头mark word会指向类对象所对应的monitor对象,
monitor对象记录了当前持有锁的线程放在owner里,还有等待锁的线程放在entrylist队列里,
通过调用wait方法,当前持有锁的线程会处与阻塞状态,再waitset里等待
轻量级锁:在轻量级锁状态下,当前线程会在栈帧下创建Lock Record,,对象分对象头(mark word(存储hash码,分带年龄,加锁的状态)和Klass world)和对象体
每个线程栈帧中都包含一个锁记录的结构,一部分存储将来加锁对象的markword,一部分是指向对象的指针
1.创建锁记录,object reference指向锁对象,锁记录里的数据和对象的mark word做交换(cas)
2.cas成功 锁对象的对象头变成了锁地址,锁状态也变成了00 轻量级锁
3.cas失败 此时会有两种情况
情况一,其他线程已经持有该对象的轻量级锁,表明有锁竞争,进入锁膨胀
此时该线程加轻量级锁失败,进入锁膨胀流程
1.对象申请Monitor锁,让对象mark word指向重量级锁地址,锁状态变成10
2.然后该线程进入monitor的entrylist
3.当前持有锁的线程解锁时,想还原对象的mark word,由于已经升级重量级锁,对象头记录的是monitor的地址,此时就会进去重量级锁的解锁流程,找到monitor让owner置为空,唤醒entrylist的线程
情况2,本线程持有该对象的锁,就是锁重入,添加一条锁记录,锁的内容不是对象的mark word是null
自旋优化
发生在重量级锁,如果owner有线程持有该对象锁,其他线程请求锁,先不进入entrylist,如果自旋成功(持锁线程已经退出同步快,释放锁),这时当前线程可以避免阻塞(注意:自旋肯定会用到cpu,所以单核cpu自选是没有意义的,cpu在执行同步代码块,自选没意义,在多核cpu环境使用)
在 Java 6 之后自旋锁是自适应的,比如对象刚刚的一次自旋操作成功过,那么认为这次自旋成功的可能性会高,就多自旋几次;反之,就少自旋甚至不自旋,总之,比较智能。
偏向锁
轻量级锁,每次重入都会尝试将锁记录,和对象头mark word进行cas,也会有性能损耗,
偏向锁是做了进一步优化,只有第一次使用cas将线程id设置到对象头,之后发现线程id是自己的就表示没有竞争,不用重新cas,以后只要不发生竞争,这个对象就归该线程所有
Lock锁
Lock lock=new ReentrantLock();
lock.lock();加锁
lock.unlock();释放锁
死锁
同步中嵌套同步,导致锁无法释放.
死锁解决办法:不要再同步中嵌套同步.
线程状态
interrupt();停止线程
wait();等待
notify();唤醒线程
sleep(毫秒);计时等待
wait()和sleep的区别?
- sleep()是Thread类中的方法,wait()是Object类中的
- sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。wait()是把控制权交出去,然后进入等待此对象的等待锁定池处于等待状态,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。
- 在调用sleep()方法的过程中,线程不会释放对象锁。而当调用wait()方法的时候,线程会放弃对象锁。
线程优先级不能决定线程的执行顺序,可以决定执行次数setPriority(1)参数值越大优先级越高.
join(); thread.Join把指定的线程加入到当前线程,比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
线程并发三大特性
java内存模型(JMM) ,jmm如何保证可见性
通过控制主内存与每个线程的本地内存之间的交互,来为java程序员提供内存可见性的保证
解决可见性
1.synchronized解决可见性,加锁的时候可以进行数据同步,在解锁前把所有变量刷新到主内存中,解锁后会从内存中获取最新数据
synchronized的同步操作实现原理,monitorenter和monitorexit这两个jvm指令实现的
2.使用volatile修饰要求可见的数据,不能保证原子性和有序性.
synchronized
多线程中提供的锁机制
jdk1.6对synchronized进行了优化,如自旋锁,适应性自旋锁,锁清除,锁粗化,偏向锁,轻量级锁等技术来减少锁操作的开销.
锁存在的四种状态:无锁状态,偏向锁状态,轻量级锁状态,重量级锁状态,他们会随着竞争的激烈而逐渐升级.
锁可以升级,不可以降级,为了提供获得锁和释放锁的效率
自旋锁
通过死循环的方式,让你当前线程不去进行锁的资源释放,处于一个等待状态(类似于wait,但是不释放锁,也不进行线程的状态切换),设定一定的满足条件,只要条件满足,那么当前线程就结束.
适应自旋锁
volatile
实现原理
写操作时,通过在写操作指令后**加入一条store屏障指令,让本地内存中的变量的值能够刷新到主内存中,
在读操作时,通过在读操作前**加入一条load屏障指令,及时读取到变量在主内存中的值.
使用volatile会出现原子性问题,解决方案:
- 使用synchronized
- 使用lock锁,
使用原子类,AtomicInteger(推荐)(自旋+cas)
volatile适合使用场景
变量真正独立于其他变量和自己以前的值,在单独使用的时候,使用volatile
volatile和synchronized的区别
volatile不需要加锁,比synchronized更轻便,不会阻塞线程
synchronized既能保证可见性,又能保证原子性,而volatile只能保证可见性,无法保证原子性.
十.JUC
synchronized性能低的原因
synchronized的同步操作主要是monitorenter和monitorexit这两个jvm指令实现的
CAS原理
compare and swap 比较并交换,保证数据的原子性.
多cpu下cas,使用缓存在锁保证数据一致
cas缺陷:循环时间太长,aba
cas失败通过自旋来等待,juc有些地方限制了自旋次数,
cas能针对一个共享变量实现原子性,
aba问题:cas检查时没有发生改变,但实际上发生了改变,解决方案在变量上加一个版本号.AQS
锁机制
互斥锁(当前线程进行操作其他线程时不能进行操作的),
阻塞锁(一个线程进行操作其他线程处于阻塞状态),自旋锁,读写锁,公平锁(考虑排队等待线程,优先排队等待先来),ReentrantLock可重入锁
可以减少死锁的概率
以可通过构造选择公平锁和非公平锁(true是公平锁)
底层是通过aqs队列同步器实现的ReentrantLock与synchronized的区别
ReentrantLock 使用起来比较灵活,但是必须有释放锁的配合动作。ReentrantLock 必须手动获取与释放锁,而 synchronized 不需要手动释放和开启锁。ReentrantLock 只适用于代码块锁,而 synchronized 可用于修饰方法、代码块等。
JUC下的并发工具类
CyclicBarrier 同步屏障
也叫线程同步屏障,就是线程在屏障处等待,满足屏障要求后,进行同步执行.
两个构造方法:
CyclicBarrier(int parties):parties拦截线程的数量
CyclicBarrier(int parties, Runnable barrierAction):barrierAction给定的屏障操作
await()方法的逻辑:如果该线程不是到达的最后一个线程,则他会一直处于等待状态,除非发生以下情况:最后一个线程到达,即index == 0
- 超出了指定时间(超时等待)
- 其他的某个线程中断当前线程
- 其他的某个线程中断另一个等待的线程
- 其他的某个线程在等待屏障超时
- 其他的某个线程在此屏障调用reset()方法。reset()方法用于将屏障重置为初始状态.
CountDownLatch 计数闭锁
用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回
- CountDownLatch:一个或者多个线程,等待其他多个线程完成某件事情之后才能执行;
对于CountDownLatch来说,重点是“一个线程(多个线程)等待”,而其他的N个线程在完成“某件事情”之后,可以终止,也可以等待。而对于CyclicBarrier,重点是多个线程,在任意一个线程没有完成,所有的线程都必须等待。
Semaphore(信号量)
维护了一个信号量的许可集,线程可以获取许可,前提是许可集中必须有,没有的话就处于等待状态, 线程可以释放许可,释放的许可会归还给许可集.
内部包含公平锁和非公平锁,默认是非公平锁.
acquire()//获取信号量
release();/释放信号量十一.redis
1.数据类型 5个 String hash set zset list 应用场景
2.redis中一个字符串的最大容量是多少:512M默认存储最大值
3.redis常用命令
4.redis的使用场景 满足后续几个条件都可以使用redis
频繁访问,一致性要求较低,缓存数据(评论)
- 访问频繁的临时数据(验证码)
-
5.为什么单线程(单线程模型)?这个问题现在不对
现在是6.0 从redis4.0就支持多线程了
通过io多路复用机制监听多个socket,然后将socket放入一个队列中,从队列中取出一个socket给事件分派器,事件分派器会根据当前socket产生的事件,来选择对应的事件处理器来处理.
为什么单线程还支持高并发
非阻塞io多路复用监听socket不做事件处理,只把事件压到队列中,文件分派器基于内存快速处理事件6.redis提供的持久化方案
RDB:每隔一段时间,自动持久化数据到硬盘, 是把当前的redis数据保存 (快照)
- AOF:保存的是命令(我们操作redis的命令)redis官方推荐使用rdb+aof
7.RDB与AOF如何选择
结合使用,因为RDB会丢失数据,AOF恢复数据没有RDB快,没有RDB健壮,所以结合使用,8.什么是缓存穿透,缓存雪崩,缓存击穿,如何避免.
9.redis分布式锁
用户发送请求,到服务器,如果是单体架构,为了保证原子性(比如修改库存的操作),只要加一个同步代码快就可以了,但是如果服务器做集群或者分布式,那么jvm层面的锁就会出现bug,因为每一个tomcat都是一个进程.这时就需要分布式锁来解决.
redis的setnx(key,value);是如果存在key,不进行添加value,不存在当前key,再添加 stringRedisTemplate.opsForValue().setIfAbsent(“key”,”value”);返回一个布尔值 执行成功reids中没有key值返回true
多个请求会再redis中进行排队,因为redis是单线程模型,setnx只对第一个key,value进行存储.
业务代码可能会异常,所以释放锁(删除reids中的key),放再finally中执行.
web服务挂了,finally释放锁就执行不了,设置一个过期时间 stringRedisTemplate.expire(“key”,time,时间单位) stringRedisTemplate.opsForValue().setIfAbsent(key,value,过期时间,过期单位)(可以保证原子性) 就是把setIfAbsent和expire合成一个方法来写.
存在问题,可能当前设置的过期时间,代码还没执行完成,所以锁已经过期了,这是在多个线程并发访问的情况下,可能释放锁的环节,释放的是它的线程的锁.所以要保证当前线程的锁必须在业务代码执行完的时候才释放.使用redisson获取锁,redisson.getLock(key);,加一个过期时间.他会开启一个子线程,来执行延时的操作,延时的间隔是过期时间的三分之一,把过期时间重置,其他的线程拿不到锁处于阻塞的状态.代码
提升性能 分段库存.redis如何删除设置了过期时间的key(redis的过期策略)
定期删除+惰性删除
定期删除 : redis默认会每隔100ms随机抽取一部分key,进行检查是否过期,如果过期就删掉
此时还会有key已经过期了但是没有被删除的情况
惰性删除 : 当你获取某个key时,redis还是会检查一下过期时间,如果该key已经过期,就直接删掉,不返回任何东西redis的内存淘汰机制
当redis内存快耗尽时,redis会启动内存淘汰机制,将部分key清掉以腾出内存
redis提供了6种淘汰策略,可以再redis.conf中配置: maxmemory-policy noeviction
- noeviction:禁止驱逐数据。默认配置都是这个。当内存使用达到阀值的时候,所有引起申请内存的命令都会报错。
- volatile-lru:从设置了过期时间的数据集中挑选最近最少使用的数据淘汰。
- volatile-ttl:从已设置了过期时间的数据集中挑选即将要过期的数据淘汰。
- volatile-random:从已设置了过期时间的数据集中任意选择数据淘汰。
- allkeys-lru:从数据集中挑选最近最少使用的数据淘汰。
- allkeys-random:从数据集中任意选择数据淘汰。
十二.事务
事务的基本要素(ACID)
1、原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。
2、一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。
3、隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。
4、持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。事务的并发问题
1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表MySQL事务隔离级别
| 事务隔离级别 | 脏读 | 不可重复读 | 幻读 | | —- | —- | —- | —- | | 读未提交(read-uncommitted) | 是 | 是 | 是 | | 读提交(read-committed) | 否 | 是 | 是 | | 可重复读(repeatable-read) | 否 | 否 | 是 | | 串行化(serializable) | 否 | 否 | 否 |
事务的传播行为
十三.Mysql
悲观锁:通过锁表的方式来进行锁定(排它锁),绝对安全,性能低,用于写数据比较多
乐观锁(逻辑锁): 添加一个version ,先查询版本,在给版本加1,添加一个version=version,容易出现冲突,用于写比较少的情况
行级锁,表级锁,页面锁
1.查询优化
Mysql执行流程
慢查询
慢查询日志开启
在配置文件my.cnf或my.ini中在[mysqld]一行下面加入两个配置参数
配合explain 分析当前的sql
EXPLAIN SELECT * FROM products
2.索引优化(使用频率高,面试问的多)
索引的类型
主键索引 PRIMARY KEY 不能为null 不能重复
唯一索引 UNIQUE 不能重复
普通索引 INDEX
组合索引 INDEX
全文索引 FULLTEXT
空间索引 搜索条件计算图形坐标,再找到相应字段
索引存储结构
BTree
- 使用b+tree存储数据
- 加快查询
-
哈希索引
将索引的键值进行运算,得到的hash值存入hash表中,每次检索,把检索条件进行相同的hash运算,然后再和hash表中的hash值进行比较
不能范围查询
- 不能排序操作
- 不能利用部分索引键查询(组合查询)
- 任何时候不能避免表扫描
- 大量hash值相等,性能不一定好
Full-text全文索引
spring事务传播机制????
6/10看一看mysql调优回答模板
Mysql首先sql调优,有条最基本的,表要有主键,因为主键的表Mysql会创建一个聚族索引,那聚族索引的好处是它的主键和数据行是在一行的, 你在exlain查询语句的时候会发现,它的type级别是const,他是一种很高的级别,然后,当有主键的时候,如果一条SQL语句很慢,你可以去查看它是否建立了相应的索引,然后建立索引要尽量选择where条件后面的字段,还有order by或者是group by,还有像是join链接的字段,作为你的索引列, 并且这些索引列也要排个序,要符合一个最左匹配原则,另外呢,你选的时候要根据他们的索引选择器,就是你的非重复的数据行和重复的数据行中间的排列,大的放左小的放右这样的形式,并且这种多列的索引列,你要去建立联合索引而非单个索引,这种是索引的选择。另外一个,就是你在SQL书写的时候,你不要将索引列放到一个表达式中,或者说有可能你用了一些反向判断,比如Notnull、不等于、Nothl等这种关键词,他都会让你的索引失效。还有一点,如果你的数据查询非常频繁,你可以考虑给它使用那种覆盖索引,因为覆盖索引是可以直接在索引中查询到数据的,相对来说还是很快的。嗯,关于你的设计的索引要是没有生效,也可以去考虑下是不是Mysql的一些其他原因造成的,因为mysql的底层他是一个随机采样,他会根据你的索引基数,但他不可能全都给你标记上,但它就是会根据随机采样来计算你的索引基数如何的,如果它采错了,虽然你的索引选择性比较大,但它认为你的索引选择性比较小,可能就不走你的索引(走错索引),他可以通过加force index强制让它走该走的索引,然后看行不行,不过不行的话,你可能需要刷新下它的信息,要用analyze Tables,看看它有没有再重新组队。
建索引的时候,是建唯一索引还是普通索引好一点?聚族索引,他确切的来说应该是一个数据结构,它是讲它的主键和它的数据行放在一块的,这种聚族索引是通向真实数据行的有利途径,那另外一种的话,你要查的数据可能就是覆盖索引了,而非聚族索引就是我们说的普通索引,他的话就是索引存放的叶子节点和它的主键还有它的索引列,然后它在查询的时候其实是通过它的索引列查询到它的主键,再通过主键去回表查询,还是走一个聚族索引查的(他有个回表的过程,速度会相对来说慢一点),具体还是看自己选择吧,比如你要对主键进行查询的话,那么你只要建立主键,那直接就聚族索引就完事了,如果用那些不同字段,然后你想去查的话,你就需要创建一个非聚族索引来加块它的查询速度。十四.RabbitMQ
好处
解耦合,异步调用,消峰添谷.死信队列
给队列设置过期时间,但是有的消息没有来得及被消费,这些消息会通过DLX(死信交换机)发送到绑定的队列.消息成为死信的三种情况
队列消息长度到达限制.
消费者拒绝接受消息,并且不把消息放回原有的队列.
原队列存在消息过期设置,消息到达超时时间未被消费.延迟队列
TTL+死信队列
给死信队列的正常队列添加一个过期时间.消息队列如何保证消息不丢失
https://www.jianshu.com/p/4616cb865558==和equals方法的区别
共同点:都可以做比较,返回值都是boolean
区别:1.==是比较运算符,可以比较基本数据类型,也可以比较引用数据类型,基本数据类型比较的是值,引用数据类型比较的是地址值.
2.equals方法,只能比较引用数据类型,在没重写之前比较的是地址值,底层依赖的是==,但是比较地址值是没意义的,需要重写equals方法,比较对象中的属性值.Java中基本数据类型和包装类型有什么区别?
1、包装类是对象,拥有方法和字段,对象的调用都是通过引用对象的地址,基本类型不是 2、包装类型是引用的传递,基本类型是值的传递 3、声明方式不同,基本数据类型不需要new关键字,而包装类型需要new在堆内存中进行new来分配内存空间 4、存储位置不同,基本数据类型直接将值保存在值栈中,而包装类型是把对象放在堆中,然后通过对象的引用来调用他们 5、初始值不同,eg: int的初始值为 0 、 boolean的初始值为false 而包装类型的初始值为null 6、使用方式不同,基本数据类型直接赋值使用就好 ,而包装类型是在集合如 coolection Map时会使用linux常用命令
cd+路径 改变路径
cat 表示读取文件内容及拼接文件。
rm 用于删除文件或文件夹
mkdir 用于创建文件夹
cp <文件><目标文件> 用于复制文件或文件夹
kill PID码 结束当前进程十五.薪资构成
基本工资 岗位工资 绩效工资 补助(交通补助,电脑补助,餐补,花补) 奖金
3000 4000 2000十六.项目问题
1.开发流程
二面的一些问题
1.如果你审查代码 你审查别人代码的标准是什么,
我没审查过代码,标注应该是阿里提供的开发手册,或者是自己的开发习惯,比如警告,异常,注释.\2.工作模式
按模块开发,每个人分不同的模块去做,git管理分支.3.工作中遇到的困难,怎么解决
98%都是自己解决,查看日志,debug,去百度,博客,然后解决剩下的2%可能请教同事领导进行解决【面试题】-java分布式及微服务面试题汇总
https://blog.csdn.net/lovexiaotaozi/article/details/89713937?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase#3.%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%B9%82%E7%AD%89%E6%80%A7%E9%97%AE%E9%A2%98SpringCloud 与 SpringBoot 微服务 架构 | 面试题及答案详解
https://blog.csdn.net/qq_41497111/article/details/92067565?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase#1.%E4%BB%80%E4%B9%88%E6%98%AF%E5%BE%AE%E6%9C%8D%E5%8A%A1%EF%BC%9F面试日志
2022/04/07 杭州市民卡(外包一面)
== 和equals的区别
== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同 一个对象。(基本数据类型==比较的是值,引用数据类型==比较的是内存地址) equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:
- 情况 1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个 对象时,等价于通过“==”比较这两个对象。
- 情况 2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来 两个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两 个对象相等)。
public class test1 {
public static void main(String[] args) {
String a = new String(“ab”); // a 为一个引用
String b = new String(“ab”); // b 为另一个引用,对象的内容一样
String aa = “ab”; // 放在常量池中
String bb = “ab”; // 从常量池中查找
if (aa == bb) // true
System.out.println(“aa==bb”);
if (a == b) // false,非同一对象
System.out.println(“a==b”);
if (a.equals(b)) // true
System.out.println(“aEQb”);
if (42 == 42.0) { // true
System.out.println(“true”);
}
}
}
说明
- String 中的 equals 方法是被重写过的,因为 object 的 equals 方法是 比较的对象的内存地址,而 String 的 equals 方法比较的是对象的值。
- 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存 在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没 有就在常量池中重新创建一个 String 对象。
String能被继承吗
不能被继承,因为String类有final修饰符,而final修饰的类是不能被继承的。
public final class String implements java.io.Serializable, Comparable, CharSequence {
// 省略…
}ArrayList和LinkedList区别
略redis持久化几种方式
RDB AOF2022/04/08中软外包银行
1.说项目
2.工作流
3.用了哪些技术
4.数据库查询优化5.redis使用场景
- 可以做分布式的缓存,缓解数据库压力
- 根据redis可以设置过期时间,具有时效性的业务也可以做,比如手机验证码的存储,订单的存储,token的存储
- 利用zset类型,可以做排行榜之类的业务[产品编号,该产品客户数量]
- 解决分布式集群中分布式锁的问题
- 会话缓存,session共享
6.并发量比较高怎么处理接口报错的问题
7.dubbo的工作原理
8.队列消息丢失
9.队列重复消费
10.分布式事务
11.zk集群运行的流程
1.业务窄
2.
kafka一致性
2022/04/10中软银行外包
kafka有什么特性
redis过期策略
redis出现雪崩解决方案
[TOC]
一.集合
1.arrayList
1.arrayList 存储特性
有序且可重复 为什么?
底层是object数组,数组不会对元素进行判断,所以可以重复,基于数组下标得连续存储,所以有序
增删改慢,遍历查询快
2.arrayList 初始容量
默认情况下arrayList 初始大小是多少
初始容量是0,在第一次调用add 方法后,进行第一次扩容,大小为10,
3.arrayList 如果存储满后,如何进行扩容
扩容大小是多少
用户第一次调用add方法
arrayList 会进行第一次扩容
扩容后大小为10
何时扩容?存储11个元素时,扩容
扩容大小是原来得1.5倍(原来大小+原来大小右移一位)
4.为什么arrayList 会引发并发修改异常
arrayList 的remove方法,是把下标移动,将最后一个元素置为null,
当我们遍历arraylist时 在使用下标,同使用remove方法会改动这个下标(改变下标原有的位置),这是不可能的 ,就会出现并发修改异常.
解决方法:
- 不动用迭代器,使用普通for循环
- CopyOnWriteArrayList使用这个,为什么 因为加了lock
2.linkedList 链表集合
1.linkedList 特性
有序,可重复,底层是双向链表
增删改效率快,遍历查询慢
3.hashSet
存储特性
无序且不可重复
HashSet的构造 是new 了一个hashMap
add()方法是把值存在了hashMap的key中,value存的是一个不变的object
4.hashMap
1.hashMap的存储结构是什么样子的(jdk1.7)
Entry对象数组+单向链表(Entry就是一个对象)
为什么无序:通过key计算hash,通过hash和数组长度计算当前元素存储在数组的小标位置,所以无序.
不可重复:一旦重复会进行替换.
初始化的时候默认长度为0 数组长度16
hashMap什么时候第一次扩容:长度大于等于12并且存在hash冲突.
hashMap构造器
put方法
2.已知需要存储1000个长度,初始化hashMap时 初始化大小应该设置为多少.
扩容机制很损耗性能,
初始大小应该时2048,因为长度要是2的幂次方, 1024*0.75=700多的时候要进行扩容,所以不是1024.
3.HashMap(jdk1.8)
node数组+链表+红黑树
扩容机制:不会计算hash冲突(与1.7不一样) 数组长度只要>=12就直接扩容
为什么hashMap1.8要引入红黑树,因为1.7的扩容机制是:长度大于12并且出现hash冲突进行扩容,容易导致某一个链表变得特别长.
在多线程高并发情况下,容易出现环链(尾节点指向头节点)
原理
链表转红黑树的阈值
在put方法 链表(节点node)长度>8转换成红黑树(treenode)
在delete方法还原链表结构 节点<6 红黑树转变链表
值<=6使用链表
扩容:默认存储第13个值(12是阈值)必定进行扩容机制.数组长度增长为16*2=32
阈值12*2=24
二叉树
1.每个节点下,有两个叶子节点.
2.左叶子节点比根节点小,右叶子节点比根节点大
3.二叉树的查询最大次数是二叉树的高度.
二叉树存在的问题
长脚怪问题(一边的脚特别长)导致效率低
红黑树(平衡二叉树,解决某一节点某一节点过长问题)
5.HashTable
1.hashMap线程不安全,如何保证hashMap线程安全?
hashTable线程安全,jdk1.5之后就已经弃用了
2.结构
entry数组+链表
3.简单分析源码
- hashTable默认在构造的时候,底层entry数组就已经初始化了,初始大小是11.
- hashMap value可以为null ————hashTable value不能为null
- hashTable key也不能为null,因为null没有hashcode方法
- hashTable 所有方法上加了synchronized(淘汰原因使用一把大锁,锁住了所有方法,导致性能及其低)
6.ConcurrentHashMap(1.7)
1.如何保证线程安全,并且性能比hashTable高
1.7引入了一个概念 分段锁机制
问题:默认底层entry数组的长度是2 总长度是多少? 总长度是32。
问题2:Segment中的entry数组 默认情况,什么时候扩容.
Segment是不能扩容的,entry可以是原来的2倍
存储第二个值得时候就应该扩容了.
1.8是synchnized+cas
二.JVM
- JVM通用内存模型
- 每个区的作用
- 虚拟机栈的流程
- java堆 eden,from,to,old 轻GC fullGC
- jvm调优,调的是什么 fullGC执行频率,执行时间.
- jvm调优工具,jconsole(jdk自带),jvisualvm.exe
- System.gc();手动执行GC,执行的fullGC 执行也没有什么用
- finally代码,在什么情况下不会执行. System.exit(0)//结束jvm
- String ccc=”aaa”+”bbb”;创建了几个String对象 1.6之后 答案是1个或者2个
引用对象算对象就是2个ccc,aaabbb 一个就是aaabbb.简易对象
三.spring
创建对象?
生命周期
作用域
aop具体能做什么事情
IOC
控制反转
控制的是什么,反转的是什么.
控制的是bean对象
反转的是bean对象的获取方式以及对象之间的依赖关系.
以前是要什么对象就new什么对象,自己new
现在我们要什么对象就从spring容器中要,创建对象的过程交给spring,我们把创建对象的权力做了反转.
原来是自己new 现在是由spring去创建 bean对象之间的依赖关系也做了反转.
bean对象的生命周期
单例对象
出生:当容器创建对象出生
活着:只要容器还在,对象一直活着
死亡:容器销毁,对象消亡
总结:单例对象的生命周期和容器相同
多例对象
出生:当我们使用对象时spring框架为我们创建
活着:对象只要是在使用过程中就一直活着
死亡:当对象长时间不用,且没有别的对象引用时,由java的垃圾回收器回收
多利对象的销毁不是spring控制的
init-method:指定对象初始化之前调用的方法,方法名称任意
destroy-method:指定对象在销毁之前调用的方法,方法名称任意
回答模板
找工作的时候有些人会被问道Spring中Bean的生命周期,其实也就是考察一下对Spring是否熟悉,工作中很少用到其中的内容,那我们简单看一下。
在说明前可以思考一下Servlet的生命周期:实例化,初始init,接收请求service,销毁destroy;
Spring上下文中的Bean也类似,如下
1、实例化一个Bean--也就是我们常说的new;
2、按照Spring上下文对实例化的Bean进行配置--也就是IOC注入;
3、如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,此处传递的就是Spring配置文件中Bean的id值
4、如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(setBeanFactory(BeanFactory)传递的是Spring工厂自身(可以用这个方式来获取其它Bean,只需在Spring配置文件中配置一个普通的Bean就可以);
5、如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文(同样这个方式也可以实现步骤4的内容,但比4更好,因为ApplicationContext是BeanFactory的子接口,有更多的实现方法);
6、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;
7、如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。
8、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法、;
注:以上工作完成以后就可以应用这个Bean了,那这个Bean是一个Singleton的,所以一般情况下我们调用同一个id的Bean会是在内容地址相同的实例,当然在Spring配置文件中也可以配置非Singleton,这里我们不做赘述。
9、当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法;
10、最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。
以上10步骤可以作为面试或者笔试的模板,另外我们这里描述的是应用Spring上下文Bean的生命周期,如果应用Spring的工厂也就是BeanFactory的话去掉第5步就Ok了。
bean实例化三种方式
无参构造方法实例化
- 在spring的配置文件中配置bean标签,配置id,class属性
工厂实例方法实例化
- 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)
工厂静态方法实例化
- 使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)
bean的三种注入方式
- 使用构造函数提供
- 使用set方法提供
- 使用注解提供
@atuowired
@qualifier
@Resource
AOP
目标(target):别增强的类
连接点(joinpoint):目标类中的所有方法
切入点(pointcut):目标类中的真正被增强的方法
通知/增强(advice):增强类
切面(aspect):切入点+通知
织入(weaving):把通知类动态的加入到切入点位置的过程
目标类,增强类需要自己写,配置aop切面(spring配置文件中写)
- 告诉spring我的目标类是谁
- 我的增强类是谁
- 我要增强目标类中的哪个方法(切入点)
为什么有接口用jdk,没接口用cglib
jdk动态代理的大致逻辑即是
传入代理类 类加载器,与接口数组和自定义的InvocationHandler,然后通过分析接口信息生成java文件的字节码数据,然后调用本地方法将类加载到内存中,最后返回构造参数为InvocationHandler的代理类,该类实现代理接口,并继承Proxy类(所以jdk动态代理只能代理接口,java单继承),我们调用方法实际上是调用代理类的方法,代理类则可以通过我们传入的InvocationHandler反射调用原本的方法来实现无侵入的修改原有方法逻辑
https://www.cnblogs.com/hetutu-5238/p/11988946.html
Spring 框架中都用到了哪些设计模式?
(1)工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
(2)单例模式:Bean默认为单例模式。
(3)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
(4)模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
(5)观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现—ApplicationListener。
spring的bean对象,时单例还是多例?
是单例,会不会引起线程安全问题,spring controller bean 对象存不存在线程安全问题.
单例: 容器初始化的时候创建一个对象 让所有线程使用, 资源不会浪费,性能较好.线程不安全.
多例: 用户请求一次,创建一个当前对象.存在资源浪费,损耗性能.线程安全(因为你每个请求进来都会构建一个对象,不公用一个对象)
所以尽量不要再spring的bean 中使用成员属性.
怎么能既保证线程安全,有使用线程不安全的类?
解决方案:ThreadLocal
ThreadLocal
- 存储空间,存储数值,变量
- 线程之间的隔离存储
ThreadLocal其实就是一个map,key是系统自己维护的,是当前线程
四.springMVC
1.springMVC 0配置加载实现的补充
spring提供了实现0配置,实现webApplicationInitialiazer重写了onStartup方法(tomact服务器启动时调用)
servlet3.0 引入了一个技术SPI(接口发现服务)是一种服务发现机制,他通过ClassPath路径下META-INF/services文件夹查找文件,自动加载文件里所定义的类
所以tomcat启动时会加载META-INF/services文件
加载SpringServletContainerInitializer这个类 实现了ServletContainerInitializer
ServletContainerInitializer这个接口有onstartup方法
2.springMVC源码(执行流程)
springMVC加载流程
dispatherServlet加载的时候,就会初始化很多处理器
dispatherServlet就是一个servlet,会有一个init方法.会初始化很多处理器
springMVC请求流程(用户发送请求controller如何处理的)
在处理用户请求的时候,(dispatherServlet会处理请求),会调用service doget dopost某一个方法
会调用doDispatch(req,res)方法
springMVC执行流程
1、 用户发送请求至前端控制器DispatcherServlet。
2、 DispatcherServlet收到请求调用HandlerMapping处理器映射器。(找资源)
3、 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4、 DispatcherServlet调用HandlerAdapter处理器适配器。(执行对应的资源)[əˈdæptər]
5、 HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
6、 Controller执行完成返回ModelAndView。
7、 HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
8、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器。(解析ModelAndView)
9、 ViewReslover解析后返回具体View。
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11、 DispatcherServlet响应用户。
五.网络
用户发送www.changgou.com是如何到达服务器的(网络传输是怎么做的)
DNS域名解析(这个域名解析器会查找本地host文件解析,host有对应得配置文件,就走当前对应得配置信息,没有就通过域名解析器,解析当前地址)
一般是对应的厂商(电信,移动)
用户再杭州,服务器再北京
用户发送请求给电信杭州域名解析服务器
电信杭州域名解析服务器会把www.changgou.com解析成服务器ip地址
中间会通过很多路由服务器,转发转发到对应的北京电信服务器
北京电信服务器再发送到畅购中,再响应回去
公司怎么保证网络安全
防火墙
物理防火墙
通常做一些流量访问的限制(物理限流)
买一个设备,这个设备可以做一个请求的拦截
逻辑防火墙
windows电脑的防火墙
我们学的限流,再nginx中配置的一些限流都是逻辑限流(逻辑限流)
电商项目复习
线上问题
redis出现过什么问题.
为什么redis单线程,为什么不使用多线程,读写速度快为什么
Dubbo超时时长.
feign接口超时时间.
Linux命令,看日志排查问题
mq出现数据丢失.怎么解决
线程池必问
ThreadLocal可能引发内存泄漏
抛出亮点别展开
项目亮点
用了多少台服务器搭建:
中小型公司:4-6
如果当前用户第一次访问提供者服务,注册中心宕机,问当前提供者还能访问嘛?
消费者可以访问提供者,因为数据不是通过注册中心传过去的,注册中心只是把当前提供者的服务器地址端口,给消费者,消费者会有缓存,拿到地址直接访问提供者(中间件技术基本都是这样)
六.Mybatis
一级缓存和二级缓存
一级缓存:默认开启,也叫sqlSession缓存,作用于同一个sqlSession.用户执行select操作会存入到一级缓存中,如果下次执行同样的操作,那么会直接从缓存中获取,不会执行mysql查询,mybatis在整合spring框架后,1级缓存,其实是没有任何作用的.因为ssm.我们会配置sqlSessionFactory,sqlSession是通过sqlSessionFactory来构建的,每次请求都用的是不一样的sqlSession.执行增删改操作的时候会清空一级缓存.
二级缓存:会作用在不同的sqlSession中,如果进行多表外连接查询时修改某一表中的数据,就会出现,数据不一致的问题,所以对数据实时性不高的可以使用二级缓存.
#{}和${}的区别
{} 会通过预编译的形式设置占位符,在调用的时候传入参数并执行SQL语句
${} 会直接把参数拼接进SQL语句,有SQL注入的风险
${}的用处,数据量比较大的系统,比如需要记录大量的日志,而这些日志不会存在一张表里,存多个表,以时间为表名比如log_6_9 select * from ${}
所以在为表名传值的时候用${};
七.spirngCloud
五大组件
Eureka,Ribbon,Hystrix,网关,config配置中心
八.springBoot
spring和springBoot的区别关系
简答:Spring Boot 并不是用来替代 Spring 的解决方案,它是基于Spring 框架用于快速构建spring应用的工具。通过自动化配置减少了很多重复性的配置信息。
Spring Boot有哪些优点?
简答:
- 快速创建独立运行的spring项目与主流框架集成
- 项目可独立运行,无需外部依赖 Servlet 容器
- 使用了Starter(起步依赖包)管理依赖并版本控制
- 大量的自动配置,简化开发,方便集成第三方
- 提供准生产环境运行时的监控,如指标,健康,外部配置等
springboot自动配置原理(聊的越深,越有谈资)
简答:@EnableAutoConfiguration注解 通过@Import会导入一个EnableAutoConfigurationImportSelector的类,并执行其selectImports方法。selectImports方法最终调用SpringFactoriesLoader.loadFactoryNames,加载META-INF/spring.factories文件,将配置文件载入到内存中。然后过滤出key为org.springframework.boot.autoconfigure.EnableAutoConfiguration全限定名对应的配置类,spring最终将解析出的类注册到spring容器中。
在b站看的:
ioc容器会,实例化bean首先把所有的bean定义解析出来,存放到beanDefinitionMap中,然后从这个当中挨个取出来进行实例化
@SpringBootApplication注解是一个组合注解里面有:
@SpringBootConfiguration配置类
@EnableAutoConfiguration
@ComponentScan包扫描
自动装配,装配了什么?
根据你maven依赖的情况导入了多个自动配置类
装配了你导入的maven依赖的自动配置类@configruation
装配了bean定义加载装配到了beanDefinitionMap
原理:
通过import(importSelector.class)注解
读取meta-inf里面的spring.factories
把里面的配置类解析成bean定义导入到beanDefinitionMap
springBoot 0配置
springBoot不是增强spring只是快速搭建spring项目
spring容器是父容器,springMVC是子容器,父容器不能访问子容器任何对象,子容器可以访问父容器的任何对象
web.xml里contextLoaderListenr,初始化spring容器并且存放到servletContext对象中
dispatcherServlet.java实现了servlet肯定有初始化方法,里面设置spring为父容器
springBoot 0配置和spring实现0配置原理是一样的,使用config来替换xml
springBoot如何实现jar工程发布
springBoot内置了tomcat,
new 了一个tomcat
tomcat.start();
tomcat.addwebapp(“虚拟路径”,”项目发布路径”);
tomcat.getServer().await();让tomcat一直开着
九.线程池
为什么需要线程池,创建线程池的方式有哪些
线程的生命周期,什么时候会出现线程僵死 (新建,就绪,运行,阻塞以及死亡)
什么是线程安全,如何实现线程安全(Synchronized ,volatile,原子类cas实现,lock)
线程池的几个重要参数,如何合理配置线程池的大小
分析线程池的实现原理和线程的调度过程,
ThreadLocal,volatile的实现原理和使用场景
ThreadLocal什么时候出现oom的情况,为什么
volatile,sychronized区别sychronized锁粒度,
模拟死锁场景,
原子性与可见性
线程池
好处
- 降低资源消耗:通过重用已经创建的线程来降低线程创建和销毁的消耗
- 提高响应速度:任务到达时不需要等待线程创建就可以立即执行。
- 提高线程的可管理性:线程池可以统一管理、分配、调优和监控。
java的线程池支持主要通过ThreadPoolExecutor来实现,我们使用的ExecutorService的各种线程池策略都是基于ThreadPoolExecutor实现的
线程池的运行机制
运行机制
首先,线程池它是有一个核心线程数,当你线程启动的时候,如果你没有设置成预启动加载,它的首发线程为0,当你提交一个新任务的时候,它会首先建立一个核心线程去执行任务,此时如果你一直来任务,它之前的又没有执行完,那么就会一直建立核心线程,当达到最大核心线程数时,如果还都在忙, 那么此时就会放到BlockingQueue(阻塞队列)里面作为节点,如果BlockingQueue也放满了, 核心线程也都在忙,那他就会建立新线程,叫非核心线程,那如果一直创建,数量达到非核心线程数max access,就会触发一个拒绝策略,JDK内置了四种拒绝策略,第一种是AbortPolicy,直接抛出异常来解决,第二种是DiscardPolicy,就是悄无声息的丢弃你的任务,第三种是DiscardOldestPolicy,丢弃你最早未执行的任务。最后一种是,CallerRunsPolicy ,就是谁调用我的这个线程去之执行你的这个任务,它这种方式而是会影响你新任务的提交速度。关于阻塞队列的话,JDK提供了两种,一个是SynchronousQueue,它是不保存任务的那种,这个是jkd提供的new catch线程池使用的这个队列,第二种的话,他是一个有界队列,ArrayBlockingQueue,你指定数量,如果它要是超了,它可能会OOM的,第三种是无界队列,LinkedBlockingQueue,他是一个可能因超出上下文而OOM的,最后是优先级队列,这个我没怎么去用过它。关于线程池中还有一个比较重要的参数,我觉得开发中比较重要,就是线程构造,在创建一个线程池的时候,你要提供一个线程的threadFactory,你一定要指定它的名称,这是很重要的一个点,并且你也可以设置它为守护线程,当你的BM关闭的时候,你可以让线程跟着它一块消亡。(这些队列的底层使用AQS做的)
线程池状态
- running:处于RUNNING状态的线程池能够接受新任务,以及对新添加的任务进行处理。
- shutdown:处于SHUTDOWN状态的线程池不可以接受新任务,但是可以对已添加的任务进行处理。
- stop:处于STOP状态的线程池不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
- tidying:当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
- terminated:线程池彻底终止的状态。
共有七个参数,每个参数含义如下:
- corePoolSize
线程池中核心线程的数量(也称为线程池的基本大小)。当提交一个任务时,线程池会新建一个线程来执行任务,直到当前线程数等于corePoolSize。如果调用了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程。 - maximumPoolSize
线程池中允许的最大线程数。线程池的阻塞队列满了之后,如果还有任务提交,如果当前的线程数小于maximumPoolSize,则会新建线程来执行任务。注意,如果使用的是无界队列,该参数也就没有什么效果了。 - keepAliveTime
线程空闲的时间。线程的创建和销毁是需要代价的。线程执行完任务后不会立即销毁,而是继续存活一段时间:keepAliveTime。默认情况下,该参数只有在线程数大于corePoolSize时才会生效。 - unit
keepAliveTime的单位。TimeUnit - workQueue
用来保存等待执行的任务的BlockQueue阻塞队列,等待的任务必须实现Runnable接口。选择如下:ArrayBlockingQueue:基于数组结构的有界阻塞队列,FIFO。 LinkedBlockingQueue:基于链表结构的有界阻塞队列,FIFO。 PriorityBlockingQueue:具有优先级别的阻塞队列。 SynchronousQueue:不存储元素的阻塞队列,每个插入操作都必须等待一个移出操作。
- threadFactory
用于设置创建线程的工厂。ThreadFactory的作用就是提供创建线程的功能的线程工厂。他是通过newThread()方法提供创建线程的功能,newThread()方法创建的线程都是“非守护线程”而且“线程优先级都是默认优先级”。 - handler
RejectedExecutionHandler,线程池的拒绝策略。所谓拒绝策略,是指将任务添加到线程池中时,线程池拒绝该任务所采取的相应策略。当向线程池中提交任务时,如果此时线程池中的线程已经饱和了,而且阻塞队列也已经满了,则线程池会选择一种拒绝策略来处理该任务。
线程池提供了四种拒绝策略:AbortPolicy:直接抛出异常,默认策略; CallerRunsPolicy:用调用者所在的线程来执行任务;DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务; DiscardPolicy:直接丢弃任务; 当然我们也可以实现自己的拒绝策略,例如记录日志等等,实现RejectedExecutionHandler接口即可。
四种常见的线程池
FixedThreadPool正规线程
我的理解这是一个有指定的线程数的线程池,有核心的线程,里面有固定的线程数量,响应速度快.正规的并发线程,多用于服务器.固定的线程数由系统资源设置.
SingleThreadExecutor单线程线程池
作为单一worker线程的线程池,它把corePool和maximumPoolSize均被设置为1,和FixedThreadPool一样使用的是无界队列LinkedBlockingQueue,所以带来的影响和FixedThreadPool一样.
CachedThreadPool缓存线程池
这个线程池在执行大量短生命周期的异步任务时,可以显著提高程序性能.它把corePool设置为0,maximumPoolSize设置为integer.MaxValue.
(执行短操作,并发量过高不适合)
ScheduledThreadPool延迟线程池
可以实现线程池的周期和延迟调度.
实现多线程的方式
继承Thead类的实现 不支持线程池
实现Runable接口,重写run方法,run没有返回值
实现Callable接口 重写run方法,有返回值,返回了一个Object
解决现线程安全问题
synchronized
同步代码块:用的是对象锁
同步方法 :用的是this锁
Lock锁
Lock lock=new ReentrantLock();
lock.lock();加锁
lock.unlock();释放锁
死锁
同步中嵌套同步,导致锁无法释放.
死锁解决办法:不要再同步中嵌套同步.
线程状态
interrupt();停止线程
wait();等待
notify();唤醒线程
sleep(毫秒);计时等待
wait()和sleep的区别?
- sleep()是Thread类中的方法,wait()是Object类中的
- sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。
wait()是把控制权交出去,然后进入等待此对象的等待锁定池处于等待状态,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。 - 在调用sleep()方法的过程中,线程不会释放对象锁。而当调用wait()方法的时候,线程会放弃对象锁。
线程优先级不能决定线程的执行顺序,可以决定执行次数setPriority(1)参数值越大优先级越高.
join(); thread.Join把指定的线程加入到当前线程,比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
线程并发三大特性
原子性,可见性(线程修改立马同步给其他线程),有序性
java内存模型(JMM) ,jmm如何保证可见性
通过控制主内存与每个线程的本地内存之间的交互,来为java程序员提供内存可见性的保证
解决可见性
1.synchronized解决可见性,加锁的时候可以进行数据同步,在解锁前把所有变量刷新到主内存中,解锁后会从内存中获取最新数据
synchronized的同步操作实现原理,monitorenter和monitorexit这两个jvm指令实现的
2.使用volatile修饰要求可见的数据,不能保证原子性和有序性.
synchronized
是重量级锁,对当前性能有一定影响.
多线程中提供的锁机制
jdk1.6对synchronized进行了优化,如自旋锁,适应性自旋锁,锁清除,锁粗化,偏向锁,轻量级锁等技术来减少锁操作的开销.
锁存在的四种状态:无锁状态,偏向锁状态,轻量级锁状态,重量级锁状态,他们会随着竞争的激烈而逐渐升级.
锁可以升级,不可以降级,为了提供获得锁和释放锁的效率
自旋锁
通过死循环的方式,让你当前线程不去进行锁的资源释放,处于一个等待状态(类似于wait,但是不释放锁,也不进行线程的状态切换),设定一定的满足条件,只要条件满足,那么当前线程就结束.
适应自旋锁
不在固定自旋的次数
volatile
实现原理
写操作时,通过在**写操作指令后加入一条store屏障指令,让本地内存中的变量的值能够刷新到主内存中,
在读操作时,通过在**读操作前加入一条load屏障指令,及时读取到变量在主内存中的值.
使用volatile会出现原子性问题,解决方案:
- 使用synchronized
- 使用lock锁,
- 使用原子类,AtomicInteger(推荐)(自旋+cas)
volatile适合使用场景
变量真正独立于其他变量和自己以前的值,在单独使用的时候,使用volatile
volatile和synchronized的区别
- volatile不需要加锁,比synchronized更轻便,不会阻塞线程
- synchronized既能保证可见性,又能保证原子性,而volatile只能保证可见性,无法保证原子性.
十.JUC
synchronized性能低的原因
synchronized的同步操作主要是monitorenter和monitorexit这两个jvm指令实现的
CAS原理
compare and swap 比较并交换,保证数据的原子性.
多cpu下cas,使用缓存在锁保证数据一致
cas缺陷:循环时间太长,aba
cas失败通过自旋来等待,juc有些地方限制了自旋次数,
cas能针对一个共享变量实现原子性,
aba问题:cas检查时没有发生改变,但实际上发生了改变,解决方案在变量上加一个版本号.
AQS
基于数据同步的基础框架底层时同步队列.
锁机制
互斥锁(当前线程进行操作其他线程时不能进行操作的),
阻塞锁(一个线程进行操作其他线程处于阻塞状态),自旋锁,读写锁,公平锁(考虑排队等待线程,优先排队等待先来),
ReentrantLock可重入锁
可以减少死锁的概率
以可通过构造选择公平锁和非公平锁(true是公平锁)
底层是通过aqs队列同步器实现的
ReentrantLock与synchronized的区别
ReentrantLock 使用起来比较灵活,但是必须有释放锁的配合动作。
ReentrantLock 必须手动获取与释放锁,而 synchronized 不需要手动释放和开启锁。
ReentrantLock 只适用于代码块锁,而 synchronized 可用于修饰方法、代码块等。
JUC下的并发工具类
CyclicBarrier 同步屏障
也叫线程同步屏障,就是线程在屏障处等待,满足屏障要求后,进行同步执行.
两个构造方法:
CyclicBarrier(int parties):parties拦截线程的数量
CyclicBarrier(int parties, Runnable barrierAction):barrierAction给定的屏障操作
await()方法的逻辑:如果该线程不是到达的最后一个线程,则他会一直处于等待状态,除非发生以下情况:
- 最后一个线程到达,即index == 0
- 超出了指定时间(超时等待)
- 其他的某个线程中断当前线程
- 其他的某个线程中断另一个等待的线程
- 其他的某个线程在等待屏障超时
- 其他的某个线程在此屏障调用reset()方法。reset()方法用于将屏障重置为初始状态.
CountDownLatch 计数闭锁
用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回
- CountDownLatch:一个或者多个线程,等待其他多个线程完成某件事情之后才能执行;
- 对于CountDownLatch来说,重点是“一个线程(多个线程)等待”,而其他的N个线程在完成“某件事情”之后,可以终止,也可以等待。而对于CyclicBarrier,重点是多个线程,在任意一个线程没有完成,所有的线程都必须等待。
Semaphore(信号量)
维护了一个信号量的许可集,线程可以获取许可,前提是许可集中必须有,没有的话就处于等待状态, 线程可以释放许可,释放的许可会归还给许可集.
内部包含公平锁和非公平锁,默认是非公平锁.
acquire()//获取信号量
release();/释放信号量
十一.redis
1.数据类型 5个 String hash set zset list 应用场景
2.redis中一个字符串的最大容量是多少:512M默认存储最大值
3.redis常用命令
4.redis的使用场景 满足后续几个条件都可以使用redis
- 频繁访问,一致性要求较低,缓存数据(评论)
- 访问频繁的临时数据(验证码)
- redis 自身特性(incrment)
5.为什么单线程(单线程模型)?这个问题现在不对
现在是6.0 从redis4.0就支持多线程了
通过io多路复用机制监听多个socket,然后将socket放入一个队列中,从队列中取出一个socket给事件分派器,事件分派器会根据当前socket产生的事件,来选择对应的事件处理器来处理.
为什么单线程还支持高并发
非阻塞io多路复用监听socket不做事件处理,只把事件压到队列中,文件分派器基于内存快速处理事件
6.redis提供的持久化方案
- RDB:每隔一段时间,自动持久化数据到硬盘, 是把当前的redis数据保存 (快照)
- AOF:保存的是命令(我们操作redis的命令)
redis官方推荐使用rdb+aof
7.RDB与AOF如何选择
结合使用,因为RDB会丢失数据,AOF恢复数据没有RDB快,没有RDB健壮,所以结合使用,
8.什么是缓存穿透,缓存雪崩,缓存击穿,如何避免.
9.redis分布式锁
用户发送请求,到服务器,如果是单体架构,为了保证原子性(比如修改库存的操作),只要加一个同步代码快就可以了,但是如果服务器做集群或者分布式,那么jvm层面的锁就会出现bug,因为每一个tomcat都是一个进程.这时就需要分布式锁来解决.
redis的setnx(key,value);是如果存在key,不进行添加value,不存在当前key,再添加 stringRedisTemplate.opsForValue().setIfAbsent(“key”,”value”);返回一个布尔值 执行成功reids中没有key值返回true
多个请求会再redis中进行排队,因为redis是单线程模型,setnx只对第一个key,value进行存储.
业务代码可能会异常,所以释放锁(删除reids中的key),放再finally中执行.
web服务挂了,finally释放锁就执行不了,设置一个过期时间 stringRedisTemplate.expire(“key”,time,时间单位) stringRedisTemplate.opsForValue().setIfAbsent(key,value,过期时间,过期单位)(可以保证原子性) 就是把setIfAbsent和expire合成一个方法来写.
存在问题,可能当前设置的过期时间,代码还没执行完成,所以锁已经过期了,这是在多个线程并发访问的情况下,可能释放锁的环节,释放的是它的线程的锁.所以要保证当前线程的锁必须在业务代码执行完的时候才释放.使用redisson获取锁,redisson.getLock(key);,加一个过期时间.他会开启一个子线程,来执行延时的操作,延时的间隔是过期时间的三分之一,把过期时间重置,其他的线程拿不到锁处于阻塞的状态.
代码
提升性能 分段库存.
redis如何删除设置了过期时间的key(redis的过期策略)
定期删除+惰性删除
定期删除 : redis默认会每隔100ms随机抽取一部分key,进行检查是否过期,如果过期就删掉
此时还会有key已经过期了但是没有被删除的情况
惰性删除 : 当你获取某个key时,redis还是会检查一下过期时间,如果该key已经过期,就直接删掉,不返回任何东西
redis的内存淘汰机制
当redis内存快耗尽时,redis会启动内存淘汰机制,将部分key清掉以腾出内存
redis提供了6种淘汰策略,可以再redis.conf中配置: maxmemory-policy noeviction
- noeviction:禁止驱逐数据。默认配置都是这个。当内存使用达到阀值的时候,所有引起申请内存的命令都会报错。
- volatile-lru:从设置了过期时间的数据集中挑选最近最少使用的数据淘汰。
- volatile-ttl:从已设置了过期时间的数据集中挑选即将要过期的数据淘汰。
- volatile-random:从已设置了过期时间的数据集中任意选择数据淘汰。
- allkeys-lru:从数据集中挑选最近最少使用的数据淘汰。
- allkeys-random:从数据集中任意选择数据淘汰。
十二.事务
事务的基本要素(ACID)
1、原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。
**2、一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。
**
3、隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。
4、持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。
事务的并发问题
1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表
MySQL事务隔离级别
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(read-uncommitted) | 是 | 是 | 是 |
读提交(read-committed) | 否 | 是 | 是 |
可重复读(repeatable-read) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |
事务的传播行为
十三.Mysql
悲观锁:通过锁表的方式来进行锁定(排它锁),绝对安全,性能低,用于写数据比较多
乐观锁(逻辑锁): 添加一个version ,先查询版本,在给版本加1,添加一个version=version,容易出现冲突,用于写比较少的情况
行级锁,表级锁,页面锁
1.查询优化
添加索引会导致增删改效率降低,但是会大大增加查询的效率.
Mysql执行流程
慢查询
慢查询日志开启
在配置文件my.cnf或my.ini中在[mysqld]一行下面加入两个配置参数
配合explain 分析当前的sql
EXPLAIN SELECT * FROM products
2.索引优化(使用频率高,面试问的多)
索引的类型
主键索引 PRIMARY KEY 不能为null 不能重复
唯一索引 UNIQUE 不能重复
普通索引 INDEX
组合索引 INDEX
全文索引 FULLTEXT
空间索引 搜索条件计算图形坐标,再找到相应字段
索引存储结构
BTree
- 使用b+tree存储数据
- 加快查询
- 更适合范围查询
哈希索引
将索引的键值进行运算,得到的hash值存入hash表中,每次检索,把检索条件进行相同的hash运算,然后再和hash表中的hash值进行比较
- 不能范围查询
- 不能排序操作
- 不能利用部分索引键查询(组合查询)
- 任何时候不能避免表扫描
- 大量hash值相等,性能不一定好
Full-text全文索引
spring事务传播机制????
6/10看一看
mysql调优回答模板
Mysql
首先sql调优,有条最基本的,表要有主键,因为主键的表Mysql会创建一个聚族索引,那聚族索引的好处是它的主键和数据行是在一行的, 你在exlain查询语句的时候会发现,它的type级别是const,他是一种很高的级别,然后,当有主键的时候,如果一条SQL语句很慢,你可以去查看它是否建立了相应的索引,然后建立索引要尽量选择where条件后面的字段,还有order by或者是group by,还有像是join链接的字段,作为你的索引列, 并且这些索引列也要排个序,要符合一个最左匹配原则,另外呢,你选的时候要根据他们的索引选择器,就是你的非重复的数据行和重复的数据行中间的排列,大的放左小的放右这样的形式,并且这种多列的索引列,你要去建立联合索引而非单个索引,这种是索引的选择。另外一个,就是你在SQL书写的时候,你不要将索引列放到一个表达式中,或者说有可能你用了一些反向判断,比如Notnull、不等于、Nothl等这种关键词,他都会让你的索引失效。还有一点,如果你的数据查询非常频繁,你可以考虑给它使用那种覆盖索引,因为覆盖索引是可以直接在索引中查询到数据的,相对来说还是很快的。嗯,关于你的设计的索引要是没有生效,也可以去考虑下是不是Mysql的一些其他原因造成的,因为mysql的底层他是一个随机采样,他会根据你的索引基数,但他不可能全都给你标记上,但它就是会根据随机采样来计算你的索引基数如何的,如果它采错了,虽然你的索引选择性比较大,但它认为你的索引选择性比较小,可能就不走你的索引(走错索引),他可以通过加force index强制让它走该走的索引,然后看行不行,不过不行的话,你可能需要刷新下它的信息,要用analyze Tables,看看它有没有再重新组队。
建索引的时候,是建唯一索引还是普通索引好一点?
聚族索引,他确切的来说应该是一个数据结构,它是讲它的主键和它的数据行放在一块的,这种聚族索引是通向真实数据行的有利途径,那另外一种的话,你要查的数据可能就是覆盖索引了,而非聚族索引就是我们说的普通索引,他的话就是索引存放的叶子节点和它的主键还有它的索引列,然后它在查询的时候其实是通过它的索引列查询到它的主键,再通过主键去回表查询,还是走一个聚族索引查的(他有个回表的过程,速度会相对来说慢一点),具体还是看自己选择吧,比如你要对主键进行查询的话,那么你只要建立主键,那直接就聚族索引就完事了,如果用那些不同字段,然后你想去查的话,你就需要创建一个非聚族索引来加块它的查询速度。
十四.RabbitMQ
好处
解耦合,异步调用,消峰添谷.
死信队列
给队列设置过期时间,但是有的消息没有来得及被消费,这些消息会通过DLX(死信交换机)发送到绑定的队列.
消息成为死信的三种情况
队列消息长度到达限制.
消费者拒绝接受消息,并且不把消息放回原有的队列.
原队列存在消息过期设置,消息到达超时时间未被消费.
延迟队列
TTL+死信队列
给死信队列的正常队列添加一个过期时间.
消息队列如何保证消息不丢失
https://www.jianshu.com/p/4616cb865558
==和equals方法的区别
共同点:都可以做比较,返回值都是boolean
区别:1.==是比较运算符,可以比较基本数据类型,也可以比较引用数据类型,基本数据类型比较的是值,引用数据类型比较的是地址值.
2.equals方法,只能比较引用数据类型,在没重写之前比较的是地址值,底层依赖的是==,但是比较地址值是没意义的,需要重写equals方法,比较对象中的属性值.
Java中基本数据类型和包装类型有什么区别?
1、包装类是对象,拥有方法和字段,对象的调用都是通过引用对象的地址,基本类型不是
2、包装类型是引用的传递,基本类型是值的传递
3、声明方式不同,基本数据类型不需要new关键字,而包装类型需要new在堆内存中进行new来分配内存空间
4、存储位置不同,基本数据类型直接将值保存在值栈中,而包装类型是把对象放在堆中,然后通过对象的引用来调用他们
5、初始值不同,eg: int的初始值为 0 、 boolean的初始值为false 而包装类型的初始值为null
6、使用方式不同,基本数据类型直接赋值使用就好 ,而包装类型是在集合如 coolection Map时会使用
linux常用命令
cd+路径 改变路径
cat 表示读取文件内容及拼接文件。
rm 用于删除文件或文件夹
mkdir 用于创建文件夹
cp <文件><目标文件> 用于复制文件或文件夹
kill PID码 结束当前进程
十五.薪资构成
基本工资 岗位工资 绩效工资 补助(交通补助,电脑补助,餐补,花补) 奖金
3000 4000 2000
十六.项目问题
1.开发流程
二面的一些问题
1.如果你审查代码 你审查别人代码的标准是什么,
我没审查过代码,标注应该是阿里提供的开发手册,或者是自己的开发习惯,比如警告,异常,注释.\
2.工作模式
按模块开发,每个人分不同的模块去做,git管理分支.
3.工作中遇到的困难,怎么解决
98%都是自己解决,查看日志,debug,去百度,博客,然后解决剩下的2%可能请教同事领导进行解决
【面试题】-java分布式及微服务面试题汇总
SpringCloud 与 SpringBoot 微服务 架构 | 面试题及答案详解
面试日志
2022/04/07 杭州市民卡(外包一面)
== 和equals的区别
== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同 一个对象。(基本数据类型**比较的是值,引用数据类型**比较的是内存地址) equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:
- 情况 1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个 对象时,等价于通过“==”比较这两个对象。
- 情况 2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来 两个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两 个对象相等)。
public class test1 {
public static void main(String[] args) {
String a = new String("ab"); // a 为一个引用
String b = new String("ab"); // b 为另一个引用,对象的内容一样
String aa = "ab"; // 放在常量池中
String bb = "ab"; // 从常量池中查找
if (aa == bb) // true
System.out.println("aa==bb");
if (a == b) // false,非同一对象
System.out.println("a==b");
if (a.equals(b)) // true
System.out.println("aEQb");
if (42 == 42.0) { // true
System.out.println("true");
}
}
}
说明
- String 中的 equals 方法是被重写过的,因为 object 的 equals 方法是 比较的对象的内存地址,而 String 的 equals 方法比较的是对象的值。
- 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存 在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没 有就在常量池中重新创建一个 String 对象。
String能被继承吗
不能被继承,因为String类有final修饰符,而final修饰的类是不能被继承的。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
// 省略...
}
ArrayList和LinkedList区别
略
redis持久化几种方式
RDB AOF
2022/04/08中软外包银行
1.说项目
2.工作流
3.用了哪些技术
4.数据库查询优化
5.redis使用场景
- 可以做分布式的缓存,缓解数据库压力
- 根据redis可以设置过期时间,具有时效性的业务也可以做,比如手机验证码的存储,订单的存储,token的存储
- 利用zset类型,可以做排行榜之类的业务[产品编号,该产品客户数量]
- 解决分布式集群中分布式锁的问题
- 会话缓存,session共享
6.并发量比较高怎么处理接口报错的问题
7.dubbo的工作原理
8.队列消息丢失
9.队列重复消费
10.分布式事务
11.zk集群运行的流程
1.业务窄
kafka一致性
2022/04/10中软银行外包
kafka有什么特性
redis过期策略
redis出现雪崩解决方案