面试|面试总结(zhuan)

一 数据库 1.1 事务的特性 原子性(A**):原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚
一致性(C**):一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
隔离性(I**):隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
持久性(D**):持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
1.2 事务的隔离级别 1:脏读:某事务已更新一份数据,而另一个事务此时读取了同一份数据,某些原因前一个更新做了回滚Rollback操作,则后一个事务数据是不正确的(读到了脏的数据)
2:不可重复读:不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。例如事务T1在读取某一数据,而事务T2立马修改了这个数据并且提交事务给数据库,事务T1再次读取该数据就得到了不同的结果,发送了不可重复读。
3:幻读:是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。,例如事务T1批量对一个表中某一列列值为1的数据修改为2的变更,但是在这时,事务T2对这张表插入了一条列值为1的数据,并完成提交。此时,如果事务T1查看刚刚完成操作的数据,发现还有一条列值为1的数据没有进行修改,而这条数据其实是T2刚刚提交插入的,这就是幻读。
四大隔离级别:
Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
Repeatable read (可重复读):可避免脏读、不可重复读的发生。(MYSQL默认)
Read committed (读已提交):可避免脏读的发生。(SQLServer默认)
Read uncommitted (读未提交):最低级别,任何情况都无法保证。
丢失更改:任何锁都没有,多个用户同时更改同一条数据的时候会覆盖掉别人的修改。通过写的时候加锁实现(乐观锁),但是不能避免读未提交,因此使用乐观锁最少要开启数据库的读已提交。
https://blog.csdn.net/evilcry2013/article/details/79661275?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2aggregatepagefirst_rank_v2~rank_aggregation-1-79661275.pc_agg_rank_aggregation&utm_term=%E6%95%B0%E6%8D%AE%E5%BA%934%E7%A7%8D%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86&spm=1000.2123.3001.4430
这篇:https://blog.csdn.net/qq_38238296/article/details/88362999
1.3 死锁

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FHQOA9mt-1641825722965)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20210405204732938.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aTxlYvlu-1641825722966)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20210405204944476.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JyCdIg0u-1641825722966)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20210405204959771.png)]
参见https://blog.csdn.net/woshiyeguiren/article/details/80277475
死锁解决:https://blog.csdn.net/u013256816/article/details/108970784
1.4 数据库查询优化 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ABYiXSpk-1641825722966)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20210405214953863.png)]
1.5 B+树 https://zhuanlan.zhihu.com/p/54102723
特征:
B+树的特征:
1.有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。
2.所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。
3.所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。
总结:非叶子节点(路径节点)不保存数据,只保存索引(关键字),并且所有路径节点的关键字都会存在于子节点,在子节点元素中是最大(或最小)元素,所以说,叶子节点包含了所有的数据,这些数据要么是指向数据库的指针,要么直接就是数据库数据,并且所有叶子节点按照关键字大小形成一个链表。
优势:
1.路径节点存储更多的元素,使得树看起来更加扁平,使得查询的IO次数更少,查询效率更高。
2.所有查询都要查找到叶子节点,查询性能稳定。
3.所有叶子节点形成有序链表,便于范围查询。
1.6 drop, delete truncate的区别 三者都是删除的意思,但是三者个有些区别
· delete和truncate只删除表的数据不删除表的结构
· 速度 drop > truncate > delete
· 想删除部分数据时, delete 删除时要带上where语句
· 保留表而想删除所有的数据时用truncate
1.7 数据库三大范式 · 1NF 字段是最小单元,不可再分
· 2NF 满足1NF 表中字段必须完全依赖全部主键而并非部分主键(其实就是一个表只能是一个实体)
· 3NF 满足2NF,非主键外的所有字段必须互不依赖(要消除传递依赖)
1.8 存储过程,函数和触发器 面试|面试总结(zhuan)
文章图片

面试|面试总结(zhuan)
文章图片

面试|面试总结(zhuan)
文章图片

存储过程使用call调用,函数使用select调用,具体对比如下:
面试|面试总结(zhuan)
文章图片

1)一般来说,存储过程实现的功能要复杂一点,而函数实现的功能针对性比较强。
2)存储过程一般是作为一个独立的部分来执行(参照下面“存储过程-调用方法”),而函数可以作为查询语句的一个部分来调用(用在select后面,或者from后面)。由于函数可以返回一个表对象,因此它可以在查询语句中位于FROM关键字的后面。
存储过程
存储过程(Stored Procedure )是一组为了完成特定功能的SQL 语句集,经编译后存储在数据库中。存储过程经过语法检查和编译的SQL语句,所以执行速度比普通的SQL语句的执行速度快。用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。
1.9 数据库连接池 为什么使用数据库连接池?
? 建立一个数据库连接是一件非常耗时(消耗时间)耗力(消耗资源)的事情。之所以会这样,是因为连接到数据库服务器需要经历几个漫长的过程:建立物理通道(例如套接字或命名管道),与服务器进行初次握手,分析连接字符串信息,由服务器对连接进行身份验证,运行检查以便在当前事务中登记等等。
什么是数据库连接池?
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;
连接池就是这样一个容器:它存放了一定数量的与数据库服务器的物理连接。因此,当我们需要连接数据库服务器的时候,只需去池(容器)中取出一条空闲的连接,而不是新建一条连接。这样的话,我们就可以大大减少连接数据库的开销,从而提高了应用程序的性能。
连接池的管理策略?
当客户请求数据库连接时,
  • 1)如果池中有空闲连接可用,返回该连接。
  • 2)如果没有空闲连接,池中连接都已用完,创建一个新连接添加到池中。
  • 3)如果池中连接已达到最大连接数,请求按设定的最大等待时间进入等待队列直到有空闲连接可用。
  • 4)如果超出最大等待时间,则抛出异常给客户。
    当客户释放数据库连接时,先判断该连接的引用次数是否超过了规定值,如果超过就从连接池中删除该连接,否则保留为其他客户服务。
    该策略保证了数据库连接的有效复用,避免频繁的建立、释放连接所带来的系统资源开销。
    如果连接长时间空闲,或检测到与服务器的连接已断开,连接池管理器也会将该连接从池中移除。
1.10 哪些情况会导致索引失效? 很好:https://www.jianshu.com/p/3ccca0444432
1 前导模糊查询不能利用索引(like ‘%XX’),需要使用覆盖索引来解决,即只查询这个有索引的列
2 or语句使用不当导致全表扫描
3 索引无法存储null值,所以where的判断条件如果对字段进行了null值判断,将导致数据库放弃索引而进行全表查询,如
select id from t where num is null
4 避免在where子句中使用!= <>
5 如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引
等等。
6 对查询的列上有运算或者函数
7 如果mysql估计使用全表扫描要比使用索引快,则不使用索引
8 整数类型的列中查询加了引号时会导致索引失效(同1)
发现在MySQL5.6 版本后引入了参数 innodb_large_prefix,开启情况下,Innodb表的行记录格式是Dynamic或Compressed的前提下,能让单列索引的长度达到3072个字节长度,如果不开启这个参数,那么单列索引的长度就不能超过767个字节长度。
1.11 mysql存储引擎 面试|面试总结(zhuan)
文章图片

