总结(一)
一、Java相关
Arraylist与LinkedList默认空间是多少;
1.ArrayList
ArrayList 初始化大小是 10 (如果你知道你的arrayList 会达到多少容量,可以在初始化的时候就指定,能节省扩容的性能开支)
扩容点规则是,新增的时候发现容量不够用了,就去扩容
扩容大小规则是,扩容后的大小= 原始大小+原始大小/2 + 1。(例如:原始大小是 10 ,扩容后的大小就是 10 + 5+1 = 16)
2.linkedList
linkedList 是一个双向链表,没有初始化大小,也没有扩容的机制,就是一直在前面或者后面新增就好。 对于双向链表的理解
3.HashMap
HashMap 初始化大小是 16 ,扩容因子默认0.75(可以指定初始化大小,和扩容因子)
扩容机制.(当前大小和当前容量的比例超过了扩容因子,就会扩容,扩容后大小为一倍。例如:初始大小为 16 ,扩容因子 0.75 ,当容量为12的时候,比例已经是0.75 。触发扩容,扩容后的大小为 32.)
· Arraylist与LinkedList区别与各自的优势List 和 Map 区别;
· 谈谈HashMap,哈希表解决hash冲突的方法;
· 1. 开放寻址法:Hi=(H(key) + di) MODm,i=1,2,…,k(k<=m-1),其中H(key)为散列函数,m为散列表长,di为增量序列,可有下列三种取法:
1.1. di=1,2,3,…,m-1,称线性探测再散列;顺序查看表的下一单元,直至找到某个空单元,或查遍全表。
1.2. di=1^2,-1^2,2^2,-2^2,⑶^2,…,±(k)^2,(k<=m/2)称二次探测再散列;在表的左右进行跳跃式探测。
1.3. di=伪随机数序列,称伪随机探测再散列。根据产生的随机数进行探测。
· 2 再散列法:建立多个hash函数,若是当发生hash冲突的时候,使用下一个hash函数,直到找到可以存放元素的位置。
· 3 拉链法(链地址法):就是在冲突的位置上简历一个链表,然后将冲突的元素插入到链表尾端,
· 4 建立公共溢出区:将哈希表分为基本表和溢出表,将与基本表发生冲突的元素放入溢出表中。
· 底层的hashMap 是由数组和链表来实现的,就是上面说的拉链法。首先当插入的时候,会根据key 的hash 值然后计算出相应的数组下标,计算方法是index = hashcode%table。长度,(这个下标就是上面提到的桶),当这个下标上面已经存在元素的时候那么就会形成链表,将后插入的元素放到尾端,若是下标上面没有存在元素的话,那么将直接将元素放到这个位置上。当进行查询的时候,同样会根据键的哈希值先计算相应的下标,然后到相应的位置上进行查找,若是这个下标上面有很多元素的话,那么将在这个链表上一直查找直到找到对应的元素。
·
为什么要重写哈希码()和equals()方法以及他们之间的区别与关系;
首先等于与哈希码间的关系是这样的:
1、如果两个对象相同(即用equals比较返回true),那么它们的hashCode值一定要相同;
2、如果两个对象的hashCode相同,它们并不一定相同(即用equals比较返回false)
由于为了提高程序的效率才实现了hashcode方法,先进行hashcode的比较,如果不同,那没就不必在进行equals的比较了,这样就大大减少了equals比较的次数,这对比需要比较的数量很大的效率提高是很明显的,一个很好的例子就是在集合中的使用;
我们都知道java中的List集合是有序的,因此是可以重复的,而set集合是无序的,因此是不能重复的,那么怎么能保证不能被放入重复的元素呢,但靠equals方法一样比较的话,如果原来集合中以后又10000个元素了,那么放入10001个元素,难道要将前面的所有元素都进行比较,看看是否有重复,欧码噶的,这个效率可想而知,因此hashcode就应遇而生了,java就采用了hash表,利用哈希算法(也叫散列算法),就是将对象数据根据该对象的特征使用特定的算法将其定义到一个地址上,那么在后面定义进来的数据只要看对应的hashcode地址上是否有值,那么就用equals比较,如果没有则直接插入,只要就大大减少了equals的使用次数,执行效率就大大提高了。
继续上面的话题,为什么必须要重写hashcode方法,其实简单的说就是为了保证同一个对象,保证在equals相同的情况下hashcode值必定相同,如果重写了equals而未重写hashcode方法,可能就会出现两个没有关系的对象equals相同的(因为equal都是根据对象的特征进行重写的),但hashcode确实不相同的。
·
· Object的hashcode()是怎么计算的?
· 若hashcode方法永远返回1或者一个常量会产生什么结果?
· JavaCollections和Arrays的sort方法默认的排序方法是什么;
引用计数法与GC Root可达性分析法区别;
引用计数法
引用计数法的逻辑非常简单,但是存在问题,java并不采用这种方式进行对象存活判断。
引用计数法的逻辑是:在堆中存储对象时,在对象头处维护一个counter计数器,如果一个对象增加了一个引用与之相连,则将counter++。如果一个引用关系失效则counter–。如果一个对象的counter变为0,则说明该对象已经被废弃,不处于存活状态。
这种方法来标记对象的状态会存在很多问题:
1 jdk从1.2开始增加了多种引用方式:软引用、弱引用、虚引用,且在不同引用情况下程序应进行不同的操作。如果我们只采用一个引用计数法来计数无法准确的区分这么多种引用的情况。
引用计数法无法解决多种类型引用的问题。但这并不是致命的,因为我们可以通过增加逻辑区分四种引用情况,虽然麻烦一些但还算是引用计数法的变体,真正让引用计数法彻底报废的下面的情况。
2 如果一个对象A持有对象B,而对象B也持有一个对象A,那发生了类似操作系统中死锁的循环持有,这种情况下A与B的counter恒大于1,会使得GC永远无法回收这两个对象。
可达性分析算法
在主流的商用程序语言中(Java和C#),都是使用可达性分析算法判断对象是否存活的。这个算法的基本思路就是通过一系列名为GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,下图对象object5,object6, object7虽然有互相判断,但它们到GC Roots是不可达的,所以它们将会判定为是可回收对象。
那么那些点可以作为GC Roots呢?一般来说,如下情况的对象可以作为GC Roots:
1. 虚拟机栈(栈桢中的本地变量表)中的引用的对象
2. 方法区中的类静态属性引用的对象
3. 方法区中的常量引用的对象
4. 本地方法栈中JNI(Native方法)的引用的对象
·
浅拷贝和深拷贝的区别;
浅拷贝---能复制变量,如果对象内还有对象,则只能复制对象的地址
深拷贝---能复制变量,也能复制当前对象的 内部对象
·
String s="abc"和String s=new String("abc")区别; Java运行环境有一个字符串池,由String类维护。执行语句String s="abc"时,首先查看字符串池中是否存在字符串"abc",如果存在则直接将"abc"赋给s,如果不存在则先在字符串池中新建一个字符串 "abc",然后再将其赋给s。 执行语句String s=new String("abc")时,不管字符串池中是否存在字 符串"abc",直接新建一个字符串"abc"(注意:新建的字符串"abc"不是在字符串池中),然后将其付给s。 前一语句的效率高,后一语句的效率低,因为新建字符串占用内存空间。
·
· HashSet方法里面的hashcode存在哪,如果重写equals不重写hashcode会怎么样?
其实简单的说就是为了保证同一个对象,保证在equals相同的情况下hashcode值必定相同,如果重写了equals而未重写hashcode方法,可能就会出现两个没有关系的对象equals相同的(因为equal都是根据对象的特征进行重写的),但hashcode确实不相同的。
· 反射的作用与实现原理;(https://blog.csdn.net/xiaoxian8023/article/details/9154227)
· Java中的回调机制;
· 模板方法模式;
一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现;
· 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复;
· 控制子类的扩展。
·
· 开闭原则说一下;
软件实现应该对扩展开放,对修改关闭,其含义是说一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化的
发布/订阅使用场景;
Redis发布订阅与ActiveMQ的比较
1. (1)ActiveMQ支持多种消息协议,包括AMQP,MQTT,Stomp等,并且支持JMS规范,但Redis没有提供对这些协议的支持;
2. (2)ActiveMQ提供持久化功能,但Redis无法对消息持久化存储,一旦消息被发送,如果没有订阅者接收,那么消息就会丢失;
3. (3)ActiveMQ提供了消息传输保障,当客户端连接超时或事务回滚等情况发生时,消息会被重新发送给客户端,Redis没有提供消息传输保障。
4.
5. 总之,ActiveMQ所提供的功能远比Redis发布订阅要复杂,毕竟Redis不是专门做发布订阅的,
6. 但是如果系统中已经有了Redis,并且需要基本的发布订阅功能,就没有必要再安装ActiveMQ了,
7. 因为可能ActiveMQ提供的功能大部分都用不到,而Redis的发布订阅机制就能满足需求。
·
· KMP算法(一种改进的字符串匹配算法);
· JMM里边的原子性、可见性、有序性是如何体现出来的,JMM中内存屏障是什么意思,
二、多线程
· AtomicInteger底层实现原理;
CAS机制它的作用是将指定内存地址的内容与所给的某个值相比,如果相等,则将其内容替换为指令中提供的新值,如果不相等,则更新失败
· CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则返回V
· synchronized与ReentraLock哪个是公平锁;
·
CAS机制会出现什么问题;
虽然使用CAS可以实现非阻塞式的原子性操作,但是会产生ABA问题,关于ABA问题:
有ABA问题(即在更新前的值是A,但在操作过程中被其他线程更新为B,又更新为 A),这时当前线程认为是可以执行的,其实是发生了不一致现象,如果这种不一致对程序有影响(真正有这种影响的场景很少,除非是在变量操作过程中以此变量为标识位做一些其他的事,比如初始化配置),则需要使用AtomicStampedReference(除了对更新前的原值进行比较,也需要用更新前的 stamp标志位来进行比较)。
·
· 用过并发包下边的哪些类;
CyclicBarrier、CountDownLatch、ConcurrentHashMap、Lock、Atomic包
· 一个线程连着调用start两次会出现什么情况?
java.lang.IllegalThreadStateException 线程状态非法异常
· wait方法能不能被重写,wait能不能被中断;
· 线程池的实现?四种线程池?重要参数及原理?任务拒接策略有哪几种?
· corePoolSize:线程池维护线程的最少数量
· maximumPoolSize:线程池维护线程的最大数量
· keepAliveTime:线程池维护线程所允许的空闲时间
· unit:线程池维护线程所允许的空闲时间的单位
· workQueue:线程池所使用的缓冲队列
· handler:线程池对拒绝任务的处理策略
一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是 Runnable类型对象的run()方法。
当一个任务通过execute(Runnable)方法欲添加到线程池时:
1. 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
2. 如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
3. 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
4. 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
5. 当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
workQueue常用的是:java.util.concurrent.ArrayBlockingQueue
handler有四个选择:
· ThreadPoolExecutor.AbortPolicy() --- 抛出java.util.concurrent.RejectedExecutionException异常
· ThreadPoolExecutor.CallerRunsPolicy() --- 重试添加当前的任务,他会自动重复调用execute()方法
· ThreadPoolExecutor.DiscardOldestPolicy() --- 抛弃旧的任务
· ThreadPoolExecutor.DiscardPolicy() --- 抛弃当前的任务
·
常用的避免死锁方法;
1、避免一个线程同时获取多个锁
2、避免一个线程同时占用多个资源,尽量保证每个锁只占用一个资源
3、尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制
4、对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况
三、JVM
· MinorGC与Full GC分别在什么时候发生?什么时候触发Full GC;
· GC收集器有哪些?CMS收集器与G1收集器的特点。
https://blog.csdn.net/linhu007/article/details/48897597
Java在什么时候会出现内存泄漏;
静态集合类
在使用Set、Vector、HashMap等集合类的时候需要特别注意,有可能会发生内存泄漏。当这些集合被定义成静态的时候,由于它们的生命周期跟应用程序一样长,这时候,就有可能会发生内存泄漏,看下面代码:
classStaticTest
{
privatestatic
Vector v =
newVector(
10);
publicvoidinit()
{
for
(
inti =
1; i <
100; i++)
{
Object
object=
newObject();
v.add(
object);
object
=
null;
}
}
}
在上面的代码中,循环申请了Object
对象,并添加到Vector
中,然后将对象设置为null
,可是这些对象因为被Vector
引用着,因此并不能被GC回收,因此造成了内存泄漏。因此,要释放这些对象,还需要被它们从Vector
删除,最简单的方法就是将Vector
设置为null
。
集合里的对象属性值被改变
看以下代码:
publicstaticvoidmain(String[] args)
{
Set<Student>
set=
newHashSet<Student>();
Student s1 =
newStudent(
"Jack");
Student s2 =
newStudent(
"Mary");
Student s3 =
newStudent(
"Eason");
set
.add(s1);
set
.add(s2);
set
.add(s3);
System.
out.println(
set.size());
//3
s2.setName(
"Jackson");
//修改属性,此时s2元素对应的hashcode值发生改变
set
.remove(s2);
// remove不掉,造成内存泄漏
set
.add(s2);
// 添加成功
System.
out.println(
set.size());
//4
}
在这个例子中,由于对象s2
的属性值被改变了,因此不能从set
中删除,所以set
中会一直保持着s2
的引用,不能被回收,造成了内存泄漏。
监听器
在Java中,我们经常会使用到监听器,如对某个控件添加单击监听器addOnClickListener()
,但往往释放对象的时候会忘记删除监听器,这就有可能造成内存泄漏。好的方法就是,在释放对象的时候,应该记住释放所有监听器,这就能避免了因为监听器而导致的内存泄漏。
各种连接
Java中的连接包括数据库连接、网络连接和io
连接,如果没有显式调用其close()
方法,是不会自动关闭的,这些连接就不能被GC回收而导致内存泄漏。一般情况下,在try
代码块里创建连接,在finally
里释放连接,就能够避免此类内存泄漏。
外部模块的引用
调用外部模块的时候,也应该注意防止内存泄漏。如模块A调用了外部模块B的一个方法,如:public void register(Object o)
这个方法有可能就使得A模块持有传入对象的引用,这时候需要查看B模块是否提供了去除引用的方法,如unregister()
。这种情况容易忽略,而且发生了内存泄漏的话,比较难察觉,应该在编写代码过程中就应该注意此类问题。
单例模式
使用单例模式的时候也有可能导致内存泄漏。因为单例对象初始化后将在JVM的整个生命周期内存在,如果它持有一个外部对象(生命周期比较短)的引用,那么这个外部对象就不能被回收,而导致内存泄漏。如果这个外部对象还持有其它对象的引用,那么内存泄漏会更严重,因此需要特别注意此类情况。这种情况就需要考虑下单例模式的设计会不会有问题,应该怎样保证不会产生内存泄漏问题。
·
· Java中的大对象如何进行存储;
rt.jar被什么类加载器加载,什么时间加载; 当一个类加载器接收到一个类加载的任务时,不会立即展开加载,而是将加载任务委托给它的父类加载器去执行,每一层的类都采用相同的方式,直至委托给最顶层的启动类加载器为止。如果父类加载器无法加载委托给它的类,便将类的加载任务退回给下一级类加载器去执行加载。
双亲委托模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委托给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需要加载的类)时,子加载器才会尝试自己去加载。
使用双亲委托机制的好处是:能够有效确保一个类的全局唯一性,当程序中出现多个限定名相同的类时,类加载器在执行加载时,始终只会加载其中的某一个类。
使用双亲委托模型来组织类加载器之间的关系,有一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存放在rt.jar之中,无论哪一个类加载器要加载这个类,最终都是委托给处于模型最顶端的启动类加载器进行加载,因此Object类在程序的各种加载器环境中都是同一个类。相反,如果没有使用双亲委托模型,由各个类加载器自行去加载的话,如果用户自己编写了一个称为java.lang.Object的类,并放在程序的ClassPath中,那系统中将会出现多个不同的Object类,Java类型体系中最基础的行为也就无法保证,应用程序也将会变得一片混乱。如果自己去编写一个与rt.jar类库中已有类重名的Java类,将会发现可以正常编译,但永远无法被加载运行。
双亲委托模型对于保证Java程序的稳定运作很重要,但它的实现却非常简单,实现双亲委托的代码都集中在java.lang.ClassLoader的loadClass()方法中,逻辑清晰易懂:先检查是否已经被加载过,若没有加载则调用父类加载器的loadClass()方法,若父加载器为空则默认使用启动类加载器作为父加载器。如果父类加载器加载失败,抛出ClassNotFoundException异常后,再调用自己的findClass方法进行加载。
·
· 自己写的类被什么加载,什么时间加载;
· 自己写的两个不同的类是被同一个类加载器加载的吗?为什么?
· 为什么新生代内存需要有两个Survivor区?
· 几种常用的内存调试工具:jmap、jstack、jconsole;
· 类加载的五个过程:加载、验证、准备、解析、初始化;
· G1停顿吗,CMS回收步骤,CMS为什么会停顿,停顿时间;
1、CMS收集器
CMS收集器是一种以获取最短回收停顿时间为目标的收集器。基于“标记-清除”算法实现,它的运作过程如下:
1)初始标记
2)并发标记
3)重新标记
4)并发清除
初始标记、从新标记这两个步骤仍然需要“stop the world”,初始标记仅仅只是标记一下GC Roots能直接关联到的对象,熟读很快,并发标记阶段就是进行GC Roots Tracing,而重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生表动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长点,但远比并发标记的时间短。
CMS是一款优秀的收集器,主要优点:并发收集、低停顿。
缺点:
1)CMS收集器对CPU资源非常敏感。在并发阶段,它虽然不会导致用户线程停顿,但是会因为占用了一部分线程而导致应用程序变慢,总吞吐量会降低。
2)CMS收集器无法处理浮动垃圾,可能会出现“Concurrent Mode Failure(并发模式故障)”失败而导致Full GC产生。
浮动垃圾:由于CMS并发清理阶段用户线程还在运行着,伴随着程序运行自然就会有新的垃圾不断产生,这部分垃圾出现的标记过程之后,CMS无法在当次收集中处理掉它们,只好留待下一次GC中再清理。这些垃圾就是“浮动垃圾”。
3)CMS是一款“标记--清除”算法实现的收集器,容易出现大量空间碎片。当空间碎片过多,将会给大对象分配带来很大的麻烦,往往会出现老年代还有很大空间剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前触发一次Full GC。
2、G1收集器
G1是一款面向服务端应用的垃圾收集器。G1具备如下特点:
1、并行于并发:G1能充分利用CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短stop-The-World停顿时间。部分其他收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让java程序继续执行。
2、分代收集:虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但是还是保留了分代的概念。它能够采用不同的方式去处理新创建的对象和已经存活了一段时间,熬过多次GC的旧对象以获取更好的收集效果。
3、空间整合:与CMS的“标记--清理”算法不同,G1从整体来看是基于“标记整理”算法实现的收集器;从局部上来看是基于“复制”算法实现的。
4、可预测的停顿:这是G1相对于CMS的另一个大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,
5、G1运作步骤:
1、初始标记;2、并发标记;3、最终标记;4、筛选回收
上面几个步骤的运作过程和CMS有很多相似之处。初始标记阶段仅仅只是标记一下GC Roots能直接关联到的对象,并且修改TAMS的值,让下一个阶段用户程序并发运行时,能在正确可用的Region中创建新对象,这一阶段需要停顿线程,但是耗时很短,并发标记阶段是从GC Root开始对堆中对象进行可达性分析,找出存活的对象,这阶段时耗时较长,但可与用户程序并发执行。而最终标记阶段则是为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程Remenbered Set Logs里面,最终标记阶段需要把Remembered Set Logs的数据合并到Remembered Set Logs里面,最终标记阶段需要把Remembered Set Logs的数据合并到Remembered Set中,这一阶段需要停顿线程,但是可并行执行。最后在筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划。
·
· 栈主要存的数据是什么,堆呢?
(https://blog.csdn.net/Fiorna0314/article/details/49757195)
· 堆分为哪几块,比如说新生代老生代,那么新生代又分为什么?
· 软引用和弱引用的使用场景(软引用可以实现缓存,弱引用可以用来在回调函数中防止内存泄露);
四、数据库
· 数据库索引,什么是全文索引,全文索引中的倒排索引是什么原理;
(https://blog.csdn.net/zlts000/article/details/52137679)
· 数据库最佳左前缀原则是什么?
最左前缀:查询条件中的所有字段需要从左边起按顺序出现在多列索引中,查询条件的字段数要小于等于多列索引的字段数,中间字段不能存在范围查询的字段(<,like等),这样的sql可以使用该多列索引。
· 数据库的三大范式;
· 1.1 第一范式(1NF)无重复的列
· 1.2 第二范式(2NF)属性完全依赖于主键 [ 消除部分子函数依赖 ]
· 1.3 第三范式(3NF)属性不依赖于其它非主属性 [ 消除传递依赖 ]
悲观锁和乐观锁的原理和应用场景;
悲观锁(Pessimistic Lock):
每次获取数据的时候,都会担心数据被修改,所以每次获取数据的时候都会进行加锁,确保在自己使用的过程中数据不会被别人修改,使用完成后进行数据解锁。由于数据进行加锁,期间对该数据进行读写的其他线程都会进行等待。
乐观锁(Optimistic Lock):
每次获取数据的时候,都不会担心数据被修改,所以每次获取数据的时候都不会进行加锁,但是在更新数据的时候需要判断该数据是否被别人修改过。如果数据被其他线程修改,则不进行数据更新,如果数据没有被其他线程修改,则进行数据更新。由于数据没有进行加锁,期间该数据可以被其他线程进行读写操作。
适用场景:
悲观锁:比较适合写入操作比较频繁的场景,如果出现大量的读取操作,每次读取的时候都会进行加锁,这样会增加大量的锁的开销,降低了系统的吞吐量。
乐观锁:比较适合读取操作比较频繁的场景,如果出现大量的写入操作,数据发生冲突的可能性就会增大,为了保证数据的一致性,应用层需要不断的重新获取数据,这样会增加大量的查询操作,降低了系统的吞吐量。
总结:两种所各有优缺点,读取频繁使用乐观锁,写入频繁使用悲观锁。
·
· 左连接、右连接、内连接、外连接、交叉连接、笛卡儿积等;
· 一般情况下数据库宕机了如何进行恢复(什么是Write Ahead Log机制,什么是Double Write机制,什么是Check Point);
· 什么是redo日志、什么是undo日志;
Undo日志记录某数据被修改前的值,可以用来在事务失败时进行rollback;Redo日志记录某数据块被修改后的值,可以用来恢复未写入data file的已成功事务更新的数据
· checkpoint是为了定期将db buffer的内容刷新到data file。当遇到内存不足、db buffer已满等情况时,需要将db buffer中的内容/部分内容(特别是脏数据)转储到data file中。在转储时,会记录checkpoint发生的”时刻“。在故障回复时候,只需要redo/undo最近的一次checkpoint之后的操作。
· 数据库中的隔离性是怎样实现的;原子性、一致性、持久性又是如何实现的;
什么是组合索引,组合索引什么时候会失效;
联合索引又叫复合索引。对于复合索引:Mysql从左到右的使用索引中的字段,一个查询可以只使用索引中的一部份,但只能是最左侧部分。例如索引是keyindex (a,b,c). 可以支持a | a,b| a,b,c 3种组合进行查找,但不支持 b,c进行查找 .当最左侧字段是常量引用时,索引就十分有效。
·
· 关系型数据库和非关系型数据库区别;
· 数据库死锁如何解决;
· MySQL并发情况下怎么解决(通过事务、隔离级别、锁);
· MySQL中的MVCC机制是什么意思,根据具体场景,MVCC是否有问题;
(https://blog.csdn.net/whoamiyang/article/details/51901888)
· MySQL数据库的隔离级别,以及如何解决幻读;
· 幻读问题是指一个事务的两次不同时间的相同查询返回了不同的结果集。例如:一个select语句执行了两次,但是在第二次结果中第二次没有返回的行,那么这些行就是“幻影”行。
· read view(或者说MVCC)实现了一致性不锁定读(Consistent Nonlocking Reads),从而避免了幻读
·
· 下面,我们通过InnoDB的MVCC实现来分析MVCC使怎样进行并发控制的.
InnoDB的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的,这两个列,分别保存了这个行的创建时间,一个保存的是行的删除时间。这里存储的并不是实际的时间值,而是系统版本号(可以理解为事务的ID),没开始一个新的事务,系统版本号就会自动递增,事务开始时刻的系统版本号会作为事务的ID.下面看一下在REPEATABLEREAD隔离级别下,MVCC具体是如何操作的.
五、缓存服务器
· Redis中zSet跳跃表问题;
· Redis的set的应用场合?
(https://blog.csdn.net/qq_19943157/article/details/50495925)
· Redis高级特性了解吗?
(https://blog.csdn.net/u011204847/article/details/51302109)
Redis的pipeline有什么用处? Pipeline在某些场景下非常有用,比如有多个command需要被“及时的”提交,而且他们对相应结果没有互相依赖,而且对结果响应也无需立即获得,那么pipeline就可以充当这种“批处理”的工具;而且在一定程度上,可以较大的提升性能,性能提升的原因主要是TCP链接中较少了“交互往返”的时间。不过在编码时请注意,pipeline期间将“独占”链接,此期间将不能进行非“管道”类型的其他操作,直到pipeline关闭;如果你的pipeline的指令集很庞大,为了不干扰链接中的其他操作,你可以为pipeline操作新建Client链接,让pipeline和其他正常操作分离在2个client中。不过pipeline事实上所能容忍的操作个数,和socket-output缓冲区大小/返回结果的数据尺寸都有很大的关系;同时也意味着每个redis-server同时所能支撑的pipeline链接的个数,也是有限的,这将受限于server的物理内存或网络接口的缓冲能力。
·
· Redis集群宕机如何处理,怎么样进行数据的迁移;
· Redis的集群方案;
· Redis原子操作怎么用比较好;
· Redis过期策略是怎么实现的呢?
· (https://blog.csdn.net/xiangnan129/article/details/54928672)
六、SSM相关
Spring中@Autowired和@Resource注解的区别?
1、@Autowired与@Resource都可以用来装配bean.都可以写在字段上,或写在setter方法上。
2、 @Autowired默认按类型装配(这个注解是属业spring的),默认情况下必须要求依赖对象必须存在,如果要允许null ,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用,如下:
@Autowired()
@Qualifier("baseDao")
private BaseDao baseDao;
3、@Resource(这个注解属于J2EE的),默认安装名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
@Resource(name="baseDao")
private BaseDao baseDao;
·
· Spring声明一个 bean 如何对其进行个性化定制;
七、操作系统
· Linux静态链接和动态链接;
· 什么是IO多路复用模型(select、poll、epoll);
· Linux中的grep管道用处?Linux的常用命令?
· 操作系统中虚拟地址、逻辑地址、线性地址、物理地址的概念及区别;
· 内存的页面置换算法;
· 内存的页面置换算法;
· 进程调度算法,操作系统是如何调度进程的;
· 父子进程、孤儿进程、僵死进程等概念;
· fork进程时的操作;
· kill用法,某个进程杀不掉的原因(僵死进程;进入内核态,忽略kill信号);
· 系统管理命令(如查看内存使用、网络情况);
· find命令、awk使用;
· Linux下排查某个死循环的线程;
八、网络相关
· 数据链路层是做什么的?
· 数据链路层的流量控制?
· 网络模型的分层、IP和Mac地址在那个层、TCP和HTTP分别在那个层;
· TCP滑动窗口;
· TCP为什么可靠;
· TCP的同传,拆包与组装包是什么意思;
· Https和Http有什么区别;
· Http 为什么是无状态的;
· TCP三次握手,为什么不是三次,为什么不是四次;
· TCP的拥塞控制、流量控制详细说明?
· Http1.0和Http2.0的区别;
· 两个不同ip地址的计算机之间如何通信;
· 地址解析协议ARP;
· OSI七层模型分别对应着五层模型的哪一部分;
· TCP三次握手数据丢失了怎么办?那如果后面又找到了呢?
九、分布式相关
· 消息队列使用的场景介绍和作用(应用耦合、异步消息、流量削锋等);
(https://blog.csdn.net/seven__________7/article/details/70225830)
· 如何解决消息队列丢失消息和重复消费问题;
· Kafka使用过吗,什么是幂等性?怎么保证一致性,持久化怎么做,分区partition的理解,LEO是什么意思,如何保证多个partition之间数据一致性的(ISR机制),为什么Kafka可以这么快(基于磁盘的顺序读写);
· 异步队列怎么实现;
· 你项目的并发是多少?怎么解决高并发问题?单机情况下Tomcat的并发大概是多少,MySQL的并发大致是多少?
· 什么是C10K问题;
· 高并发情况下怎么办;
· 分布式理论,什么是CAP理论,什么是Base理论,什么是Paxos理论;
· 分布式协议的选举算法;
· 说一下你对微服务的理解,与SOA的区别;
· Dubbo的基本原理,RPC,支持哪些通信方式,服务的调用过程;
· Dubbo如果有一个服务挂掉了怎么办;
· 分布式事务,操作两个表不在一个库,如何保证一致性。
· 分布式系统中,每台机器如何产生一个唯一的随机值;
· 系统的量级、pv、uv等;
· 什么是Hash一致性算法?分布式缓存的一致性,服务器如何扩容(哈希环);
· 正向代理、反向代理;
· 什么是客户端负载均衡策略、什么是服务器端负载均衡策略;
· 如何优化Tomcat,常见的优化方式有哪些;
· Nginx的Master和Worker,Nginx是如何处理请求的;
十、系统设计相关
· 如何防止表单重复提交(Token令牌环等方式);
· 有一个url白名单,需要使用正则表达式进行过滤,但是url量级很大,大概亿级,那么如何优化正则表达式?如何优化亿级的url匹配呢?
· 常见的Nginx负载均衡策略;已有两台Nginx服务器了,倘若这时候再增加一台服务器,采用什么负载均衡算法比较好?
· 扫描二维码登录的过程解析;
· 如何设计一个生成唯一UUID的算法?
· 实现一个负载均衡的算法,服务器资源分配为70%、20%、10%;
· 有三个线程T1 T2 T3,如何保证他们按顺序执行;
· 三个线程循环输出ABCABCABC....
十一、安全相关
· 什么是XSS攻击,XSS攻击的一般表现形式有哪些?如何防止XSS攻击;
上一篇: call方法
下一篇: ABI 及 ARM EABI