《面试集:自用》《待完善... ...》

1. Java并发类:

1、ConcurrentHashMap

01、和HashMap功能基本一致,主要是为了解决HashMap线程不安全问题;

02、java7中的基本设计理念就是切分成多个Segment块, 默认是16个,也就是说并发度是16,可以初始化时显式指定,后期不能修改,每个Segment里面可以近似看成一个HashMap,每个Segment块都有自己独立的ReentrantLock锁,所以并发操作时每个Segment互不影响;

03、java8中将Segment块换成了Node,每个Node有自己的锁,即每个Node都有自己的并发度;

04、不允许空值和空键,否则会抛出异常;

那ConcurrentHashMap是如何保证数据操作的一致性呢?
对于数据元素的大小,ConcurrentHashMap将对应数组(HashEntry的长度)的变量为voliate类型的,也就是任何HashEntry发生变更,所有的地方都会知道数据的大小。对于元素,如何保证我取出的元素的next不发生变更呢?(HashEntry中的数据采用链表存储,当读取数据的时候可能又发生了变更),这一点,ConcurrentHashMap采取了最简单的做法,hash值、key和next取出后都为final类型的,其next等数据永远不会发生变更。

另外ConcurrentHashMap采用的锁结构是将读和写分开的,大大的提升了性能。

2.CopyOnWriteArrayList
CopyOnWriteArrayList这是一个ArrayList的线程安全的变体,其原理大概可以通俗的理解为:初始化的时候只有一个容器,很常一段时间,这个容器数据、数量等没有发生变化的时候,大家(多个线程),都是读取(假设这段时间里只发生读取的操作)同一个容器中的数据,所以这样大家读到的数据都是唯一、一致、安全的,但是后来有人往里面增加了一个数据,这个时候CopyOnWriteArrayList 底层实现添加的原理是先copy出一个容器(可以简称副本),再往新的容器里添加这个新的数据,最后把新的容器的引用地址赋值给了之前那个旧的的容器地址,但是在添加这个数据的期间,其他线程如果要去读取数据,仍然是读取到旧的容器里的数据。

优点:

1.解决的开发工作中的多线程的并发问题。

缺点:

1.内存占有问题:很明显,两个数组同时驻扎在内存中,如果实际应用中,数据比较多,而且比较大的情况下,占用内存会比较大,针对这个其实可以用ConcurrentHashMap来代替。

2.数据一致性:CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。所以如果你希望写入的的数据,马上能读到,请不要使用CopyOnWrite容器

2.Spring的IOC以及AOP.

IOC(DI):把对象的创建权限交给Spring容器,让spring帮我们实例化对象,我们只是从spring容器中取得实例。

AOP:把一些非核心业务的代码抽取到一个通知类(增强),再创建需要被增强的类的代理对象,在调用代理对象的方法时,织入增强代码,并调用目标方法的一种面向切面技术,一种对OOP进行补充的编程方式。

3.Spring的Bean的加载过程.

Spring通过资源加载器加载相应的XML文件,使用读取器读取资源加载器中的文件到读取器中,在读取过程中,解析相应的xml文件元素,转化为spring定义的数据结BeanDefinition,把相应的BeanDefinition注册到注册表中。注册表中包含的BeanDefinition的数据结构,没有经过加工处理过,无法得到我们想要的bean对象。
我们如何得到Bean对象,spring都做了那些工作?BeanFactory提供了多种方式得到bean对象,getBean()方法是最核心得到bean对象
getBean主要由AbstractBeanFactory、AbstractAutowireCapableBeanFactory、以及DefaultListableBeanFactory实现
AbstractBeanFactory 实现了依赖关系处理
AbstractAutowireCapableBeanFactory 实现了bean的create过程
DefaultListableBeanFactory 实现了BeanDefinition的管理

以下是getBean方法的实现流程。