1、事务安全:
InnoDB支持事务安全,MyISAM和MEMORY两个不支持。
2、存储限制:
InnoDB有64TB的存储限制,MyISAM和MEMORY要是具体情况而定。
3、空间使用:
InnoDB对空间使用程度较高,MyISAM和MEMORY对空间使用程度较低。
4、内存使用:
InnoDB和MEMORY对内存使用程度较高,MyISAM对内存使用程度较低。
5、插入数据的速度:
InnoDB插入数据的速度较低,MyISAM和MEMORY插入数据的速度较高。
6、对外键的支持:
InnoDB对外键支持情况较好,MyISAM和MEMORY两个不支持外键。
三种引擎特点如下:
1、InnoDB存储引擎
InnoDB是事务型数据库的首选引擎,支持事务安全表(ACID),其它存储引擎都是非事务安全表,支持行锁定和外键,MySQL5.5以后默认使用InnoDB存储引擎。
InnoDB特点: 支持事务处理,支持外键,支持崩溃修复能力和并发控制。如果需要对事务的完整性要求比较高(比如银行),要求实现并发控制(比如售票),那选择InnoDB有很大的优势。
如果需要频繁的更新、删除操作的数据库,也可以选择InnoDB,因为支持事务的提交(commit)和回滚(rollback)。
2、MyISAM存储引擎
MyISAM基于ISAM存储引擎,并对其进行扩展。它是在Web、数据仓储和其他应用环境下最常使用的存储引擎之一。MyISAM拥有较高的插入、查询速度,但不支持事务,不支持外键。
MyISAM特点: 插入数据快,空间和内存使用比较低。如果表主要是用于插入新记录和读出记录,那么选择MyISAM能实现处理高效率。如果应用的完整性、并发性要求比较低,也可以使用
3、MEMORY存储引擎
MEMORY存储引擎将表中的数据存储到内存中,为查询和引用其他表数据提供快速访问。
MEMORY特点: 所有的数据都在内存中,数据的处理速度快,但是安全性不高。如果需要很快的读写速度,对数据的安全性要求较低,可以选择MEMOEY。
它对表的大小有要求,不能建立太大的表。所以,这类数据库只使用在相对较小的数据库表。
1.12 联合索引 https://zhuanlan.zhihu.com/p/108179618
索引生效(我将它比喻为联动的菜单):最左侧生效。
索引失效:
  • 不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描
  • 存储引擎不能使用索引范围条件右边的列
  • 尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select *
  • mysql在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描
  • is null,is not null也无法使用索引
  • like以通配符开头(’%abc…’)mysql索引失效会变成全表扫描的操作。
1.13 聚集索引和非聚集索引 聚集索引一个表只能有一个,而非聚集索引一个表可以存在多个
聚集索引存储记录是物理上连续存在,而非聚集索引是逻辑上的连续,物理存储并不连续
聚集索引:物理存储按照索引排序;聚集索引是一种索引组织形式,索引的键值逻辑顺序决定了表数据行的物理存储顺序
非聚集索引:物理存储不按照索引排序;非聚集索引则就是普通索引了,仅仅只是对数据列创建相应的索引,不影响整个表的物理存储顺序.
索引是通过二叉树的数据结构来描述的,我们可以这么理解聚簇索引:索引的叶节点就是数据节点。而非聚簇索引的叶节点仍然是索引节点,只不过有一个指针指向对应的数据块。
对于Innodb,主键毫无疑问是一个聚集索引。但是当一个表没有主键,或者没有一个索引,Innodb会如何处理呢。请看如下规则:
? 1.如果一个主键被定义了,那么这个主键就是作为聚集索引
? \2. 如果没有主键被定义,那么该表的第一个唯一非空索引被作为聚集索引
? \3. 如果没有主键也没有合适的唯一索引,那么innodb内部会生成一个隐藏的主键作为聚集索引,这个隐藏的主键是一个6个字节的列,改列的值会随着数据的插入自增。
4.自增主键会把数据自动向后插入,避免了插入过程中的聚集索引排序问题。聚集索引的排序,必然会带来大范围的数据的物理移动,这里面带来的磁盘IO性能损耗是非常大的。 而如果聚集索引上的值可以改动的话,那么也会触发物理磁盘上的移动,于是就可能出现page分裂,表碎片横生。所以不应该修改聚集索引。
1.14 SQL语句的解析和执行过程 from -> where -> group by having -> 聚合函数 ->select -> order by
1.15 索引建立原则 适合索引的列是经常出现在where子句中的列,或者连接子句中指定的列
基数较小的列,索引效果较差,没有必要在此列建立索引(例如一些记录状态列的)
使用短索引,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间
不要过度索引。索引需要额外的磁盘空间,并降低写操作的性能。在修改表内容的时候,索引会进行更新甚至重构,索引列越多,这个时间就会越长。所以只保持需要的索引有利于查询即可。
主键设计
二:java基础 2.1 HashMap原理 (1)HaspMap用于存放key-value,键不能重复,并且存放数据是无序的。和HashTable相比,键和值都可以为null,但是它不是线程安全的,如果要实现线程安全,jdk中的Collections类提供了线程安全的hashMap或者concurrentHashMap(原理)。
(2)在jdk1.8之前,hashmap底层数据结构是数组+链表。数组从jdk1.8开始,hashmap的底层数组长度大于64并且链表长度大于阈值8的时候链表就会变为红黑树!原因就是红黑树是平衡二叉树,在查找性能方面比链表要高.
(3)数组是HashMap的主体,链表则是主要为了解决哈希冲突!!!哈希表的存储过程是,假如我要向集合中存放1,1这个数据,先根据1调用Integer类中重写之后的hashCode()方法计算出值,然后结合数组长度计算出向Node数组中存储数据的空间的索引,若计算出的索引空间没有数据,则直接将1-1存到数组中;如果已经存放了数据,那么会通过equals方法比较两个key的hash值是否一致,如果不等,那么继续向下和其他的数据的key进行比较,若都不相等,则划出一个节点存储数据。相等的话则产生哈希冲突,这时就是用后面的数据覆盖掉已经存在的重复数据。
(4)HashMap中的两个重要的参数:初始容量大小和加载因子,初始容量大小是创建时给数组分配的容量大小,默认值为16,用数组容量大小乘以加载因子得到一个值,一旦数组中存储的元素个数超过该值就会调用resize方法将数组容量增加到原来的两倍,专业术语叫做扩容.
在做扩容的时候会生成一个新的数组,原来的所有数据需要重新计算哈希码值重新分配到新的数组,所以扩容的操作非常消耗性能.
为什么HashMap的数组长度总是2的n次幂?
1:hashcode%length == hashcode&((2^n)-1),Java之所有使用位运算(&)来代替取模运算(%),最主要的考虑就是效率。位运算(&)效率要比代替取模运算(%)高很多,主要原因是位运算直接对内存数据进行操作,不需要转成十进制,因此处理速度非常快。
HashMap线程不安全样例
https://blog.csdn.net/weixin_29938067/article/details/112355349
多个put之间发生元素丢失情况:两个线程都执行到p.next!=null这条判断语句,在执行插入的时候,后面插入的节点会直接覆盖掉前面的节点。
get和put方法之间:一个线程执行put方法导致重新hash,调用resize方法,先计算出新的数组容量和扩容的一个阈值,然后构建一个新的空的Node数组,并把它赋值给原来的Node数组,这个时候去读的话只能读到null了。
HashMap的resize方法解读
https://www.cnblogs.com/zengcongcong/p/11295349.html
不错:https://www.jianshu.com/p/75adf47958a7
2.2 ==和equals方法的区别 ==只是比较两个参数的值是否相等,而equals方法是按照自己的方式去比较大小,如果不复写equals方法的话,只是比较内存地址。
2.3 线程和进程 进程:进程是程序的基本执行实体,也是系统分配和资源调度的基本单元。
线程是进程的一部分,是cpu进行调度和分派的基本单位,一个线程只能属于一个进程,而一个进程至少有一个线程,一个进程内的所有线程共享进程的内存空间,线程有5种状态,分别是创建、执行、等待、阻塞和消亡。而多线程程序就是通过cpu在同一个进程的不同线程之间高速的切换来实现的。
2.4 实现多线程的方式和区别(继承Thread类和实现Runnable接口,Callable接口)
  • Java只支持单继承,如果有一个类中有部分代码需要被线程执行(线程的任务),但是这个类已经继承了其他的父类,这时是无法去继承Thread的。
  • 一般在Java中类都有自己所属的体系结构,如果让某个类继承的Thread,那么这个类的体系结构发生改变,那么这个体系中的任何一个类都变成线程类了。
  • 定义实现Runnable接口,那么这个类与Thread类没有直接关系,但是这个类具备接口中的行为(规则),后期就可以按照接口的规则去使用这个实现类。Runnable接口相当于线程的任务接口,只是来明确写出要干的活,而不和线程产生关系。定义实现Runnable接口仅仅只是来明确线程的任务。
  • 在创建Thread对象的时候,可以将实现Runnable接口的类的对象交给Thread,那么就相当于告诉了线程的任务了。
    Runnable和Callable接口区别
    1.Callable规定的方法是call(),而Runnable规定的方法是run().
    2.Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
    3.call() 方法可抛出异常,而run() 方法是不能抛出异常的。
    4.运行Callable任务可拿到一个Future对象, Future表示异步计算的结果。
    5.它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。
    6.通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。
    7.Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。
