总结面试问题

%%%%%%%java8新特性
lambda表达式,(java代码更简洁紧凑->)stream(集合的操作,类似数据库的复杂操作,
特性:不存储,不改变源数据,延迟执行 流程: 创建stream,中间操作,终止操作(只有它执行,中间操作才执行),optional(null),方法引用和构造器引用,接口的默认方法(default,有具体实现)与静态方法
Java8
默认方法和静态方法
JAVA9
接口有私有方法,不想向客户端公开任何其他方法
java11
ZGC 低延迟垃圾回收器,适用于大内存低延迟服务的内存管理和回收, 128G 的大堆下,最大停顿时间才 1.68 ms,停顿时间远胜于 G1 和 CMS。
初试标记-并发标记-再标记-再并发标记-初试转移-并发转移
ZGC通过着色指针和读屏障技术,解决了转移过程中准确访问对象的问题,实现了并发转移。

字符串加强新方法:去除空格,复制字符串
optional加强
Stream加强,是java8的新特性

自动装箱和自动拆箱
基本类-》包装类
Integer a=1;在IntegerCache,INteger a=Integer(1)不在,
Integer a = Integer.valueOf(1);
如果每次自动装箱都触发new,在堆中分配内存,就显得太慢了;所以不如预先将那些常用的值提前生成好,自动装箱时直接拿出来返回。哪些值是常用的?就是-128到127了。
Integer有缓存valueof
①、无论如何,Integer与new Integer不会相等。不会经历拆箱过程,因为它们存放内存的位置不一样。
②、两个都是非new出来的Integer,如果数在-128到127之间,则是true,否则为false。
③、两个都是new出来的,则为false。
④、int和integer(new或非new)比较,都为true,因为会把Integer自动拆箱为int,其实就是相当于两个int类型比较。
String不会相等,double也是没法缓存

TCP粘包/拆包
tcp发送数据时,出现多个字符串粘在一起或被拆开,
解决办法:自定义序列化解码器,分隔符
零拷贝:避免在用户态和内核态来回拷贝数据

流量控制算法

漏桶算法
水(请求)先进入到漏桶里,漏桶以一定的速度出水,当水流入速度过大会直接溢出,就拒绝请求,可以看出漏桶算法能强行限制数据的传输速率。
在某些情况下,漏桶算法不能够有效地使用网络资源。因为漏桶的漏出速率是固定的参数,所以即使网络中不存在资源冲突(没有发生拥塞),漏桶算法也不能使某一个单独的流突发到端口速率。因此,漏桶算法对于存在突发特性的流量来说缺乏效率。
令牌桶算法
系统会按恒定1/QPS时间间隔(如果QPS=100,则间隔是10ms)往桶里加入Token,如果桶已经满了就不再加了,新请求来临时,会各自拿走一个Token,如果没有Token可拿了就阻塞或者拒绝服务。对于在流量限制外的数据包可以以不同的方式处理:
1)它们可以被丢弃;
2)它们可以排放在队列中以便当令牌桶中累积了足够多的令牌时再传输;
3)它们可以继续发送,但需要做特殊标记,网络过载的时候将这些特殊标记的包丢弃。
两者主要区别在于“漏桶算法”能够强行限制数据的传输速率,而“令牌桶算法”在能够限制数据的平均传输速率外,还允许某种程度的突发传输。

深拷贝和浅拷贝
值传递和引用传递
JSON方法实现
用for…in实现遍历和复制

通过拷贝构造方法实现浅拷贝:
通过重写clone()方法进行浅拷贝:
使用object.assign方法)
Array.copyof System.arraycopy()

1、为什么在实例化子类的对象时,会先调用父类的构造器?
答:子类继承父类后,获取到父类的属性和方法,这些属性和方法在使用前必须先初始化,所以须先调用父类的构造器进行初始化
2、在哪里调用父类的构造器?
答:在子类构造器的第一行会隐式的调用 super();,即调用父类的构造器
如果父类中没有定义空参的构造器,则必须在子类的构造器的第一行显示的调用super(参数); ,以调用父类中构造器
如果子类中构造器的第一行写了this();,则就隐式的super();会消失,因为super()和this()都只能在构造器的第一行定义

TCP
端口号,源端口,目的端口,序号和确认号,控制位ack,fin,syn,数据偏移,校验和,填充。
HTTP 方法+url+协议 协议+响应码
host,User-Agent,Connection(keep-alive),Accept-charset,encoding,language;
UDP首部有8个字节,由4个字段构成,每个字段都是两个字节,
1.源端口: 源端口号,需要对方回信时选用,不需要时全部置0.
2.目的端口:目的端口号,在终点交付报文的时候需要用到。
3.长度:UDP的数据报的长度(包括首部和数据)其最小值为8(只有首部)
4.校验和:检测UDP数据报在传输中是否有错,有错则丢弃。

%%%%%%%%%jvm调优工具 cpu/内存高
调整jvm参数,监控GC状态,分析内存泄漏,分析dump文件
(arthas(dashboard看哪个线程cpu占用,thread然后看这个线程具体执行,heapdump(频繁fullGC)),jvisualvm jmc,)
命令 jps jstack-cpu过高,看进程步骤找线程 jinfo jmap -heap -dump -histo 堆 jstat(-gccause /gcutilGC相关信息)

FullGC

元数据满
System.gc()
Minor GC Old区没有足够分配给yong区晋升的对象
老年代连续空间不足
年轻代到达年龄升级
大对象超过阈值
Concurrent Mode Failure(内存碎片):CMS预留的空间不足以分配给新的对象,一个是在年老代被用完之前不能完成对无引用对象的回收;一个是当新空间分配请求在年老代的剩余空间中得到满足。减低阈值

内存泄漏
1.静态集合类,与对象生命周期一直
2.各种连接未关闭,数据库,网络,IO
3.变量不合理的作用域
4.内部类持有外部类
5 Threadlocal

%%%%%HTTP状态码
1请求正在处理 100post数据包 http header/data分两次发送
2 成功200正确处理,201,created成功并创建资源202accepteed,未处理完204 not content
3 301和302 非常相似, 一个是永久转移,一个是临时转移。重定向 304 客户端发送附带条件的请求时,但因发生请求为未满足条件的情况后,直接返回304,304未修改,客户的缓存资源是最新的,要客户端使用缓存
4客户端错误 400 Bad Request服务器端无法理解客户端请求错误401 unanthorized 未通过http认证,403 Forbidden不允许访问那个资源,404 Not Found, 未找到请求资源
408请求超时 409请求资源与当前资源冲突,410表示服务器某个资源被永久删除
5服务器错误 500 Internal server error 内部资源出故障啦 503 Service unavailable 停机维护无法处理

%%%%%%%%get与post区别:浏览器和服务器交互
http1.0 get post head 1.1 options,put,delete,trace,connect
head 类似get请求,只不过返回的响应中没有具体的内容,用于获取报头
put从客户端向服务器传送的数据取代指定的文档中的内容
delete请求服务器删除指定的页面
options允许客户端查看服务器的性能
trace,主要用于测试或诊断

GET和POST本质上就是TCP链接,并无差别。但是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同
get加requestbody post带url参数
GET在浏览器回退时是无害的,而POST会再次提交请求。
GET请求会被浏览器主动cache,而POST不会,除非手动设置。
GET请求只能进行url编码,而POST支持多种编码方式。
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
GET请求在URL中传送的参数是有长度限制的,而POST么有。
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
1安全 2 数据有限制2kB 3 执行效率 4 tcp数据包(对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。) 5 编码形式,字符类型 6 主动缓存保留在浏览器