getBean经过方法重载后,最终调用的是doGetBean方法,
需要的方法参数如下:
1.name 你要得到bean对象的名称 不能为空
2.requiredType 这个bean对象的Class类型,可以为null
3.args 可以为null,如果有参数,则代表在找到这个bean定义后,通过构造方法或工厂方法或其他方法传入args参数来改变这个bean实例。
spring 工厂开始自动化处理了.

4. Spring的循环依赖处理方式.

所谓Spring的循环依赖,指的是这样一种场景:

当我们注入一个对象A时,需要注入对象A中标记了某些注解的属性,这些属性也就是对象A的依赖,把对象A中的依赖都初始化完成,对象A才算是创建成功。那么,如果对象A中有个属性是对象B,而且对象B中有个属性是对象A,那么对象A和对象B就算是循环依赖,如果不加处理,就会出现:创建对象A–>处理A的依赖B–>创建对象B–>处理B的对象A–>创建对象A–>处理A的依赖B–>创建对象B…这样无限的循环下去。

这事显然不靠谱。

Spring处理循环依赖的基本思路是这样的:

虽说要初始化一个Bean,必须要注入Bean里的依赖,才算初始化成功,但并不要求此时依赖的依赖也都注入成功,只要依赖对象的构造方法执行完了,这个依赖对象就算存在了,注入就算成功了,至于依赖的依赖,以后再初始化也来得及(参考Java的内存模型)。

因此,我们初始化一个Bean时,先调用Bean的构造方法,这个对象就在内存中存在了(对象里面的依赖还没有被注入),然后把这个对象保存下来,当循环依赖产生时,直接拿到之前保存的对象,于是循环依赖就被终止了,依赖注入也就顺利完成了。

举个例子:

假设对象A中有属性是对象B,对象B中也有属性是对象A,即A和B循环依赖。

创建对象A,调用A的构造,并把A保存下来。
然后准备注入对象A中的依赖,发现对象A依赖对象B,那么开始创建对象B。
调用B的构造,并把B保存下来。
然后准备注入B的构造,发现B依赖对象A,对象A之前已经创建了,直接获取A并把A注入B(注意此时的对象A还没有完全注入成功,对象A中的对象B还没有注入),于是B创建成功。
把创建成功的B注入A,于是A也创建成功了。
于是循环依赖就被解决了。

不会出现循环依赖
单例,无参,set注入,动态代理

5.缓存穿透与缓存击穿问题

5.1缓存穿透(数据查询不到—>假数据、set集合放ID布隆过滤器过滤)
在这里插入图片描述
解决方案(防止mysql宕机)
在Redis中放入
1.假数据
2.set集合,里面放入所有mysql中的id,再通过布隆过滤器过滤,没有这个id的请求就不在mysql中找了

5.2、缓存击穿(热点数据到期—>访问一次加一次访问时间、加锁)

在这里插入图片描述
解决方案
1.从Redis处理:一个请求,给这个热点数据加一点时间(避免热点数据过期)
2.分布式锁:Tomcat集群synchronized-Tomcat分布式锁-Redis(避免大量数据访问数据库)

6、MySql存储引擎

mysql支持存储引擎有好几种,咱们这里主要讨论一下常用的几种存储引擎。Innodb,myisam

索引是 MySQL数据库很重要的一部分,它对数据表查询性能的好坏起着决定性的作用,尤其是对大表作用更加明显。 作为索引中最为常见的一种类型,B-Tree索引大都采用的是 B+Tree数据结构来存储数据(NDB集群存储引擎内部实际上采用 T-Tree结构存储这种索引)。B-Tree通常也意味着所有的值都是按顺序存储的。 大多数的 MySQL引擎都支持这种索引,而不同的存储引擎以不同的方式来实现 B-Tree索引,性能方面各有优劣。

1.INNODB索引实现

与 MyISAM相同的一点是,InnoDB 也采用 B+Tree这种数据结构来实现 B-Tree索引。而很大的区别在于,InnoDB 存储引擎采用“聚集索引”的数据存储方式实现B-Tree索引,所谓“聚集”,就是指数据行和相邻的键值紧凑地存储在一起,注意 InnoDB 只能聚集一个叶子页(16K)的记录(即聚集索引满足一定的范围的记录),因此包含相邻键值的记录可能会相距甚远。