2.5 wait,sleep,join,notify,notifyAll 1、sleep()
使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是说如果有synchronized同步快,其他线程仍然不能访问共享数据。注意该方法要捕捉异常。
例如有两个线程同时执行(没有synchronized)一个线程优先级为MAX_PRIORITY,另一个为MIN_PRIORITY,如果没有 Sleep()方法,只有高优先级的线程执行完毕后,低优先级的线程才能够执行;但是高优先级的线程sleep(500)后,低优先级就有机会执行了。
总之,sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的线程有执行的机会。
2、join()
join()方法使调用该方法的线程在此之前执行完毕,也就是等待该方法的线程执行完毕后再往下继续执行。注意该方法也需要捕捉异常。
3、yield()
该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会。
4、wait()和notify()、notifyAll()
这三个方法用于协调多个线程对共享数据的存取,所以必须在synchronized语句块内使用。synchronized关键字用于保护共享数据,阻止其 他线程对共享数据的存取,但是这样程序的流程就很不灵活了,如何才能在当前线程还没退出synchronized数据块时让其他线程也有机会访问共享数据 呢?此时就用这三个方法来灵活控制。
wait() 方法使当前线程暂停执行并释放对象锁标示,让其他线程可以进入synchronized数据块,当前线程被放入对象等待池中。当调用notify()方法 后,将从对象的等待池中移走一个任意的线程并放到锁标志等待池中,只有锁标志等待池中线程能够获取锁标志;如果锁标志等待池中没有线程,则 notify()不起作用。
notifyAll()则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。
注意 这三个方法都是java.lang.Object的方法。
wait和sleep方法区别
1、 sleep 来自 Thread 类,而 wait 来自 Object 类。
2、最主要是sleep方法没有释放锁,而wait方法释放了 锁,使得其他线程可以使用同步控制块或者方法。
3、wait,notify和 notifyAll 只能在同步控制方法或者同步控制块里面使用,而 sleep 可以在任何地方使用(使 用范围)
4、 sleep 必须捕获异常,而 wait , notify 和 notifyAll 不需要捕获异常
``
package others; /** * @description:两个线程交叉执行一秒 * @create: 2021-04-04-09:00 **/ public class main3 { static int state = 1; static final Object lock = new Object(); public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { for(int i = 1; i< 6; ++i){ synchronized (lock){ if(state!=1){ try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() +"" + i); state = 2; try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } lock.notifyAll(); } } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { for(int i = 1; i< 6; ++i){ synchronized (main3.lock){ if(state!=2){ try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() +"" + i); state = 1; try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } lock.notifyAll(); } } } }); t1.start(); t2.start(); } }

2.6 ArrayList和LinkedList特点 共同点:都是一个存储元素有序,元素可以重复的容器。
区别:ArrayList底层是数组,扩容为原来的两倍,每当需要扩容的时候,都会重新创建一个数组,并将旧数组的数据迁移到新的数组里。ArrayList可以做到用下表来随机访问,效率较高,但是在中间插入或者删除元素的效率会比较慢,因为这会导致元素平移。
LinkedList底层是链表,不能随机访问,只能通过指针(迭代器)的方式一个一个遍历,所以查询效率低。但是它插入或者删除效率比较高,因为只需要修改当前一个或者两个节点的指针信息。
线程安全的list:CopyOnWriteArrayList
2.7 网络编程 ? udp是用户数据报协议,其主要特点是面向无连接,不可靠。jdk提供了两个主要的类来支持udp编程。分别是datagrampacket和datagramsocket,用datagramsocket来创建发送或者接受的对象,如果是服务端对象的话,是必须指定端口号的。datagrampacket用来创建数据包,该类提供了多个不同的构造方法,携带了目的地址和端口号的构造是用来发送数据包的。
? tcp是传输控制协议,其主要特点是面向连接的,并且是可靠的。jdk也提供了连个核心的类来支持tcp编程。分别是socket和serversocket,分别代表着客户端对象和服务端对象。tcp编程首先需要建立连接,然后通过输入输出流的方式来发送和接收数据,用shutdownOutput()方法作为数据传输完毕的标志。
2.8 String、StringBuffer和StringBuilder String是不可变的字符串常量,每次对字符串常量进行操作的时候都会生成新的字符串,效率低且浪费空间。
StringBuilder和StringBuffer都是可变的字符串,但是StringBuffer是线程安全的,通过在方法上添加同步关键字来实现,适用于多线程环境下。而StringBuilder是非线程安全的,在单线程环境下效率高。
StringBuilder不安全的点在哪?
https://blog.csdn.net/weixin_38405253/article/details/100151578
1:字符串长度值不正确 count+=len操作(非原子操作)
2:发送数组下标越界异常:多个都执行完ensureCapacityInternal方法(确定可以容纳下新的字符串),但是只能容纳下一个线程插入的字符串,这个时候就会出现数组下标越界异常。
2.9 java内存模型(JMM)
  • 堆 用来存储对象 线程共享
  • 栈 方法的运行都在栈中运行,方法中的局部变量(基础数据类型和对象的引用)都存在栈中 线程私有
  • 方法区 存储.class文件的相关信息,静态变量,常量池等
  • 本地方法区 存储一些本地方法
  • 寄存器区
2.10 排序算法 选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法;
冒泡排序、插入排序、归并排序、桶排序、二叉树排序、和基数排序是稳定的排序算法。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IgwMgotK-1641825722967)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20210406155739554.png)]
2.11 ConcurrentHashMap jdk1.7提供分段锁机制,用segment数组和HashEntry来实现。
jdk1.8为每个node数组节点分配一把锁。
2.12 volatile关键字 volatile在Java并发编程中常用于保持内存可见性和防止指令重排序。
https://jingyan.baidu.com/article/fec4bce23c975ab3608d8b59.html
2.13 Lock锁 Lock接口提供了相对于synchronized关键字,而更为灵活的一种同步手段
它的核心与本质仍旧是为了线程的同步与协作通信
所以它的核心仍旧是锁与监视器,也就是Lock接口与Condition接口
但是灵活是有代价的,所以并不需要在所有的地方都尝试使用显式锁,如果场景满足需要,synchronized仍旧是一种很好的解决方案(也是应该被优先考虑的一种方式)
与synchronized再次对比下
  • synchronized是JVM底层实现的,Lock是JDK接口层面的
  • synchronized是隐式的,Lock是显式的,需要手动加锁与解锁
  • synchronized乌无论如何都会释放,即使出现错误,Lock需要自己保障正确释放
  • synchronized是阻塞式的获取锁,Lock可以阻塞获取,可中断,还可以尝试获取,还可以设置超时等待获取
  • synchronized无法判断锁的状态,Lock可以进行判断
  • synchronized可重入,不可中断,非公平,Lock可重入,可中断、可配置公平性(公平和非公平都可以)
  • 如果竞争不激烈,两者的性能是差不多的,可是synchronized的性能还在不断的优化,当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized
lock锁高级功能:
1 等待可中断
是指当有锁的线程长期不释放锁的时候,正常等待的线程可以选择放弃等待,改为处理其他事情,
可中断特性对处理执行时间非常长的同步块很有帮助
2 公平锁
是指多个线程在等待同一个锁时,必须要按照申请锁的时间顺序来依次获得锁
非公平锁不能保证这一点,在锁被释放时,任何一个等待锁的线程都有机会获得锁,synchronized 中的锁就是典型非公平锁
ReentrantLock 默认为非公平锁,可以通过带布尔值的构造函数来使用公平锁,不过使用公平锁后,将会导致 ReentrantLock 的性能急剧下降,会明显影响吞吐量
3 锁绑定多个条件 (选择性通知)
是指一个 ReentrantLock 对象可以同时绑定多个 Condition 对象
在 synchronized 中,锁对象的 wait() 方法和 notify() / notifyAll() 方法相结合可以实现等待 / 通知机制;在 ReentrantLock 中,则需要借助 Condition 接口和 newCondition() 方法来实现
Condition 是在 JDK5 中引入的,具有很好的灵活性,比如可以实现多路通知功能,及在一个 Lock 对象中创建多个 Condition 实例 (即对象监视器),线程对象可以注册在指定的 Condition 中,从而可以有选择性的进行线程通知,在线程调度上更加灵活。而在 synchronized 中使用 notify() / notifyAll() 方法进行通知时,被通知的线程是有 JVM 选择的。因此用 ReentrantLock 类结合 Condition 实例可以实现"选择性通知",由 Condition 接口默认提供
synchronized 相当于整个 Lock 对象中只有一个 Condition 实例,所有线程都注册在它身上,如果执行 notifyAll() 方法的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jxitAxDi-1641825722967)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20210419180914795.png)]
可重入锁;https://www.jianshu.com/p/f47250702ee7
2.14 java线程池 https://www.jianshu.com/p/7726c70cdc40
2.15 java三大特性 1.封装
将类的属性和行为进行封装,并通过不同访问权限修饰符去限制成员的访问。这样做的好处是可以将类的某些信息,实现的细节隐藏起来,提供一致的对外接口,保证类的安全性。
2.继承
一个类继承另一个类,称继承的类为子类,被继承的称为父类.
继承后子类自动拥有父类的属性和方法,实现代码复用.
另外,子类还可以写自己特有的属性和方法,目的是实现功能的扩展,子类也可以复写父类的方法即方法的重写.
3.多态(重载和重写)
重载:发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序 不同,方法返回值和访问修饰符可以不同,发生在编译时。
重写:发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父 类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类 方法访问修饰符为 private 则子类就不能重写该方法。
重写的前提是继承,是在父子类之间,子类重写父类的方法,并且使父类的对象指向子类的实例。
2.16 接口和抽象类的特点和区别? 共同点:
  1. 都是上层的抽象层。
  2. 都不能被实例化
  3. 都能包含抽象的方法,这些抽象的方法用于描述类具备的功能,但是不比提供具体的实现。