JAVA语言为什么把String类型设计成不可变
第一:在Java程序中String类型是使用最多的,这就牵扯到大量的增删改查,每次增删改差之前其实jvm需要检查一下这个String对象的安全性,就是通过hashcode,当设计成不可变对象时候,就保证了每次增删改查的hashcode的唯一性,也就可以放心的操作。
第二:网络连接地址URL,文件路径path通常情况下都是以String类型保存, 假若String不是固定不变的,将会引起各种安全隐患。就好比我们的密码不能以String的类型保存,,如果你将密码以明文的形式保存成字符串,那么它将一直留在内存中,直到垃圾收集器把它清除。而由于字符串被放在字符串缓冲池中以方便重复使用,所以它就可能在内存中被保留很长时间,而这将导致安全隐患
第三:字符串值是被保留在常量池中的,也就是说假若字符串对象允许改变,那么将会导致各种逻辑错误

WAITING、TIMED_WAITING与BLOCKED的区别:
(1)WAITING:进入等待状态,方式:wait/join/park方法进入无限等待,通过notify/notifyAll/unpark唤醒;

(2)TIMED_WAITING:与WAITING类似,方式:a. 给定等待时间的wait/join/park方法;b. sleep方法;

(3)BLOCKED:被动进入等待状态,方式:进入Synchronized块;

内存泄漏:
大量内存泄漏会造成内存溢出
当一个对象已经不需要再使用本该被回收时,另外一个正在使用的对象持有它的引用从而导致它不能被回收,这导致本该被回收的对象不能被回收而停留在堆内存中,这就产生了内存泄漏。
为什么用使用弱引用
假如每个key都强引用指向threadlocal,也就是上图虚线那里是个强引用,那么这个threadlocal就会因为和entry存在强引用无法被回收!造成内存泄漏 ,
原因:
1.大量使用静态变量(随对象而死亡) 2连接资源未关闭 3finailize方法会进行执行队列,但一直没有被调用的这段时间,会一直占用内存4 ThreadLocal的错误使用
ThreadLocal是null了,也就是要被垃圾回收器回收了,但是此时我们的ThreadLocalMap生命周期和Thread的一样,它不会回收,这时候就出现了一个现象。那就是ThreadLocalMap的key没了,但是value还在,这就造成了内存泄漏。
检测内存泄漏
1.LeakCanary则是由Square开源的一款轻量级的第三方内存泄漏检测工具,当检测到程序中产生内存泄漏时,它将以最直观的方式告诉我们哪里产生了内存泄漏和导致谁泄漏了而不能被回收。
2.垃圾回收 分析Java VisualVM他可以帮助我们分析是哪一个对象或者是类内存的飙升。也可以看到内存CPU的等等各种情况
ThreadLocal应用场景
Spring的事务管理器通过Aop切入业务代码,会根据对应的事务管理器取出对应的事务对象,通过一定包装后将其保存在Threadlocal中,:;因为Spring在aop后并不能向应用程序传递参数,应用程序的每个业务代码是事先定义好的,spring并不会要求业务代码的入口参数中必须编写Connection的入口参数,此时Spring选择了ThreadLocal,通过它保证连接对象始终在线程内部,任何时候都能拿到,此时Spring非常清楚什么时候回收这个连接,也就是非常清楚什么时候从ThreadLocal中删除这个元素

Timewait主动关闭 closewait被动关闭

解决思路很简单,就是让服务器能够快速回收和重用那些TIME_WAIT的资源。TIME_WAIT状态可以通过优化服务器参数得到解决,因为发生TIME_WAIT的情况是服务器自己可控的,要么就是对方连接的异常,要么就是自己没有迅速回收资源,

1.防止上一次连接中的包,迷路后重新出现,影响新连接(经过2MSL,上一次连接中所有的重复包都会消失)
2. 可靠的关闭TCP连接。
就是在对方关闭连接之后服务器程
序自己没有进一步发出ack信号。换句话说,就是在对方连接关闭之后,程序里没有检测到,或者程序压根就忘记了这个时候需要关闭连接,于是这个资源就一直
被程序占着。
所以如果将大量CLOSE_WAIT的解决办法总结为一句话那就是:查代码。因为问题出在服务器程序里头啊。

数字签名的签名和验证过程
数字签名的签名:发送方是将原文用hash算法求得数字摘要,用(CA)签名私钥对数字摘要非对称加密得数字签名,发方将原文和数字签名以及签名证书的公钥一起进行分装,形成签名结果(数字证书)发送给接收方
数字签名的验证:接收方首先用发方公钥解密数字签名,导出数字摘要,并对原文做同样哈希算法得出一个新的数字摘要,将两个摘要得hash值进行结果比较,相同签名得到验证,否则无效。SDA256

%%%%%%%%%HTTP 1.0/1.1 /HTTP2
1.0:短连接,每次请求都需要建立连接 1.1长连接,更多的缓存处理策略,更多错误状态响应码
HTTP2引入多路复用技术,解决了同一个域名下的请求数量的问题,提高网页的性能。

%%%%%%浏览器访问过程以及https过程
1、DNS解析过程-TCP连接-发送HTTP请求-服务器处理并返回报文-浏览器解析渲染页面-连接结束
DNS缓存:浏览器缓存-路由器缓存-IPS服务器缓存-DNS本地,根,顶级(很明显使用基于UDP的DNS协议只要一个请求、一个应答就好了
而使用基于TCP的DNS协议要三次握手、发送数据以及应答、四次挥手
明显基于TCP协议的DNS更浪费网络资源!
当然以上只是从数据包的数量以及占有网络资源的层面来进行的分析,那数据一致性层面呢?
DNS数据包不是那种大数据包,所以使用UDP不需要考虑分包,如果丢包那么就是全部丢包,如果收到了数据,那就是收到了全部数据!所以只需要考虑丢包的情况,那就算是丢包了,重新请求一次就好了。而且DNS的报文允许填入序号字段,对于请求报文和其对应的应答报文,这个字段是相同的,通过它可以区分DNS应答是对应的哪个请求
DNS通常是基于UDP的,但当数据长度大于512字节的时候,为了保证传输质量,就会使用基于TCP的实现方式)
https:1客户端发起https请求,连接443https服务器端口,
服务器把自己的公开密钥登录至数字证书认证机构。
2. 数字证书认证机构用自己的私有密钥向服务器的公开密码署数字签名并颁发公钥证书。
3. 客户端拿到服务器的公钥证书后,使用数字签名认证机构的公开密钥,向数字证书认证机构验证公钥证书上的数字签名,以确认服务器的公开密钥的真实性。
2服务器采用一套数字证书,本质是公钥和私钥
3 服务器将公钥传送证书传递给客户端
4 客户端解析证书,由TLS完成,验证公钥/证书是否有效,然后生成随机值(用于对称加密),然后对随机值进行非对称加密
5将证书非对称加密后的随机值传给服务器
6 服务器使用私钥对其进行非对称解密,得到随机值,然后把内容通过该随机值进行对称加密,并发给客户端
7客户端用之前随机值进行对称解密获取内容
总结:两次请求,对称加密和非对称加密