在 InnoDB 中,表被称为 索引组织表(index organized table),InnoDB 按照主键构造一颗 B+Tree (如果没有主键,则会选择一个唯一的并且非空索引替代,如果没有这样的索引,InnoDB则会隐式地定义一个主键来作为聚集索引),同时叶子页中存放整张表的行记录数据,也可以将聚集索引的叶子节点称为数据页,非叶子页可以看做是叶子页的稀疏索引。

全表扫描:
当InnoDB做全表扫描时并不高效,因为 InnoDB 实际上并没有顺序读取,在大多情况下是在随机读取。做全表扫描时,InnoDB 会按主键顺序扫描页面和行。这应用于所有的InnoDB 表,包括碎片化的表。如果主键页表没有碎片(存储主键和行的页表),全表扫描是相当快,因为读取顺序接近物理存储顺序。但是当主键页有碎片时,该扫描就会变得十分缓慢

行级锁
提供行锁(locking on row level),提供与 Oracle 类型一致的不加锁读取(non-locking read in SELECTs),另外,InnoDB表的行锁也不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表,例如update table set num=1 where name like “%aaa%”

2.MyISAM索引的实现
每个MyISAM在磁盘上存储成三个文件。
树中叶子保存的是对应行的物理位置。通过该值,存储引擎能顺利地进行回表查询,得到一行完整记录。同时,每个叶子页也保存了指向下一个叶子页的指针。从而方便叶子节点的范围遍历。 而对于二级索引,在 MyISAM存储引擎中以与上图同样的方式实现,这也说明了 MyISAM的索引方式是“非聚集的”,与 Innodb的“聚集索引”形成了对比

MyISAM 默认会把索引读入内存,直接在内存中操作;

小结:Innodb强调多功能性,支持的拓展功能比较多,myisam主要侧重于性能

区别
InnoDB支持事务,MyISAM不支持,对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务;
InnoDB是聚集索引,数据文件是和索引绑在一起的,必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。而MyISAM是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
InnoDB不保存表的具体行数,执行select count(*) from table时需要全表扫描。而MyISAM用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快;
Innodb不支持全文索引,而MyISAM支持全文索引,查询效率上MyISAM要高;

如何选择
是否要支持事务,如果要请选择innodb,如果不需要可以考虑MyISAM;
如果表中绝大多数都只是读查询,可以考虑MyISAM,如果既有读写也挺频繁,请使用InnoDB。
系统奔溃后,MyISAM恢复起来更困难,能否接受;
MySQL5.5版本开始Innodb已经成为Mysql的默认引擎(之前是MyISAM),说明其优势是有目共睹的,如果你不知道用什么,那就用InnoDB,至少不会差。

7.Mybatis的一级缓存和二级缓存的区别?

在这里插入图片描述

Mybatis的一级缓存和二级缓存的区别

一级缓存(默认开启)
SqlSession级别的缓存,实现在同一个会话中数据的共享.
基于sqlSession默认开启,在操作数据库时需要构造SqlSession对象,在对象中有一个HashMap用于存储缓存数据。不同的SqlSession之间的缓存数据区域是互相不影响的。
二级缓存
SqlSessionFactory级别的缓存,实现不同会话中数据的共享,是一个全局变量。
不同的sqlSession两次执行相同的namespace下的sql语句,且向sql中传递的参数也相同,即最终执行相同的sql语句,则第一次执行完毕会将数据库中查询的数据写到缓存,第二次查询会从缓存中获取数据,不再去底层数据库查询,从而提高效率。

8.HashMap