他们的区别如下:
1)抽象类用abstract修饰,接口使用interface关键字
2)抽象类可以有构造方法,接口没有构造方法
3)类只能被单继承,接口可以被多实现,多继承
4)抽象类可以有具体方法,接口从1.8开始才能有默认的具体方法。
5)接口中的成员变量都用public static final修饰,方法为public abstract(除了java8的default关键字修饰的方法)
2.17 ThreadLocal ThreadLocal基本实现思路是:它会为每个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突,因为每个线程都拥有自己的变量副本,从而也就没必要对该变量进行同步啦。
ThreadLocal特性 ThreadLocal和Synchronized都是为了解决多线程中相同变量的访问冲突问题,不同的点是
  • Synchronized是通过线程等待,牺牲时间来解决访问冲突
  • ThreadLocal是通过每个线程单独一份存储空间,牺牲空间来解决冲突,并且相比于Synchronized,ThreadLocal具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问到想要的值。
正因为ThreadLocal的线程隔离特性,使他的应用场景相对来说更为特殊一些。在android中Looper、ActivityThread以及AMS中都用到了ThreadLocal。当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用ThreadLocal。
2.18 异常 非检查异常( unckecked exception ): Error 和 RuntimeException 以及他们的子类。 javac 在编译时, 不会提示和发现这样的异常,不要求在程序处理这些异常。所以如果愿意,我们可以编写代码处理(使用 try… catch…finally )这样的异常,也可以不处理。对于这些异常,我们应该修正代码,而不是去通过异常处理器处 理 。这样的异常发生的原因多半是代码写的有问题。如除0错误 ArithmeticException ,错误的强制类型转换错 误 ClassCastException ,数组索引越界 ArrayIndexOutOfBoundsException ,使用了空对象 NullPointerException 等等。
检查异常( checked exception ):除了 Error 和 RuntimeException 的其它异常。 javac 强制要求程序员 为这样的异常做预备处理工作(使用 try…catch…finally 或者 throws )。在方法中要么用 try-catch 语句捕 获它并处理,要么用throws子句声明抛出它,否则编译不会通过。这样的异常一般是由程序的运行环境导致的。因 为程序可能被运行在各种未知的环境下,而程序员无法干预用户如何使用他编写的程序,于是程序员就应该为这样 的异常时刻准备着。如 SQLException , IOException , ClassNotFoundException,FileNotFoundException 等。 需要明确的是:检查和非检查是对于 javac 来说的,这样就很好理解和区分了
2.19 hashCode与 equals的相关规定
  1. 如果两个对象相等,则 hashcode一定也是相同的
  2. 两个对象相等,对两个对象分别调用 equals 方法都返回 true
  3. 两个对象有相同的 hashcode 值,它们也不一定是相等的,即equals方法不一定返回true (比如哈希冲突)
  4. 因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖
  5. hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个 对象指向相同的数据)
2.20 红黑树 https://www.jianshu.com/p/2c7a4a4e1f53
2.21 java7和java8中GC的区别 java8取消了永久代,被替换成了元空间,而元空间使用的是本地内存。原因是:
1、字符串存在永久代中,容易出现性能问题和内存溢出。
2、类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。
3、永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。
4、Oracle 可能会将HotSpot 与 JRockit 合二为一。
2.22 cas和synchronized区别 2.23 设计模式 1:单例模式(饿汉式和懒汉式,必要要会书写,并且懒汉式必须要会实现同步)
2:代理模式(静态代理和动态代理,了解动态代理的核心方法,利用反射机制)
3:工厂模式(静态工厂和抽象工厂),spring用的是实例工厂,属于静态工厂
4:适配器模式(类适配器和对象适配器,要注意区别)
5:策略模式
6:装配器模式
2.24 final关键字用法,以及和finally,finalize的区别 final:
1:修饰类,表示这个类不能被继承,比如String类
2:修饰方法,表示该方法不能被重写,重载没有问题
3:修饰变量,当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。本质上是一回事,因为引用的值是一个地址,final要求值,即地址的值不发生变化。
finally:
jvm对final关键字的优化:如
final String x = "1"; String y = x+"2"; System.out.println(y == "12"); // true

用于和try,catch搭配使用,表示一定要执行的语句,如果try或catch语句块中有return,最终都会执行finally中的return。
2.25 java8新特性 1:stream用法
2:函数式接口
3:方法引用
2.26 深拷贝和浅拷贝 2.27 java双亲委派机制 概念:当某个类加载器需要加载某个.class文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。
类加载器的类别
BootstrapClassLoader(启动类加载器)
c++编写,加载java核心库 java.*,构造ExtClassLoaderAppClassLoader。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作
ExtClassLoader (标准扩展类加载器)
java编写,加载扩展库,如classpath中的jrejavax.*或者
java.ext.dir 指定位置中的类,开发者可以直接使用标准扩展类加载器。
AppClassLoader(系统类加载器)
java编写,加载程序所在的目录,如user.dir所在的位置的class
CustomClassLoader(用户自定义类加载器)
java编写,用户自定义的类加载器,可加载指定路径的class文件
双亲委派机制的作用 1、防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。
2、保证核心.class不能被篡改。通过委托方式,不会去篡改核心.class,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全。
2.28 String源码解读 2.29 synchronized静态锁和对象锁 三 框架技术 3.1 spring框架的核心(IOC,AOP) IOC:控制反转,就是将对象的交给spring容器来统一管理,不管是在那一层编写代码,只要需要对象,直接找spring框架获取。对象的创建、维护、销毁全部交给spring框架来管理,我们编程的时候只要关注逻辑上的代码,具体对象的创建、销毁不用操心。
AOP:面向切面编程,它的应用场景是软件已经正常使用,现在需要对其中的功能进行增强操作。AOP是一种横向增强的策略,在原有的逻辑不变的情况下(原有的代码不需要更改),在正常执行的过程中,拦截到需要被增强的功能,在执行原有功能之前,添加额外逻辑,这样既保证原有功能可以正常运转,同时还能执行新加的逻辑(底层依赖的反射技术)。
在Spring中关于AOP技术提供两种不同的方式实现:
? 1. 基于JDK原有的动态代理完成
? 要求被增强的功能必须有接口,只能增强接口中的方法。只要被增强的类实现了接口,那么Spring在使用AOP的使用 “ 优先” 采用动态代理机制完成增强。
? 2. 基于第三方的cglib技术完成。
? 一个类中有功能需要被增强,但是这个类又没有对应的接口,这时Spring会借助cglib技术,动态的给当前的类产生一个子类,然后再其中添加增强的逻辑代码。
? 可以说一下5中通知!分别是前置,后置,环绕,异常,最终。
面向切面编程的几个重要概念:
3.2 spring实例化bean的几种方式
  • 基于无参数的构造方法
  • 静态工厂
  • 实例工厂
  • FactoryBean