%%%%%%%% cookie session token
cookie 客户端 保存sessionid,服务器为了记录用户状态,发给客户端保存,下次请求连同cookie一起提交给服务器,以此辨认用户身份,有限制最多保存20个cookie,session无限制
session 服务器端,是依赖cookie实现 sessionId获取当前对话对应服务器session,然后确定会话的身份信息
token:客户端存放,服务器端产生代替session使用,相当于令牌,减轻服务器的压力,减少频繁的查询数据库,

servlet不是线程安全的,多线程并发读写会导致数据不同步的问题,init,service->调用对应的doget/dopost方法,destory,

%%%%%%%null和’ '空字符串
空值不一定为空,timstamp,auto_increment插进去null实际有值
空值不一定等于空字符串,代表含义不一样,一个没创建,一个创建啦,查询null列需要用is null,ifnull判断,排序null排在最前面,count()不会把null列统计进去

匿名类就是new Person没有实例对象

  1. 静态内部类不持有外部类的引用
    在普通内部类中,可以直接访问外部类的属性、方法,即使是private类型也可以,这是因为内部类持有外部类的引用,可以自由访问。而静态内部类只能访问外部类的静态方法和静态属性,如果是private也能访问,其他则不能访问。
  2. 静态内部类不依赖外部类
    普通内部类与外部类之间是相互依赖关系,内部类实例不能脱离外部类实例,也就是说他们会同生共死,一起声明,一起被垃圾回收。而静态内部类是可以独立存在的,即使外部类消亡了,静态内部类还是可以存在的。静态内部类形似内部,神似外部。编译之后的类文件名格式为:外部类$内部类
  3. 普通内部类不能声明static的方法和变量
    普通内部类不能声明static的方法和变量,允许static常量,静态内部类形似外部类,没有任何限制

Java泛型(类,方法,接口,擦除,泛型通配符)
将类型参数化,用在集合笔记多
https://www.sohu.com/a/245549100_796914
http://www.imooc.com/article/18159
泛型的意义在于代码的复用”
好处:可读性,从字面上就可以判断集合中的内容类型(List),原来是继承object对象
类型检查,避免非法类型,从运行时提前到编译时,
获取数据时不再需要强制类型转换

泛型擦除
泛型只在编译阶段有效,编译后类型被擦除了,也就是说jvm中没有泛型对象,只有普通对象。所以完全可以把代码编译为jdk1.0可以运行的字节码。,编译后泛型对象变普通对象
Java泛型擦除的缺陷及补救措施
不能使用8个基本类型实例化类型参数,object不能存储基本类型
类型检查不可使用泛型
不能创建泛型对象数组因为类型擦除的原因无法在为元素赋值时类型检查,因此jdk强制不允许。

泛型类型不能显式地运用在运行时类型的操作当中,例如:转型、instanceof 和 new。(解决方法,工厂模式+泛型方法)

不能在泛型类的静态域中使用泛型类型
private static T singleton; //error
public static T getInstance(){} //error

但是,静态的泛型方法可以使用泛型类型:
T相当于一个隐形变量,属于对象级别,所以静态方法直接类级别获取有问题,《T》在对象初始化时才知道类型

不能捕获泛型类型的对象
Throwable类不可以被继承,自然也不可能被catch。
泛型有一个原则:一个类或类型变量不可成为两个不同参数化的接口类型的子类型。

继承的原则
继承泛型类时,必须对父类中的类型参数进行初始化。或者说父类中的泛型参数必须在子类中可以确定具体类型。

例如:有一个泛型类Parent,那么Son类定义时有两种方式初始化父类型的类型参数:
1 用具体类型初始化:
public class Son extends Parent{}
2 用子类中的泛型类型初始化父类:
public class Son extends Parent{}

泛型通配符
上界<? extends T>代表继承它的所有子类型,通配符匹配的类型不允许作为参数传入,只能作为返回值。
Pair<? super P>,又称下边界通配符(lower bound wildcard Generics),通配符匹配的类型可以为方法提供参数,不能得到返回值。
Pair<?> 就是 Pair<? extends Object>
因此,无限定通配符可以作为返回值,不可做入参。
返回值只能保存在Object中。

上界<? extends T>不能往里存,只能往外取,适合频繁往外面读取内容的场景。(如果bean2指向的是Parent,而传入的对象是Double的)
下界<? super T>不影响往里存,但往外取只能放在Object对象里,适合经常往里面插入数据的场景(由于限定类型的超类可能有很多,getName返回类型不可预知,无法保证类型安全)。

<?>无限通配符

无界通配符 意味着可以使用任何对象,因此使用它类似于使用原生类型。但它是有作用的,原生类型可以持有任何类型,而无界通配符修饰的容器持有的是某种具体的类型。举个例子,在List类型的引用中,不能向其中添加Object, 而List类型的引用就可以添加Object类型的变量。

类型通配符小结

  1. 限定通配符总是包括自己;
  2. 子类型通配符:set方法受限,只可读,不可写;
  3. 超类型通配符:get方法受限,不可读(Object除外),只可写;
  4. 无限定通配符,只可读不可写;
  5. 如果你既想存,又想取,那就别用通配符;
  6. 不可同时声明子类型和超类型限定符,及extends和super只能出现一个。

%%%%% 动态代理jdk/CGLIB
1.定义一个接口及其实现类;
2.自定义 InvocationHandler 并重写invoke方法,在 invoke 方法中3.我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;
通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法创建代理对象;
4.获取代理对象的工厂getProxy方法

1;定义一个类;
自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;(Method args,proxy)
通过 Enhancer 类的 create()创建代理类;setclassloader,setSupclass,setMethodIntercepter,不能代理被final修饰的类,通过生成被代理类的子类实现