Java集合之---------------HashMap
HashMap刚创建时,table是null,为了节省空间,当添加第一个元素时,table容量 调整为16,当元素个数大于阈值(16*0.75=12)时,会进行扩容,扩容后大小为原来的2倍。目的是 减少调整元素的个数,jdk1.8 当每个链表长度大于8,并且数组元素个数大于等于64时,会调整为红黑树,目 的提高执行效率,jdk1.8 当链表长度小于等于6时,调整成链表,jdk1.8以前,链表是头插入(容易在扩容的时候,形成循环链表),jdk1.8以后是尾插入(避免循环链表)

哈希冲突

如果两个不同的元素,通过哈希函数得出的实际存储地址相同怎么办?也就是说,当我们对某个元素进行哈希运算,得到一个存储地址,然后要进行插入的时候,发现已经被其他元素占用了,其实这就是所谓的哈希冲突,也叫哈希碰撞。前面我们提到过,哈希函数的设计至关重要,好的哈希函数会尽可能地保证 计算简单和散列地址分布均匀,但是,我们需要清楚的是,数组是一块连续的固定长度的内存空间,再好的哈希函数也不能保证得到的存储地址绝对不发生冲突。那么哈希冲突如何解决呢?哈希冲突的解决方案有多种:开放定址法(发生冲突,继续寻找下一块未被占用的存储地址),再散列函数法,链地址法,而HashMap即是采用了链地址法,也就是数组+链表的方式。
循环链表
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

HashMap的安全问题

jdk1.7及之前的HashMap

在HashMap扩容的是时候会调用resize()方法中的transfer()方法,在这里由于是头插法所以在多线程情况下可能出现循环链表,所以后面的数据定位到这条链表的时候会造成数据丢失。和读取的可能导致死循环。

jdk1.8HashMap

1.8的HashMap对此做了优化,resize采用了尾插法,即不改变原来链表的顺序,所以不会出现1.7的循环链表的问题。但是它也不是线程线程安全的。不安全性如下:

在多线程情况下put时计算出的插入的数组下标可能是相同的,这时可能出现值的覆盖从而导致size也是不准确的。

9. final , finally , finalize区别

final修饰成员变量,成员方法,类
finally修饰代码块
finalize是Object类中的一个方法 -> 复活币 × 1

10.static关键字修饰什么

内部类
方法
变量
代码块
导包

11.volatile关键字

1.被volatile修饰的变量保证对所有线程可见。
2.禁止指令重排优化

12.CAS乐观锁(比较和交换)

CAS介绍(compare and swap比较和交换):
CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。CAS是一种非阻塞式的同步方式。
CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。
CAS导致ABA问题:添加版本
线程1准备用CAS将变量的值由A替换为B,在此之前,线程2将变量的值由A替换为C,又由C替换为A,然后线程1执行CAS时发现变量的值仍然为A,所以CAS成功。但实际上这时的现场已经和最初不同了,尽管CAS成功,但可能存在潜藏的问题,如果CAS次数过多,会额外损耗CPU性能
解决ABA问题:增加版本号

13.GC的垃圾回收

1.JVM内存分代模型
年轻代和老年代
JVM将堆内存划分为了两个区域,即年轻代和老年代。年轻代主要存放的是创建和使用完即将被回收的对象,老年代存放的是一些长期被使用的对象。
2.确定是否回收

1、引用计数算法

判断对象的引用数量
   通过判断对象的引用数量来决定对象是否可以被回收;
   每个对象实例都有一个引用计数器,被引用则+1,完成引用则-1;
   任何引用计数为0的对象实例可以被当作垃圾收集。
优点:执行效率高,程序执行受影响较小。
缺点:无发检测出循环引用的情况,导致内存泄露。
2.可达性分析算法

有一系列名为GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径被称
为“引用链”。
如果一个对象到GC Roots没有任何引用链相连接时,说明这个对象是不可用的。如果一个对象
到GC Roots有引用链相连接时,说明这个对象是可用的。
也就是说,给定一个集合的引用作为根节点出发,通过引用关系遍历对象图,能够遍历到的对象
就被判定为存活,不能够被遍历到的对象说明对象死亡。

