前言
原文史上最全Java面试题目:算法+缓存+TCP+JVM+搜索+分布式+数据库
正文
JAVA基础
- String类能被继承吗,为什么。
不能,因为String被final修饰,被final修饰的类不能被继承
扩展:
- String 类
public final class String implements java.io.Serializable, Comparable
- final 关键字
- 对于一个 final 变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
- 当用 final 修饰一个类时,表明这个类不能被继承。final 类中的所有成员方法都会被隐式地指定为 final 方法。
- 使用 final 方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的 Java 实现版本中,会将 final 方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升(现在的 Java 版本已经不需要使用 final 方法进行这些优化了)。类中所有的 private 方法都隐式地指定为 final。
- String s = new String(“xyz”); //创建了几个对象
String s=new String(“xyz”)究竟创建String Object分为两种情况
如果String常理池中,已经创建”xyz”,则不会继续创建,此时只创建了一个对象new String(“xyz”)
如果String常理池中,没有创建”xyz”,则会创建两个对象,一个对象的值是”xyz”,一个对象new String(“xyz”)。
- Java中的String为什么是不可变的
- 因为使用final关键字
- StringBuffer, StringBuilder
- String、StringBuffer线程安全(使用了synchronized),StringBuilder线程不安全,但是效率高
- JAVA中的几种基本数据类型是什么,各自占用多少字节。
1字节=8bit(位)
| 基本类型 | 字节 |
|---|---|
| boolean | 1bit 不到一个字节 |
| byte | 8bit 1字节 |
| short | 16bit 2字节 |
| char | 16bit 2字节 |
| int | 32bit 4字节 |
| float | 32bit 4字节 |
| double | 64bit 8字节 |
| long | 64bit 8字节 |
-
ArrayList和LinkedList有什么区别。
- ArrayList 底层实现是动态数组;扩容过程是:默认初始长度是10,并且每次先扩大1.5倍,在跟需要的数据长度做比较,如果不够的话,以数据需要的长度为准,然后在检查期望容量是否越界,越界的话,以最大容量为准;ArrayList 还实现了 Cloneable 接口,这表明 ArrayList 是支持拷贝的;ArrayList 还实现了 Serializable 接口,标记“实现了这个接口的类支持序列化”,但是ArrayList 中的关键字段elementData 使用了 transient 关键字修饰,这个关键字的作用是,让它修饰的字段不被序列化。为什么要这样,因为序列化的时候,如果把整个数组都序列化的话,会将没有值的字段一起序列化。当存储的元素数量非常非常多的时候,闲置的空间就非常非常大,序列化耗费的时间就会非常非常多。所以arraylist内部提供了两个私有方法 writeObject 和 readObject 来完成序列化和反序列化。它使用了 ArrayList 的实际大小 size 而不是数组的长度(elementData.length)来作为元素的上限进行序列化。
- LinkedList 底层实现是双向链表;LinkedList 还实现了 Cloneable 接口,这表明 LinkedList 是支持拷贝的。LinkedList 还实现了 Serializable 接口,这表明 LinkedList 是支持序列化的,但是LinkedList 中的关键字段 size、first、last 都使用了 transient 关键字修饰,LinkedList 在序列化的时候只保留了元素的内容 item,并没有保留元素的前后引用。这样就节省了不少内存空间,当使用反序列化readObject()方法的时候,它可以把链表重新链接起来,这样就恢复了链表序列化之前的顺序
-
讲讲类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数,字段,当new的时候,他们的执行顺序。
- 父类静态代码块、静态变量按照代码的先后顺序执行
- 子类静态代码块、静态变量按照代码的先后顺序执行
- 父类非静态代码块、非静态变量按照代码先后顺序执行、构造函数
-
子类非静态代码块、非静态变量按照代码先后顺序执行、构造函数
- 静态代码块、静态变量只会执行一次
-
用过哪些Map类,都有什么区别,HashMap是线程安全的吗,并发下使用的Map是什么,他们内部原理分别是什么,比如存储方式,hashcode,扩容,默认容量等。
- Map类 HashMap、LinkedHashMap、TreeMap、HashTable、ConcurrentHashMap
- HashMap线程不安全、HashTable线程安全(Synchronized)
- 并发下使用ConcurrentHashMap
- HashMap底层使用数组+链表+红黑树 (当链表长度超过8的时候,转为红黑树)
- HashMap 初始长度位16,当数组中有数据的个数超过总数的0.75时,扩容为2n
- HashMap的并发问题,在扩容rehash中容易导致死锁
-
JAVA8的ConcurrentHashMap为什么放弃了分段锁,有什么问题吗,如果你来设计,你如何设计。
使用节点锁,只锁表头,颗粒度更低,性能更好
-
有没有有顺序的Map实现类,如果有,他们是怎么保证有序的。
- LinkedHashMap(链表) 和 TreeMap(树)
-
抽象类和接口的区别,类可以继承多个类么,接口可以继承多个接口么,类可以实现多个接口么。
- 单继承多实现
-
IO模型有哪些,讲讲你理解的nio ,他和bio,aio的区别是啥,谈谈reactor模型。
- nio :非阻塞同步io
- bio:阻塞同步io
- aio:异步非阻塞io
-
反射的原理,反射创建类实例的三种方式是什么。
- Class.forName(类路径)
- Object.class
- new Object().getClass()
-
反射中,Class.forName和ClassLoader区别。
- Class.forName:加载+拼接
- ClassLoader加载
- 动态代理与cglib实现的区别。
- JDK动态代理:利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
- 利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理,CGLib原理是动态生成被代理类的子类。
- 何时使用JDK还是CGLIB?
- 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
- 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
- 如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换。
- 为什么CGlib方式可以对接口实现代理。
- 对接口进行代理的cglib,最后生成的源码是实现了该接口和Factory接口
- 对实现类进行代理的cglib,最后生成的源码是继承了实现类并实现了Factory接口
- final的用途。
- 当final修饰变量时,被修饰的变量必须被初始化(赋值),且后续不能修改其值,实质上是常量;
- 当final修饰方法时,被修饰的方法无法被所在类的子类重写(覆写);
- 当final修饰类时,被修饰的类不能被继承,并且final类中的所有成员方法都会被隐式地指定为final方法,但成员变量则不会变
- 写出三种单例模式实现 。
//懒汉式 public class Singleton{ private static Singleton instance = null; private Singleton(){ } private static Singleton getInstance(){ if(instance==null){ instance = new Singleton(); } return instance; } } //线程不安全//饿汉式 public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; } } //线程安全,但不能延迟加载//双重校验锁 public class Singleton(){ private static volatile Singleton instance = null; private Singleton(){ } private static Singleton getInstance(){ if(instance==null){ synchronized(Singleton.class){ if(instance==null){ instance = new Singleton(); } } } return instance; } } //双重校验锁,线程安全,推荐使用 - 深拷贝和浅拷贝区别。
- 基本数据类型:数据存放在栈中
- 引用数据类型:数据存在堆中,栈中存放的是指针,该指针指向堆中该实体的起始地址
- 浅拷贝:浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存,实现方式:实现Cloneable类,并重写Object的clone方法,然后在方法内部调用super.clone()方法
- 深拷贝:深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。实现方式:1.需要实现Cloneable接口并且重写clone方法,在方法内部创建一个新的对象;2.通过序列化实现深拷贝
- 数组的Arrays.copyOf和clone()都是浅拷贝
- 数组和链表数据结构描述,各自的时间复杂度。
- (静态)数组从栈中分配空间, 对于程序员方便快速,但自由度小。
- 链表从堆中分配空间, 自由度大但申请管理比较麻烦。
- 数组利用下标定位,时间复杂度为O(1),链表定位元素时间复杂度O(n);
- 数组插入或删除元素的时间复杂度O(n),链表的时间复杂度O(1)
- error和exception的区别,CheckedException,RuntimeException的区别。
- error和exception都继承了throwable接口
- Error类一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和预防,遇到这样的错误,建议让程序终止。
- Exception类表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常
- 检查异常:程序编写时需要try catch住的异常
- 运行时异常:程序运行时出现的异常
-
请列出5个运行时异常。
- ClassCastException(类转换异常)
- NullPointException
- IndexOutOfBoundsExcepiton
- ArrayStoreException
-
IOException
- 全局异常处理:@ControllerAdvice
- 在自己的代码中,如果创建一个java.lang.String类,这个类是否可以被类加载器加载?为什么。
- 双亲委派模型
- 如果一个类加载器收到了类加载的请求,它首先不会自己尝试去加载这个类,而是把这个请求委派给父类加载器,每一个层次的类加载器都是加此,因此所有的加载请求最终到达顶层的启动类加载器,只有当父类加载器反馈自己无法完成加载请求时(指它的搜索范围没有找到所需的类),子类加载器才会尝试自己去加载
- 不能
- 因加载某个类时,优先使用父类加载器加载需要使用的类。如果我们自定义了java.lang.String这个类, 加载该自定义的String类,该自定义String类使用的加载器是AppClassLoader,根据优先使用父类加载器原理, AppClassLoader加载器的父类为ExtClassLoader,所以这时加载String使用的类加载器是ExtClassLoader, 但是类加载器ExtClassLoader在jre/lib/ext目录下没有找到String.class类。然后使用ExtClassLoader父类的加载器BootStrap, 父类加载器BootStrap在JRE/lib目录的rt.jar找到了String.class,将其加载到内存中。这就是类加载器的委托机制。
- 双亲委派模型
- 在jdk1.5中,引入了泛型,泛型的存在是用来解决什么问题。
- 解决强制类型转换
-
有没有可能2个不相等的对象有相同的hashcode。
- 可能
-
Java中的HashSet内部是如何工作的。
- set是无序的、唯一的,底层使用hashMap的key实现
-
什么是序列化,怎么序列化,为什么序列化,反序列化会遇到什么问题,如何解决。
- 序列化:将对象写入到IO流中
- 反序列化:从IO流中恢复对象
- 为什么要序列化:序列化机制允许将实现序列化的Java对象转换位字节序列,这些字节序列可以保存在磁盘上,或通过网络传输,以达到以后恢复成原来的对象。序列化机制使得对象可以脱离程序的运行而独立存在。
- 如果一个可序列化的类的成员不是基本类型,也不是String类型,那这个引用类型也必须是可序列化的;否则,会导致此类不能序列化。
- java序列化算法潜在的问题:如果序列化一个可变对象(对象内的内容可更改)后,更改了对象内容,再次序列化,并不会再次将此对象转换为字节序列,而只是保存序列化编号。
- 可选的自定义序列化:某些属性不需要序列化。使用transient关键字选择不需要序列化的字段。
JVM知识
-
什么情况下会发生栈内存溢出。
- 栈是线程私有的,栈的生命周期和线程一样,每个方法在执行的时候就会创建一个栈帧,它包含局部变量表、操作数栈、动态链接、方法出口等信息,局部变量表又包括基本数据类型和对象的引用;
- 当线程请求的栈深度超过了虚拟机允许的最大深度时,会抛出StackOverFlowError异常,方法递归调用可能会出现该问题;
- 调整参数-xss去调整jvm栈的大小
-
JVM的内存结构