hashmap
数据结构 数组链表,优势与hashtable比较(,开放定址法null值不允许,扩容机制,hash方法,线程安全,效率)
put get 工作原理/扰动函数:hashCode的高16或低16位实现,
1.7,1.8区别(阈值为8而且满足大于64,退化6,泊松分布),为什么改用红黑树,头插法,尾插法
扩容机制 0.75(11(2n+1,16,10arraylist)
1.7头插法死循环问题 1.8数据覆盖,数据值不同hash值相同hash值为空直接插入 2n幂,扩容好计算,位运算
currenthashmap 1.7 1.8 (reetrantlock CAS+synch)
讲讲红黑树特征(叶节点是黑色空节点,路径包含相同数目的黑色节点)

进程间通信
1管道 2 信号量 wait signal函数 3 消息队列 4共享内存
线程间通信: 互斥量 锁机制 , event 信号量-锁共享锁
countdownlatch,cyclicBarrior机制
线程之间同步机制:临界区,互斥对象,信号量,事件对象,
共享内存 阻塞队列 , 信号量
shmget:该函数用来创建共享内存
shmat第一次创建完共享内存时,它还不能被任何进程访问,shmat函数的作用就是用来启动对该共享内存的访问,
shmctl
shmdt该函数用于将共享内存从当前进程中分离

孤儿进程和僵尸进程

孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。

springboot自动配置原理
springbootConfiguration 组合configuration注解,实现配置文件的功能
enableAutoConfiguration , ComponentScan(service repository controller)

spring security 和shiro
Spring Security 是一个重量级的安全管理框架;Shiro 则是一个轻量级的安全管理框架
Spring Security 概念复杂,配置繁琐;Shiro 概念简单、配置简单
Spring Security 功能强大;Shiro 功能简单

ArrayList线程安全-》CopyOnWriteList 是一个写时复制的策略保证 list 的一致性,所以在其增删改的操作中都使用了独占锁 ReentrantLock 来保证某个时间只有一个线程能对 list 数组进行修改。其底层是对数组的修改,调用 Arrays.copyarray() 方法进行对数组的复制
在添加元素的这段时间里,如果多线程访问容器中的元素,将会读取到旧的数据,等添加元素成功之后会将新的引用地址赋值给旧的list引用地址
缺点:内存占用/数据一致性问题/耗时/适合多读少写

CAS::底层实现即为CPU指令cmpxchg。volatile可见性:缓存一致性MESI,有序性:happens-before规则+内存屏障 synchronized-lock指令
JUC 线程池,AQS,并发容器,atomic原子类(基本类型,数组类型,引用类型AtomicStamp/markedReferenece,解决cas的aba问题,fieldUpdater,Integer,long)
Callable,Runable,Executor一个方法,ExecutorService(状态方法有无返回值,抛出异常,execute,submit);
线程池状态running,shutdown不接受新任务,stop,tidying-所有任务完成,执行钩子函数》,terminated彻底终止,(IO密集2cpu,CPU密集单倍,cpu等待时间和执行时间比例)
ThreadPoolExecutor(corePoolsize,maximumPoolsize,keepaliveTime,timeunit,workQueue,Threadfactory,rejectExecutorhandler)
4个,参数 Timeunit单位,hanlder策略
AQS: retreentlock(condition) readwritelock,semaphere ,cycleBarrier,countDownlatch,
并发容器 copyonwriteArraylist,concurrenthashmap,
ArrayBlockingQueue,LinkedBlockQueue/Deque,concurrentLinkedQueue/Deque
同步容器;hashtable,vector

Redis
都能直接获取长度
sds 底层char数组 对于string 数据类型,因为string 类型是二进制安全的,可以用来存放图片,视频等内容,另外由于Redis的高性能读写功能,而string类型的value也可以是数字,可以用作计数器(INCR,DECR),比如分布式环境中统计系统的在线人数,秒杀等。

hash数组 对于 hash 数据类型,value 存放的是键值对,比如可以做单点登录存放用户信息。

无环双端链表 对于 list 数据类型,可以实现简单的消息队列,另外可以利用lrange命令,做基于redis的分页功能

字典 对于 set 数据类型,由于底层是字典实现的,查找元素特别快,另外set 数据类型不允许重复,利用这两个特性我们可以进行全局去重,比如在用户注册模块,判断用户名是否注册;另外就是利用交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。

跳表 对于 zset 数据类型,有序的集合,可以做范围查找,排行榜应用,取 TOP N 操作等。

Redis使用简单字符串SDS作为字符串的表示,相对于C语言字符串,SDS具有常数复杂度获取字符串长度,杜绝了缓存区的溢出,减少了修改字符串长度时所需的内存重分配次数,以及二进制安全能存储各种类型的文件,并且还兼容部分C函数。
通过为链表设置不同类型的特定函数,Redis链表可以保存各种不同类型的值,除了用作列表键,还在发布与订阅、慢查询、监视器等方面发挥作用(后面会介绍)。
Redis的字典底层使用哈希表实现,每个字典通常有两个哈希表,一个平时使用,另一个用于rehash时使用,使用链地址法解决哈希冲突。
跳跃表通常是有序集合的底层实现之一,表中的节点按照分值大小进行排序。
整数集合是集合键的底层实现之一,底层由数组构成,升级特性能尽可能的节省内存。
压缩列表是Redis为节省内存而开发的顺序型数据结构,通常作为列表键和哈希键的底层实现之一。

redis 主从复制的核心原理
当启动一个 slave node 的时候,它会发送一个 PSYNC 命令给 master node。

如果这是 slave node 初次连接到 master node,那么会触发一次 full resynchronization 全量复制。此时 master 会启动一个后台线程,开始生成一份 RDB 快照文件,同时还会将从客户端 client 新收到的所有写命令缓存在内存中。RDB 文件生成完毕后, master 会将这个 RDB 发送给 slave,slave 会先写入本地磁盘,然后再从本地磁盘加载到内存中,接着 master 会将内存中缓存的写命令发送到 slave,slave 也会同步这些数据(offset维护以防中断)。slave node 如果跟 master node 有网络故障,断开了连接,会自动重连,连接之后 master node 仅会复制给 slave 部分缺少的数据(backlog),。

redis脑裂
redis的集群脑裂是指因为网络问题,导致redis master节点跟redis slave节点和sentinel集群处于不同的网络分区,此时因为sentinel集群无法感知到master的存在,所以将slave节点提升为master节点。此时存在两个不同的master节点,就像一个大脑分裂成了两个。
集群脑裂问题中,如果客户端还在基于原来的master节点继续写入数据,那么新的master节点将无法同步这些数据,当网络问题解决之后,sentinel集群将原先的master节点降为slave节点,此时再从新的master中同步数据,将会造成大量的数据丢失。
min-slaves-to-write 3
min-slaves-max-lag 10
第一个参数表示连接到master的最少slave数量
第二个参数表示slave连接到master的最大延迟时间

Redis与数据库的数据一致性

先删除缓存,再去更新数据库
读的时候想,先读取缓存,没有就读数据库然后再放入缓存
可以把修改DB的操作放在个jvm队列里面,更新缓存操作也放在里面,多个读操作可以过滤,遇到更新频繁可以通过扩机器增加吞吐量??缓存需要设置一个失效时间

Redis哨兵模式
info replication slaveof
定时任务 10秒info命令 2秒发送消息 1秒ping命令
主观下线超过一定时间被判定下线
客观下线 询问其它哨兵节点quorum 2个以上确定
选取领头哨兵 rift算法 发现的哨兵节点要求自己成为,半数同意,没有的话会等待随机事件重新发起下一轮选举
故障转移 选优先级高的从节点 (跟master断开时间,复制offset,runid),更新其他节点
配置中心 如果原来节点回来则变为从节点

Redis为什么单线程,以及为什么快
完全基于内存,数据结构简单,单线程避免切换,使用Io多路复用
官方FAQ表示,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,没有必要多线程,而且还耗费资源
这里强调的单线程是指处理网络请求的时候只有一个线程来处理,正式redis server肯定不止一个线程,例如reedis持久化的时候会以子进程或子线程的方式执行,
Redis分布式锁实现
1.通过setnx(lock_timeout)实现,如果设置了锁返回1, 已经有值没有设置成功返回0

  • 2.死锁问题:通过实践来判断是否过期,如果已经过期,获取到过期时间get(lockKey),然后getset(lock_timeout)判断是否和get相同,
  • 相同则证明已经加锁成功,因为可能导致多线程同时执行getset(lock_timeout)方法,这可能导致多线程都只需getset后,对于判断加锁成功的线程,
  • 再加expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS)过期时间,防止多个线程同时叠加时间,导致锁时效时间翻倍
  • 3.针对集群服务器时间不一致问题,可以调用redis的time()获取当前时间

zookeeper
基于zookeeper瞬时有序节点实现的分布式锁,其主要逻辑如下(该图来自于IBM网站)。大致思想即为:每个客户端对某个功能加锁时,在zookeeper上的与该功能对应的指定节点的目录下,生成一个唯一的瞬时有序节点。判断是否获取锁的方式很简单,只需要判断有序节点中序号最小的一个。当释放锁的时候,只需将这个瞬时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。