3.常用垃圾算法
在这里插入图片描述

1.复制算法(新生代回收算法)

复制算法主要运用在新生代中,把新生代内存划分为两块内存区域,然后只使用其中一块,待
那块内存快满的时候,就把里面的存活对象一次性转移到另外一块内存区域,然后回收原来那
块的垃圾对象。

整个垃圾回收的流程就是,刚开始创建的对象都是在Eden区域的,如果Eden区域快满了,就会
触发垃圾回收。Eden区把存活的对象都转移到空着的S1区域,接着Eden区就会被请客。然后再
次分配对象到Eden区中直到满了进行下一次垃圾回收,这时会将S1中存活的对象和Eden区存活
的对象转移到空的Survivor区中(S2) 。这就是为什么新生代会被划分为8:1:1的结构了,
这样对内存的利用率会大大提升。
2.标记清除法

标记-清除算法采用从根集合(GC Roots)进行扫描,对存活的对象进行标记,标记完毕后,
再扫描整个空间中未被标记的对象,进行回收,如下图所示。标记-清除算法不需要进行对
象的移动,只需对不存活的对象进行处理,在存活对象比较多的情况下极为高效,但由于标
记-清除算法直接回收不存活的对象,因此会造成内存碎片。
3.标记整理算法(老年代回收算法)

标记-整理算法采用标记-清除算法一样的方式进行对象的标记,但在清除时不同,在回收不存活
的对象占用的空间后,会将所有的存活对象往左端空闲空间移动,并更新对应的指针。标记-整
理算法是在标记-清除算法的基础上,又进行了对象的移动,因此成本更高,但是却解决了内存
碎片的问题。

可以作为GC Root的对象
虚拟机栈中引用的对象(栈帧中的本地变量)
方法区中的常量引用的对象
方法区中的类静态属性引用的对象
本地方法中JNI(native方法)的引用对象
活跃线程的引用对象

15.class文件是如何加载到JVM内存的

在这里插入图片描述
各个加载器的工作责任:

1)Bootstrap ClassLoader:负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类

2)Extension ClassLoader:负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包

3)App ClassLoader:负责记载classpath中指定的jar包及目录中class

工作过程:


1、当AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载
请求委派给父类加载器ExtClassLoader去完成。

2、当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加
载请求委派给BootStrapClassLoader去完成。

3、如果BootStrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class)
,会使用ExtClassLoader来尝试加载;

4、若ExtClassLoader也加载失败,则会使用AppClassLoader来加载

5、如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException

这就是所谓的双亲委派模型。简单来说:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上。

好处:防止内存中出现多份同样的字节码(安全性角度)

特别说明:类加载器在成功加载某个类之后,会把得到的 java.lang.Class类的实例缓存起来。下次再请求加载该类的时候,类加载器会直接使用缓存的类的实例,而不会尝试再次加载。

类加载详细过程

加载,查找并加载类的二进制数据,在Java堆中也创建一个java.lang.Class类的对象。
连接,连接又包含三块内容:验证、准备、解析。
1)验证,文件格式、元数据、字节码、符号引用验证;
2)准备,为类的静态变量分配内存,并将其初始化为默认值;
3)解析,把类中的符号引用转换为直接引用
初始化,为类的静态变量赋予正确的初始值。

16.JVM内存模型

在这里插入图片描述

1.程序计数器(PC寄存器):

程序计数器是一块较小的内存空间,是当前线程正在执行的哪一条字节码指令的地址,若当前
线程正在执行的是一个本地方法
2.Java虚拟机栈(待完善)

描述Java方法运行过程的内存模型,保存局部变量、基本数据类型以及堆内存中对象的引用变量
随着线程创建而创建,随着线程的结束而销毁
3.本地方法栈

为JVM提供使用native方法的服务,本地方法栈描述本地方法运行过程的内存模型
也会抛出StackOverFlowError和OutOfMemoryError异常
4.堆