3.3 springMVC工作原理 ? 1、我们先输入一个网址,浏览器发出请求,请求进入到springMVC的核心对象dispacterServlet对象里面。这个核心对象首先会去调用处理器映射器,处理器映射器根据URL判断你到底要请求什么路径,返回这个路径给核心对象。
? 2、SpringMVC的核心对象接受到请求路径之后,会将请求路径交给处理器适配器,适配器拿到路径,在SpringMVC的核心配置文件中找那个自定义的Controller的name值与请求的路径匹配,匹配到之后,就会调用对应的Controller程序。
? 3、自定义的Controller类默认是实现的Controller接口的,同时在类中复写handleRequest方法。在其中处理请求,并返回ModelAndView对象,将对象返回给适配器
? 4、适配器将ModelAndView再交给SpringMVC的核心对象,SpringMVC的核心对象将ModelAndView交给视图解析器,视图解析器解析出视图名字返回
? 5、SpringMVC的核心对象中,根据名字找到对应的jsp进行数据渲染,最终返回渲染的结果,SpringMVC将结果响应给tomcat,tomcat服务器将最终的数据响应给浏览器。
3.4 spring对事务的支持 Spring中提供关于事务控制的接口:PlatformTransactionManager 控制事务,Spring针对不同的框架提供给基于上面接口的一个实现类:
  • DataSourceTransactionManager:针对JDK中原生的JDBC、DbUtils、Mybatis进行具体的事务控制
  • HibernateTransactionManager:针对Hibernate框架进行具体的事务控制
  • JpaTransactionManager:针对Spring Data JPA框架进行具体的事务控制
3.5 mybatis两级缓存 3.5.1 一级缓存
mybatis框架默认已经开启一级缓存。当我们在程序获取到SqlSession对象之后,这时就会有一个缓存对象存在,然后只要是通过当前SqlSession对象查询数据库,首先会将数据给缓存中存放一份,如果后续还使用这个SqlSession对象查询相同的数据,这时mybatis默认从缓存中查询,而不会发起sql语句,到数据库中查询。
重点:同一个SqlSession对象,查询的数据是相同。
3.5.2 二级缓存
mybatis的二级缓存指的是当前mapper文件。
一般使用mybatis框架都会存在一张表对应一个接口,并且对应一个pojo,而这时还会有个mapper文件与其对应。
二级缓存是针对的mapper文件,也就是不同的sqlSession去执行查询操作,只要操作的同一个mapper文件中的不同的select标签,那么查询的数据是可以放到同一个缓存中的。而不同的select查询的是相同的数据,这时mybatis如果开启的二级缓存,那么在查询的时候mybatis就先从二级缓存中找数据。
3.6 静态代理和动态代理 https://www.cnblogs.com/gonjan-blog/p/6685611.html
代理模式分为两种,一种是静态代理模式,一种是动态代理模式。 静态代理模式:在程序运行之前需要写好代理类,也就是说运行之前.class文件已经存在 动态代理模式:在程序运行期间动态生成代理类,运行期间动态产生class文件

简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。
动态代理优势:动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。是因为所有被代理执行的方法,都是通过在InvocationHandler中的invoke方法调用的,所以我们只要在invoke方法中统一处理,就可以对所有被代理的方法进行相同的操作了。
缺点:动态代理的过程,代理对象和被代理对象的关系不像静态代理那样一目了然,清晰明了。因为动态代理的过程中,我们并没有实际看到代理类,也没有很清晰地的看到代理类的具体样子,而且动态代理中被代理对象和代理对象是通过InvocationHandler来完成的代理过程的,其中具体是怎样操作的,为什么代理对象执行的方法都会通过InvocationHandler中的invoke方法来执行。
动态代理如何动态产生java文件并编译然后加载到虚拟机中
https://blog.csdn.net/qq_31859365/article/details/82902349
为什么动态代理一定要实现接口
代理对象和被代理对象具有一样的契约。体现了“代理”的概念。
代理出来的类继承了Propty不能再继承,只能通过接口来约定契约。
为什么动态代理类继承Proxy方法
标识该类是一个代理类
InvocationHandler中invoke方法中的第一个参数proxy的用途
\1. 可以使用反射获取代理对象的信息(也就是proxy.getClass().getName())。
\2. 可以将代理对象返回以进行连续调用,这就是proxy存在的目的,因为this并不是代理对象(可以在invoke方法中返回这个代理对象)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yQVv4qrE-1641825722967)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20210927130859549.png)]
3.7 事务传播行为和嵌套事务 事务传播行为:如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为
1. TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。2. TransactionDefinition.PROPAGATION_REQUIRES_NEW:(作为内部事务)创建一个新的事务,如果当前存在事务,则把当前事务挂起。3. TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。4. TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。5. TransactionDefinition.PROPAGATION_NEVER以非事务方式运行,如果当前存在事务,则抛出异常。6. TransactionDefinition.PROPAGATION_MANDATORY如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。7. TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7dPtU6uy-1641825722967)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20210419202354511.png)]
3.8 springboot的自动化配置原理 三个父注解:
@SpringBootConfiguration(表明这是一个配置类,并将这个类的对象交给spring容器管理) @EnableAutoConfiguration(自动配置相关) @ComponentScan(用于扫描被Component注解修饰的类)

Spring Boot启动的时候会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有自动配置类,并根据项目导入的启动器对其进行选择性的加载,而这些自动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig形式的Spring容器配置类,它能通@EnableConfigurationProperties导入以Properties结尾命名的类中的配置属性,而XxxxProperties类是通过@ConfigurationProperties注解与全局配置文件中对应的属性进行绑定的。
3.9 springboot配置文件的优先级别 yml > yaml > properties
3.10 springboot多环境配置 https://jingyan.baidu.com/article/425e69e60b5377be15fc16cc.html
3.11 springAOP的应用场景 Authentication 权限
Caching 缓存
Context passing 内容传递
Error handling 错误处理
Lazy loading 懒加载
Debugging调试
logging, tracing, profiling and monitoring 记录跟踪 优化 校准
Performance optimization 性能优化
Persistence持久化
Resource pooling 资源池
Synchronization 同步
Transactions 事务
3.12 springboot相比于SSM带来的好处 1 内嵌 Servlet 容器,可以选择内嵌 Tomcat 或者 jetty。
2 可以以 jar 包的形式独立运行springboot项目。
3 提供了一系列的 starter,简化了 maven 配置,比如经常使用 spring-boot-starter-web。
4 配置统一放到yml或者properties文件中,方便管理
5.快速整合第三方框架,无需配置文件。
3.13 spring中bean的初始化过程 https://www.jianshu.com/p/33c471aff85c
1、入口
ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("spring-test.xml");

2、解析传入的路径中的占位符,集合org.springframework.core.env.AbstractPropertyResolverorg.springframework.util.PropertyPlaceholderHelper
3、刷新上下文
1、prepareRefresh() : 准备刷新,设置一些活动标志,比如开始时间,当前的状态
2、ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory():从spring的配置文件中加载bean,封装成BeanDefinition,注册到注册表中,创建beanFactory
3、prepareBeanFactory(beanFactory); :准备BeanFactory,设置累加载器,添加后置处理器,SPL表达式解析器,向ioc容器中注入一些组件
4、postProcessBeanFactory(beanFactory); : 允许子类做一些处理操作
5、invokeBeanFactoryPostProcessors(beanFactory); :调用BeanFactoryProcessor,先是调用BeanDefitionRegistoyPostProcessor,之后调用BeanFactoryProcessor
6、registerBeanPostProcessors(beanFactory); : 将配置文件中读取的Bean的后置处理器注册到容器中
7、initMessageSource(); :初始化消息源,用于国际化
8、initApplicationEventMulticaster() : 初始化事件广播器,判断容器中是否已经注册了该组件,如果没有该组件,那么使用默认的
9、onRefresh(); :子类初始化一些特殊的bean
10、registerListeners(); :注册事件监听器
11、finishBeanFactoryInitialization(beanFactory) :完成初始化,初始化非懒加载的bean
12、finishRefresh(); :完成刷新,最后一步,初始化生命周期处理器,派发事件
3.14 springboot热部署 3.15 @transactional注解失效 https://www.jianshu.com/p/f334fe856266
1:最好不要加在接口上
2:和数据库存储引擎相关
3:不能加在非public方法上
4:注解propagation配置错误,使得当前方法以非事务方式运行
5:使用了rollbackFor指定异常,然而抛出其他异常
四:nginx 4.1 动静分离
Nginx提供的动静分离是指把动态请求和静态请求分离开,合适的服务器处理相应的请求,使整个服务器系统的性能、效率更高。