按值调用(call by value)表示方法接收的是调用者提供的值,而按引用调用(call by reference)表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。 它用来描述各种程序设计语言(不只是 Java)中方法参数传递方式。
Java 程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。

关于acquire的方法源码已经分析完毕,我们来简单总结下

a.首先tryAcquire获取同步状态,成功则直接返回;否则,进入下一环节;判断持有锁的线程是不是当前线程

b.线程获取同步状态失败,就构造一个结点,加入同步队列中,这个过程要保证线程安全;
c.加入队列中的结点线程进入自旋状态,若是老二结点(即前驱结点为头结点),才有机会尝试去获取同步状态;否则,当其前驱结点的状态为SIGNAL,线程便可安心休息,进入阻塞状态,直到被中断或者被前驱结点唤醒。

工作内存和主内存交互指令
JMM
lock(锁定):作用于主内存的变量,一个变量在同一时间只能一个线程锁定,该操作表示这条线成独占这个变量
unlock(解锁):作用于主内存的变量,表示这个变量的状态由处于锁定状态被释放,这样其他线程才能对该变量进行锁定
read(读取):作用于主内存变量,表示把一个主内存变量的值传输到线程的工作内存,以便随后的load操作使用
load(载入):作用于线程的工作内存的变量,表示把read操作从主内存中读取的变量的值放到工作内存的变量副本中(副本是相对于主内存的变量而言的)
use(使用):作用于线程的工作内存中的变量,表示把工作内存中的一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时就会执行该操作
assign(赋值):作用于线程的工作内存的变量,表示把执行引擎返回的结果赋值给工作内存中的变量,每当虚拟机遇到一个给变量赋值的字节码指令时就会执行该操作
store(存储):作用于线程的工作内存中的变量,把工作内存中的一个变量的值传递给主内存,以便随后的write操作使用
write(写入):作用于主内存的变量,把store操作从工作内存中得到的变量的值放入主内存的变量中
JVM对Java内存模型的实现
堆,栈

类加载的时机

隐式加载 new 创建类的实例,
显式加载:loaderClass,forName等
访问类的静态变量,或者为静态变量赋值
调用类的静态方法
使用反射方式创建某个类或者接口对象的Class对象。
初始化某个类的子类
直接使用java.exe命令来运行某个主类

drop,truncate,delete的区别
truncate是DDL,会隐式提交,所以不能会滚,不会触发触发器。
drop是DDL,会隐式提交,所以,不能回滚,不会触发触发器
drop语句删除表结构及所有数据,并将表所占用的空间全部释放。
delete是DML,执行delete操作时,每次从表中删除一行,并且同时将该行的的删除操作记录在redo和undo表空间中以便进行回滚(rollback)和重做操作
truncate,
保留表而将所有数据删除,如果和事务无关,用truncate即可;
DDL(数据定义语言)是指CREATE,ALTER和DROP语句
DDL允许添加/修改/删除包含数据的逻辑结构,或允许用户访问/维护数据(数据库,表,键,视图…)的逻辑结构。DDL是关于“元数据”的。

DML(数据操作语言)是指INSERT,UPDATE和DELETE语句
DML允许自己添加/修改/删除数据。
DQL(数据查询语言)是指SELECT,SHOW和HELP语句(查询)
SELECT是主要的DQL指令。它会检索您需要的数据。SHOW检索有关元数据的信息。HELP…适合需要帮助的人。
DCL(数据控制语言)是指GRANT和REVOKE语句
DCL用于授予/撤消对数据库及其内容的权限。DCL很简单,但MySQL的权限相当复杂。DCL是关于安全性的。
DTL(数据事务语言)是指START TRANSACTION,SAVEPOINT,COMMIT和ROLLBACK [TO SAVEPOINT]语句

mysql主从复制

步骤一:主库db的更新事件(update、insert、delete)被写到binlog
步骤二:从库发起连接,连接到主库
步骤三:此时主库创建一个binlog dump thread线程,把binlog的内容发送到从库
步骤四:从库启动之后,创建一个I/O线程,读取主库传过来的binlog内容并写入到relay log.
步骤五:还会创建一个SQL线程,从relay log里面读取内容,从Exec_Master_Log_Pos位置开始执行读取到的更新事件,将更新内容写入到slave的db,从而实现主从数据同步功能。
mysql主从复制存在的问题:
主库宕机后,数据可能丢失
从库只有一个sql Thread,主库写压力大,复制很可能延时
解决方法:
半同步复制—解决数据丢失的问题

介于异步复制和全同步复制之间,主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到relay log中才返回给客户端。

并行复制----解决从库复制延迟的问题binlog和relaylog异步
master每次事务都能实时刷新到磁盘,在slave开启relaylog自动修复机制,

如何保持强一致性

先写缓存,设置一定超时时间
(1)cache里有这个key,说明1s内刚发生过写请求,数据库主从同步可能还没有完成,此时就应该去主库查询

(2)cache里没有这个key,说明最近没有发生过写请求,此时就可以去从库查询

UUID–16个字节

UUID含义是通用唯一识别码,指在一台机器上生成的数字,出现数据拆分、合并存储的时候,能达到全局的唯一性
影响插入速度,占用空间大,适合分布式环境

ID
数据库自动编号,速度快,有序,占空间小
新老系统集成,发生主键冲突

MVCC
版本链:db_trx_id最近修改事务id,db_roll_pointer回滚指针,db_row_id隐含自增ID,
这意味着同一事务的多个实例,在同时运行时,无论每个实例运行多久,它们看到的数据视图是一致的。而同一时间,对于同一张表,不同事务看到的数据却是不同的。
对于select语句,只有同时满足了下面两个条件的行,才能被返回:

•行的被修改版本号小于或者等于该事务号

•行的被删除版本号要么没有被定义,要么大于事务的版本号:
对新插入的行,行的更新版本被修改为该事务的事务号
对于删除,innodb直接把该行的被删除版本号设置为当前的事务号,相当于标记为删除,而不是实际删除
在更新行的时候,innodb会把原来的行复制一份到回滚段中,并把当前的事务号作为该行的更新版本

select & poll & epoll比较

每次调用select都需要把所有要监听的文件描述符拷贝到内核空间一次,fd很大时开销会很大。epoll会在epoll_ctl()中注册,只需要将所有的fd拷贝到内核事件表一次,不用再每次epoll_wait()时重复拷贝
每次select需要在内核中遍历所有监听的fd,直到设备就绪;epoll通过epoll_ctl注册回调函数,也需要不断调用epoll_wait轮询就绪链表,当fd或者事件就绪时,会调用回调函数,将就绪结果加入到就绪链表。
select能监听的文件描述符数量有限,默认是1024;epoll能支持的fd数量是最大可以打开文件的数目,具体数目可以在/proc/sys/fs/file-max查看
select, poll在函数返回后需要查看所有监听的fd,看哪些就绪,而epoll只返回就绪的描述符,所以应用程序只需要就绪fd的命中率是百分百。
表面上看epoll的性能最好,但是在连接数少并且链接都十分活跃的情况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制需要很多函数回调。

网络编程

TCP总结:
Server端:create – bind – listen(udp没有)– accept(udp没有)– recv/send– close
Client端:create——- conncet(udp没有)——send/recv——close.
1.服务器端
1)创建套接字create;
2)绑定端口号bind;
3)监听连接listen;
4)接受连接请求accept,并返回新的套接字;
5)用新返回的套接字recv/send;
6)关闭套接字。
2.客户端
1)创建套接字create;
2)发起建立连接请求connect;
3)发送/接收数据send/recv;
4)关闭套接字。