线程共享、垃圾回收的主要场地,在虚拟机启动的时候就被创建
堆这块区域是JVM中最大的,堆内存的大小是可以调节的
5.方法区

线程共享、 存储的是类信息+普通常量+静态常量+编译器编译后的代码等,
常量池(Constant Pool)是方法区的一部分

16.1虚拟机栈如何执行方法的

在这里插入图片描述

16.2方法区在JDK各个版本的更新

在这里插入图片描述

17.SQL语句的优化

sql本身优化,避免全盘扫描

18.聚簇索引和非聚簇索引

主键索引:
其他索引:

19.索引的常见结构

hash:数据量不大,效率远高于BTree

BTree,B+Tree:数据量大,更加稳定
树高问题,叶子节点指针

20.ES保存数据后,为什么不能立即查看到

我们经常有这样的需求,在对 Elasticsearch 数据进行操作的时候,要及时返回刚刚操作完毕的数据,或者数据列表。

比如加入存储一条数据后,我马上要返回数据的总条数,这个时候,会出问题,Elasticsearch会返回操作之前的数据,也就是假如开始有500条数据,我Insert了一条进去,按道理来说应该是501条,但是这个时候查询会发现,只有500条数据,再次请求又得到501条数据。

BulkRequestBuilder bulkRequest = ESTools.client.prepareBulk().setRefresh(true);

这里的setRefresh(true);

就是自动刷新的用处。所以在我们CRUD的时候,如果对数据增删改操作的时候,如果要及时返回最新数据,那么我们就需要加这个方法,及时刷新数据。

21.SpringMVC运行流程

在这里插入图片描述

22.SpringBoot自动装配

@SpringBootApplication中有一个@import的注解,这个注解里面加载一个
在这里插入图片描述
里面有一个selectImports方法,将所有需要导入的组件以全类名的方式返回;这些组件就会被添加到容器中。
在这里插入图片描述
会从META-INF/spring.factories中获取资源,然后通过Properties加载资源:
在这里插入图片描述
Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作。以前我们需要自己配置的东西,自动配置类都帮我们完成了。

23.Redis集群架构