? Nginx可以根据配置对不同的请求做不同转发,这是动态分离的基础。静态请求对应的静态资源可以直接放在Nginx上做缓冲,更好的做法是放在相应的缓冲服务器上。动态请求由相应的后端服务器处理。
4.2 正向代理 在如今的网络环境下,我们如果由于技术需要要去访问国外的某些网站,此时你会发现位于国外的某网站我们通过浏览器是没有办法访问的,此时大家可能都会用一个操作FQ进行访问,FQ的方式主要是找到一个可以访问国外网站的代理服务器,我们将请求发送给代理服务器,代理服务器去访问国外的网站,然后将访问到的数据传递给我们!
上述这样的代理模式称为正向代理,正向代理最大的特点是客户端非常明确要访问的服务器地址;服务器只清楚请求来自哪个代理服务器,而不清楚来自哪个具体的客户端;正向代理模式屏蔽或者隐藏了真实客户端信息。
4.3 反向代理 多个客户端给服务器发送的请求,nginx服务器接收到之后,按照一定的规则分发给了后端的业务处理服务器进行处理了。此时~请求的来源也就是客户端是明确的,但是请求具体由哪台服务器处理的并不明确了,nginx扮演的就是一个反向代理角色
反向代理,主要用于服务器集群分布式部署的情况下,反向代理隐藏了服务器的信息!
4.4 负载均衡 常见负载均衡策略:轮询,ip_hash,随机,加权
4.5 nginx进程模型 nginx模型有两种进程,master进程和worker进程。master进程主要用来管理worker进程,管理包含:接收来自外界的信号,向各worker进程发送信号,监控worker进程的运行状态,当worker进程退出后(异常情况下),会自动重新启动新的worker进程。而基本的网络事件,则是放在worker进程中来处理了。多个worker进程之间是对等的,他们同等竞争来自客户端的请求,各进程互相之间是独立的。一个请求,只可能在一个worker进程中处理,一个worker进程,不可能处理其它进程的请求。worker进程的个数是可以设置的,一般我们会设置与机器cpu核数一致,这里面的原因与nginx的进程模型以及事件处理模型是分不开的。
4.6 nginx处理请求 worker进程之间是平等的,每个进程,处理请求的机会也是一样的。当我们提供80端口的http服务时,一个连接请求过来,每个进程都有可能处理这个连接,怎么做到的呢?首先,每个worker进程都是从master进程fork过来,在master进程里面,先建立好需要listen的socket之后,然后再fork出多个worker进程,这样每个worker进程都可以去accept这个socket(当然不是同一个socket,只是每个进程的这个socket会监控在同一个ip地址与端口,这个在网络协议里面是允许的)。一般来说,当一个连接进来后,所有在accept在这个socket上面的进程,都会收到通知,而只有一个进程可以accept这个连接,其它的则accept失败,这是所谓的惊群现象。当然,nginx也不会视而不见,所以nginx提供了一个accept_mutex这个东西,从名字上,我们可以看这是一个加在accept上的一把共享锁。有了这把锁之后,同一时刻,就只会有一个进程在accpet连接,这样就不会有惊群问题了。accept_mutex是一个可控选项,我们可以显示地关掉,默认是打开的。当一个worker进程在accept这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,这样一个完整的请求就是这样的了。我们可以看到,一个请求,完全由worker进程来处理,而且只在一个worker进程中处理。
4.7 nginx异步非阻塞机制 系统调用比如 read(),默认就是阻塞的,你调用它之后,可能此时还没有数据发送过来,它就一直阻塞,直到从内核读到数据才返回。而 epoll 之所以为非阻塞是因为,它一直循环,直到某个连接已经将数据发送过来后才会去调用 read() ,因为此时内核区是有数据的,所以不会发生阻塞,这就是非阻塞。
而同步异步这个要对比起来说。严谨来说,OS 层的 epoll 是同步非阻塞的,在系统调用中有与之相反的异步读,你调用之后立即返回,内核自动将内核缓冲区的数据读到用户区。
然而像 Nginx、Node.js 这种应用层实现,把它们称为异步,是因为和代码的同步 I/O 方式比起来,它们是用回调这种方式进行 I/O,所以称为异步。
五 MQ 5.1 什么是消息队列? 消息队列 是指利用 高效可靠 的 消息传递机制 进行与平台无关的 数据交流,并基于 数据通信 来进行分布式系统的集成。
5.2 消息队列的特点(作用)
  • 应用解耦
  • 异步通信
  • 流量削峰
  • (海量)日志处理
  • 消息通讯