生产者和消费者模型实现方式
https://segmentfault.com/a/1190000016260650
首先是最原始的synchronized方式,锁对象,
Jdk1.5之后加入了Lock接口,一个lock对象可以有多个Condition类,Condition类负责对lock对象进行wait,notify,notifyall操作
Semaphore mutex,share
第四种方式:BlockingQueue
BlockingQueue的put和take底层实现其实也是使用了第二种方式中的ReentrantLock+Condition,并且帮我们实现了库存队列,方便简洁

spring事务传播机制
REQUIRED(默认):支持使用当前事务,如果当前事务不存在,创建一个新事务。
SUPPORTS:支持使用当前事务,如果当前事务不存在,则不使用事务。
MANDATORY:中文翻译为强制,支持使用当前事务,如果当前事务不存在,则抛出Exception。
REQUIRES_NEW:创建一个新事务,如果当前事务存在,把当前事务挂起。
NOT_SUPPORTED:无事务执行,如果当前事务存在,把当前事务挂起。
NEVER:无事务执行,如果当前有事务则抛出Exception。
NESTED:嵌套事务,如果当前事务存在,那么在嵌套的事务中执行。如果当前事务不存在,则表现跟REQUIRED一样。

BeanFactory 和ApplicationContext的区别
BeanFactory和ApplicationContext都是接口,并且ApplicationContext间接继承了BeanFactory。
BeanFactory是Spring中最底层的接口,提供了最简单的容器的功能,只提供了实例化对象和获取对象的功能,而ApplicationContext是Spring的一个更高级的容器,提供了更多的有用的功能。
ApplicationContext提供的额外的功能:获取Bean的详细信息(如定义、类型)、国际化的功能、统一加载资源的功能、强大的事件机制、对Web应用的支持等等。
加载方式的区别:BeanFactory采用的是延迟加载的形式来注入Bean;ApplicationContext则相反的,它是在Ioc启动时就一次性创建所有的Bean,好处是可以马上发现Spring配置文件中的错误,坏处是造成浪费。

BEANFACTORY和FACTORYBEAN的区别与联系
两者都是接口;
BeanFactory主要是用来创建Bean和获得Bean的;
FactoryBean跟普通Bean不同,其返回的对象不是指定类的一个实例,而是该FactoryBean的getObject方法所返回的对象;
通过BeanFactory和beanName获取bean时,如果beanName不加&则获取到对应bean的实例;如果beanName加上&,则获取到FactoryBean本身的实例
FactoryBean 通常是用来创建比较复杂的bean(如创建mybatis的SqlSessionFactory很复杂),一般的bean 直接用xml配置即可,但如果创建一个bean的创建过程中涉及到很多其他的bean 和复杂的逻辑,用xml配置比较困难,这时可以考虑用FactoryBean。

spring的循环依赖
(1)构造器的循环依赖 ,无法解决,因为第三级缓存的前提是执行构造函数
(2)field属性的循环依赖。三级缓存 spring采用的是提前暴露对象的方法。
基于JAVA的引用传递机制,当我们获取到对象的引用后,对象的属性可以延迟设置;

这三级缓存分别指:
singletonFactories : 单例对象工厂的cache createBeanInstance实例化
earlySingletonObjects :提前暴光的单例对象的Cache populateBean填充属性
singletonObjects:单例对象的cache initializeBean
三级缓存这个cache的类型是ObjectFactory。这里就是解决循环依赖的关键,发生在createBeanInstance之后,也就是说单例对象此时已经被创建出来(调用了构造器)。以Spring此时将这个对象提前曝光出来让大家认识,让大家使用