(1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽;

(2)节点的Fail是通过集群中超过半数的节点检测失效时才生效;

(3)客户端与redis节点直连,不需要中间proxy层。客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可;

(4)redis-cluster把所有的物理节点映射到[0-16383]slot(插槽)上,cluster 负责维护node<->slot<->value。

24.懒汉式和饿汉式

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

25.springBoot的自动装载实现原理

注解加载XXXselector.class加载springXX文件

26.SpringBoot能打成war包吗?

27.Redis双写一致性

删除两次缓存

28.单例和多例的区别?谁的高效,谁的安全

单例:类的对象在整个程序中只有一个
多例:类的对象会存在多个

懒汉
饿汉

29.redis的数据类型?应用场景?redis是多线程还是单线程?

string,list,hash,set,zset

string:—

redis接收客户端请求命令时是单线程处理,
内部命令,持久化等,都是多线程

30.maven的常用命令

package
clean
install
test
compile

31.post和get处理乱码,他们处理方式区别

区别?
post将参数放到请求体,get放到地址栏

用户禁用cookie怎么办?
参数,URL重写,请求头

32.Mybatis中#{}和${}区别

#{}是占位符:没有sql注入问题
${}是拼接符:有sql注入问题

33.Mybatis的select标签中,有一个statement属性,statement,prepared,callable

34.说一下事务?分布式事务

事务四大特性?A.原子性 C.一致性 I.隔离性 D.持久性

事务并发产生的问题?
脏读:读了别人修改的事务
不可重复读:多次查询结果不同,别人修改了数据
幻读:多次查询结果不同,第一次查询不在,第二次出现

事务的隔离级别(默认不可重复读)
在这里插入图片描述
分布式事务?
不同tomcat的commition是不同的,2PC,3PC,TCC,TXC,LCN(TXLCN框架使用的方式),Seata

35.搜索出来怎么排序的?

使用ES默认的基于关键字匹配分数进行排序

36.怎么实现抢购的?

37.用过那些琐,什么场景下的?

38.sql语句

避免模糊查询解决:建立冗余字段,倒叙%查询
左外连接那个建立索引方便一些:

39.动态代理

JDK动态代理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
CGlib动态代理:利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

区别:JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理final修饰的类。

总结:1.JDK代理使用的是反射机制实现aop的动态代理,CGLIB代理使用字节码处理框架asm,通过修改字节码生成子类。所以jdk动态代理的方式创建代理对象效率较高,执行效率较低,cglib创建效率较低,执行效率高;2.JDK动态代理机制是委托机制,具体说动态实现接口类,在动态生成的实现类里面委托hanlder去调用原始实现类方法,CGLIB则使用的继承机制,具体说被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,如果被代理类有接口,那么代理类也可以赋值给接口。

40.AOP哪里用到

1.日志处理
2.用户登录
3.权限(Authentication )
4.性能优化(Performance optimization)
5.事务(Transactions )
6.记录跟踪 优化 校准(logging, tracing, profiling and monitoring)
7.调试(Debugging)
8.懒加载(Lazy loading)
9.错误处理(Error handling)
10.资源池(Resource pooling)
11.同步(Synchronization)

41.线程死锁

42.springBoot和spring区别

43.Threadlocal 构建内存

44.线程死锁排查

controller前监控所有线程,结束监控所有,定时排查线程运行时间

45.分布式事务处理模式

  1. LCN事务模式
    一、原理介绍:
    LCN模式是通过代理Connection的方式实现对本地事务的操作,然后在由TxManager统一协调控制事务。当本地事务提交回滚或者关闭连接时将会执行假操作,该代理的连接将由LCN连接池管理。
    二、模式特点:
    该模式对代码的嵌入性为低。
    该模式仅限于本地存在连接对象且可通过连接对象控制事务的模块。
    该模式下的事务提交与回滚是由本地事务方控制,对于数据一致性上有较高的保障。
    该模式缺陷在于代理的连接需要随事务发起方一共释放连接,增加了连接占用的时间。

  2. TCC事务模式
    一、原理介绍:
    TCC事务机制相对于传统事务机制(X/Open XA Two-Phase-Commit),其特征在于它不依赖资源管理器(RM)对XA的支持,而是通过对(由业务系统提供的)业务逻辑的调度来实现分布式事务。主要由三步操作,Try: 尝试执行业务、 Confirm:确认执行业务、 Cancel: 取消执行业务。
    二、模式特点:
    该模式对代码的嵌入性高,要求每个业务需要写三种步骤的操作。
    该模式对有无本地事务控制都可以支持使用面广。
    数据一致性控制几乎完全由开发者控制,对业务开发难度要求高。

  3. TXC事务模式
    一、原理介绍:
    TXC模式命名来源于淘宝,实现原理是在执行SQL之前,先查询SQL的影响数据,然后保存执行的SQL快走信息和创建锁。当需要回滚的时候就采用这些记录数据回滚数据库,目前锁实现依赖redis分布式锁控制。
    二、模式特点:
    该模式同样对代码的嵌入性低。
    该模式仅限于对支持SQL方式的模块支持。
    该模式由于每次执行SQL之前需要先查询影响数据,因此相比LCN模式消耗资源与时间要多。
    该模式不会占用数据库的连接资源。

46.分布式锁

分布式锁实现的三种方式
基于数据库实现分布式锁
基于缓存redis实现分布式锁
基于Zookeeper实现分布式锁
基于数据库实现分布式锁
基于数据库的实现方式核心思想:在数据库中创建一个表,表中包含方法名等字段,并在方法名字段上创建唯一索引,想要执行某个方法,就使用这个方法名向表中插入数据,成功插入则获取锁,执行完成后删除对应的行数据释放锁。
创建一个表:
DROP TABLE IF EXISTS method_lock; CREATE TABLE method_lock ( id int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT ‘主键’, method_name varchar(64) NOT NULL COMMENT ‘锁定的方法名’, desc varchar(255) NOT NULL COMMENT ‘备注信息’, update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uidx_method_name (method_name) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT=‘锁定中的方法’;
想要执行某个方法,就使用这个方法名向表中插入数据:
INSERT INTO method_lock (method_name, desc) VALUES (‘methodName’, ‘测试的methodName’);
因为我们对method_name做了唯一约束,这里如果有多个请求同时提交到数据库的话,数据库会保证只有一个操作可以成功,那么我们就可以认为操作成功的那个线程获得了该方法的锁,可以执行方法体内容。
成功插入则获取锁,执行完成后删除对应的行数据释放锁:
delete from method_lock where method_name =‘methodName’;
使用基于数据库的这种实现方式很简单,但是对于分布式锁应该具备的条件来说,它有一些问题需要解决及优化:
(1)因为是基于数据库实现的,数据库的可用性和性能将直接影响分布式锁的可用性及性能,所以,数据库需要双机部署、数据同步、主备切换。 (2)不具备可重入的特性,因为同一个线程在释放锁之前,行数据一直存在,无法再次成功插入数据,所以,需要在表中新增一列,用于记录当前获取到锁的机器和线程信息,在再次获取锁的时候,先查询表中机器和线程信息是否和当前机器和线程信息相同,若相同则直接获取锁; (3)没有锁失效机制,因为有可能出现成功插入数据后,服务器宕机了,对应的数据没有被删除,当服务恢复后一直获取不到锁,所以,需要在锁中新增一列,用于记录失效时间,并且需要有定时任务清除这些失效的数据; (4)不具备阻塞锁特性,获取不到锁直接返回失败,所以需要优化获取逻辑,循环多次去获取。
在实施过程中会遇到各种不同问题,为了解决这些问题,实现方式将会越来越复杂;依赖数据库需要一定的资源开销,性能问题需要考虑。
基于redis的实现方式
选择redis分布式锁的原因:
(1)redis有很高的性能; (2)redis对此支持的命令较好,实现起来比较方便
使用分布式锁的时候主要用到的命令介绍:
(1)SETNX SETNX key val:当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做,返回0。 (2)expire expire key timeout:当key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁。 (3)delete delete key:删除key
实现思想:
(1)获取锁的时候,使用setnx加锁,并使用expire命令给锁加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。 (2)获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。 (3)释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。
基于ZooKeeper的实现方式
ZooKeeper是一个为分布式应用提供一致性服务的开源组件,它内部是一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一文件名。
基于ZooKeeper实现分布式锁的步骤如下:
(1)创建一个目录mylock; (2)线程A想获取锁就在mylock目录下创建临时顺序节点; (3)获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁; (4)线程B获取所有节点,判断自己不是最小节点,设置监听比自己小的节点; (5)线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小节点,如果是则获得锁。
这里推荐一个Apache的开源库Curator,它是一个ZooKeeper客户端,Curator提供的InterProcessMutex是分布式锁的实现,acquire方法用于获取锁,release用于释放锁。
优点:具备高可用、可重入、阻塞锁特性,可解决失效死锁问题。
缺点:因为需要频繁的创建和删除节点,性能上不如redis方式。

47.事务的传播方式

事务的传播方式
在这里插入图片描述

48.主流的权限模型:RBAC权限模型

在这里插入图片描述
在这里插入图片描述

49.单例的时候,使用反射,变成多线程,那么单例还有什么意义

设计模式,减少沟通

50.ArrayList中的add和addAll有什么区别?

add是将传入的参数作为当前List中的一个Item存储,即使你传入一个List也只会另当前的List增加1个元素,而addAll是传入一个List,将此List中的所有元素加入到当前List中,也就是当前List会增加的元素个数为传入的List的大小

51.char和varchar有什么区别?

char是固定长度,varchar是可变长度,char快一些

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值