5.3 MQ应用场景 根据消息队列的特点,可以衍生出很多场景,或者说很多场景都能用到。下面举几个例子:
1)异步通信
注册时的短信、邮件通知,减少响应时间;
2)应用解耦
信息发送者和消息接受者无需耦合,比如调用第三方;
3)流量削峰
例如秒杀系统;
5.4 ActiveMQ两种模式 点对点方式(PTP):一个消费者对应一个生产者
发布/订阅模式(Publish/Sub):一个生产者产生消息发送后,可以被多个消费者进行接收。
补充:由于topic传递消息的特点是,一个生产者可以有多个消费者,生产者生产的消息在没有被消费者消费之前,并不会将消息持久化到activemq的服务端,发送的消息会自动消失。所以 测试的时候需要先创建消费者对象,然后在发送消息,防止消息丢失。
Queue与Topic区别:
1、点对点(point-to-point,简称PTP)Queue消息传递模型:
通过该消息传递模型,一个应用程序(即消息生产者)可以向另外一个应用程序(即消息消费者)发送消息。在此传递模型中,消息目的地类型是队列(即Destination接口实现类实例由Session接口实现类实例通过调用其createQueue方法并传入队列名称而创建)。消息首先被传送至消息服务器端特定的队列中,然后从此对列中将消息传送至对此队列进行监听的某个消费者。同一个队列可以关联多个消息生产者和消息消费者,但一条消息仅能传递给一个消息消费者。如果多个消息消费者正在监听队列上的消息,JMS消息服务器将根据“先来者优先”的原则确定由哪个消息消费者接收下一条消息。如果没有消息消费者在监听队列,消息将保留在队列中,直至消息消费者连接到队列为止。这种消息传递模型是传统意义上的懒模型或轮询模型。在此模型中,消息不是自动推动给消息消费者的,而是要由消息消费者从队列中请求获得。
2、发布/订阅(publish/subscribe,简称pub/sub)Topic消息传递模型:
? 通过该消息传递模型,应用程序能够将一条消息发送给多个消息消费者。在此传送模型中,消息目的地类型是主题(即Destination接口实现类实例由Session接口实现类实例通过调用其createTopic方法并传入主题名称而创建)。消息首先由消息生产者发布至消息服务器中特定的主题中,然后由消息服务器将消息传送至所有已订阅此主题的消费者。**主题目标也支持长期订阅。长期订阅表示消费者已注册了主题目标,但在消息到达目标时该消费者可以处于非活动状态。当消费者再次处于活动状态时,将会接收该消息。如果消费者均没有注册某个主题目标,该主题只保留注册了长期订阅的非活动消费者的消息。**与PTP消息传递模型不同,pub/sub消息传递模型允许多个主题订阅者接收同一条消息。JMS一直保留消息,直至所有主题订阅者都接收到消息为止。pub/sub消息传递模型基本上是一个推模型。在该模型中,消息会自动广播,消息消费者无须通过主动请求或轮询主题的方法来获得新的消息。
5.5 ActiveMQ消息持久化 https://www.jianshu.com/p/deb1816271d1
5.6 MQ使用过程中的一些问题 MQ如何保证消息的幂等性(主要是如何防止消息被重复消费):https://blog.csdn.net/m0_46614240/article/details/106626475
MQ消息丢失问题:https://www.cnblogs.com/756623607-zhang/p/10507267.html
死信队列:
RabbitMQ集群:https://www.jianshu.com/p/b7cc32b94d2a
主备模式
远程模式
镜像模式
多活模式
六 redis 6.1 什么是 Redis?简述它的优缺点? redis底层数据结构:https://blog.csdn.net/qq_52212721/article/details/116376274?utm_term=redisset%E5%8E%BB%E9%87%8D%E5%8E%9F%E7%90%86&utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2allsobaiduweb~default-3-116376274&spm=3001.4430
Redis 的全称是:Remote Dictionary.Server,本质上是一个 Key-Value 类型的内存数据库,很像 memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据 flush 到硬盘 上进行保存。 因为是纯内存操作,Redis 的性能非常出色,每秒可以处理超过 10 万次读写操作,是已知性能最快的 Key-Value DB。 Redis 的出色之处不仅仅是性能,Redis 最大的魅力是支持保存多种数据结构,此外单个 value 的最大限 制是 1GB,不像 memcached 只能保存 1MB 的数据,因此 Redis 可以用来实现很多有用的功能。 比方说用他的 List 来做 FIFO 双向链表,实现栈或队列,或者实现一个轻量级的高性 能消息队列服务,用他的 Set 可以做高 性能的 tag 系统等等。 另外 Redis 也可以对存入的 Key-Value 设置 expire 时间,因此也可以被当作一 个功能加强版的 memcached 来用。 Redis 的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能 读写,因此 Redis 适合的场景主要局限在较小数据量的高性能操作和运算上。
优点:内存数据库,读写快
支持数据类型较多
缺点:受物理内存大小的限制,不能用作海量数据的高性能读写。
只支持通过key的方式来查询数据,不能做到像数据库一样多列查询。
应用场景:
list:用来作为栈或者队列,或者实现一个轻量级的消息队列服务。
Set:用来去重,通过集合的运算实现共同喜好,共同好友,个人偏好等
zset:用来做排行榜等。
hash:可以用来存储对象。
6.2 什么是缓存穿透?如何避免?什么是缓存雪崩?何如避免? 缓存穿透 一般的缓存系统,都是按照 key 去缓存查询,如果不存在对应的 value,就应该去后端系统查找(比如 DB)。一些恶意的请求会故意查询不存在的 key,请求量很大,就会对后端系统造成很大的压力。这就叫 做缓存穿透。 如何避免?
1:对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该 key 对应的数据 insert 了之后清理 缓存。 2:对一定不存在的 key 进行过滤。可以把所有的可能存在的 key 放到一个大的 Bitmap 中,查询时通过 该 bitmap (可以判断某个key一定不存在)过滤。 https://www.cnblogs.com/ysocean/p/12594982.html
布隆过滤器优缺点:
优点:优点很明显,二进制组成的数组,占用内存极少,并且插入和查询速度都足够快。
缺点:随着数据的增加,误判率会增加;还有无法判断数据一定存在;另外还有一个重要缺点,无法删除数据。
缓存雪崩 当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,会给后端系统带来很大压 力。导致系统崩溃。 如何避免?
1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个 key 只允许一个线 程查询数据和写缓存,其他线程等待。
2:做二级缓存,A1 为原始缓存,A2 为拷贝缓存,A1 失效时,可以访问 A2,A1 缓存失效时间设置为 短期,A2 设置为长期
3:不同的 key,设置不同的过期时间,让缓存失效的时间点尽量均匀
4:跑定时任务,在缓存失效前刷进新的缓存
5:设置键永不失效
https://www.zhihu.com/question/421946305/answer/1482420252
6.3 Redis 和 Mysql 数据库数据如何保持一致性 https://zhuanlan.zhihu.com/p/91770135:延迟双删策略【第二次删除失败重做(通过单独的程序读取mysql的binlog日志,完成对缓存的清除)或者设置缓存的过期时间,这种方式可以实现数据最终一致性,但是有效期间可能导致数据不一致的现象】
为什么要双删?因为删除缓存之后到更新数据库之前这段时间可能存在并发的访问,可能导致数据库和缓存数据不一致(数据库是更新后的数据,而缓存中的是旧的数据),所以采用延迟双删的策略。
延迟如何实现?
1:单独开一个线程来做延时。(较差)
2:利用线程池,从池中取线程来做延迟。(一般)
3:
还不错:https://blog.csdn.net/gly1256288307/article/details/88739612
加入已经发生了不一致情况,该怎么解决?
6.4 Redis持久化 RDB:官方出厂配置默认是 900秒内有1个更改,300秒内有10个更改以及60秒内有10000个更改,则将内存中的数据快照写入磁盘。
AOF方式:
https://www.jianshu.com/p/cbe1238f592a
6.5 redis集群 1:单节点
2:主从模式
3:sentinel模式 撰写
4:cluster模式:https://www.jianshu.com/p/813a79ddf932
6.6 redis配置文件详解 https://blog.csdn.net/suprezheng/article/details/90679790
6.7 redis数据淘汰策略 redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。redis 提供 6种数据淘汰策略:
voltile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
no-enviction(驱逐):禁止驱逐数据
6.8 redis线程问题 https://blog.csdn.net/stackCSDN/article/details/79982982?utm_term=Redis%E9%AB%98%E5%B9%B6%E5%8F%91%E4%BC%9A%E5%87%BA%E7%8E%B0%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98&utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2allsobaiduweb~default-1-79982982&spm=3001.4430
6.9 redis操作原子性问题 https://www.jianshu.com/p/f42d5f5c495b
6.10 redis分布式锁 https://www.jianshu.com/p/47fd7f86c848
七 微服务 7.1 服务发现与注册 为什么要引入服务发现?
1:当增加一个服务实例的时候,
2:负载均衡,解决单点故障
zookeeper,eruka,nginx
7.2 负载均衡 zookeeper,ribbon,nginx
7.3 服务的降级限流熔断 参见:https://www.jianshu.com/p/7c0a3723e2e4?utm_source=oschina-app
面试|面试总结(zhuan)
文章图片

服务雪崩:因服务提供者的不可用导致服务调用者的不可用,并将不可用逐渐放大的过程,就叫服务雪崩效应。
到这里就知道了雪崩的原因是服务提供者的不可用导致的,那么什么是导致服务提供者的不可用呢?无非就这么几点:大流量请求(高并发),提供者硬件问题,缓存击穿,程序的bug,超时等等。
当服务调用者使用同步调用的时候,会产生大量的等待线程占用系统资源,一旦线程资源被耗尽,服务调用者提供的服务也将处于不可用状态,于是服务雪崩效应产生了!
知道了根本原因,问题来了,怎么解决呢?这里才入正题,是不是引子有些长?
解决方法
1,超时机制 2,服务限流 3,服务熔断 4,服务降级

超时机制
如果我们加入超时机制,例如2s,那么超过2s就会直接返回了,那么这样就在一定程度上可以抑制消费者资源耗尽的问题
服务限流
通过线程池+队列的方式,通过信号量的方式。比如商品评论比较慢,最大能同时处理10个线程,队列待处理5个,那么如果同时20个线程到达的话,其中就有5个线程被限流了,其中10个先被执行,另外5个在队列中
服务熔断
这个熔断可以理解为我们自己家里的电闸。
当依赖的服务有大量超时时,在让新的请求去访问根本没有意义,只会无畏的消耗现有资源,比如我们设置了超时时间为1s,如果短时间内有大量请求在1s内都得不到响应,就意味着这个服务出现了异常,此时就没有必要再让其他的请求去访问这个服务了,这个时候就应该使用熔断器避免资源浪费
服务降级
有服务熔断,必然要有服务降级。
所谓降级,就是当某个服务熔断之后,服务将不再被调用,此时客户端可以自己准备一个本地的fallback(回退)回调,返回一个缺省值。 例如:(备用接口/缓存/mock数据),这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强,当然这也要看适合的业务场景
自动降级:超时、失败次数、故障、限流
(1)配置好超时时间(异步机制探测回复情况);
(2)不稳的的api调用次数达到一定数量进行降级(异步机制探测回复情况);
(3)调用的远程服务出现故障(dns、http服务错误状态码、网络故障、Rpc服务异常),直接进行降级。
八 Git、SVN 8.1 Git拉取和提交 拉取:pull -> recolve conflect
提交:【commit】 -> pull -> recolve conflect -> push
在提交项目之前必须先对项目进行更新,此项特别重要,如果不进行更新,别人有项目提交到服务器上,那么你的项目将会提交不上去,使用git解决冲突会比较麻烦,即使你解决了冲突,但是有时候不注意会冲掉别人写的代码,不像svn使用那么简单,所以提交自己项目前必须进行更新(特别重要);
8.2 merge 九 ElasticSearch、Solr 十 Zookeeper
  1. 我们可以使用临时节点来实现服务注册与发现。在某个服务注册到 ZooKeeper 时,我们可以让这个这个服务创建一个临时节点,并把它的访问信息交给到 Zookeeper 维护。当这个服务与 Zookeeper 断开连接时,这个临时节点就会被销毁,这时 Zookeeper 发现这个服务离线了,就会移除它的访问地址,避免出现 404 的情况。我们也可以手动删除某个临时节点让相对应的服务下线;
  2. 当我们有大量的服务时,一旦配置信息需要修改,会消耗我们大量的时间去每个服务中修改,而且还有可能会出错,这种情况我们就可以使用持久节点来保存全局的配置信息。当某个服务注册到 ZooKeeper 时,可以去保存配置信息的节点读取配置信息。当我们修改配置时,Zookeeper 会通知这些服务,服务就会重新去读取配置信息;
  3. 我们可以使用临时顺序节点来实现分布式锁。当多个服务同时对一个资源进行修改时,会出现数据错误,所以我们要避免这种情况。我们这里采取的方式就是为这个资源加锁,想要获取这个资源的服务会创建一个临时顺序节点,根据它的顺序判断这个临时顺序节点是否为第一个,如果是第一个则成功获得锁,这时这个服务就可以对这个资源进行修改了。完成操作后或者这个服务断线了都会删除这个节点,也就是释放了锁。后面的服务会根据它创建的临时顺序节点的顺序来依次对资源进行操作。