Spring中自定义注解
@Documented 可以被文档化
@Retention(RetentionPolicy.RUNTIME)定义了注解的保留范围
@Target(ElementType.METHOD)Target说明了注解所修饰的对象范围
@Inherited 可以被继承
public @interface testAop {

String param();//注解传递的参数,可不加

新建一个注解@MyLog,加在需要注解申明的方法上面
新建一个类MyLogAspect,通过@Aspect注解使该类成为切面类。
通过@Pointcut 指定切入点 ,这里指定的切入点为MyLog注解类型,也就是被@MyLog注解修饰的方法,进入该切入点。
MyLogAspect中的方法通过加通知注解(@Before、@Around、@AfterReturning、@AfterThrowing、@After等各种通知)指定要做的业务操作。

Bean的实例化过程
BeanDefintion可以描述springbean当中的scope、lazy,以及属性和方法等等其他信息
xml,java类,注解获取配置信息,并使用Resource表示这个配置文件的资源,BeandefinitonReader读取Resource并解析配置文件形成Beandifination,并保存到BeanDefinationRegistry中,然后BeanfactoryPostProcessor对BeanDefinitionRegistory的Beanfinitnation进行加工,调用InstantiationStrategy对其进行加工,然后将Bean实例化放到Spring容器中(Bean缓存池hashmap),应用程序进行读取

spring实现IOC的四种方式:
xml配置,零配置实现,注解实现,自动装配
使用xml配置来实例化bean共分为三种方式,分别是普通构造方法创建、静态工厂创建、实例工厂创建
spring创建对象的四种方式
通过有参构造/通过下标id/通过参数名 /通过参数类型/通过无参构造
默认会用无参构造

Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?

启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:
@SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能,被标注的类等于在spring的XML配置文件中(applicationContext.xml),装配所有bean事务,提供了一个spring的上下文环境
@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
@ComponentScan:Spring组件扫描。

springMVC容器启动过程
web.xml的contextLoaderListener中会进行spring IOC的初始化,完毕后开始配置Servlet->dispatchServlet

Springboot启动流程
第一部分进行SpringApplication的初始化模块,配置一些基本的环境变量、资源、构造器、监听器,第二部分实现了应用具体的启动方案,包括启动流程的监听模块、加载配置环境模块、及核心的创建上下文环境模块,第三部分是自动化配置模块,该模块作为springboot自动配置核心,应该跟bean配置启动一样
初始化模块:
SpringbootApplication run方法会调用注解进行bean扫描配置,创建SpringApplicationRunlisterner开始监听,加载Springboot配置环境ConfigurableEnvironment,创建应用上下文application,然后开始实例化Bean,然后在进行自动配置
应用的具体启动方案
1.创建了应用的监听器SpringApplicationRunListeners并开始监听
2.加载SpringBoot配置环境(ConfigurableEnvironment)
3.配置环境(Environment)加入到监听器对象中
4.创建run方法的返回对象:ConfigurableApplicationContext(应用配置上下文)

springboot自动化配置EnableAutoConfiguration

springFactoriesLoader.loadfactoryName(factoryClass,classloader)
收集配置文件获取传入的工厂类名,类加载器-》通过类加载器,获取指定的spring.factories文件-》获取文件的工厂类的全路径-》通过类路径反射得到工厂的class对象,构造方法-》生成工厂类实例返回-》通过@import加载到(IOC容器)就可以在ApplicationContext中然后可以创建需要的bean对象

Spring中自动装配的方式有哪些
@Resource默认按照ByName自动注入,
@Autowired注解是按照类型(byType)装配依赖对象,(byName)来装配,可以结合@Qualifier注解一起使用。您创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时
no:不进行自动装配,手动设置Bean的依赖关系。
byName:根据Bean的名字进行自动装配。
byType:根据Bean的类型进行自动装配。
constructor:类似于byType,不过是应用于构造器的参数,如果正好有一个Bean与构造器的参数类型相同则可以自动装配,否则会导致错误。
autodetect:如果有默认的构造器,则通过constructor的方式进行自动装配,否则使用byType的方式进行自动装配。
局限性:
基本数据类型:你不能自动装配简单的属性,如基本数据类型,String字符串,和类。
模糊特性:自动装配不如显式装配精确,如果有可能,建议使用显式装配。

ApplicationContext通常的实现是什么?
FileSystemXmlApplicationContext :此容器从一个XML文件中加载beans的定义,XML Bean 配置文件的全路径名必须提供给它的构造函数。
ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,这里,你需要正确设置classpath因为这个容器将在classpath里找bean配置。
WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。

AOP:

(1)切面(Aspect):切面是通知和切点的结合。通知和切点共同定义了切面的全部内容。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @AspectJ 注解来实现。
(2)连接点(Join point):指方法,在Spring AOP中,一个连接点 总是 代表一个方法的执行。 应用可能有数以千计的时机应用通知。这些时机被称为连接点。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
(3)通知(Advice):在AOP术语中,切面的工作被称为通知。
(4)切入点(Pointcut):切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。
(5)引入(Introduction):引入允许我们向现有类添加新方法或属性。
(6)目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。它通常是一个代理对象。也有人把它叫做 被通知(adviced) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象。
(7)织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。在目标对象的生命周期里有多少个点可以进行织入:

什么是 JavaConfig?
它提供了配置 Spring IoC 容器的纯Java 方法。因此它有助于避免使用 XML 配置。使用 JavaConfig 的优点在于
(1)面向对象的配置。由于配置被定义为 JavaConfig 中的类,因此用户可以充分利用 Java 中的面向对象功能。一个配置类可以继承另一个,重写它的@Bean 方法等
(3)类型安全和重构友好。

IOC/AOP
(1) IOC 利用了反射,自己有个id,classtype,hashmap,所有的功能都在hashmap中,然后利用反射的Class.forName把把classtype转化成类,然后利用反射的setFieldValue()从hashMap中把属性和方法取出来,注入进去。最终把类创建出来,

(2)AOP是动态代理,其实底层也是反射;

Spring Boot、Spring MVC 和 Spring 有什么区别?
spring框架相当于一个大家族,有许多产品security,boot,cloud等,但他们基础都是Spring的IOC和AOP,
Spring MVC提供一种轻度耦合的MVC方式来开发web应用,通过DispatcherServlet,ModelAndView和View Resolver开发web应用很容易
Spring boot实现自动配置,解决spring框架需要大量的配置麻烦问题,同时集成大量常用的第三方配置(jdbs,mongo,redis,mail)
Spring 是一个“引擎”;
Spring MVC 是基于Spring的一个 MVC 框架;
Spring Boot 是基于Spring4的条件注册的一套快速开发整合包。

SpringMVC处理请求的流程
1、用户发送请求至前端控制器DispatcherServlet
2、DispatcherServlet收到请求调用HandlerMapping处理器映射器。
3、处理器映射器根据请求url找到具体的处理器,生成处理器对象Handler及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4、DispatcherServlet通过HandlerAdapter(让Handler实现更加灵活)处理器适配器调用处理器
5、执行处理器(Controller,也叫后端控制器)。
6、Controller执行完成返回ModelAndView(连接业务逻辑层和展示层的桥梁,持有一个ModelMap对象和一个View对象)。
7、HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
8、DispatcherServlet将ModelAndView传给ViewReslover视图解析器
9、ViewReslover解析后返回具体View
10、DispatcherServlet对View进行渲染视图(将ModelMap模型数据填充至视图中)。
11、DispatcherServlet响应用户

同一个类中调用 @Transaction注解的方法会有事务效果吗?

没有,可以Autowired注入自己,然后再调用注入的类中的方法,即自己依赖自己,循环依赖;
这里在一个内部调用应该是相当于单纯的调用方法this.methodName(),并没有AOP代理。

拦截器与过滤器的区别 :

1、拦截器是基于java的反射机制的,而过滤器是基于函数回调。

2、拦截器不依赖与servlet容器,过滤器依赖与servlet容器。

3、拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。

4、拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。

5、在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次

spring优点

(1)方便解耦,简化开发 (高内聚低耦合)
Spring就是一个大工厂(容器),可以将所有对象创建和依赖关系维护,交给Spring管理
spring工厂是用于生成bean
(2)AOP编程的支持,非侵入式设计
Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能
(3) 声明式事务的支持
只需要通过配置就可以完成对事务的管理,而无需手动编程
(4) 方便程序的测试
Spring对Junit4支持,可以通过注解方便的测试Spring程序
(5)方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的直接支持

PreparedStatement对象
#{ } 解析为一个 JDBC 预编译语句(prepared statement)的参数标记符?
仅 仅 为 一 个 纯 碎 的 s t r i n g 替 换 , 在 动 态 S Q L 解 析 阶 段 将 会 进 行 变 量 替 换 , { } 仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换, stringSQL{ } 在预编译之前已经被变量替换了,这会存在 sql 注入问题
预编译阶段可以优化 sql 的执行,预编译阶段可以合并多次操作为一个操作。
预编译语句对象可以重复利用,进行缓存

Mybatis的二级缓存

一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个(内存区域)数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。 一级缓存的作用域是同一个SqlSession,在第一个sqlSession执行相同的sql语句后结果放在内存中,第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。Mybatis默认开启一级缓存。
本地缓存不能被关闭,可以调用clearCache()来清空本地缓存,或者改变缓存的作用域

二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession去操作数据库得到数据会存在二级缓存区域,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。
Mybatis默认没有开启二级缓存需要在setting全局参数中配置开启二级缓存。
如果缓存中有数据就不用从数据库中获取,大大提高系统性能。
二级缓存是保存在Mapper对象中的,现在有一张user表,两个Mapper文件,AMapper.xml和BMapper.xml,B修改了user表中的内容,A是感知不到的,那么再从A里查询如果用到了缓存,就是旧的数据。

Mybatis工作原理
1、加载mybatis全局配置文件(数据源、mapper映射文件等),解析配置文件,
2,加载映射文件mapper.xml,MyBatis基于XML配置文件生成Configuration,和一个个MappedStatement(包括了参数映射配置、动态SQL语句、结果映射配置),其对应着<select | update | delete | insert>标签项。
2、SqlSessionFactoryBuilder通过Configuration对象生成SqlSessionFactory,用来开启SqlSession。

3、SqlSession对象完成和数据库的交互:
a、用户程序调用mybatis接口层api(即Mapper接口中的方法)
b、SqlSession通过调用api的Statement ID找到对应的MappedStatement对象
c、通过Executor(负责动态SQL的生成和查询缓存的维护)将MappedStatement对象进行解析,sql参数转化、动态sql拼接,生成jdbc Statement对象
d、JDBC执行sql。
e、借助MappedStatement中的结果映射关系,将返回结果转化成HashMap、JavaBean等存储结构并返回。
SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
ReuseExecutor:就是重复使用Statement对象。
BatchExecutor:执行update(没有select,JDBC批处理不支持select)

在Spring中有两种方式访问Hibernate:

控制反转 Hibernate Template和 Callback。 继承 HibernateDAOSupport提供一个AOP 拦截器。

volatile(原子性-lock指令和有序性-内存屏障-》,8个交互指令)与缓存Cache
MESI modeified exculsive shared invalid
但是上面的方式会有一个问题,由于在lock#锁住总线期间,其他CPU无法访问内存,导致效率低下。
所以就出现了缓存一致性协议。最出名的就是Intel 的MESI协议,MESI协议保证了每个缓存中使用的共享变量的副本是一致的。它核心的思想是:当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。

explain执行计划
id id越大优先级越高,代表子查询
select_type 普通查询,联合查询,子查询
table
type all不走索引全表,eq_ref唯一索引 const主键索引或unique索引,只需匹配一行数据,ref非唯一索引,range,index索引遍历全表

possible_keys 涉及到的索引 key使用索引 key_len
ref rows行数 Extra

分布式事务
一般数据库进行undo或者是redo回滚,这样保证数据的强一致性
CAP定理
C:一致性:客户端知道一系列的操作都会同时发生
A:可用性:每个操作都必须以可预期的响应结束
P:分区容错性:即使出现单个组件无法可用,操作依然可以完成
BASE定理
Basiciallty aviailable 基本可用 Soft state软状态Eventually consistent最终一致性

分布式事务的解决方案:

1&2PC两阶段提交协议
第一阶段:事务协调器要求每个涉及到事务的数据库预提交此操作,并反映是否可以提交
第二阶段:事务协调器要求每个数据库提交数据
优点: 尽量保证了数据的强一致,适合对数据强一致要求很高的关键领域。(其实也不能100%保证强一致)
缺点: 实现复杂,牺牲了可用性,对性能影响较大,不适合高并发高性能场景,如果分布式系统跨接口调用

2&TCC补偿事务(实现以及流程简单,但数据一致性错)
针对每个操作,都要注册一个与其对应的确认和补偿操作
它分为三个阶段:
Try阶段主要是对业务系统做检测及资源预留。
Comfirm阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行Confirm阶段时,默认Confirm阶段是不会出错的。即:try成功,Confirm一定成功
Cancel阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。

3&本地消息表(业界使用最多)
将分布式事务拆成本地事务进行处理

基本思路就是:

消息生产方,需要额外建一个消息表,并记录消息发送状态。消息表和业务数据要在一个事务里提交,也就是说他们要在一个数据库里面。然后消息会经过MQ发送到消息的消费方。如果消息发送失败,会进行重试发送。
消息消费方,需要处理这个消息,并完成自己的业务逻辑。此时如果本地事务处理成功,表明已经处理成功了,如果处理失败,那么就会重试执行。如果是业务上面的失败,可以给生产方发送一个业务补偿消息,通知生产方进行回滚等操作。
生产方和消费方定时扫描本地消息表,把还没处理完成的消息或者失败的消息再发送一遍。如果有靠谱的自动对账补账逻辑,这种方案还是非常实用的。

优点: 一种非常经典的实现,避免了分布式事务,实现了最终一致性。在 .NET中 有现成的解决方案。
缺点: 消息表会耦合到业务系统中,如果没有封装好的解决方案,

Record gap Next-key锁
假设有记录1, 3, 5, 7,现在记录5上加next-key lock,则会锁定区间(3, 5],任何试图插入到这个区间的记录都会阻塞。
注意,由于其效果相当于(3, 5)上的gap lock加5上的record lock,而且gap lock是可重入的,相互不阻塞的(上文讲过),当其它事务试图获取(3, 5)的gap lock时,不会被阻塞;但如果要获取5上的record lock,就会阻塞;如果要获取5上的next-key lock,同样会阻塞。

JNDI破坏双亲委派模型
JNDI是Java标准服务,它的代码由启动类加载器去加载。但是JNDI需要回调独立厂商实现的代码,而类加载器无法识别这些回调代码(SPI)。
为了解决这个问题,引入了一个线程上下文类加载器。 可通过Thread.setContextClassLoader()设置。
利用线程上下文类加载器去加载所需要的SPI代码,即父类加载器请求子类加载器去完成类加载的过程,而破坏了双亲委派模型
Spring破坏双亲委派模型
Spring要对用户程序进行组织和管理,而用户程序一般放在WEB-INF目录下,由WebAppClassLoader类加载器加载,而Spring由Common类加载器或Shared类加载器加载。
那么Spring是如何访问WEB-INF下的用户程序呢?
使用线程上下文类加载器。 Spring加载类所用的classLoader都是通过Thread.currentThread().getContextClassLoader()获取的。当线程创建时会默认创建一个AppClassLoader类加载器(对应Tomcat中的WebAppclassLoader类加载器): setContextClassLoader(AppClassLoader)。
利用这个来加载用户程序。即任何一个线程都可通过getContextClassLoader()获取到WebAppclassLoader

Spring中tomcat启动流程
SpringbootTomcatStarter 继承SpringbootServletIntializer,run方法返回ConfigurableApplicationContext,初始化ApplicationArguments,创建配置环境,准备环境加入Apllicationrunlistener监听器,打印Banner,创建应用上下文,预处理上下文,刷新上下文,再刷新上下文,发布应用已经启动事件,发布应用启动完成事件。
在Springboot启动tomcat的工作在刷新上下文这一步,而tomcat的启动主要是实例化两个组件:Connector、Container,一个tomcat实例就是一个Server,一个Server包含多个Service,也就是多个应用程序,每个Service包含多个Connector和一个Container,而一个Container下又包含多个子容器。
Connector用来处理连接相关的事情,并提供Socket到Request和Response相关转化。Container用于封装和管理Servlet,以及处理具体的Request请求。

外置tomcat部署
1继承SpringBootServletInitializer
2pom.xml移除对tomcat依赖
3由jar包变成war包

数据库和redis一致性
给缓存设置有效期
先删除缓存,再写数据库,再删除缓存,延时双删
也可以先更新数据库,再删除缓存
消息队列

零拷贝过程
避免CPU将数据从一块存储拷贝到另外一块存储,减少不必要的拷贝,让CPU解脱出来专注于别的任务。没有在内存层面去复制数据(Netty,NIO-Channel)

直接存储器访问,DMA技术就是我们在主板上放⼀块独立的芯片。在进行内存和I/O设备的数据传输的时候,我们不再通过CPU来控制数据传输,而直接通过 DMA控制器(DMA Controller,简称DMAC)

第一次传输,是从硬盘上,读到操作系统内核的缓冲区里。这个传输是通过DMA搬运的。
第二次传输,需要从内核缓冲区里面的数据,复制到我们应用分配的内存里面。这个传输是通过CPU搬运的。
第三次传输,要从我们应用的内存里面,再写到操作系统的Socket的缓冲区里面去。这个传输,还是由CPU搬运的。
最后一次传输,需要再从Socket的缓冲区里面,写到网卡的缓冲区里面去。这个传输又是通过DMA搬运的。
可以通过Channel直接从读缓冲区到写到网卡缓冲区

CMS内存碎片
CMS 提供了 -XX:+UseCMSCompactAtFullCollection 用于在 CMS 收集器顶不住要进行 FullGC 时开启内存碎片的合并整理过程。
增大Xmx或者减少Xmn
在应用访问量最低的时候,在程序中主动调用System.gc(),比如每天凌晨。
降低-XX:CMSInitiatingOccupancyFraction参数以提早执行CMSGC动作
年轻代晋升老年代年龄

MESI
在这里插入图片描述

在这里插入图片描述

Redis单线程模型
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值