- 程序计数器:线程私有,作为当前线程的行号指示器,用于记录当前虚拟机正在执行的线程指令地址
- 虚拟机栈:线程私有,每个方法执行的时候都会创建一个栈桢,用来存储局部变量表、操作数、动态链接和方法返回等信息,当线程请求的栈深度
- 本地方法栈:线程私有的,保存的是native方法的信息,当一个jvm创建的线程调用native方法后,jvm不会在虚拟机中为该线程创建栈桢,而是简单的动态链接并直接调用该方法
- java堆:所有线程共享,几乎所有对象的实例和数组都要在堆上分配内存
- 方法区:存放已被类加载的类信息、常量、静态变量、即时编译器编译后代码数据,即永久代,在jdk1.8已不存在方法区,被元数据区代替,原方法区被分成两部分;1:加载的类信息,2:运行时常量池;加载的类信息被保存在元数据区中,运行时常量池保存在堆中;
- 如何判断一个对象是否存活
- 引用计数器
- 给每一个对象设置一个引用计数器,当有一个地方引用该对象的时候,引用计数器就+1,引用失效时,引用计数器就-1;当引用计数器为0的时候,就说明这个对象没有被引用,也就是垃圾对象,等待回收;
- 缺点:无法解决循环引用的问题,当A引用B,B也引用A的时候,此时AB对象的引用都不为0,此时也就无法垃圾回收,所以一般主流虚拟机都不采用这个方法;
- 可达性分析算法
- 引用计数器
-
JVM中一次完整的GC流程是怎样的,对象如何晋升到老年代,说说你知道的几种主要的JVM参数。
- java堆 = 新生代+老年代;
- 新生代 = Eden + Suivivor(S0 + S1),默认分配比例是8:1:1;
- 当Eden区空间满了的时候,就会触发一次Minor GC,以收集新生代的垃圾,存活下来的对象会被分配到Survivor区
- 大对象(需要大量连续内存空间的对象)会直接被分配到老年代
- 如果对象在Eden中出生,并且在经历过一次Minor GC之后仍然存活,被分配到存活区的话,年龄+1,此后每经历过一次Minor GC并且存活下来,年龄就+1,当年龄达到15的时候,会被晋升到老年代;
- 当老年代满了,而无法容纳更多对象的话,会触发一次full gc;full gc存储的是整个内存堆(包括年轻代和老年代);
- Major GC是发生在老年代的GC,清理老年区,经常会伴随至少一次minor gc;
- 你知道哪几种垃圾收集器,各自的优缺点,重点讲下cms和G1,包括原理,流程,优缺点。
- serial:单线程的收集器,收集垃圾时,必须stop the world,使用复制算法。
- parNew:serial收集器的多线程版本,也需要stop the world,使用复制算法
- Parallel Scavenge:新生代收集器,复制算法的收集器,并发的多线程收集器,目标是达到一个可控的吞吐量,和ParNew的最大区别是GC自动调节策略;虚拟机会根据系统的运行状态收集性能监控信息,动态设置这些参数,以提供最优停顿时间和最高的吞吐量
- Serial Old:Serial收集器的老年代版本,单线程收集器,使用标记整理算法
- Parallel Old:是Parallel Scavenge收集器的老年代版本,使用多线程,标记-整理算法
- CMS:是一种以获得最短回收停顿时间为目标的收集器,标记清除算法,运作过程:初始标记,并发标记,重新标记,并发清除,收集结束会产生大量空间碎片;
- 初始标记 (Stop the World事件 CPU停顿, 很短)
- 重新标记 (Stop the World事件 CPU停顿,比初始标记稍微长,远比并发标记短)
- 并发标记 (收集垃圾跟用户线程一起执行)
- 并发清理 -清除算法
- 优点:并发收集,低停顿
- 缺点
- CMS收集器对CPU资源非常敏感
- CMS处理器无法处理浮动垃圾
- CMS是基于“标记–清除”算法实现的,所以在收集结束的时候会有大量的空间碎片产生
- G1(garbage first)
- 初始标记(stop the world事件 CPU停顿只处理垃圾);
- 并发标记(与用户线程并发执行);
- 最终标记(stop the world事件 ,CPU停顿处理垃圾)
- 筛选回收(stop the world事件 根据用户期望的GC停顿时间回收)
-
垃圾回收算法的实现原理。
- 标记清除
- 利用可达性去遍历内存,把存活对象和垃圾对象进行标记
- 在遍历一遍,将所有标记的对象回收掉
- 特点:效率不行,标记和清除的效率都不高;标记和清除后会产生大量的不连续的空间分片,可能会导致之后程序运行的时候需分配大对象而找不到连续分片而不得不触发一次GC;
- 标记整理
- 利用可达性去遍历内存,把存活对象和垃圾对象进行标记
- 将所有的存活的对象向一段移动,将端边界以外的对象都回收掉
- 适用于存活对象多,垃圾少的情况;需要整理的过程,无空间碎片产生
- 复制算法
- 将内存按照容量大小分为大小相等的两块,每次只使用一块,当一块使用完了,就将还存活的对象移到另一块上,然后在把使用过的内存空间移除;
- 特点:不会产生空间碎片;内存使用率极低;
- 分代收集算法
- 根据内存对象的存活周期不同,将内存划分成几块,java虚拟机一般将内存分成新生代和老生代,在新生代中,有大量对象死去和少量对象存活,所以采用复制算法,只需要付出少量存活对象的复制成本就可以完成收集;老年代中因为对象的存活率极高,没有额外的空间对他进行分配担保,所以采用标记清理或者标记整理算法进行回收;
- 标记清除
- 当出现了内存溢出,你怎么排错。
- 首先,控制台查看错误日志。
- 然后,使用 JDK 自带的 jvisualvm 工具查看系统的堆栈日志。
- 定位出内存溢出的空间:堆,栈还是永久代(JDK8 以后不会出现永久代的内存溢出)。
- 如果是堆内存溢出,看是否创建了超大的对象。
- 如果是栈内存溢出,看是否创建了超大的对象,或者产生了死循环。
- JVM内存模型的相关知识了解多少,比如重排序,内存屏障,happen-before,主内存,工作内存等。
- 简单说说你了解的类加载器,可以打破双亲委派么,怎么打破。
- 讲讲JAVA的反射机制。
- 你们线上应用的JVM参数有哪些。
- g1和cms区别,吞吐量优先和响应优先的垃圾收集器选择。
- 怎么打出线程栈信息。
- 请解释如下jvm参数的含义:
- -server -Xms512m -Xmx512m -Xss1024K
- -XX:PermSize=256m -XX:MaxPermSize=512m -
- XX:MaxTenuringThreshold=20XX:CMSInitiatingOccupancyFraction=80 -
- XX:+UseCMSInitiatingOccupancyOnly。
- 内存泄漏和内存溢出
- 内存泄漏:是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光
- 内存溢出:是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出
- 强引用、软引用、弱引用、虚引用
- 强引用
//强引用是最普遍的一种引用,我们写的代码,99.9999%都是强引用: Object o = new Object();//将会被回收 o = null; - 软引用
SoftReference<Student>studentSoftReference=new SoftReference<Student>(new Student());//软引用就是把对象用SoftReference包裹一下,当我们需要从软引用对象获得包裹的对象,只要get一下就可以了 SoftReference<Student>studentSoftReference=new SoftReference<Student>(new Student()); Student student = studentSoftReference.get(); System.out.println(student);- 软引用当内存不足,会触发JVM的GC,如果GC后,内存还是不足,就会把软引用的包裹的对象给干掉,也就是只有在内存不足,JVM才会回收该对象。
- 弱引用
WeakReference<byte[]> weakReference = new WeakReference<byte[]>(new byte[1024*1024*10]); System.out.println(weakReference.get());- 一旦失去最后一个强引用,就会被 GC回收
- 虚引用
- 当发生GC,虚引用就会被回收,并且会把回收的通知放到ReferenceQueue中
- 虚引用有什么用呢?在NIO中,就运用了虚引用管理堆外内存。
- 强引用
-
调优工具
常用调优工具分为两类,jdk自带监控工具:jconsole和jvisualvm,第三方有:MAT(Memory AnalyzerTool)、GChisto。
-
jconsole,Java Monitoring and Management Console是从java5开始,在JDK中自带的java监控和管理控制台,用于对JVM中内存,线程和类等的监控
-
jvisualvm,jdk自带全能工具,可以分析内存快照、线程快照;监控内存变化、GC变化等。
-
MAT,Memory Analyzer Tool,一个基于Eclipse的内存分析工具,是一个快速、功能丰富的Javaheap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗
-
GChisto,一款专业分析gc日志的工具
-
开源框架知识
1.简单讲讲tomcat结构,以及其类加载器流程,线程模型等。 2.tomcat如何调优,涉及哪些参数 。 3.讲讲Spring加载流程。 4.Spring AOP的实现原理。 5.讲讲Spring事务的传播属性。 6.Spring如何管理事务的。 7.Spring怎么配置事务(具体说出一些关键的xml 元素)。 8.说说你对Spring的理解,非单例注入的原理?它的生命周期?循环注入的原理,aop的实现原理,说说aop中的几个术语,它们是怎么相互工作的。 9.Springmvc 中DispatcherServlet初始化过程。 10.netty的线程模型,netty如何基于reactor模型上实现的。 11.为什么选择netty。 12.什么是TCP粘包,拆包。解决方式是什么。 13.netty的fashwheeltimer的用法,实现原理,是否出现过调用不够准时,怎么解决。 14.netty的心跳处理在弱网下怎么办。 15.netty的通讯协议是什么样的。 16.springmvc用到的注解,作用是什么,原理。 17.springboot启动机制。
操作系统
- Linux系统下你关注过哪些内核参数,说说你知道的。
- Linux下IO模型有几种,各自的含义是什么。
- epoll和poll有什么区别。
- 平时用到哪些Linux命令。
- 用一行命令查看文件的最后五行。
- 用一行命令输出正在运行的java进程。
- 介绍下你理解的操作系统中线程切换过程。
- 进程和线程的区别。
- top 命令之后有哪些内容,有什么作用。
- 线上CPU爆高,请问你如何找到问题所在。
- 分段和分页
首先,这两个技术都是为了利用和管理好计算机的资源–内存。
直接操作物理地址带来的问题
- 地址空间不隔离
- 程序运行时候的地址不确定
-
内存使用率低下
- 分段:它把虚拟地址空间映射到了物理地址空间,并且你写的程序操作的是虚拟地址
- 分页:比分段粒度更小
- 线程同步的方式有哪些
- 互斥量
- 信号量
- 事件
- 操作系统中进程调度策略
- FCFS(先来先服务),优先级,时间片轮转,多级反馈
- SJF(最短作业优先调度算法):平均等待时间最短,但难以知道下一个CPU区间长度
- 优先级调度算法(可以是抢占的,也可以是非抢占的)
- 时间片轮转调度算法(可抢占的)
- 多级队列调度算法
- 多级反馈队列调度算法 多线程
-
多线程的几种实现方式,什么是线程安全。
-
继承Thread类,重写run方法,调用start()方法启动线程
-
实现Runnable接口,new Thread(new Runnable( run())).start()
-
使用内部类
-
使用定时器
- 使用future
- 当我们提交一个Callable任务后,我们会同时获得一个Future对象,然后,我们在主线程某个时刻调用Future对象的get()方法,就可以获得异步执行的结果。在调用get()时,如果异步任务已经完成,我们就直接获得结果。如果异步任务还没有完成,那么get()会阻塞,直到任务完成后才返回结果。
- 使用线程池
-
2.volatile的原理,作用,能代替锁么。
volatile 保证可见性、不保证原子性,禁止指令重排序
i = 0; // 在 Java 中,对基本数据类型的变量和赋值操作都是原子性操作 j = i ; // 包含了两个操作:读取 i,将 i 值赋值给 j 。 i++; // <3> i = j + 1; // <4>
- 画一个线程的生命周期状态图。
-
sleep和wait的区别。
- sleep和sleep(0)的区别。
- Lock与Synchronized的区别 。
-
synchronized的原理是什么,一般用在什么地方(比如加在静态方法和非静态方法的区别,静态方法和非静态方法同时执行的时候会有影响吗),解释以下名词:重排序,自旋锁,偏向锁,轻量级锁,可重入锁,公平锁,非公平锁,乐观锁,悲观锁。
- synchronized:可重入、非公平锁,代码块是monitorenter、monitorexit,方法是ACC_SYNCHRONIZED标识方法的同步
- 重排序:指令重排序
- 偏向锁
- 未加锁的时候,锁标志为01,包含哈希值、年龄分代和偏向锁标志位(0)
- 施加偏向锁时,哈希值和一部分无用内存会转化为锁主人的线程信息,以及加锁时的时间戳epoch,此时锁标志位没变,偏向锁标志改为1
- 加锁时先判断当前线程id是否与MarkWord的线程id是否一致,一致则执行同步代码;不一致则检查偏向标志是否偏向,未偏向则使用CAS加锁;未偏向CAS加锁失败和存在偏向锁会导致偏向锁膨胀为轻量级锁,或者重新偏向
- 偏向锁只有遇到其他线程竞争偏向锁时,持有偏向锁的线程才会释放锁,线程不会主动去释放偏向锁
- 轻量级锁
- 当发生多个线程竞争时,偏向锁会变为轻量级锁,锁标志位为00
- 获得锁的线程会先将偏向锁撤销(在安全点),并在栈桢中创建锁记录LockRecord,对象的MarkWord被复制到刚创建的LockRecord,然后CAS尝试将记录LockRecord的owner指向锁对象,再将锁对象的MarkWord指向锁,加锁成功
- 如果CAS加锁失败,线程会自旋一定次数加锁,再失败则升级为重量级锁
- 重量级锁
- 重量级锁就是上面介绍到synchronized使用监视器Monitor实现的锁机制
- 竞争线程激烈,锁则继续膨胀,变为重量级锁,也是互斥锁,锁标志位为10,MarkWord其余内容被替换为一个指向对象锁Monitor的指针
- 自旋锁:减少不必要的CPU上下文切换;在轻量级锁升级为重量级锁时,就使用了自旋加锁的方式
- 锁粗化:多次加锁操作在JVM内部也是种消耗,如果多个加锁可以合并为一个锁,就可减少不必要的开销
- 锁消除
- 删除不必要的加锁操作,如果变量是独属一个线程的栈变量,加不加锁都是安全的,编译器会尝试消除锁
- 开启锁消除需要在JVM参数上设置-server -XX:+DoEscapeAnalysis -XX:+EliminateLocks
- 悲观锁:每次去请求数据的时候,都认为数据会被抢占更新(悲观的想法);所以每次操作数据时都要先加上锁,其他线程修改数据时就要等待获取锁。适用于写多读少的场景,synchronized就是一种悲观锁
- 乐观锁:在请求数据时,觉得无人抢占修改。等真正更新数据时,才判断此期间别人有没有修改过(预先读出一个版本号或者更新时间戳,更新时判断是否变化,没变则期间无人修改);和悲观锁不同的是,期间数据允许其他线程修改
- 公平锁:讲究先来先到,线程在获取锁时,如果等待队列中以及有线程在等待,那么当前线程就会进入等待队列中。
- 非公平锁:不管是否有等待队列,都会去尝试获得锁。
8.用过哪些原子类,他们的原理是什么。 9.JUC下研究过哪些并发工具,讲讲原理。 10.用过线程池吗,如果用过,请说明原理,并说说newCache和newFixed有什么区别,构造函数的各个参数的含义是什么,比如coreSize,maxsize等。 11.线程池的关闭方式有几种,各自的区别是什么。 12.假如有一个第三方接口,有很多个线程去调用获取数据,现在规定每秒钟最多有10个线程同时调用它,如何做到。 13.spring的controller是单例还是多例,怎么保证并发的安全。 14.用三个线程按顺序循环打印abc三个字母,比如abcabcabc。
-
ThreadLocal用过么,用途是什么,原理是什么,用的时候要注意什么。
- 当使用ThreadLocal声明变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本
- 从上面的概念可知,ThreadLocal其实并不能保证变量的同步性,只是给每一个线程分配一个变量副本
16.如果让你实现一个并发安全的链表,你会怎么做。 17.有哪些无锁数据结构,他们实现的原理是什么。 18.讲讲java同步机制的wait和notify。
-
CAS机制是什么,如何解决ABA问题。
- ABA问题
- 线程X准备将变量的值从A改为B,然而这期间线程Y将变量的值从A改为C,然后再改为A;最后线程X检测变量值是A,并置换为B。但实际上,A已经不再是原来的A了
- 解决方法,是把变量定为唯一类型。值可以加上版本号,或者时间戳。如加上版本号,线程Y的修改变为A1->B2->A3,此时线程X再更新则可以判断出A1不等于A3
- 只能保证一个共享变量的原子操作
- 只保证一个共享变量的原子操作,对多个共享变量同步时,循环CAS是无法保证操作的原子
- ABA问题
-
基于volatile + CAS 实现同步锁的原理
- CAS只能同步一个变量的修改,我们又应该如何用它来锁住代码块呢?
- 先说说实现锁的要素
- 1 同步代码块同一时刻只能有一个线程能执行
- 2 加锁操作要happens-before同步代码块里的操作,而代码块里的操作要happens-before解锁操作
- 3 同步代码块结束后相对其他线程其修改的变量是可见的 (内存可见性)
- 要素1:可以利用CAS的原子性来实现,任意时刻只有一个线程能成功操作变量
- 先设想CAS操作的共享变量是一个关联代码块的同步状态变量,同步开始之前先CAS更新状态变量为加锁状态,同步结束之后,再CAS状态变量为无锁状态
- 如果期间有第二个线程来加锁,则会发现状态变量为加锁状态,则放弃执行同步代码块
- 要素2:使用volatile修饰状态变量,禁止指令重排
- volatile保证同步代码里的操作happens-before解锁操作,而加锁操作happens-before代码块里的操作
- 要素3:还是用volatile,volatile变量写指令前后会插入内存屏障
- volatile修饰的状态变量被CAS为无锁状态前,同步代码块的脏数据就会被更新,被各个线程可见
//伪代码
volatile state = 0 ; // 0-无锁 1-加锁;volatile禁止指令重排,加入内存屏障
...
if(cas(state, 0 , 1)){ // 1 加锁成功,只有一个线程能成功加锁
... // 2 同步代码块
cas(state, 1, 0); // 3 解锁时2的操作具有可见性
}
20.多线程如果线程挂住了怎么办。 21.countdowlatch和cyclicbarrier的内部原理和用法,以及相互之间的差别(比如countdownlatch的await方法和是怎么实现的)。
-
对AbstractQueuedSynchronizer(AQS)了解多少,讲讲加锁和解锁的流程,独占锁和公平所加锁有什么不同。
- AQS其实就是基于volatile+cas实现的锁模板;如果需要线程阻塞等待,唤醒机制,则使用LockSupport挂起、唤醒线程
- AQS内部维护一个同步队列,元素就是包装了线程的Node
- 同步队列中首节点是获取到锁的节点,它在释放锁的时会唤醒后继节点,后继节点获取到锁的时候,会把自己设为首节点
- 线程会先尝试获取锁,失败则封装成Node,CAS加入同步队列的尾部。在加入同步队列的尾部时,会判断前驱节点是否是head结点,并尝试加锁(可能前驱节点刚好释放锁),否则线程进入阻塞等待
- volatile+cas机制保证了代码的同步性和可见性,而AQS封装了线程阻塞等待挂起,解锁唤醒其他线程的逻辑。AQS子类只需根据状态变量,判断是否可获取锁,是否释放锁成功即可
23.使用synchronized修饰静态方法和非静态方法有什么区别。 24.简述ConcurrentLinkedQueue和LinkedBlockingQueue的用处和不同之处。
- 导致线程死锁的原因?怎么解除线程死锁。
- 产生死锁的四个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用。
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
- 解决线程死锁的方式
- 鸵鸟算法:死锁放生,不去处理
- 死锁预防
- 破坏“互斥使用/资源独占”条件
- 破坏“占有且等待”条件
- 破坏“不可抢占”条件
- 破坏“循环等待”条件
- 死锁避免
- 利用银行家算法避免死锁
- 哲学家就餐问题
- 死锁检测与解除 26.非常多个线程(可能是不同机器),相互之间需要等待协调,才能完成某种工作,问怎么设计这种协调方案。 27.用过读写锁吗,原理是什么,一般在什么场景下用。 28.开启多个线程,如果保证顺序执行,有哪几种实现方式,或者如何保证多个线程都执行完再拿到结果。 29.延迟队列的实现方式,delayQueue和时间轮算法的异同。
- 产生死锁的四个必要条件:
- 守护线程:方法和普通线程一样,只是在调用start()方法前,调用setDaemon(true)把该线程标记为守护线程
- 中断线程:中断一个线程非常简单,只需要在其他线程中对目标线程调用interrupt()方法,目标线程需要反复检测自身状态是否是interrupted状态,如果是,就立刻结束运行
- ReentrantLock
- private final Lock lock = new ReentrantLock(); lock.lock(); lock.unlock();
- 可以使用tryLock()尝试获取锁。
计算机网络 传送门
- http1.0和http1.1有什么区别。
- http1.0 没有官方的keepalive操作,通常是在现有协议上添加一个指数 keep-alive保证链接不中断,http1.1默认所有请求都是持续链接,除非特殊声明不支持
- http1.1
- 改进持久连接和CDN域名的分片机制
- 不成熟的http管道化
- 提供虚拟主机支持
- 对动态生成的内容完美支持
- 引入cookie以及安全机制
- TCP三次握手和四次挥手的流程,为什么断开连接要4次,如果握手只有两次,会出现什么。
- 三次握手
- 第一次握手:建立连接时, 客户端发送syn包(syn=j)到服务器, 并进入SYN_SENT状态, 等待服务器确认
- 第二次握手:服务器收到syn包, 必须确认客户的SYN(ack=j+1), 同时自己也发送一个SYN包(syn=k), 即SYN+ACK包, 此时服务器进入SYN_RCVD状态
- 第三次握手:客户端收到服务器的SYN-ACK包, 向服务器发送确认包ACK(ack=k+1), 此时发送完毕, 客户端与服务器进入ESTABLISHED状态, 完成三次握手
- 四次挥手
- 第一次挥手:客户端发送一个FIN, 用来关闭Client到服务器的数据传送, 客户端进入FIN_WAIT_1状态
- 第二次挥手:服务器收到FIN后, 发送一个ACK给客户端, 确认序号为收到序号+1(与SYN相同, 一个FIN占用一个序号), 服务器进入CLOSE_WAIT状态。 此时TCP链接处于半关闭状态, 即客户端已经没有要发送的数据了, 但服务端若发送数据, 则客户端仍要接受,客户端处于FIN_WAIT_2状态
- 第三次挥手:服务器发送一个FIN, 用来关闭服务器到客户端的数据传送,服务器进入LAST_ACK状态
- 第四次挥手:客户端收到FIN后, 客户端进入TIME_WAIT状态, 接着发送一个ACK给服务器, 确认序号为收到序号+1, 服务器进入CLOSED状态, 完成四次挥手
- 不行,采用三次握手是为了防止失效的连接请求报文突然又传送到了服务端, 因而产生错误。失效的连接请求报文是指:主机A发出的连接请求没有收到主机B的确认, 于是经过一段时间后, 主机A又向主机B发送连接请求, 且建立成功, 顺序完成数据传输。 考虑一种特殊情况, 主机A第一次发送的连接请求并没有丢失, 而是因为网络节点导致延迟到达主机B, 主机B以为是主机A又发起的新连接, 于是主机B同意连接, 并向主机A发回确认,但是此时主机A根本不会理会, 主机B就一直等待主机A发送数据, 导致主机B资源浪费
- ACK报文丢失导致第三次握手失败:当客户端收到服务端的SYNACK应答后,其状态变为ESTABLISHED,并会发送ACK包给服务端,准备发送数据了。如果此时ACK在网络中丢失(如上图所示),过了超时计时器后,那么服务端会重新发送SYNACK包,重传次数根据/proc/sys/net/ipv4/tcp_synack_retries来指定,默认是5次。如果重传指定次数到了后,仍然未收到ACK应答,那么一段时间后,Server自动关闭这个连接。如果在重传过程中,有其他报文到达服务端,则链接正常;总的来说,如果一个ACK 报文丢失了,但它的下一个数据包没有丢失,那么连接正常,否则,连接会被重置。
- 知道SYN攻击吗?如何防范?
- syn攻击就是,拿很多ip来请求服务端建立链接,服务端收到请求后,为这些虚假ip分配了缓存后会进入到syn-rcvd状态,导致服务器资源被耗尽;预防方法就是使用syn cookie,所谓的SYN Cookie防御系统,与前面接收到SYN 报文就分配缓存不同,此时暂不分配资源;同时利用SYN 报文的源和目的地IP和端口,以及服务器存储的一个秘密数,使用它们进行散列,得到server_isn,然后附着在SYNACK 报文中发送给客户端,接下来就是对ACK 报文进行判断,如果其返回的ack字段正好等于server_isn + 1,说明这是一个合法的ACK,那么服务器才会为其生成一个具有套接字的全开的连接。
- 三次握手
- TIME_WAIT和CLOSE_WAIT的区别。
- 为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
- 虽然按道理, 四个报文都发送完毕, 我们可以直接进入CLOSE状态了, 但是我们必须假想网络是不可靠的。 有可能最后一个ACK丢失, 所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出的最后的ACK回复, 但该ACK可能丢失。 Server如果没有收到ACK, 将不断重复发送FIN片段。 所以Client不能立即关闭, 它必须确认Server接收到了该ACK。 Client会在发送出ACK之后进入到TIME_WAIT状态。 Client会设置一个记时器, 等待2MSL的时间。 如果在该时间内再次收到FIN, 那么Client会重发ACK并再次等待2MSL, 所谓的2MSL是两倍的MSL。 MSL指一个片段在网络中最大的存活时间, 2MSL就是一个发送和一个回复所需的最大时间。 如果直到2MSL, Client都没有再次收到FIN, 那么Client推断ACK已经被成功接收, 则结束TCP链接
- 说说你知道的几种HTTP响应码,比如200, 302, 404。
- 对于1xx的状态码,为信息状态码,100 为继续,表示确认,成功返回具体参数信息。
- 对于2xx的状态码,200 表示正常返回信息,201表示请求成功并且服务器创建了新的资源,202表示服务器已接受请求,但尚未处理。
- 对于3xx,重定向,301表示,请求的网页已永久移动到新位置,302表示,临时性重定向,303表示临时性重定向,且总是使用 GET 请求新的 URI。304表示,自从上次请求后,请求的网页未修改过。
- 对于4xx,客户端错误,404,服务器无法理解请求的格式,客户端不应当尝试再次使用相同的内容发起请求,401,请求未授权,403,禁止访问,404,找不到如何与 URI 相匹配的资源。
- 对于5xx,服务器错误,500,最常见的服务器端错误,503,服务器端暂时无法处理请求,可能是过载或维护。
- 当你用浏览器打开一个链接(如:http://www.javastack.cn)的时候,计算机做了哪些工作步骤。
- 客户端浏览器通过DNS解析到www.baidu.com的IP地址220.181.27.48,通过这个IP地址找到客户端到服务器的路径。客户端浏览器发起一个HTTP会话到220.161.27.48,然后通过TCP进行封装数据包,输入到网络层。
- 在客户端的传输层,把HTTP会话请求分成报文段,添加源和目的端口,如服务器使用80端口监听客户端的请求,客户端由系统随机选择一个端口如5000,与服务器进行交换,服务器把相应的请求返回给客户端的5000端口。然后使用IP层的IP地址查找目的端。
- 客户端的网络层不用关心应用层或者传输层的东西,主要做的是通过查找路由表确定如何到达服务器,期间可能经过多个路由器,这些都是由路由器来完成的工作,我不作过多的描述,无非就是通过查找路由表决定通过那个路径到达服务器。
- 客户端的链路层,包通过链路层发送到路由器,通过邻居协议查找给定IP地址的MAC地址,然后发送ARP请求查找目的地址,如果得到回应后就可以使用ARP的请求应答交换的IP数据包现在就可以传输了,然后发送IP数据包到达服务器的地址。
- TCP/IP如何保证可靠性,说说TCP头的结构。
-
TCP是提供一种面向连接的可靠的字节流服务。其中, 面向连接意味着两个使用TCP的应用(通常是一个客户和一个服务器)在彼此交换数据之前必须先建立一个TCP连接。在一个TCP连接中, 仅有两方进行彼此通信;而字节流服务意味着两个应用程序通过TCP连接交换8bit字节构成的字节流, TCP不在字节流中插入记录标识符,对于可靠性,TCP通过以下方式进行保证:
- 数据包检验:目的是检测数据在传输过程的任何变化, 若检验出包有错, 则丢弃报文段并且不会给出响应, 这时TCP发送数据端超时后会重发数据
- 对失序数据包重排序:既然 TCP报文段作为IP数据报来传输, 而IP数据报的到达可能会失序, 因此TCP报文段的到达也可能会失序。TCP将对失序数据进行重新排序, 然后才交给应用层
- 丢失重复数据:对于重复数据, 能够丢弃重复数据
- 应答机制:当TCP收到发自TCP连接另一端的数据, 它将发送一个确认。这个确认不是立即发送, 通常是推迟几分之一秒
- 超时重发:当TCP发出一个段后, 它启动一个定时器, 等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段
- 流量控制:TCP链接的每一方都有固定大小的缓冲空间。 TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据, 这可以防止较快主机致使较慢主机的缓冲区溢出, 这就是流量控制。TCP使用的流量控制协议是可变大小的滑动窗口协议
-
- 如何避免浏览器缓存。
- 如何理解HTTP协议的无状态性。
- 简述Http请求get和post的区别以及数据包格式。
- GET是用于信息获取,使用URL传递参数,发送信息的数量有限;
- POST是用于修改服务器上的资源;
- 一般使用POST,当无法使用缓存文件,向服务器发送大量的数据,发送未知的字符
- HTTP有哪些method
- GET方法获取资源
- POST方法传输资源
- PUT方法更新资源
- DELETE方法删除资源
- HEAD方法获得报文首部
- 简述HTTP请求的报文格式。
- HTTP的长连接是什么意思。 链接不中断,keep-alive
- HTTPS的加密方式是什么,讲讲整个加密解密流程。
- Http和https的三次握手有什么区别。
- http传输方式:明文传输,网站或相关服务与用户之间的数据交互无加密,容易被监听,篡改。
- https传输方式:在HTTP加入了SSL层,用于数据传输加密。
- http身份认证:无任何身份认证,用户无法通过http辨认出网站的真实身份。
- https身份认证:经过CA多重认证,包含域名管理权限认证等。
- http成本:无任何使用成本,所有网站默认是http模式。
- https需要成本,需要申请SSL证书来实现https。
- http连接端口:80端口。
- https连接端口:443端口。
- 什么是分块传送。
- 说一说cookies,sessionStorage 和 localStorage 的区别?
- cookie是网站用来标识用户身份而存储在用户本地终端上的数据
- cookie数据始终在同源的http请求中携带,即使是不需要的情况,so,会在浏览器和服务器间来回传递
- sessionStorage和localStorage不会自动把数据发送给服务器,仅仅在本地保存
- cookie的数据大小不能超过4k;sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或者更大。
- localStorage存储持久数据,浏览器关闭后数据不会丢失,除了主动删除数据
- sessionStorage数据在当前浏览器窗口关闭后自动删除
- 设置得cookie过期时间之前都有效,就算窗口或者是浏览器关闭
- Http和Https有什么区别?
- Http协议运行在TCP之上, 明文传输,客户端与服务端都无法验证对方的身份;
- Https是身披SSL(Secure socket layer)外壳的Http, 运行于SSL上, SSL运行于TCP之上,是添加了加密和认证机制的Http。
- 端口不同:Http:80 Https:443
- 资源消耗:Https通信会由于加减密处理消耗更多的CPU和内存资源
- 开销:Https通信需要证书, 而证书一般需要向认证机构(CA)购买
- Https的加密机制是一种共享密钥加公开密钥加密并用的混合加密机制
- 如果已经建立了连接, 但是客户端突然出现了故障怎么办?
- TCP设置有一个保活计时器, 显然, 客户端如果出现故障, 服务器不能一直等下去, 白白浪费资源。 服务器每收到一次客户端的请求后都会重新复位这个计时器, 时间通常是设置为两小时, 若两小时还没收到客户端的任何数据, 服务器就会发送一个探测报文段 , 以后每隔75秒钟就会发送一次, 若一连发送10个探测报文仍然没反应, 服务器就认为客户顿除了故障,接着就会关闭连接
- DDos攻击
- 客户端向服务端发送请求链接数据包
- 服务端向客户端发送确认数据包
- 客户端不向服务端发送确认数据包, 服务器一直等待来自客户端的确认
- DDos预防(没有彻底根治办法, 除非不使用TCP)
- 限制同时打开SYN半链接的数目
- 缩短SYN半链接的Time out时间
- 关闭不必要的服务
- TCP的拥塞处理(拥塞窗口)
- 拥塞控制就是防止过多的数据注入网络中。这样可以使网络中的路由器或链路不致过载、 注意, 拥塞控制和流量控制不同, 前者是一个全局性过程, 而后者指。 拥塞控制的方法主要有以下四种:
- 慢启动:不要一开始就发送大量的数据, 先探测一下网络的拥塞程度, 也就是说由小到大逐渐增加拥塞窗口的大小
- 拥塞避免:拥塞避免算法让拥塞窗口缓慢增长, 即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1, 而不是加倍, 这样拥塞窗口按线性规律缓慢增长
- 快重传:快重传要求接收方在收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方)而不要等到自己发送数据时捎带确认。快重传算法规定, 发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段, 而不必继续等待设置的重传计时器时间周期
- 快恢复:快重传配合使用的还有快恢复算法, 当发送方连续收到三个重复确认时, 就会执行“乘法减小”算法, 把ssthresh门限减半, 但是接下去并不执行慢开始算法, 因为如果网络出现拥塞的话就不会收到 好几个重复的确认, 所以发送方现在认可网络可能没有出现拥塞, 所以此时不执行慢开始算法, 而是将cwnd设置为ssthresh的大小, 然后执行拥塞避免算法
- 拥塞控制就是防止过多的数据注入网络中。这样可以使网络中的路由器或链路不致过载、 注意, 拥塞控制和流量控制不同, 前者是一个全局性过程, 而后者指。 拥塞控制的方法主要有以下四种:
- http管道化
- 在相应到达之前,可以将多条请求放入队列,当第一条请求发往服务器的时候,第二第三条请求也可以开始发送了,在高延时网络条件下,这样做可以降低网络的环回时间,提高性能。
- HTTP管道化的限制
- 管道化要求服务端按照请求发送的顺序返回响应(FIFO),原因很简单,HTTP请求和响应并没有序号标识,无法将乱序的响应与请求关联起来。
- 客户端需要保持未收到响应的请求,当连接意外中断时,需要重新发送这部分请求。
- 只有幂等的请求才能进行管道化,也就是只有GET和HEAD请求才能管道化,否则可能会出现意料之外的结果
- 前面提到HTTP管道化要求服务端必须按照请求发送的顺序返回响应,那如果一个响应返回延迟了,那么其后续的响应都会被延迟,直到队头的响应送达。
- 队头阻塞
- 队头阻塞(head-of-line blocking)发生在一个TCP分节丢失,导致其后续分节不按序到达接收端的时候。该后续分节将被接收端一直保持直到丢失的第一个分节被发送端重传并到达接收端为止。该后续分节的延迟递送确保接收应用进程能够按照发送端的发送顺序接收数据
- 如何解决HTTP队头阻塞
- 对于HTTP1.1中管道化导致的请求/响应级别的队头阻塞,可以使用HTTP2解决。HTTP2不使用管道化的方式,而是引入了帧、消息和数据流等概念,每个请求/响应被称为消息,每个消息都被拆分成若干个帧进行传输,每个帧都分配一个序号。每个帧在传输是属于一个数据流,而一个连接上可以存在多个流,各个帧在流和连接上独立传输,到达之后在组装成消息,这样就避免了请求/响应阻塞。当然,即使使用HTTP2,如果HTTP2底层使用的是TCP协议,仍可能出现TCP队头阻塞。
- 如何解决TCP队头阻塞
- TCP中的队头阻塞的产生是由TCP自身的实现机制决定的,无法避免。想要在应用程序当中避免TCP队头阻塞带来的影响,只有舍弃TCP协议。比如google推出的quic协议,在某种程度上可以说避免了TCP中的队头阻塞,因为它根本不使用TCP协议,而是在UDP协议的基础上实现了可靠传输。而UDP是面向数据报的协议,数据报之间不会有阻塞约束。此外还有一个SCTP(流控制传输协议),它是和TCP、UDP在同一层次的传输协议。SCTP的多流特性也可以尽可能的避免队头阻塞的情况。
- 对于解决HTTP的队头阻塞问题是:并发连接和域名分片
- web性能优化技术
- DNS查询优化
- 客户端缓存
- 优化TCP连接
- 避免重定向
- 网络边缘的缓存
- 条件缓存
- 压缩和代码极简化
- 图片优化
- 慢启动和拥塞避免算法
- WAN和LAN
- WAN:Wide Area Network广域网
- LAN:局域网
- 安全问题:CSRF和XSS
- CSRF:(Cross-site request forgery)跨站请求伪造,攻击者盗用了你的身份,以你的名义发送恶意请求。
- XSS:跨域脚本攻击,恶意攻击者往web页面里插入恶意的script代码,在浏览器中运行script代码,达到恶意攻击用户的目的。
- 网络协议分层
- OSI:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层
- TCP/IP:网络接口层、网络层、传输层、应用层 架构设计与分布式
- 用java自己实现一个LRU。
- LRU:Least Recently Used:最近最少使用
- LFU:Least frequently use:最少使用次数算法
- OPT:Optimal replacement:最优置换算法
- 分布式集群下如何做到唯一序列号。
- 数据库自增长序列
- UUID
- Redis 生成id
- Twitter的snowflake
- 利用zookeeper生成唯一id
3.设计一个秒杀系统,30分钟没付款就自动关闭交易。 4.如何使用redis和zookeeper实现分布式锁?有什么区别优缺点,会有什么问题,分别适用什么场景。(延伸:如果知道redlock,讲讲他的算法实现,争议在哪里) 5.如果有人恶意创建非法连接,怎么解决。 6.分布式事务的原理,优缺点,如何使用分布式事务,2pc 3pc 的区别,解决了哪些问题,还有哪些问题没解决,如何解决,你自己项目里涉及到分布式事务是怎么处理的。 7.什么是一致性hash。 8.什么是restful,讲讲你理解的restful。 9.如何设计一个良好的API。 10.如何设计建立和保持100w的长连接。 11.解释什么是MESI协议(缓存一致性)。
- 说说你知道的几种HASH算法,简单的也可以。
- MD5
- SHA-1
- SHA-256
- SHA-512
- RipeMD-160 13.什么是paxos算法, 什么是zab协议。 14.一个在线文档系统,文档可以被编辑,如何防止多人同时对同一份文档进行编辑更新。 16.线上系统突然变得异常缓慢,你如何查找问题。
- 说说你平时用到的设计模式。
- 工厂模式
- 单例模式
- 适配器模式
- 装饰器模式
- 代理模式
18.Dubbo的原理,有看过源码么,数据怎么流转的,怎么实现集群,负载均衡,服务注册和发现,重试转发,快速失败的策略是怎样的 。 19.一次RPC请求的流程是什么。 20.自己实现过rpc么,原理可以简单讲讲。Rpc要解决什么问题。 21.异步模式的用途和意义。 22.编程中自己都怎么考虑一些设计原则的,比如开闭原则,以及在工作中的应用。 23.设计一个社交网站中的“私信”功能,要求高并发、可扩展等等。 画一下架构图。 24.MVC模式,即常见的MVC框架。 25.聊下曾经参与设计的服务器架构并画图,谈谈遇到的问题,怎么解决的。 26.应用服务器怎么监控性能,各种方式的区别。 27.如何设计一套高并发支付方案,架构如何设计。 28.如何实现负载均衡,有哪些算法可以实现。 29.Zookeeper的用途,选举的原理是什么。 30.Zookeeper watch机制原理。 31.Mybatis的底层实现原理。 32.请思考一个方案,实现分布式环境下的countDownLatch。 33.后台系统怎么防止请求重复提交。 34.描述一个服务从发布到被消费的详细过程。 35.讲讲你理解的服务治理。 36.如何做到接口的幂等性。 37.如何做限流策略,令牌桶和漏斗算法的使用场景。 38.什么叫数据一致性,你怎么理解数据一致性。 39.分布式服务调用方,不依赖服务提供方的话,怎么处理服务方挂掉后,大量无效资源请求 40.的浪费,如果只是服务提供方吞吐不高的时候该怎么做,如果服务挂了,那么一会重启,该怎么做到最小的资源浪费,流量半开的实现机制是什么。 41.dubbo的泛化调用怎么实现的,如果是你,你会怎么做。 42.远程调用会有超时现象,如果做到优雅的控制,JDK自带的超时机制有哪些,怎么实现的。 算法
- 10亿个数字里里面找最小的10个。
- 有1亿个数字,其中有2个是重复的,快速找到它,时间和空间要最优。
- 2亿个随机生成的无序整数,找出中间大小的值。
- 给一个不知道长度的(可能很大)输入字符串,设计一种方案,将重复的字符排重。
- 遍历二叉树。
- 有3n+1个数字,其中3n个中是重复的,只有1个是不重复的,怎么找出来。
- 写一个字符串(如:www.javastack.cn)反转函数。
- 常用的排序算法,快排,归并、冒泡。 快排的最优时间复杂度,最差复杂度。冒泡排序的优化方案。
- 二分查找的时间复杂度,优势。
- 一个已经构建好的TreeSet,怎么完成倒排序。
- 什么是B+树,B-树,列出实际的使用场景。
- 一个单向链表,删除倒数第N个数据。
- 200个有序的数组,每个数组里面100个元素,找出top20的元素。
- 单向链表,查找中间的那个元素。
- PriorityQueue 优先级队列,放入PriorityQueue的元素,必须实现Comparable接口 数据库知识
- 数据库隔离级别有哪些,各自的含义是什么,MYSQL默认的隔离级别是是什么。
- 未提交读
- 已提交读
- 可重复读(默认)
- 序列化
- 什么是幻读。
- MYSQL有哪些存储引擎,各自优缺点。
- InnoDB:行级锁
- MyISAM:表级锁
- MEMORY: 4.高并发下,如何做到安全的修改同一行数据。 5.乐观锁和悲观锁是什么,INNODB的标准行级锁有哪2种,解释其含义。 6.SQL优化的一般步骤是什么,怎么看执行计划,如何理解其中各个字段的含义。 7.数据库会死锁吗,举一个死锁的例子,mysql怎么解决死锁。 8.MYsql的索引原理,索引的类型有哪些,如何创建合理的索引,索引如何优化。 9.聚集索引和非聚集索引的区别。 10.select for update 是什么含义,会锁表还是锁行或是其他。 11.为什么要用Btree实现,它是怎么分裂的,什么时候分裂,为什么是平衡的。 12.数据库的ACID是什么。 13.某个表有近千万数据,CRUD比较慢,如何优化。 14.Mysql怎么优化table scan的。 15.如何写sql能够有效的使用到复合索引。 16.mysql中in 和exists 区别。 17.数据库自增主键可能的问题。 18.MVCC的含义,如何实现的。 19.你做过的项目里遇到分库分表了吗,怎么做的,有用到中间件么,比如sharding jdbc等,他们的原理知道么。 20.MYSQL的主从延迟怎么解决。
- mysql六种日志
- binlog
- redolog
- undolog
- errorlog
- slow query log
- general log
- relay log
消息队列
1.消息队列的使用场景。 2.消息的重发,补充策略。 3.如何保证消息的有序性。 4.用过哪些MQ,和其他mq比较有什么优缺点,MQ的连接是线程安全的吗,你们公司的MQ服务架构怎样的。 5.MQ系统的数据如何保证不丢失。 6.rabbitmq如何实现集群高可用。 7.kafka吞吐量高的原因。 8.kafka 和其他消息队列的区别,kafka 主从同步怎么实现。 9.利用mq怎么实现最终一致性。 10.使用kafka有没有遇到什么问题,怎么解决的。 11.MQ有可能发生重复消费,如何避免,如何做到幂等。 12.MQ的消息延迟了怎么处理,消息可以设置过期时间么,过期了你们一般怎么处理。缓存
1.常见的缓存策略有哪些,如何做到缓存(比如redis)与DB里的数据一致性,你们项目中用到了 2.什么缓存系统,如何设计的。 3.如何防止缓存击穿和雪崩。 4.缓存数据过期后的更新如何设计。 5.redis的list结构相关的操作。 6.Redis的数据结构都有哪些。 7.Redis的使用要注意什么,讲讲持久化方式,内存设置,集群的应用和优劣势,淘汰策略等。 8.redis2和redis3的区别,redis3内部通讯机制。 9.当前redis集群有哪些玩法,各自优缺点,场景。 10.Memcache的原理,哪些数据适合放在缓存中。 11.redis和memcached 的内存管理的区别。 12.Redis的并发竞争问题如何解决,了解Redis事务的CAS操作吗。 13.Redis的选举算法和流程是怎样的。 14.redis的持久化的机制,aof和rdb的区别。 15.redis的集群怎么同步的数据的。 16.知道哪些redis的优化操作。 17.Reids的主从复制机制原理。 18.Redis的线程模型是什么。 19.请思考一个方案,设计一个可以控制缓存总体大小的自动适应的本地缓存。 20.如何看待缓存的使用(本地缓存,集中式缓存),简述本地缓存和集中式缓存和优缺点。 21.本地缓存在并发使用时的注意事项。搜索
1.elasticsearch了解多少,说说你们公司es的集群架构,索引数据大小,分片有多少,以及一些调优手段 。elasticsearch的倒排索引是什么。 2.elasticsearch 索引数据多了怎么办,如何调优,部署。 3.elasticsearch是如何实现master选举的。 4.详细描述一下Elasticsearch索引文档的过程。 5.详细描述一下Elasticsearch搜索的过程。 6.Elasticsearch在部署时,对Linux的设置有哪些优化方法? 7.lucence内部结构是什么。参考资料 [基础篇:详解锁原理,synchronized、volatile+cas底层实现] (https://segmentfault.com/a/1190000023315634)
结语
想去鹅厂啊,啊啊啊~