面试官可能问我的问题? 能接受出差吗? 能,短期出差可以接受,不能接受长期出差。
介绍一下你做的项目? 课程资源集成系统目的是为了辅助教学,实现课堂同步学习与课后异步学习的整合,在该平台中,可以上 传教学视频,发布考试和作业等等,学生可以选择课程进行学习、讨论评价,完成在线考试和作业等,增强师生互动,帮助提 高教学质量。
我想问面试官或者HR的问题? HR:
  • 加班有调休吗?或者是加班费?
  • 三方什么时候签订?假如要实习,一般实习多久可以转正?
  • 薪资多少,一年多少个薪?有年终奖吗?有餐补或者车补吗?
  • 带薪年假有多少天?
  • 该地对应届大学生的住房补贴有多少?
  • 一年调薪几次,每次可以调的空间?
技术面试官:
  • 我会被安排到什么部门?这个部门主要做些什么业务?用的什么技术?
  • 贵公司做一些什么产品?用的技术栈是什么?
  • 如果在做微服务,用的是springcould还是dubbo框架?
  • 集群部署中,用什么来实现负载均衡?
介绍一下你的项目? 前端使用的vue和element,后端用的是springboot+springsecurity,用nginx部署前端项目,实现动静分离,利用nginx反向代理来解决跨域,使用redis来存储token和用户的权限信息。
token续签方案:jwt token自动续期的实现原理如下:
  1. 登录成功后将用户生成的 jwt token 作为key、value存储到cache缓存里面 (这时候key、value值一样),将缓存有效期设置为 token有效时间的2倍(如果是最后几分钟操作再更新token的话时间可以设置为token+最后几分钟的时长)。
  2. 当该用户再次请求时,通过后端的一个 jwt Filter 校验前端token是否是有效token,如果前端token无效表明是非法请求,直接抛出异常即可;
  3. 根据规则取出cache token,判断cache token是否存在,此时主要分以下几种情况:
  • cache token 不存在
    这种情况表明该用户账户空闲超时,返回用户信息已失效,请重新登录。
  • cache token 存在,则需要使用jwt工具类验证该cache token 是否过期超时,不过期无需处理。
    过期则表示该用户一直在操作只是token失效了,后端程序会给token对应的key映射的value值重新生成jwt token并覆盖value值,该缓存生命周期重新计算。
**实现逻辑的核心原理:**前端请求Header中设置的token保持不变,校验有效性以缓存中的token为准。
携程面经 1 java GC(垃圾回收) 垃圾回收就是将已经分配出去,却不再使用的内存给回收回来。
https://blog.csdn.net/laomo_bible/article/details/83112622
判断对象是否可回收(是否存活)?
四类引用:https://www.sohu.com/a/217149551_812245,必看
一种古老的辨别方法: 可以通过引用计数法(reference counting),为每个对象添加一个引用计数器,用来统计指向该对象的引用个数。 一旦某个对象的引用计数器为0,则说明该对象已经死亡,便可以被回收了。
目前Java虚拟机的主流垃圾回收器采取的是可达性分析算法。
引用计数法:https://zhuanlan.zhihu.com/p/81673709 ,需要额外的空间来存储计数器,无法解决循环引用的问题,造成内存泄漏。
可达性分析:https://zhuanlan.zhihu.com/p/109262576
https://www.jianshu.com/p/3f031756f494
2 垃圾回收算法 https://www.cnblogs.com/sjxbg/p/9388615.html
3 分代回收机制 https://blog.csdn.net/xmj15715216140/article/details/80664630
优缺点:https://blog.csdn.net/strivenoend/article/details/82793457
优点:a.不需要考虑内存管理, b.可以有效的防止内存泄漏,有效的利用可使用的内存, c.由于有垃圾回收机制,Java中的对象不再有"作用域"的概念,只有对象的引用才有"作用域"
4 finalize方法 5 数据库隔离级别,悲观锁和乐观锁 详细:https://blog.csdn.net/woshiluoye9/article/details/68954515?utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&dist_request_id=1331978.995.16185773870665191&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control
https://blog.csdn.net/woshiyeguiren/article/details/80277475
数据库隔离级别和锁的关系:https://blog.csdn.net/yinni11/article/details/81238541
6 TCP握手原理 面试|面试总结(zhuan)
文章图片

https://blog.csdn.net/jun2016425/article/details/81506353
为什么要三次握手?https://www.pianshen.com/article/2872927776/
面试|面试总结(zhuan)
文章图片

为什么需要四次挥手?https://blog.csdn.net/caogenwangbaoqiang/article/details/79997787
7 http和https、http1.0&&http2.0的区别 http:超文本传输协议,是客户端和服务端请求和应答的标准。
https:超文本传输协议+安全套接字层SSL,可以认为是安全的http协议,可以用来传输一些比较敏感的信息,如密码,信用卡号等。
HTTPS和HTTP的区别主要如下:
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
http和https优缺点对比:
https://blog.csdn.net/weixin_46024145/article/details/112910291
http1.0和http2.0区别
https://blog.csdn.net/github_37130188/article/details/89442301
1.新的二进制格式(Binary Format)
HTTP1.x的解析是基于文本。(文本的表现形式有多样性,要考虑的场景很多才能做到健壮性)
基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮。
2.HTTP2.0比HTTP1.0有路复用(MultiPlexing):
即连接共享,即每一个request都是是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面。
3.header压缩:
HTTP1.x的header带有大量信息,而且每次都要重复发送,
HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。
4.服务端推送(server push):
HTTP2.0也具有server push功能。
8 TCP和UDP的区别 https://blog.csdn.net/weixin_39789689/article/details/82560805
9 http报文结构 https://www.jianshu.com/p/a2c4ede32d11?appinstall=0
10 linux常用命令 https://blog.csdn.net/qq_23329167/article/details/83856430/
hashMap讲解过程
构造方法:
HashMap()
HashMap(初始化容量,加载因子)
成员变量:初始化容量,加载因子,进化因子(数组和链表),退化因子,node数组,entryset成员变量,size
tableSizeFor
resize方法
treeifyBin
11 Tcp长连接和短连接 由上可以看出,长连接可以省去较多的TCP建立和关闭的操作,减少浪费,节约时间。对于频繁请求资源的客户来说,较适用长连接。不过这里存在一个问题,存活功能的探测周期太长,还有就是它只是探测TCP连接的存活,属于比较斯文的做法,遇到恶意的连接时,保活功能就不够使了。在长连接的应用场景下,client端一般不会主动关闭它们之间的连接,Client与server之间的连接如果一直不关闭的话,会存在一个问题,随着客户端连接越来越多,server早晚有扛不住的时候,这时候server端需要采取一些策略,如关闭一些长时间没有读写事件发生的连接,这样可 以避免一些恶意连接导致server端服务受损;如果条件再允许就可以以客户端机器为颗粒度,限制每个客户端的最大长连接数,这样可以完全避免某个蛋疼的客户端连累后端服务。
短连接对于服务器来说管理较为简单,存在的连接都是有用的连接,不需要额外的控制手段。但如果客户请求频繁,将在TCP的建立和关闭操作上浪费时间和带宽。
长连接和短连接的产生在于client和server采取的关闭策略,具体的应用场景采用具体的策略,没有十全十美的选择,只有合适的选择。
12 http报文在网络中的详细过程 【面试|面试总结(zhuan)】https://blog.csdn.net/u013074465/article/details/47280637/

    推荐阅读