00-面试题杂项整理(新)

(持续维护…)

Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
1.阻塞队列xxxBlockingQueue:实现原理-空读取等待/满插入等待
2.不可变对象类和特点:String、基本类型包装类、BigInteger&BigDecimal - 天生线程安全
3.Java IO有哪些?NIO多路复用的实现原理?
Java的IO流,比如文件的输入输出流,属于同步、阻塞方式,代码简单,但效率和扩展有局限性;
网络通信的Socket相关IO流,也是同步、阻塞的流;
* jdk1.4引入了NIO,同步非阻塞,使用【Selector、Channel、Buffer】三个核心对象构建多路复用;
* jdk1.7引入了AIO,异步非阻塞,NIO的升级,加入了【事件和回调】来异步非阻塞处理数据。
NIO:
选择器(Selector):
Selector选择器/多路复用器,可以在单线程的情况下可以去维护多个Channel,也可以去维护多个连接。
通道(Channel):
nio操作通过通道开始,通道都会注册到一个选择器(Selector)上实现管理,在通过选择器将数据统一写入到 buffer中。
缓冲区(Buffer):
Buffer本质上就是一块内存区,可以用来读取数据,也就先将数据写入到缓冲区中、在统一的写入到硬盘上。
4.强引用、软引用、弱引用、虚引用?
强引用:直接创建的对象,如String s = "abc";
软引用:SoftReference,有用但非必需的对象。
弱引用:WeakReference,有用但非必需的对象,更短的生命周期。
虚引用:没有生命周期,也没有用的对象,在回收队列中还未被回收,准备回收的对象。
5.创建对象的方式:new、反射、clone、序列化。
6.Enumeration 与 Iterator 的区别?
Enumeration 速度是 Iterator 的 2倍,且占用更少的内存。但 Iterator 更安全。
7.字符串常量池存在内存中的哪里?
jdk 1.7 常量池在堆空间中(JVM规范中不再声明方法区)
jdk 1.8 常量池在元空间中(JVM规范声明了元空间)
8.JDK 1.8新特性:Lambda表达式、Stream API、函数式接口、接口允许default方法、接口允许static方法
9.谈谈你对 Java 平台的理解?“Java 是解释执行”,这句话正确吗?
① 一次编写,到处运行
② GC 垃圾回收器(自动回收,不需要让开发者关心内存回收问题)
JVM >> JRE >> JDK
.class 字节码文件 + JVM 实现了跨平台的到处运行。
10.请对比 Exception 和 Error,另外,运行时异常与一般异常有什么区别?
都继承自 Throwable 类
Exception 可抛出、可捕获、可处理,如NPE、下标越界异常、类型转换异常等
Error 不可处理,如OutOfMemoryError等
受查异常,编译期检查
运行异常,不会在编译期要求,但在运行时会导致程序终止退出,因此需要捕获处理异常
11.谈谈 finalfinally、 finalize 有什么不同?
final 修饰词
* final修饰类:最终类,不能被继承,如String、Math、System均为final修饰的类
* final修饰方法:最终方法,不能被覆盖
* final修饰变量:基本类型变量,值不可变;引用类型变量,地址不可变
* final作为形参使用的好处:拷贝引用,为了避免引用的地址值发生改变
例如被外部类的方法修改等,而导致内部类得到的值不一致,于是用final来让该引用不可改变。
② finalize() 方法
当对象被判定为垃圾对象时,由JVM自动调用此方法,用以标记垃圾对象,进入回收队列。
* 自动回收机制:JVM的内存耗尽,一次性回收所有辣鸡对象。
* 手动回收机制:使用 System.gc() 通知JVM触发垃圾回收。
finally 关键字
作为异常处理的一部分,它只能用在try-catch语句中,并且附带一个语句块,
表示这段语句最终一定会被执行(不管有没有抛出异常/不管是否有return),经常被用在需要释放资源的情况下。
12.String、StringBuffer、StringBuilder区别?
String: final 修饰的类,不可继承;属性也是 final修饰,拼接字符串操作都会产生新的 String 效率低些
StringBuilder: 可变长字符串,JDK5.0提供,运行效率快、线程不安全。
StringBuffer: 可变长字符串,JDK1.0提供,运行效率慢、线程安全。
13.谈谈Java反射机制,动态代理是基于什么原理?
反射是Java语言在程序运行时获取和操作类和对象的一种特殊的机制。
获取:对象的类对象Class,类的属性和方法,调用方法或者构造对象,获取类或方法的注解信息等..
动态代理,是程序在运行时动态构建代理对象,用于动态增强方法的调用的机制。
JDK自身提供的有动态代理,还有更高性能的CGLib代理等..
14.int和Integer有什么区别?谈谈Integer的值缓存范围。
int,基本数据类型,占4个字节。
Integer,包装数据类型,拥有一个int字段存储数值,和基本的数值操作、运算方法。
缓存值范围 -128~127 存储在一个static final常量cache中 JDK1.5后提供的,
赋值时调 valueOf() 方法会重用这些缓存的常用数,用于显著提高性能。
15.接口和抽象类的区别?
① 接口 interface 声明,抽象类 abstract 声明
② 接口 可以 implements 多实现,抽象类 只能 extends 单继承
③ 接口没有构造方法,抽象类 可以有构造方法
④ 接口的方法默认为公开抽象方法,属性默认为公开静态常量,抽象类方法和字段可以任意
⑤ 接口目的:接口声明 和 实现分离;抽象类目的:方法和属性代码重用
16.谈谈你知道的设计模式?
① 创建型模式:工厂模式、单例模式、建造者模式、原型模式
② 结构型模式:装饰者模式、代理模式、桥接模式、适配器模式、组合模式、外观模式、享元模式
③ 行为型模式:策略模式、解释器模式、命令模式、观察者模式、迭代器模式、模板方法模式、访问者模式、责任链模式
17.Spring等框架中使用了哪些设计模式?
① BeanFactory 和 ApplicationContext 使用了 工厂模式
② Bean的创建,不同的scope对象,使用了 单例模式 和 原型模式
③ AOP 面向切面编程 使用了 代理模式、装饰器模式、适配器模式
④ 各种事件的监听 使用了 观察者模式
⑤ 类似 JdbcTemplate 等 xxxTemplate 使用了 模板方法模式
18.有哪些方法可以在运行时动态生成一个Java类?
① 普通的开发过程,编写java代码,javac编译成class文件,然后通过类加载加载进入JVM,就成为程序运行时的java类。
② 还可以使用 Java Compiler API,这是 JDK 提供的标准 API,里面提供了与 javac 对等的编译器功能
③ 还利用 Java 字节码操纵工具和类库来实现,比如 ASM,Javassist,cglib等...
19.有人说Lambda表达式让Java程序慢30倍,你怎么看:主要原因是 lambda 的基准测试、自动装箱拆箱、启动过程变慢
20.哈希冲突?如何解决?
两个或多个不同的数据值被映射到了哈希表中相同的存储地址上。
解决:开放定址法、再哈希法

集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
1.对比Vector、ArrayList、 LinkedList 有何区别?
都继承自List接口,都属于有索引值的集合类型。
Vector:线程安全(synchronized),底层为数组,初始长度10,扩容因子1
ArrayList:非线程安全(查询快,增删慢-较LinkedList),底层为数组,初始长度10,扩容因子0.5
LinkedList:非线程安全(查询慢,增删快-较ArrayList),底层为双向链表,初始长度为0,根据元素数量递增长度
2.对比Hashtable、HashMap、TreeMap 有什么不同?谈谈对HashMap的理解?
都继承自Map接口,键值对形式存储和操作。
Hashtable:线程安全(synchronized),无序,不允许null值,子类Properties也是线程安全主要用于配置文件
HashMap:非线程安全,无序,key唯一,允许null键和值,初始容量16,扩容因子0.75(jdk1.7 数组+单向链表;
jdk1.8 数组+链表+红黑树[数组>64或链表>8时链表转为红黑树])
TreeMap:基于红黑树实现的Map,实现了SortedMap接口,key会自动排序
3.如何保证集合是线程安全的?ConcurrentHashMap如何实现高效的线程安全?
保证线程安全:synchronized 修饰集合的方法操作,或者 ReentrantLock 对公共代码区域加锁
线程安全的集合:
* Collections工具类下的静态方法,以synchronized开头的线程安全方法
* CopyOnWriteArrayList/CopyOnWriteSet/ConcurrentLinkedQueue(CAS算法+无锁)
* ConcurrentHashMap:
jdk1.7 使用分段锁设计,为每个 Segment 加锁
jdk1.8 使用CAS交换算法(比较和交换)+同步锁(锁Map中的表头-16容量,每个容量的Segment加锁)
4.并发包中的ConcurrentLinkedQueue和LinkedBlockingQueue有什么区别?
java.util.concurrent 并发包下主要是三类容器,Concurrent开头、CopyOnWrite开头、Blocking开头
ConcurrentLinkedQueue:
基于CAS算法即比较交换算法的无锁技术,即不需要在操作时使用锁而达到并发时的线程安全;
LinkedBlockingQueue:
内部则是基于锁(ReentrantLock重入锁)实现,提供了 BlockQueue 等特性方法。
如果不指定容量,则为Integer的最大值,也就是 无界队列。
5.Map类集合K/V不能存储 null 值的情况:
HashMap: key允许null, value允许null, 线程不安全
TreeMap: key不允许null, value允许null, 线程不安全
Hashtable: key不允许null, value不允许null, 线程安全(synchronized修饰方法)
ConcurrentHashMap: key不允许null, value不允许null, 线程安全
(分段锁×16,每个Segment加锁,jdk1.7分段锁,jdk1.8为CAS比较交换算法+同步锁(锁链表表头))
6.

线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
1.三种线程创建方式和四种线程池创建方式:Thread/Runnable/Callable、固定数量/动态缓存/周期/单线程
2.如何安全的终止线程方式:共享变量/中断异常
3.volatile:可见性/有序性/单次读写原子性、用于状态标记量如终止线程和单例模式双检锁
4.线程同步的方法-阻塞与线程间同步的方式:锁
5.Java线程调度算法:时间片轮转,可设优先级
6.线程的状态及状态之间的转换:
NEW --> start --> READY --> 系统调度 --> RUNNING --> 执行完成 --> TERMINATED
线程同步wait/join --> WAITING 、锁阻塞synchronized --> BLOCKED
7.一个线程两次调用start() 方法会出现什么情况? 谈谈线程的生命周期和状态转移。
Java线程不允许启动两次,第二次抛出 IllegalThreadStateException 非法的线程状态异常。-运行时异常。
线程状态:NEW创建 -> RUNNABLE就绪 -> BLOCKED阻塞 -> WAITING等待 -> TIMED_WAIT超时等待 -> TERMINATED终止
8.CAS比较并交换算法是什么?优缺点?
CAS包含3个参数:内存值V | 旧的预期值A | 新值B
当且仅当V == A时,将V改为B,如果V != A,说明有其他线程做了更新,则当前线程什么都不做,最后返回当前V的真实值。
CAS操作是抱着乐观的态度进行的(无锁,属于乐观锁范畴),它总是认为自己可以成功地完成操作。
优点:CAS由于是在硬件层面保证的原子性,不会锁住当前线程,它的效率是很高的。
缺点:
① ABA问题。即操作数据时从内存取出、在下一个时刻比较并替换,那么时间差可能导致数据发生变化。
部分乐观锁的实现通过 携带 版本号/时间戳 来解决ABA问题。
② 循环时间长开销大。自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。
因此CAS不适合竞争十分频繁的场景。
③ 只能保证一个共享变量的原子操作,多个共享变量时就得考虑使用锁。
9.Java并发类库提供的线程池有哪几种?分别有什么特点?
Executor接口:线程池顶级接口
ExecutorService接口:Executor的子接口,线程池接口
Executors类:工厂类/工具类
.newFixdThreadPool(int nThreads): 固定数量线程池
.newCachedThreadPool(): 带缓存的动态数量线程池,不够则创建,无上限
.newSingleThreadPool(): 单线程的线程池,死亡后重新创建新的代替线程继续执行
.newScheduledThreadPool(): 周期执行的线程池
10.阿里巴巴嵩山版 建议使用:ThreadPoolExecutor:
public ThreadPoolExecutor (
int corePoolSize, // 1 核心线程池大小
int maximumPoolSize, // 2 最大线程池大小
long keepAliveTime, // 3 线程最大空闲时间
TimeUnit unit, // 4 时间单位
BlockingQueue<Runnable> workQueue, // 5 线程等待队列
ThreadFactory threadFactory, // 6 线程创建工厂(一般用于给线程重命名)
RejectedExecutionHandler handler // 7 拒绝策略
) { ... }
11.高并发,处理时间短,使用哪种线程池?低并发,处理时间长,使用哪种线程池?高并发,处理时间长,如何处理?
高并发处理时间短:newCachedThreadPool()线程最大数可按照实际情况增加,无缓存队列,多核机器同步开销小
低并发处理时间长:根据业务场景使用7个参数的ThreadPoolExecutor,区分CPU密集型与IO密集型设置参数
高并发处理时间长:不是一个线程池就能解决的,需要引入分布式计算、负载均衡、集群等架构设计
12.阿里巴巴Java开发规范-嵩山版,并发模块强制注意事项
① 获取单例对象和单例的方法需要保证线程安全
② 线程资源必须通过线程池提供,且不能使用 Executors 创建,要使用 ThreadPoolExecutor 的方式,
规避资源耗尽(原因:固定线程数/单线程数/缓冲动态线程数 均可能堆积大量的线程,导致OOM[OutOfMemory])
③ SimpleDateFormat是线程不安全的类,一般不要定义为static变量,如果定义为static 必须加锁,或者使用DateUtils工具类
13.Java内存模型中的happen-before是什么?
happen-before关系,是Java内存模型中保证多线程操作可见性的机制。
14.Java 多线程在什么情况下使用?
数据同步比较慢的时候;
异步调用第三方接口的时候;
服务器需要同时接受多个客户端连接,有无需实时等待的服务处理的时候;
单线程处理很慢的时候,需要多线程并行执行的时候。
15.进程和线程之间的通信:
线程与线程通信:4
volatile/共享变量:保证所有线程对变量访问的可见性,获取或刷新共享内存
wait/notify机制:多个线程互斥访问临界区,配合 synchronized 关键字,方法 wait()、notify()、notifyaAl()
Lock/Condition机制:多个线程互斥访问临界区,Condition阻塞队列的方法 await()、signal()、signalAll()
Pipe管道:java.io.PipedInputStream 和 java.io.PipedOutputStream进行通信
Semaphore信号量:包括无名线程信号量和命名线程信号量
Socket套接字:可以跨设备的进程间通信
进程与进程通信:8
管道pipe:半双工,数据只能单向流动,只在亲缘关系(父子进程间)中
有名管道named pipe:半双工,允许在无亲缘关系中
信号signal:复杂通信方式,通知接收进程某个事件已经发生
信号量semaphore:锁机制的计数器,控制多个进程对共享资源的访问,进程间/同一进程的线程间
消息队列MQ:由消息的链表,存放在内核中并由消息队列标识符标识
共享内存shared memory:最快的 IPC 方式,映射一段能被其他进程所访问的内存,多个进程都可以访问
套接字Socket:可以跨设备的进程间通信
16.Thread.sleep(0)的作用:
①相当于插入了一个安全点(一个线程在运行 native 方法后,返回到 Java 线程后,必须进行一次 safepoint 的检测)
②避免程序GC线程长时间等待,实现GC削峰。

锁&原子类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
1.乐观锁、悲观锁:乐观每次拿数据判断再上锁-CAS-高吞吐量,悲观每次拿数据直接上锁
2.死锁条件与如何避免:资源分配不当/线程推进顺序不当
两个或多个线程,由于互相持有对方需要的锁,而一直等待对方释放锁的永久阻塞状态。
定位死锁:
① ps命令或任务管理器获取进程id,jstack 命令(win> jstack PID)获取线程栈,
找到 BLOCKED(on object monitor),进而找到死锁位置进行修复。
② Java提供的API ThreadMXBean,直接提供了 findDeadlockedThreads() 方法用于定位。
预防:
① 避免使用多个锁,确保需要时才持有锁;
② 多个锁时设计好锁的获取顺序(如银行家算法);
③ 使用带超时的方法更有可控性。
3.synchronized & ReentrantLock:
synchronized-可重入锁、悲观锁
ReentrantLock-可重入锁
区别-层面(前者JVM实现,后者java代码实现)
灵活性(后者灵活)
高级方法(后者更多)
场景(前者低并发性能高,后者高并发性能高)
4.不同锁的原理:
乐观锁&悲观锁 - 判断
可重入锁递归锁 - 外内层
公平锁&非公平锁 - 排队
读写锁、共享锁&独占锁 - 单与多
分段锁 - 思想
5.死锁、活锁、饥饿:
死:互相等待释放资源
活:无阻塞重复尝试
饥饿:无发获取资源而无法执行
6.锁的优化策略:读写分离、分段加锁、减少锁持有时间、多线程同顺序加锁-效率优化
7.synchronized 和ReentrantLock有什么区别?有人说synchronized最慢,这话靠谱吗?
synchronized:Java原生互斥锁、JVM获取/释放锁、无需手动释放锁、锁方法或代码块、低并发下性能高,可读性也好。
ReentrantLock:api层面互斥锁、手动加锁/释放锁、可超时中断、更多方法监听锁信息、在高并发下性能最好。
8.synchronized底层如何实现?什么是锁的升级、降级?
synchronized底层是有 monitorenter、monitorexit指令实现的。Monitor对象是同步的基本单元。
JDK1.6以后对其进行改进,提供三种Monitor,即三种锁:偏斜锁、轻量级锁、重量级锁,提高了性能。
锁的升级、降级:就是JVM对synchronized运行机制的优化,当JVM检测到不同的竞争状况时,自动切换到适合的锁的实现。
9.AtomicInteger底层实现原理是什么?如何在自己的产品代码中应用CAS操作?
int类型封装,提供原子性的访问和更新操作,原子性基于CAS ( compare-and-swap)技术,线程安全。
依赖于Unsafe提供的一些底层逻辑进行底层操作; 以volatile的value字段记录数值,以保证可见性。
10.分布式锁实现原理:
Redis分布式锁本质上将 SETNX 和 EXPIRE 两个命令写在同一个 Lua 脚本中,
然后通过调用 Jedis 的 eval() 方法来执行,并由 Redis 来保证整个 Lua 脚本操作的原子性。
11.synchronized锁升级过程:
无锁 ==> 偏向锁 ==> 轻量级锁 ==> 重量级锁
1>无锁:不使用锁实现线程安全,如CAS
2>偏向锁:如果一个线程多次获取锁,就会引起偏向锁,不再参与竞争,直接获取锁(提高性能)-JVM参数可设
3>轻量级锁:偏向锁被另外的线程访问则升级到轻量级锁
4>重量级锁:轻量级锁进一步升级为重量级锁,直接阻塞访问该锁的其他线程
12.Lock锁的底层AQS原理:
Lock锁的实现原理:AQS(AbstractQueueSynchronizer),即抽象的队列式同步器。
Lock --> AQS --> 设置线程同步状态 --> CAS
AQS里实现了一个双端队列,AQS里保存了队列的头尾节点,这个队列里边保存了许多线程的状态和方法,
双端队列保存线程及线程同步状态,并通过CAS提供设置同步状态的方法(AQS是基于CAS实现的)。
比如通过队列的一些方法可以获取/设置某个线程的状态 compareAndSetState() --> unsafe.compareAndSwapInt()
13.信号量Semaphore:
Semaphore,jdk5以后juc包中提供了官方实现,Semaphore是用来保护一个或者多个共享资源的访问。
一个计数器,一个等待队列,三个方法(init,down,up):
Semaphore semaphore = new Semaphore(10,true);
semaphore.acquire();
//do something here
semaphore.release();
1个线程访问共享资源 → 先获得信号量 → 当计数器>1时 → 计数器-1 → 成功访问共享资源
1个线程访问共享资源 → 先获得信号量 → 当计数器==0时 → 该线程休眠(等待唤醒访问共享资源)
某个线程用完共享资源 → 释放信号量 → 计数器+1 → 休眠线程被唤醒并获得信号量 → 计数器-1 → 成功访问共享资源
14.单例DCL可见性: (Double Check Lock 双检锁)
DCL单例不需要考虑可见性问题,因为初始化的是对象,new对象过程是原子性和有序性的。所以不存在并发问题。

JVM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
1.常见的垃圾回收算法:标记-整理/标记-清除/复制算法/分代算法
2.JVM内存模型:PC程序计数器、虚拟机栈、本地方法栈、堆、方法区/元空间,jdk1.8-元数据区方法区变为本地内存元空间
3.类加载过程?
类加载三个步骤:加载、链接、初始化。
加载:将字节码加载到JVM中。
链接:将类信息链接到JVM中运行。
初始化:执行类的初始化的逻辑,如静态字段赋值、静态代码块执行等...
4.什么是双亲委派模型?
如果一个类加载器需要加载类,那么首先它会把这个类请求委派给父类加载器去完成,每一层都是如此。
一直递归到顶层,当父加载器无法完成这个请求时,子类才会尝试去加载。
目的:使用委派模型的目的是避免重复加载Java类型。
5.谈谈JVM内存区域的划分,哪些区域可能发生 OutOfMemoryError?
PC程序计数器、虚拟机栈、本地方法栈、堆、方法区
常见发生OOM错误信息的原因:
* 堆内存不足
* 直接内存不足时
* 递归调用无退出条件不停压栈JVM去扩展栈空间的时候
* 老版本JDK上永久代回收内存不及时在大量动态类型生成时
6.监控和诊断JVM堆内和堆外内存?
① Java自带的 JConsole 图形化工具,连接到java进程,看到各内存使用情况
② 命令行工具 jstat 和 jmap 都提供了选项参数查看堆、方法区等数据
③ tomcat、weblogic 这样的 JavaEE 服务器,提供的有内存管理功能
④ GC日志的输出,也包含了内存丰富的信息
7.Java常见的垃圾收集器?
① 串行:Serial GC 单线程老年代垃圾收集器
② 并行:Parrallel GC 吞吐量优先的GC(JDK8默认)
③ CMS:CMS GC 标记-清除算法
8.GC调优思路?
1. 确定调优目标:内存占用、延时、吞吐量
2. 掌握JVM和GC的状态,定位问题,jstat查看GC状态,开启GC日志
3. 分析确定具体调整的参数或软硬件配置
4. 验证是否达到调优目标,否则重复上述步骤
9.JVM优化Java代码时都做了什么?
运行时优化、即时编译器优化。
运行时优化:解释执行和动态编译通用的一些机制,如锁机制、内存分配机制等...
即时编译器优化:代码以方法为单位转换为机器码,直接运行在底层硬件上。
10.JMM内存模型:
JMM内存模型规范中规定所有的变量都存储在主内存中,而主内存中的变量是所有的线程都可以共享的。
对主内存中的变量进行操作时,必须在线程的工作内存进行操作,即:
首先将主内存的变量copy到工作内存,进行操作后,再将变量刷回到主内存中,所有线程只有通过主内存来进行通信。
主内存:线程不安全 工作内存:线程安全
JMM主要维护程序执行的3种特性:
原子性(synchronized和Lock)
可见性(volatilesynchronized和Lock同步即串行执行所以保证了可见性)
有序性(volatilesynchronized和Lock同步即串行执行所以保证了有序性)
11.Java内存模型中的happen-before:是Java内存模型中保证多线程操作可见性的机制。
12.指令重排?为什么需要指令重排序?
即只要程序的最终结果与顺序执行的结果保持一致,则虚拟机就可以进行排序。
原因:jvm根据处理器特性(cpu多级缓存、多核处理器等)适当的对机器指令进行排序,
使机器指令能更符合CPU的执行特性,最大的限度发挥机器性能。
13.JVM调优工具:
jps: 虚拟机进程状态工具
jstat: 虚拟机统计信息监控工具
jinfo: 实时查看和调整虚拟机各项参数
jmap: 生成虚拟机的内存转储快照(heapdump文件)
jstack: 堆栈跟踪工具
JConsole: JMX的可视化管理工具,java GUI监视工具,使用率高
14.JVM中一次完整的GC流程是怎样的:
对象的正常流程:Eden 区 -> Survivor 区 -> 老年代。
新生代GC:Minor GC;老年代GC:Full GC
【总结】:内存不够用就会引发GC,JVM会“stop the world”,严重影响性能。Minor GC 避免不了,Full GC 尽量避免。
【处理方式】:保存堆栈快照日志、分析内存泄漏、调整内存设置控制垃圾回收频率,选择合适的垃圾回收器等。

MySQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
1.MySQL数据库引擎及其区别:MYISAM & InnorDB,事务、外键、锁级别、并发性能、占用空间
2.MySQL数据库索引种类、索引原则:主键、唯一、普通、全文
3.数据库三大范式:列不可再分、主键约束、外键约束
4.SQL语句优化:不*、不子查询、不IN/NOT IN、不OR、不!=、不null判断、DISTINCT与ORDER BY结合
5.大表如何优化:限定查询数据范围、读写分离、垂直分区
6.数据库并发策略:乐观锁、悲观锁、时间戳
7.并发事务带来的问题:脏读、丢失修改、不可重复读、幻读
8.分库分表后id主键如何处理:
UUID - 效率低
自增不同步长id - 成本高
redis生成 - 可用性低
美团Leaf分布式id生成系统 - 全局唯一高可用高性能
9.AUTO_INCREMENT:达最大值情况止增,报错、最后一次插入的自动增量 - LAST_INSERT_ID主键回填
10.TIMESTAMP列情况更改,时间戳:Unix和MySQL时间戳转换UNIX_TIMESTAMP/FROM_UNIXTIME
11.索引的增删改查:INDEX 对表操作,CREATE/DELETE/UPDATE/SELECT、ALTER
12.数据库货币类型:NUMERICM,D 等价于 DECIMALM,D : M代表总位数,D代表小数点后的位数
13.索引的底层原理和优化:
原理:在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的行数据记录。
这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。
一棵B+tree可以存储约2000万行数据。
聚簇索引(主键索引):
非叶子节点存储索引,叶子节点存储表的完整行数据,每张表只有一个聚簇索引。
PS:MyIsam存的是数据的物理地址。
非聚簇索引(辅助索引):存储了当前行数据对应的主键值,所以辅助索引查询数据需要二次查询主键索引树(回表)
优化:结构优化、查询优化
14.如何正确使用索引: explain、表扫描类型:all、index、range、ref、eq_ref、const、system
15.MySQL在实践中的优化:
按顺序:sql语句和索引、表结构、系统配置、硬件
优化:减少字段宽度、not nullenum、join代替子查询、事务、锁定表、外键、索引、sql语句写法
16.数据库存储过程?如何调用:
类似函数写法:预编译SQL语句,允许模块化的设计,创建一次可调多次。create procedure xx/ call xx
17.有哪些树结构?
二叉树、满二叉树、完全二叉树、二叉搜索树、平衡二叉树、平衡二叉搜索树、红黑树、B树、B+树、哈夫曼树
平衡二叉树:
平衡二叉树是空树或者左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是平衡二叉树。
平衡二叉树牺牲了插入和删除数据的代价,但是提高了查询效率(稳定查询效率为 O(log n))。
红黑树:
红黑树是一种自平衡二叉排序树,在 O(log n) 时间复杂度内实现查找/插入/删除,任何不平衡都在三次旋转内解决。
18.BTree和B+Tree:
BTree:平衡搜索多叉树,可以使用二分查找的查找方式,查找复杂度为h*log(n),
一般来说树的高度是很小的,一般为3左右,因此BTree是一个非常高效的查找结构。
B+Tree:BTree的一个变种,B+Tree磁盘读写代价更低,数据库存储引擎对B+Tree进行了优化,
关键字数和子树相同、非叶子节点仅用作索引、叶子节点存储行数据且用指针连在一起。
添加了相邻叶节点的指针,查询速度更加稳定高效。
19.MySQL事务隔离级别,悲观锁和乐观锁原理和应用场景?
读未提交:一个事务可以看到其他事务未提交的修改。【允许脏读】
读已提交:一个事务能看到其他事务已经提交的修改。【允许不可重复读和幻读】
可重复读:保证同一个事务多次读取的数据是一致的。【MySQL innoDB默认隔离级别,不会出现幻读:next-key lock机制】
串行化:并发事务之间是串行化。读取需要获取共享锁,更新需要获取排它锁。【最高隔离级别】
【脏读】:一个事务读到了另一个事务未提交的数据
【不可重复读】:一个事务读到了另一个事务已经提交的数据,导致前后不一致
【幻读】:在事务A中按照某个条件先后两次查询数据库,两次查询结果的条数不同
20.MySQL行级/表级锁的使用:
* 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
* 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
* 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般
21.分库分表使用场景:
IO瓶颈:
1.磁盘读IO瓶颈,热点数据太多,每次查询时会产生大量的IO,降低查询速度。--> 分库/垂直分表
2.网络IO瓶颈,请求的数据太多,网络带宽不够。--> 分库
CPU瓶颈:
1.SQL问题,如SQL非索引字段条件查询等,增加CPU运算的操作。--> 【优先】SQL优化/合适的索引/业务代码计算
2.单表数据量太大,查询时扫描的行太多,SQL效率低,CPU率先出现瓶颈这种情况。--> 水平分表
22.分库分表的策略:
hash取模
比如,对用户表user_t进行水平分表,分库的策略是对user_id字段进行取模。
如果取模结果是0,则放入user_t_01表;如果取模结果是1,则放入user_t_02表。
范围分片(range)
比如,user_id从110000作为一个分片,从1000120000作为另一个分片。
地理位置分片
华南区一个分片,华北一个分片。
时间分片
按月、季度、年分片等等,可以做到冷热数据。
比如,今年内的数据一般就是热数据,而往年的数据就是冷数据。
那么可以分为 user_t_2021、user_t_2020等表,user_t_2021是热数据,user_t_2020为冷数据。
23.分库分表后如何分页:
1)全局查询法:每次分页查询时,查询所有存储下相应的条数,汇总排序得到最终分页数量(性能低,CPU和内存占用高)
Sharding-JDBC采用流失处理+归并排序来避免内存过度占用。
2)禁止跳页法:只提供下一页,不提供指定页码的查询方法。查询多个表,总共只需返回1页数据即可(eg:2个表各查5条)。
3)二次查询法:
3.1求得每个表的查询条数actualPageSize,用每页查询的数量 整除 分表总数量(5)。
eg: 每页10条 / 分表总数5个表 = 2(向上取整,即舍弃小数)
3.2查询每张分表下相应页数的actualPageSize条
eg: limit page * actualPageSize, actualPageSize
3.3汇总所有表的查询,排序得到最小记录标识符minId(排序字段),并记录每个表各自最大记录标识符maxId。
ps: minId只有一个,maxId每个索引都有自己的一个。
3.4在每个表中使用 between minId,maxId 查询相应的结果后汇总排序,得到前10条记录返回。
4)备选方案:同步到ES中,用ES来分页查询。
24.MySQL主从复制和读写分离:
主从复制:同步复制、异步复制、半同步复制、增强半同步复制,成熟方案:Sharding-JDBC / MyCat
复制过程:master数据改变记录binlog → slave检测binlog改变 → slave启动I/O线程,master启动dump线程 →
线程通信保存slave本地relaylog中 → slave启动SQL线程读取二进制日志 → 解析SQL执行 → 完成复制
读写分离:
1)代码实现:根据 select、insert/update/delete进行读或写的路由分类,写走主库,读走从库
2)中间代理层实现:Cobar(阿里巴巴)、MySQL-Proxy(官方)、Atlas(奇虎360)
25.InnoDB为什么不用跳表,Redis为什么不用B+树?
B+树是一种磁盘IO友好型的数据结构:
充分利用磁盘预读和缓存机制,减少磁盘 I/O 的次数,当Innodb中存储2000万数据的时候,只需要3次磁盘IO。
而跳表是多级索引结构,索引节点数据存储分散,物理地址相隔较远,随机访问分散数据增加磁盘磁头寻道时间。
跳表则是一种内存友好型的数据结构:
Redis的集合经常需要插入、删除和更新操作,动态性能更好。

事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
1.你更倾向于那种事务管理类型:大多数Spring开发者选择声明式事务管理,对代码影响小、无代码侵入、轻量级容器
2.数据库事务的实现原理:
1)数据库会为每一个客户端都维护一个【空间独立的缓存区(回滚段)】 →
2)一个事务中所有的增删改语句的执行结果都会缓存在回滚段中 →
3)只有当事务中所有 SQL 语句均正常结束(commit),才会将回滚段中的数据同步到数据库 →
4)否则无论因为哪种原因失败,整个事务将回滚(rollback),即不会同步到数据库。
3.事务的隔离级别实现原理:
MySQL 默认隔离级别 RR(可重复读)解决脏读、不可重复读、幻读等问题,使用的是MVCC:
MVCC多版本的并发控制协议,在同一时刻,不同的事务读取到的数据可能是不同的(即多版本)。
MVCC最大的优点是读不加锁,因此读写不冲突,并发性能好。
InnoDB实现的RR,通过锁机制、数据的隐藏列、undo log和类next-key lock,基本实现隔离性,满足大多数场景。
4.Spring事务的实现原理:基于数据库事务,通过注解+AOP实现
1)@EnableTransactionManagement 注解是用来【开启事务aop】
2)@Transactional 就是切点,有这个注解的类都会被【代理】
3)代理类根据事务的注解属性,判断是否要创建【事务管理器】
4)使用事务管理器去创建【事务连接】,即 doGetConnection 通过 getResource返回一个ConnectionHolder对象
该对象是通过线程的ThreadLocalMap来获取,线程的变量副本也就对应了传播机制的内容
5)后续通过事务连接进行增删改查操作
5.Spring事务的传播行为:7种传播行为,@Transactional(propagation=Propagation.XXX)
ROPAGATION_REQUIRED : 支持当前事务,如果不存在 就新建一个
PROPAGATION_SUPPORTS : 支持当前事务,如果不存在,就不使用事务
PROPAGATION_MANDATORY : 支持当前事务,如果不存在,抛出异常
PROPAGATION_REQUIRES_NEW : 如果有事务存在,挂起当前事务,创建一个新的事务
PROPAGATION_NOT_SUPPORTED : 以非事务方式运行,如果有事务存在,挂起当前事务
PROPAGATION_NEVER : 以非事务方式运行,如果有事务存在,抛出异常
PROPAGATION_NESTED : 如果当前事务存在,则嵌套事务执行。基于 SavePoint 技术
6.分布式事务常用方案:
一阶段提交(1PC):在Spring中,通过注解,就可以完成这样的事务。非分布式的ACID。
两阶段提交(2PC):协调者(事务管理器) + 事务参与者(具体资源),XA事务,Spring的JTA事务管理器
三阶段提交(3PC):3PC比2PC多了一个步骤,那就是询问阶段。
TCC:柔性事务,大名鼎鼎的【补偿事务】,是互联网环境最常用的分布式事务。
核心思想是:为每一个操作,都准备一个确认动作和相应的补偿动作,一共3个方法: try(),confirm(),cancel()
SAGA:柔性事务,主要处理的是长活事务,但它不保证ACID,只保证最终一致性。靠消息驱动完成事务运转。
本地消息表:将需要分布式处理的任务通过消息日志的方式来异步执行。要【靠MQ实现】,有局限性。
可靠消息最终一致性:去掉了本地消息表,【轮询 prepare 消息状态】来重试或者回滚该消息替代。
最大努力通知:一种衰减式的补偿机制,最清晰的例子就是如【支付回调】。
7.Seata分布式事务框架:
Seata有 4 种分布式事务解决方案: AT 模式、TCC 模式、Saga 模式和 XA 模式。
AT模式:自动化事务,使用非常简单,对业务代码没有侵入性。
8.事务失效的场景:
不生效:
1)访问权限非public修饰的方法
2)方法用 final 修饰
3)在同一个类内调用
4)未被Spring管理的类
5)多线程调用
6)表不支持事务
7)未开启事务
不回滚:
1)错误的传播特性
2)自己吞了异常
3)手动抛了别的异常
4)自定义了回滚异常
5)嵌套事务回滚多了
解决方案:
a.规避以上场景的出现
b.使用编程式事务,避免aop的失效,还可以更小粒度控制事务范围

Redis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
1.RESP协议:
redis的服务器与客户端之间的通信协议,【Redis序列化协议】的简写,它是⼀种直观的【文本协议】。
实现简单、解析快、人类可读的特点,底层【TCP连接】方式,tcp数据传输,根据解析规则解析相应信息完成交互。
2.Redis淘汰策略:
内存已经满的时候,添加1个新数据,会执行淘汰机制:
* no-enviction: 禁止驱逐数据,内存不足,读取正常,写入报错,保证数据不丢失 --> 【默认策略】
* volatile-lru: 已设置过期时间 + 最近最少使用 --> 淘汰
* volatile-ttl: 已设置过期时间 + 将要过期 --> 淘汰
* volatile-random: 已设置过期时间 + 任意随机 --> 淘汰
* volatile-lfu: 已设置过期时间 + 使用频率最低 --> 淘汰
* allkeys-lru: 最近最少使用 --> 淘汰
* allkeys-lfu: 使用频率最低 --> 淘汰
* allkeys-lru: 任意随机 --> 淘汰
3.Redis为什么快:①内存数据库,纯内存操作;②单线程,避免频繁的上下文切换;③多路IO复用(NIO)-并发请求
4.Redis持久化:
RDB-快照写入磁盘dump.rdb文件
AOF-追加到appendonly.aof文件
5.Redis穿透/击穿/雪崩/倾斜:
穿透:查不到,缓存层+持久层压力增大 - 解决:布隆过滤器(bitmap)、缓存空对象
击穿:并发下,key大量过期导致直接访问持久层 - 解决:热点数据永不过期、分布式锁
雪崩:并发下,key大量过期导致缓存层+持久层宕机 - 解决:集群、限流降级、数据预热
倾斜:某个缓存服务器压力过大而宕机 - 解决:集群
6.Redis的删除策略:
定期删除:默认100ms查看3个过期的key,定期删除
惰性删除:查询时,redis检查是否过期,过期则删除key,返回空值
7.采用定期删除+惰性删除就没其他问题了么?
不是,如果定期删除没删除key。然后也没及时去请求key,也就是说惰性删除也没生效。
这样,redis的内存会越来越高。那么就应该采用内存淘汰机制。在redis.conf中有一行配置:
maxmemory-policy volatile-lru
8.Redis为什么采用跳表SkipList而不是红黑树?
SkipList:链表中加上多级索引的结构,就叫做跳表,查询效率会进一步提升。
原因:代码简单!在已查找到的起点开始顺序遍历的性能略微高于红黑树。
9.Redis scan代替keys的原因?
由于 keys 命令会影响 redis 性能,所以将 keys 命令改为 scan。
scan命令用于迭代当前数据库中的数据库键,最重要的是 scan 不会阻塞服务器。
10.String底层实现为什么是SDS,SDS和C字符串的比较?
* SDS simple dynamic string
* len:记录buf数组中已使用的字节的长度。
* alloc:已分配的空间,不包含头和空终止符。
* flags:前三位表示不同的sds结构体的类型,后五位预留。
* buf:字节数组,用于保存字符串。
原因:1.获取长度的复杂度降低;2.避免内存溢出;3.空间预分配;4.惰性删除
①sds 简单动态字符串,是一个C语言结构体
②获取字符串长度操作的复杂度从O(n)降到O(1)
③采用空间预分配策略杜绝缓冲区溢出(字符串增长操作),同时可以减少连续执行字符串增长操作所需的内存重分配次数
④采用惰性空间释放策略杜绝内存泄漏(字符串删除操作),同时可以减少删除后又对同一字符串增加所需的内存重分配次数
⑤二进制安全,适用于各种不同的使用场景。C字符串只能保存文本,而不能保存图片/音频/视频/压缩文件等二进制数据
⑥兼容部分 C 字符串函数
11.Redis数据类型和应用场景:
String:value可以是String也可以是数字,可用于复杂的计数功能的缓存;
Hash:可用于单点登录,设置过期时间,模拟出session的效果;
List:可用于做简单的消息队列、lrange做基于redis的分页功能时性能极佳、生产者消费者场景实现FIFO;
Set:可用于全局去重,以及利用交集、并集、差集操作计算共同喜好、全部喜好、独特喜好等;
Sorted Set:权重参数 score 可以用于排行榜应用,取top n操作。
总结:redis 的使用场景:会话缓存、全页缓存、队列、排行榜/计数器、发布/订阅
12.Redis其他数据类型:BitMap、Geo、HyperLogLogs
BitMap:通过一个 bit 位来表示某个元素对应的值或者状态
Geo:将用户给定的地理位置信息储存起来, 并对这些信息进行操作
HyperLogLogs:基数统计,可以非常省内存的去统计各种计数,比如注册IP数/每日访问IP数/页面实时UV/在线用户数等
13.Redis的同步机制:
Redis 可以使用主从同步,从从同步。
第一次同步时,主节点做一次 bgsave,并同时将后续修改操作记录到内存 buffer。
记录完成后,将 rdb文件全量同步到复制节点,复制节点接受完成后将 rdb 镜像加载到内存。
加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。
14.Redis 事务的命令:MULTI, EXEC, DISCARD, WATCH
15.Redis IO多路复用:【一个服务端进程】可以同时处理【多个套接字描述符】。
IO 多路复用只需要一个进程就能够处理多个套接字,【解决了上下文切换问题】。主要方法 select()/poll()/epoll()
多路:多个客户端连接(连接就是套接字描述符)。
复用:使用单进程就能够实现同时处理多个客户端的连接。
16.

Spring

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
1.Spring/SpringMVC/SpringBoot/SpringCloud关系和区别:
Spring-开源容器框架,配置bean,维护bean之间关系,核心为 IOC 和 AOP
SpringMVC-Web层MVC框架,替代 Servlet,相当于 Struts2 + Spring
SpringBoot-快速开发框架,延续核心 IOC/AOP,简化开发、配置、部署
Spring Cloud-基于Spring Boot的分布式系统框架,服务发现/注册、分布式配置管理、负载均衡、分布式诊断等
Spring Cloud > SpringBoot > Spring > SpringMVC
2.Spring 中有多少种IOC容器?IOC原理?IOC的理解?
BeanFactory-懒加载、ApplicationContext-即时加载,国际化
IOC原理:工厂模式 + 反射。
IOC理解:控制反转,将创建对象的控制权交给 Spring。
IOC的三种注入方式:构造、setter、注解
3.Spring AOP的理解?
基于动态代理和字节码操作。
面向切面编程,横向抽取,取代重复代码/性能监视/事务管理/安全检查/缓存,纯Java实现,底层原理是 动态代理机制。
如果类实现了 InvocationHandler 接口,则使用JDK动态代理生成对象;反之,则使用CGLib生成代理对象。
4.Spring Bean 配置方式:基于XML配置、基于注解配置、基于Java配置(如@Configuration, @Bean
5.Spring Bean 生命周期:实例化 → 依赖注入 → init-method → destory-method
6.Spring Bean 作用域:singleton、prototype、request、session、global-session
7.Spring注入的区别:构造注入-强制依赖、setter方法注入-可选依赖、注解注入
8.Spring框架中的 单例 bean是线程安全的吗:
不是。Spring框架并没有对单例bean进行任何多线程的封装处理。
一般情况下Spring bean没有可变的状态,可以理解为线程安全的。
如果Bean有多种状态变化的话(如 View Model对象),就需要自行保证线程安全。
最浅显的解决办法:配置为 prototype 多例
9.AOP的应用:
Logging 日志
Transactions 事务
Authentication 权限
Caching 缓存
Context passing 内容传递
Error handling 错误处理
Lazy loading 懒加载
Debugging 调试
logging,tracing,profiling and monitoring 记录跟踪 优化 校准
Performance optimization 性能优化
Persistence 持久化
Resource pooling 资源池
Synchronization 同步
10.@Autowired的实现原理:
默认使用byType来自动装配(@Resource<名称+类型> = @Autowired<按类型注入> + @Qualifier<按名称注入>)。
在容器启动,为对象赋值的时候,遇到@Autowired注解,会用【后置处理器机制】,来创建属性的实例
然后再利用【反射机制】,将实例化好的属性,赋值给对象。
11.Spring解决循环依赖实现原理:
循环依赖,即Spring类的属性的循环依赖,A对象依赖B对象,B对象又依赖A对象。
通过提前暴露半成品对象(Early-Stage Object)来解决。
主要依赖于两个技术:BeanPostProcessor 和三级缓存,大致分为3个步骤:
1)创建 Bean 的半成品对象,并将其添加到缓存中。
2)注入该 Bean 所依赖的其他 Bean。
3)完成 Bean 的初始化,将半成品对象转换为完整的 Bean 对象。
解决循环依赖也有一些限制:
①只适用于 singleton (单例)作用域的 Bean。
②只适用于 constructor 和 setter 注入方式。
12.Spring七大模块:
AOP/Core/Context/DAO/OEM/Web/Web MVC

SpringMVC

1
2
3
1.Springmvc运行流程:
请求 → 前端控制器 DispatcherServlet → 处理器映射器 → 适配器 → 匹配处理器 → 返回modelAndView
→ 前端控制器转给视图解析器 → 解析视图 → 将视图返回响应给用户

SpringBoot

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
1.springboot自动配置:
springboot提供了应用程序和框架所需的基本配置信息,而且约定大于配置。
SpringBoot主配置类只要标注上 @SpringBootApplication 的注解,Spring就会帮我们自动配置各个组件和实例化Bean。
【核心】启动类注解 → 自动配置注解 → 自动配置包注解(扫描包) → @Import注解导入组件 → 完成自动配置
>@SpringBootApplication
这个类是 SpringBoot 的主配置类,而且也是一个组合注解。
SpringBoot 就应该运行这个类的 main 方法来启动 SpringBoot 应用。
>>@EnableAutoConfiguration
包含一个 @AutoConfigurationPackage 注解。
将主配置类(@SpringBootConfiguration标注的类)所在包及下面所有子包里的组件【扫描】到Spring容器中。
>>>@AutoConfigurationPackage
自动配置包,主要是使用的@Import来给Spring容器中导入一个组件,这里导入的是Registrar.class。
可以用debug模式在这个类的方法中加断点,可以看到扫描包的路径。
将主配置类(即@SpringBootApplication标注的类)所在包及下面所有子包里的组件【加载】到Spring容器中。
>>>@Import
这个注解就是给Spring容器中【导入组件】使用的。
Spring Boot启动时从类路径META-INF/spring.factories中获取EnableAutoConfiguration指定的值,
将这些值作为自动配置类导入到容器中,自动配置类就生效,进行自动配置工作(以前手动,现在自动)。
>>@SpringBootConfiguration
该注解表示这是一个 SpringBoot 的配置类,其实它就是一个 @Configuration 注解,也是一个Spring组件
>>@ComponentScan
开启组件扫描。
2.SpringBoot启动流程:
【核心】:
run() → 新建SpringApplication对象 → 准备环境 → 创建Context → 刷新上下文 → 发布事件 → 执行runner → 就绪
①首先进入SpringAplication类 run() 方法
②run方法新建SpringApplication对象,创建并启动【计时监控类】
③通过configureHeadlessProperty设置java.awt.headless的值
④prepareEnvironment根据运行监听器和参数【准备spring环境】
⑤调用createApplicationContext方法创建应用上下文
⑥refreshContext方法刷新上下文
⑦调用started发布应用上下文启动完成事件
⑧callRunners方法执行所有runner运行器
⑨调用running发布应用上下文就绪事件
⑩最后返回应用上下文
3.SpringBoot约定大约配置逻辑:
Spring Boot 约定,当你导入 spring-boot-starter-web 后,就约定了你是一个 web 开发环境 和 一个内置 tomcat;
当你是一个 web 环境,就约定了你会使用 Spring MVC;
至于其它的也约定你会需要,都给你默认导入进来,当你觉得不合适的时候,可以用更少的改动,满足你的需要。
【SpringBoot 约定以 starter 的形式减少依赖】。
4.

SpringCloud(微服务)

1
2
3
4
5
6
7
8
9
10
11
12
1.SpringCloudAlibaba组件:
Gateway:网关中心
Nacos:注册中心 & 配置中心
Dubbo:高性能 RPC 框架
RocketMQ:分布式消息队列
Sentinel:分布式流控
Sleuth:链路追踪
Seata:分布式事务
Feign+Ribbon:远程调用+负载均衡
Alibaba Cloud OSS:阿里云对象存储服务
Alibaba Cloud SchedulerX:分布式任务调度
Alibaba Cloud SMS:短信服务

中间件

Nacos

1
2
3
4
5
6
7
8
9
1.nacos注册中心实现原理:
服务注册 + 服务发现
1)服务提供者 启动通过 openAPI 发起服务注册
2)服务提供者 建立心跳机制【5s上报,15s不健康,30s剔除】检查服务状态
3)服务消费者 查询服务注册表:注册表中维护服务实例名称、ip地址端口、健康状态等信息,以及控制上线/下线
4)服务消费者 定时任务,每隔【10s】拉取一次数据
5)注册中心 检测到服务提供者异常,则通过UDP协议推送更新
调用方发起调用时,通过注册中心的服务实例名称,找到ip地址端口发送请求和接收响应
PS:nacos依赖包中包含 ribbon 因此默认负载均衡为 轮询 机制。

MyBatis&MyBatisPlus

1
2
3
4
5
6
7
8
9
10
11
12
1.MyBatis的理解?
半自动ORM框架,XML或注解来配置和映射原生信息,消除JDBC冗余,与Spring集成,专注于SQL,灵活的DAO层解决方案。
2.分页原理:
PageHelper.startPage方法放入到ThreadLocal中放入page对象;
根据PageHelper的skip方法查看是否需要分页,判断条件是ThreadLocal中是否有page对象;
然后从ThreadLocal中的page对象中取出页数和总条数count,拼接到SQL语句的 limit 中。
3.MyBatis实现原理:
MyBatis应用程序根据XML配置文件创建【SqlSessionFactory】,
SqlSessionFactory根据配置(一处是配置文件,一处是Java代码的注解)获取1个【SqlSession】。
SqlSession包含了执行sql所需要的所有方法,可以通过SqlSession实例直接【运行映射的sql语句】,
完成对数据的增删改查和事务提交等,用完之后【关闭SqlSession】。
4.

ElasticSearch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
0.ES的术语:
·索引(Index):类似table表。相似结构文档的集合,分散在分片上
·文档(Document):类似row行。所有可搜索数据的最小单位,序列化为JSON格式
·字段(Field):类似column列
·类型(Type):索引中的逻辑数据分类。在6.0版本已经废除,7.01个索引只能创建一个Type,即 _doc
·映射(Mapping):类似schema逻辑结构。
·分片(Shard):分片是一个完整的索引,即一个 Lucene 的索引。
分片的作用是将数据分散存储,提高并发性能和可扩展性。
每个分片可以被分布在不同的节点上,这样可以并行处理查询和写入操作。
分片的数量在索引创建时指定,一旦创建后就不能更改。
分片的数量应该根据数据量和集群规模进行合理的配置。
主分片:解决水平扩展问题。 副本分片:解决数据高可用问题。
·分段(Segment):分段是一个独立的倒排索引文件。(每个分片又被划分为多个分段)
分段的作用是对数据进行更细粒度的管理和优化。
当索引被更新时,ES会将更新操作写入到新的分段中,而不是直接修改原有的分段。提高写入性能且避免数据不一致。
当查询时,ES会将查询请求发送到所有相关的分段上,并将结果合并返回给客户端。
·节点(Node):ES实例,本质是一个进程。(通常1台机器上部署1个节点)
主节点、候选节点、协调节点、冷热节点、机器学习节点、部落节点、预处理节点
·集群(cluster):一个或多个节点组成。
Green:主分片与副本正常分配、Yellow:主分片正常分配,有副本分片未能正常分配、Red:有主分片未能分配
1.ES索引层面调优:
①设计调优:根据增量采用日期模版创建索引、别名管理索引、凌晨定时force_merge、冷热分离(热SSD冷shrink)
②写入调优:写前副本数设置为0、禁用刷新机制、采用bulk批量写入、写入后恢复副本数和刷新间隔、使用自动生成id
③查询调优:禁用通配符、禁用批量terms、充分利用倒排索引、基于时间确定索引再检索、设置合理的路由
2.ES的倒排索引:
传统检索:是通过文档逐个遍历找到对应关键词的位置。
倒排索引:
分词策略,词和文档映射关系表,词典+映射表组成了倒排索引。
从词出发,记录了词在哪些文档中出现过。底层基于 FST 数据结构,lucene 4+版本大量使用,空间小、查询快。
3.ES数据多了如何调优:
①模板+时间+rollover api 滚动创建索引
②冷热数据分离:比如最近一周或三天的数据为热、其余为冷,定期force_merge加shrink压缩操作,节省空间和检索效率
③ES支持的动态扩展:动态新增机器。
4.ES实现master选举:
1)确认候选主节点数达标,只有候选主节点(master:true)的才能成为主节点
2)判定是否具备 master 资格,具备候选主节点资格的优先返回;若两节点都为候选主节点,则 id 小的值会主节点。
5.ES索引文档的过程:
1)客户写集群某节点写入数据,发送请求。
2)节点接收到请求,通过文档_id确定文档所属的分片,通过分片确定所属节点
3)所属节点为主节点,写成功后,将请求并行转发到其他节点上;所有副本分片写成功,主节点报告成功
路由算法:1shard = hash(_routing) % (num_of_primary_shards) --类似环形数组方式
6.ES搜索的过程:
1)query:定位到位置,不取数据。所有分片本地查询,结果返回到本地有序的队列中 → 协调节点
2)fetch:取数据。协调节点产生全局排序列表,路由节点获取所有文档返回
7.ES部署优化:
关闭缓存swap、设置内存的一半、使用SSD、设置最大文件句柄数、磁盘存储raid方式(raid10增加单节点性能和保障)
8.ES更新和删除:
都是写操作,ES文档不可变,不能被删除或改动。
磁盘上每个段都有一个.del文件,删除时文档会被标记在.del文件中,查询时会在结果中滤掉。
9.并发情况下保证读写一致:
①版本号:乐观锁控制并发 - 应用层代码逻辑处理。
②写操作:一致性级别设置为 quorum,即只有当大多数分片可用时才允许写操作。
③读操作:设置 replication 为 sync(默认),使操作在主分片和副本分片都完成后才会返回。
10.java客户端:

Netty

1
2
3
4
5
6
1.Netty如何实现高性能的吗?
1)灵活的线程模型,高效管理Channel
2)充分利用 java 的 Zero-copy 机制,降低内存分配和回收的开销,大大提高性能
3)使用更多本地代码
4)通信协议、序列化等其他角度的优化
2.

MQ消息队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1.RocketMQ底层刷盘逻辑:
同步刷盘、异步刷盘
①同步刷盘:Producer发送消息 → Broker接收 → PageCache内存写入 → 启动刷盘线程 → 当前线程等待 →
刷盘线程完成 → 唤醒当前线程 → 返回写成功 → Producer收到发送成功的ACK
②异步刷盘:Producer发送消息 → Broker接收 → PageCache内存写入 → 立即返回写成功给Producer →
另一个异步线程专门将PageCache的数据写入到磁盘(持久化)
Broker配置文件修改刷盘模式:
flushDiskType配置设置为:SYNC_FLUSH,默认值是 ASYNC_FLUSH(异步刷盘)。
2.RocketMQ底层复制逻辑:
同步复制、异步复制
①同步复制:master发送消息给slave → master等待 → master和slave都写入成功 → 返回成功
如果master故障,slave有全部数据备份,易恢复;写入延迟高、吞吐量低
②异步复制:master写入消息成功 → 返回成功 → 异步将消息发送给slave → slave自己保存数据
如果master故障,slave数据没有完成复制,数据会丢失;写入延迟低、吞吐量高
Broker配置文件里的brokerRole参数进行设置的,这个参数可以被设置成:
ASYNC_MASTER:异步复制Master
SYNC_MASTER:同步双写Master
SLAVE:就是从节点,只负责接收master的请求
3.RocketMQ怎么解决消息丢失和重复消费、顺序消费问题:
消息丢失:即消息可靠性
①发送可靠性:同步-直到服务器返回成功,默认重试2次 / 异步-开新线程回调接口 / 单向-不可靠,不建议
②存储可靠性:底层同步/异步刷盘逻辑保证
③消费可靠性:消费重试(时间level递进重试)、死信队列(相关接口单独获取)、消息回溯(按时间回退再次消费)
重复消费:消费方幂等性处理,数据库/redis的唯一业务id实现
顺序消费:普通>同一个消费队列是有顺序的,重启不保证顺序;严格>同一个Topic严格按照FIFO顺序消费
①生产端:保证有顺序的消息放到 topic 的同一个队列下,RocketMQ提供了API
②消费端:2把锁(1把锁broker上的消费队列-有过期,1把锁消费者要处理的本地消息),定时任务保证锁的同步
4.

RPC&Feign

1
2
3
4
5
6
7
8
1.IPC和RPC:
IPC:(Inter Process Communication)跨进程通信,泛指进程间通信,消息传递和共享资源并发访问。
RPC:(Inter Process Communication)跨进程通信,隐藏细节的IPC方法,服务端与服务端互相打包消息和返回。
2.常见RPC框架和区别:Dubbo、gRPC、Motan(微博)、Tars(腾讯)
3.OpenFeign实现原理:
@EnableFeignClients + @FeignClient 注解
通过动态代理生成发送请求和接收响应的 Client,可配置为 HttpClient / OkHttp等
4.

Dubbo

1
2
3
4
5
6
7
8
9
10
11
12
13
1.Dubbo的实现原理:
高性能、轻量级的开源Java RPC框架。
三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
多种协议:【Dubbo协议】解决高并发,【rmi协议】支持文件传输,【http协议】不支持传文件。
原理:
容器Container启动 → 暴露服务提供者Provider → 注册到注册中心Registry
服务消费者Consumer从注册中心订阅所需服务 → 注册中心返回服务提供者地址列表给消费者
服务消费者Consumer从地址列表中 → 智能负载均衡 → 选一个地址调用提供者
服务消费者和提供者在内存中累积调用次数和调用时间 → 每分钟发送数据到监控中心Monitor
2.Dubbo集群容错方式、代理方式:

3.Dubbo的spi机制:

OAuth2.0

1
2
3
4
5
6
7
8
1.OAuth2.0鉴权流程:
①用户访问应用 → ②应用发起授权 → ③授权服务给用户授权
④授权服务携带生成的【授权码】重定向到应用服务
⑤应用服务通过授权码从授权服务获取【令牌】token
⑥授权服务携带【令牌token、刷新令牌refresh_token】重定向到应用服务
⑦应用服务【请求真正的资源】从资源服务中
2.JWT令牌:
Header头部信息(Base64URL字符串).PayLoad有效荷载(Base64URL字符串).Sign签名信息

源码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1.ArrayList 源码分析?
① 变长集合,基于定长数组,jdk1.7 默认初始化数组长度10,jdk1.8后第一次调用add时才初始化;
② 允许控制和重复元素,扩容因数为 1.5 倍;
③ 增删慢(需要数组其他元素拷贝移动),查询快(有下标索引,复杂度O(1))
④ 线程不安全
⑤ Integer.MAX_VALUE-8 是最大的安全取值下标(因不同的JVM会加入一些数据头)
2.HashMap 源码分析?
jdk1.8 之前为 数组+链表
jdk1.8 之后为 数组+链表+红黑树(数组长度>64或链表长度>8时转为红黑树)
扩容因子0.75,也就是浪费1/4的空间,达到扩容因子会扩容,0.75是时间与空间的一个平衡值
3.ConcurrentHashMap源码分析?
初识容量默认为16段(Segment),使用分段锁设计;
不对整个Map加锁,而是为每个Segment加锁;
当多个对象存入同一个Segment时,才需要互斥;
最理想状态为16个对象分别存入16个Segment,并行线程数量16个;
使用方式与HashMap无异。
JDK1.7: 分段锁设计 Segment
JDK1.8: CAS交换算法(CAS比较和交换) + 同步锁 synchronized(锁的是表头)
4.string源码分析?
包含:一个不可变的char数组用来存放字符串,一个int型的变量hash用来存放计算后的哈希值。
提供了许多字符串操作的方法:比较、拼接、裁剪、起始判断等
5.stream源码分析?
定义输入源 → 定义流水线节点 → 组装流水线 → 启动流水线(内部for循环)
6.ThreadLocal源码分析?
一个线程内可以存多个ThreadLocal对象。
内部:ThreadLocalMap,key为this<ThreadLocal对象>,value为本线程独享的值。
ThreadLoalMap中初始化了一个大小 16 的Entry数组,Entry对象用来保存每一个key-value键值对。
Entry 对象继承自 WeakReference(弱引用),Entity一直不被 get()/set()/remove()就不会被回收,会内存泄漏:
通常在使用完 ThreadLocal 后加 finally 调用 remove() 方法进行内存的清除,解决内存泄漏问题。

设计模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1.Spring 中使用的设计模式:工厂、单例、代理、观察者、模板方法。
2.工厂模式里面的普通工厂,和抽象工厂的区别
* 简单工厂模式(又叫静态工厂模式),本质是通过传入不同的参数来实现多态,达到实例化不同对象的目的。
* 抽象工厂模式对工厂模式又进行了一层抽象,不单单是只生成一类产品而是一系列产品,可以像零件灵活配置给各工厂。
* 简单工厂模式
* 【优点】:
* 1、客户端创建对象时只需要记住特定的参数,而不需要记住复杂的类名,也不用关注实现的过程。(实现了封装和部分解耦)
* 2、创建对象不需要单独实例化,而是通过工厂类直接获取示例(实现复用)
* 【缺点】:
* 1、实例化对象的逻辑全部封装在一个工厂类里,每次需求变化都要单独修改工厂类(违反了开闭原则),而且出了异常可能没法正常工作。
* 2、不方便扩展子类
* 【应用场景】:适合业务简单或者产品较少的情况
* 工厂模式
* 【优点】:1、在简单工厂的基础上遵循了开闭原则,又进行了解耦,工厂类分为具体的工厂类
* 【缺点】:1、每增加一个工厂,就要额外开发一个工厂
* 【应用场景】:正文中符合工厂模式的情况,多由于解耦

* 抽象工厂模式
* 【优点】:1、正是由于复杂的抽象关联关系使得在类的内部对一系列产品组的管理很方便
* 【缺点】:1、扩展很费力,每次要修改很多类。
* 【应用场景】:待创建的对象是一系列相互关联或相互依赖的产品族时
3.装饰者模式应用场景:
a)在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
b)需要动态地给一个对象增加功能,这些功能也可以动态地被撤销。
当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。
4.单例模式应用场景:
1)有频繁实例化然后销毁的情况,也就是频繁的 new 对象;
2)创建对象时耗时过多或者耗资源过多,但又经常用到的对象;
3)频繁访问 IO 资源的对象,例如数据库连接池或访问本地文件;
如网站在线人数统计、配置文件类、数据库连接池、线程池等

程序设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1.数据库日增5万条,预计运维3年,如何优化:
库结构避免冗余、适当的索引、主从读写分离、规律分表、缓存机制、静态页面、高效的SQL语句
2.如何设计一个可以抗高并发的项目?
① 快速进行扛量:加机器(负载均衡) Nginx
② 合理设计项目
* A.缓存:对冗余请求的数据,需要做缓存处理,主流方案 NoSQL Redis
* B.MQ:异步消息队列,对复杂的逻辑方法进行解耦,异步处理 主流方案 RabbitMQ
* C.数据库:分库分表、读写分离(主写从读)、ES
* D.业务逻辑优化
③ 架构
* A.单体架构
* B.分布式架构(SOA)
* C.微服务架构 - Spring Cloud Alibaba:Nacos/Gateway/OpenFeign/Ribbon/Sentinal/Sleuth+ZipKin
3.常用的分布式 ID 的设计方案?Snowflake 是否受冬令时切换影响?
基于 Twitter 早期开源的Snowflake雪花算法(以及微信的 seqsvr、美团Leaf分布式id生成系统):
整体64位:
1位:符号位,固定为0(避免负值)
41位:时间戳
10位:机器码
12位:序列号
适合使用 long 类型来存储。算法本身大多依赖 System.currentTimeMills() 的时间毫秒值。
它返回当前时间和 197011号 UTC 时间相差的毫秒数,这个数值与夏/冬令时并没有关系,所以并不受其影响。
4.如何实现一个分布式链路追踪系统?
①直接在每个需要追踪的地方加上代码:最简单、最low、代码侵入、改动繁琐
②用AOP切面编程:代码侵入程度大、服务多改动量也大
③Java探针技术:jdk1.5引入,可以动态修改java字节码的技术(可以理解为 jvm层面的aop)
Java Agent 一般通过在应用启动参数中添加 -javaagent 参数添加 ClassFileTransformer 字节码转换器。
在 Java 虚拟机启动时,执行 main() 函数之前,Java 虚拟机会先找到 -javaagent 命令指定 jar 包,
然后执行 premain-class 中的 premain() 方法。
用一句概括其功能的话就是: main() 函数之前的一个拦截器。

其他概念

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
1.OOP四大特性五大原则:封装继承多态抽象,单一职能/开放关闭/里式替换/接口隔离/依赖倒置
2.过滤器和拦截器的区别:
拦截器:java反射、不依赖servlet、只action请求起作用、spring容器中的
过滤器:函数回调、依赖servlet、所有请求都起作用、servlet规范中的
3.HTTP的工作流程:
客户端浏览器请求 → TCP 连接 → 服务器收到请求 → 服务器响应请求报文(文档)→ 客户端浏览器解析响应
4.HTTPS的工作流程:
Client浏览器请求(443端口) → Server返回公钥证书
Client验证公钥证书 → 用证书公钥加密伪随机数生成的会话密钥 → Server私钥解密出会话密钥
Server会话密钥加密明文内容A → Client会话密钥解密密文 → Client得到明文内容A
Client会话密钥加密明文内容B → Server会话密钥解密密文 → Server得到明文内容B
5.常见指标缩写:
QPS:查询请求数量/s,即并发量
TPS:事务执行数量
DUV:日活,每日活跃用户量
MUV:月活,每月活跃用户量
DPV:日页面浏览量
MPV:月页面浏览量
6.Java应用开发中的注入攻击?
① SQL 注入攻击,常见于登陆功能,where条件后加 or 1=1; 就可以添加其他sql语句进行攻击
② 操作系统命令注入,如Java的 Runtime.exec(...) 的API,可以执行命令,拼接 ;rm -rf /* 就玩玩了
③ XML 注入攻击,XML本身是可以包含动态内容的,例如XPATH,可能会被恶意访问内容
7.cap和base理论:
cap理论:
一个分布式系统不可能同时满足CAP:
即一致性(C:Consistency),可用性(A:Availability),分区容错性(P:Partition tolerance)
最多只能同时满足其中的两个,通常为AP/CP
base理论:
即基本可用(Basically Available)、软状态( Soft State)、最终一致性( Eventually Consistent)
它是对 CAP 理论中 AP 策略的延伸。
核心是即便无法做到强一致性,但每个系统都应该根据自身业务特点,采取适当的方式来保证系统的最终一致性。
8.分布式与微服务:
分布式:一个项目拆分多个模块,分别部署。
微服务:垂直拆分的更小粒度的服务。

具体场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
1.MySQL里有2000w条数据,redis只存20w条数据,如何保证redis都是热点数据?
①redis sortSet(zset)数据类型(自动排序)存放对应的数据,数据被查询或写入过则zadd一次,已存在则覆盖时间戳(即score);
②计算20W数据大概占用的内存,限定 redis 的占用内存,配置淘汰策略。
2.小张看最近的大米价格,他打开百度,进行了一番操作,最后获得最近的大米价格。
上述过程中参与的硬件、软件、中间件、涉及到的技术、流程,参与者都有哪些?
答:http → 接口 → ip → 机器 → 端口 → 进程 → 服务 → 线程 → 代码逻辑 → 数据库 → 原路返回
3.做过的项目,并发量/日均pv最大的是哪个?部署了多少台机器?如果服务器流量激增,你认为会遇到什么问题?
如何处理,有什么思路?
答:按实际情况
4.举个例子,说明自己学习能力很强?以及坚持最久的一件事情?
答:写博客
5.接口响应慢或超时的原因、排查步骤和解决方案?
可能原因:
①网络延迟或丢包;
②接口的服务调用延迟;
③多线程代码执行延迟;
④代码本身问题,如RestTemplate没有设置超时时间、tcp连接超时、nginx读取后端服务默认60s等
排查步骤:
①网络问题排查:查看tomcat中请求超时的日志;
②依赖应用排查:查看是否依赖其他应用导致的超时;
③线程栈信息获取:jstack PID 查看线程是否有死锁或睡眠或其他阻塞等;
④其他方向:服务器配置/服务器软件/DNS解析时间/网络最小带宽/网页大小/数据库操作/页面大量特效JS/大图片等元素
解决方案:
① 网络或依赖应用的问题,需要第三方解决;
② 线程问题,需要解决死锁或其他阻塞问题;
③ RestTemplate设置超时时间,如 setConnectTimeout 和 setReadTimeout 均为 1s或3s
④ 如果设置了请求超时时间,可再多调用一次;
⑤ 异步机制,使用线程单独调用需要的远程接口,不影响主流程逻辑执行;
⑥ 缓存机制,专门配置缓存服务器,如redis,优先调用缓存服务器里的数据,快速调用解决超时
6.Java程序运行在Docker等容器环境有哪些新问题?
如果未配置合适的 JVM 堆和元数据区、直接内存等参数,Java 就有可能试图使用超过容器限制的内存,
最终被容器 OOM kill,或者自身发生 OOM。
错误判断了可获取的 CPU 资源,例如,Docker 限制了 CPU 的核数,JVM 就可能设置不合适的 GC 并行线程数等。
7.后台服务明显变慢,谈谈诊断思路?
区分:突然变慢、长时间运行变慢
频率:是否重复出现
慢的定义:请求的延时时间变长,还是其他?
诊断工具:
① JMC+JFR 监控应用是否出现大量某种类型的异常
② jstat 查看GC日志
③ 任务管理器/top命令,找到对应的Java进程id,通过 jstack 获取进程的线程栈
④ JVM层面的性能分析:JMC、JConsole进行运行时监控,GC日志等
8.

技能描述(旧)

  1. 熟练 Java 面向对象编程(OOP),常用数据集合IO,熟悉 python/shell 编程写过工具一直在用;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    【面向对象】
    1.什么是面向对象 / 什么是OOP?
    面向对象的方法主要是把事物给对象化,包括其属性和行为。贴近实际生活,万物皆对象。
    2.面向对象的四大特征五大原则
    4大特征:封装、继承、多态、抽象。
    5大原则:
    单一职能:每个模块、每个类、每个方法都只负责一件事情。
    开放关闭:对功能扩展开放,对修改源码关闭。Java世界里最基础的设计原则。
    里式替换:任何父类可以出现的地方,子类一定可以出现。子类可以无障碍地替换父类 - 多态
    接口隔离:多个专门的接口比使用单一的总接口要好。
    依赖倒置:面向抽象进行编程,高层不依赖底层,抽象不依赖细节实现。
    3.接口回调
    类A调用类B的方法b(),类B再回调类A的方法a(),其中方法a()是定义在接口中的,由类A来实现。这是一个双向调用的过程。

    【数据集合】
    1.List、SetMap、Queue的各种集合和特点?
    >>>List<<<
    ArrayList:数组,线程不安全,查询快,增删慢,jdk1.2。jdk1.7无参默认长度为10,jdk1.8懒加载add才有10,扩容因子1.5倍,懒加载
    Vector:数组,线程安全。方法与ArrayList相同,synchronized修饰所有操作方法。
    LinkedList:链表,增删快,查询慢。无需开辟连续空间。

    >>>Set<<<
    HashSet:不重复+无序,hashCode -> == -> equals 实现不重复
    LinkedHashSet:记录保留元素插入顺序,不重复
    TreeSet:不重复+自动排序

    >>>Map<<<
    HashMap:效率快,线程不安全,value允许null值,初识容量16,负载因子0.75。jdk1.7数组+链表,jdk1.8数组+链表+红黑树(链表>8,此时时间复杂度为O(logN))
    LinkedHashMap:HashMap子类,记录插入顺序
    Hashtable:效率慢,线程安全,value不允许null值,所有方法synchronized修饰
    TreeMap:实现SortedMap接口,对key自动排序

    >>>Queue<<<
    BlockingQueue:阻塞队列,空时读取等待,满时插入等待 -> 生产者消费者问题
    ArrayBlockingQueue:数组,有界阻塞队列,可构造设置上限
    LinkedBlockingQueue:链表,无界阻塞队列,默认上限Integer最大值
    LinkedTransferQueue:链表,无界阻塞队列
    PriorityBlockingQueue:优先级,无界阻塞队列,直到系统资源耗尽
    SynchronousQueue:无缓冲的等待队列,无界

    >>> 线程安全的集合 <<<
    Collections工具类:synchronizedXXX静态方法,性能没有提升
    CopyOnWriteArrayList:写有锁,读无锁,读写不阻塞
    CopyOnWriteArraySet:底层为CopyOnWriteArrayList,add时查重元素,会遍历数组
    ConcurrentHashMap:分段锁×16,每个Segment加锁,jdk1.7分段锁,jdk1.8为CAS比较交换算法+同步锁(锁表头)
    ConcurrentLinkedQueue:无锁设计,CAS比较交换算法

    静态cache缓冲区存储了256个地址值,类加载时就会被JVM生成:
    int a = 128;
    Integer b = 128;
    a == b 结果:true
    b.equals(a) 结果:true
    Integer a = 127;
    Integer b = 127;
    a == b 结果:true
    Integer a = 128;
    Integer b = 128;
    a == b 结果:false // 缓存cache的 -128~127
    Integer i = new Integer(100);
    Integer j = new Integer(100);
    int k = 100;
    i == j 结果:false //构造方法没有使用缓存cache的 -128~127
    i == k 结果:true

    【IO流】
    1.IO分类
    输入/输出,字节(8bit)/字符(16bit),节点/处理
    InputStream/Reader: 所有的输入流的基类,InputStream字节输入流,Reader字符输入流
    OutputStream/Writer: 所有输出流的基类,OutputStream字节输出流,Writer字符输出流
    2.IO / NIO
    NIO比IO效率高,IO单向会阻塞(面向流),NIO双向非阻塞(面向缓冲)
    NIO:Channel(通道) <=> Buffer(缓冲), Selector(事件监听-连接/读/写)
    ● NIO 比原生 socket 性能要高,但代码比 Netty 框架要臃肿
    3.BIO / NIO / AIO
    BIO 同步阻塞,TCP/UDP,同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成
    NIO 同步非阻塞-多路复用,适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器
    AIO 异步非阻塞,即NIO2,适用于连接数目多且连接比较长(重操作)的架构,比如相册服务器

    【序列化&反序列化】
    1.实现Serializable接口
    2.Transient 关键字阻止该变量被序列化到文件中
    3.序列化:Java对象 转换为 字节序列
    反序列化:字节序列 恢复为 Java对象

    【注解】
    1.四种元注解(自定义注解@interface):
    @Target 修饰的对象范围
    @Retention 被保留的时间长短
    @Documented 描述javadoc
    @Inherited 标注类是被继承的

    【多线程】
    1.创建:继承Thread类,实现Runnable接口,实现Callable接口,线程池(线程状态:新建-就绪-运行-阻塞-死亡)
    多文件上传:Callable判断 call 方法的返回值
    2.线程B知道线程A修改了变量:volatile修饰变量 -> synchronized修饰方法 -> wait/notify -> while轮询
    3.终止线程:正常运行结束,退出标志,interrupt方法结束,stop方法终止(线程不安全)
    4.线程池:降低资源消耗、提高响应速度、提高线程的可管理性
    5.线程池工作原理和参数:线程池主要避免了线程的频繁创建和销毁,消耗系统资源和导致系统不稳定
    ThreadPoolExecutor(7个参数)
    ①corePoolSize:最小线程数(核心线程数量)
    ②maximumPoolSize:最大线程数(≥核心线程数)
    ③keepAliveTime:空闲线程存活时间
    ④unit:存活时间单位,比如秒:TimeUnit.SECONDS
    ⑤workQueue:一个阻塞队列,提交的任务将会被放到这个队列里
    ⑥threadFactory:线程工厂,用来创建线程,主要是为了给线程起名字,默认名:pool-1-thread-3
    ⑦handler:拒绝策略,当线程池里线程被耗尽,且队列也满了的时候会调用。
    1>丢弃并抛异常 2>丢弃任务 3>丢弃前面的提交新的 4>调用线程来处理
    6.线程池类型:缓冲线程池、固定大小线程池、单线程线程池、默认线程池
    7.java线程调度算法:时间片轮转
    8.多线程上下文切换:轮转竞争CPU的时间片,不同的线程切换使用CPU发生的切换数据就是上下文切换
    9.线程池工作过程:
    线程任务队列通过参数传入,执行execute添加任务方法会判断队列的情况,一个线程完成就进入下一个线程,
    总数不会超过参数传入的最大线程数。

    【线程锁】
    0.并发包 java.util.concurrent 简称 juc
    1.synchronized
    悲观锁,抢占式,阻塞(同步代码块-锁对象 / 同步方法-锁类实例 / 同步静态方法-锁类对象Class)
    实现原理:Java对象头 + Monitor(存储线程的数据结构)
    2.volatile:多线程共享变量可见性,有序性,单次读写原子性 - 状态标记量和单例模式的双检锁
    3.锁的分类:
    1>乐观锁:乐观认为不会发生线程安全,读多写少,先判断再上锁
    2>悲观锁:悲观认为总会有线程安全,写多读少,直接上锁
    3>可重入锁:递归锁,同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁,
    不会因为之前已经获取过还没释放而阻塞。不仅判断锁有没有被锁上,还会判断锁是谁锁上的,
    当就是自己锁上时,那么他依旧可以再次访问临界资源,并把加锁次数state+1(优点:一定程度避免死锁),
    如 synchronized和ReentrantLock
    4>不可重入锁:若当前线程执行某个方法获取了该锁,那么在方法中尝试再次获取锁时,就会因获取不到而阻塞。
    5>公平锁:按顺序分配给请求的线程需要的锁
    6>非公平锁:JVM 按随机,就近原则分配锁的机制,抢占,直接尝试加锁,则称为不公平锁(效率高于公平锁)
    ● ReentrantLock构造中 boolean fair 可以指定是否为公平锁(FairSync)或非公平锁(NonFairSync-默认)
    ● ReentrantLock主要利用 CAS+AQS 队列来实现。
    ● AQS:AbstractQueuedsynchronizer 构建锁和同步容器的框架,使用一个FIFO的队列表示排队等待锁的线程,
    队列头节点称作“哨兵节点”或者“哑节点”,它不与任何线程关联。其他的节点与等待线程关联,
    每个节点维护一个等待状态 waitStatus.
    7>共享锁:允许多个线程同时获取锁,并发访问共享资源,一种乐观锁,如 ReentrantReadWriteLock中的ReadLock
    8>独占锁:(独享锁=排它锁=同步锁=互斥锁)只允许一个线程获取/持有锁,如 synchronized和ReentrantLock
    9>自旋锁:先让当前线程自旋等待,不是阻塞,是为了减少线程切换
    10>适应性自旋锁:jdk1.6新增,默认开启自旋锁,自旋时间不固定,JVM决定 - JVM参数可设
    如 AtomicInteger 中的 TicketLock/CLHlock/MCSlock
    ● 锁的状态 4 种:无锁、偏向锁、轻量级锁、重量级锁(从左到右也是 synchronized锁升级过程)
    11>无锁:不使用锁实现线程安全,如CAS
    12>偏向锁:如果一个线程多次获取锁,就会引起偏向锁,不再参与竞争,直接获取锁(提高性能)-JVM参数可设
    13>轻量级锁:偏向锁被另外的线程访问则升级到轻量级锁
    14>重量级锁:轻量级锁进一步升级为重量级锁,直接阻塞访问该锁的其他线程
    4.锁优化:
    jdk1.6之后对 synchronized 优化:自旋锁,适应性自旋锁,锁消除,锁粗化,偏向锁,轻量级锁等技术减少锁操作的开销
    5.synchronized和ReentrantLock的区别:
    synchronized 锁方法和代码块,JVM底层实现,不够灵活,不可中断,低并发时效率高;
    ReentrantLock 锁指定区域,比较灵活,可以通过interrupt()方法中断,封装有线程高级方法,高并发时效率高
    6.jdk1.5之后对 synchronized 锁的优化:
    1>减少锁持有时间,只在有线程安全要求的程序上加锁
    2>减小锁粒度,降低锁的竞争,触发偏向锁,轻量级锁成功率才高
    3>锁分离,根据功能进行分离成读锁和写锁
    4>锁粗化,锁持有的时间尽量短,使用完后立即释放
    5>锁消除,编译器级别的工作,编译时发现不能被共享的对象,则消除对象的锁操作
    7.CAS算法:
    3个核心值 当前内存值V、旧的预期值A、即将更新的值B。
    当且仅当预期值A和内存值V相同时,将内存值修改为B并返回true,否则什么都不做,并返回false
    原子类,如 AtomicInteger, AtomicLong... AtomicReference(操作多个值)
    关键字 volatile 来保证当前单次读写操作的原子性不可分割和打断,以及线程可见性
    类 Unsafe 用来获取当前原子操作的内存数据
    ● ABA问题和解决:
    当第一个线程执行CAS操作,尚未修改为新值之前,内存中的值已经被其他线程连续修改,使得变量值经历 A -> B -> A的过程。
    解决方案:添加版本号作为标识,每次修改变量值时,对应增加版本号; 做CAS操作前需要校验版本号。JDK1.5之后,新增 AtomicStampedReference 类来处理这种情况。
    8.ThreadLocal
    ThreadLocal是用来维护线程中的变量不被其他线程干扰而出现的一个结构。
    内部是1个 ThreadLocalMap 类,它的 key 为 ThreadLocal 对象自身,value 为要存储的对象
    这样在不同线程中,持有的都是当前线程的变量副本,与其他线程完全隔离,以此来保证线程执行过程中不受其他线程的影响。


    【网络编程】
    1.OSI网络七层模型:物理层 - 数据链路层 - 网络层 - 传输层 - 会话+应用+表示层
    2.TCP和UDP区别:
    TCP面向连接,可靠,字节流,点对点传输,头部开销20字节,保证正确性和顺序;
    ● ServerSocket - 服务端, Socket - 客户端
    UDP面向报文,无连接的,连接方式多样化,头部开销8个字节,不保证正确性且可能丢包。
    ● DatagramPacket - 数据报包(接收/发送), DatagramSocket - 数据报套接字
    3.TCP三次握手&四次挥手:
    3握手:客户端 -> SYN -> 服务器 -> SYN+ACK -> 客户端(Established) -> ACK -> 服务器(Established)
    4挥手:客户端 -> FIN -> 服务器 -> ACK、FIN 按顺序发 -> 客户端 -> ACK
    4.HTTP工作流程:
    域名解析 -> TCP3次握手 -> 发起HTTP请求 -> 服务器响应HTTP请求 -> 解析响应的HTML代码 -> 渲染代码
    5.HTTPS工作流程:
    浏览器 -> 443端口发起请求 -> 服务器
    浏览器 <- 发送公钥证书 <- 服务器
    浏览器:①验证公钥证书 ②伪随机数生成会话密钥 ③公钥加密会话密钥
    浏览器 -> 发送加密后的密文 -> 服务器
    服务器:①私钥解密接收的密文出会话密钥 ②通过会话密钥加密要发送的明文内容
    浏览器 <- 发送加密明文后的密文 <- 服务器
    后续就以该 会话密钥 进行信息请求和响应
    6.SSL协议特性:私密性、确认性、可靠性
    7.HTTP请求报文:请求行、请求头、请求体
    8.GET与POST:
    GET:参数拼在链接地址上,最大1024字节,主要用于获取/查询
    POST:请求正文中,更安全,可传大数据,主要用于更新数据/上传文件
    9.Nettry:
    提供一个易于使用的 API 的客户端/服务器 Java 网络编程框架。
    依赖 Jar 包:netty-all
    Netty封装。

    【JDK8新特性】
    1.接口:支持 default 方法,static 静态方法 均可以有方法体
    2.Lambda:函数做为方法的参数,使用->简写方式简化代码
    3.函数式接口:@FunctionalInterface注解修饰的单个方法的接口
    Predicate断言,返回真假;Consumer消费,有去无回;Supplier创造,无中生有;Function传递,返回数据
    4.Stream:聚合操作,查找、过滤、映射,包含基本操作、中间操作、终止操作
  2. 熟练 JavaWeb 编程 Servlet/JSP 以及 Java 常用框架 Spring、SpringMVC、SpringBoot、Mybatis、Mybatis-Plus、Dubbo、WebMagic等;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    【中文乱码】
    1.GET请求中文乱码:服务器IOS-8859-1,浏览器UTF-8 ①编解码 ②String方法编解码 ③server.xml配置编码方式
    2.POST请求中文乱码:request.setCharacterEncoding("UTF-8")来解决
    3.响应中文乱码:
    ①响应头 response.setHeader("Context-Type", "text/html;charset=utf-8");
    ②响应内容类型 response.setContentType("text/html;charset=utf-8");
    4.数据库中文乱码:url追加?useUnicode=true&characterEncoding=utf8&useSSL=false

    【JavaWeb】
    1.session和cookie:
    session:存储在服务器,生命周期跟随服务器周期,或过期(默认30min)
    cookie:存储在客户端,浏览器关闭则销毁,会话cookie存在浏览器内存,持久化cookie存储在客户端磁盘
    2.servlet是什么?生命周期?:
    servlet是一个可以处理客户端浏览器传来的HTTP请求并返回响应的对象。
    生命周期:加载、实例化、初始化、服务、销毁
    3.转发和重定向:
    转发:服务器,1次请求,地址不变,跳转后信息不丢失,只能转发同一个web项目内的资源
    重定向:客户端,2次请求,地址改变,跳转后信息会丢失,可以重定向到任何web资源
    4.JSP 9大内置对象和作用:
    page,pageContext, request,response, session, application,out,config,exception
    5.JSP 四大作用域:
    pageContext(当前页面),request(1次请求),session(1次会话),application(整个项目)
    6.JSP 防止表单重复提交:
    ①点击后按钮失效 ②重定向到指定页面 ③自定义重复提交过滤器
    7.过滤器与拦截器:
    过滤器:实现Filter接口,java反射,不依赖servlet,只对action请求有效,只能被调用一次
    拦截器:实现HandlerInterceptor接口,函数调用,依赖servlet,所有请求有效,spring容器的,可被多次调用
    8.监听器:对象创建/销毁,对象属性改变,对象绑定情况(事件源、监听器、绑定、事件)
    9.启动顺序:Listener 理 Filter 发 Servlet 师 (不会因为标签在配置文件中的先后顺序而改变)


    【Spring】
    1.Spring工作原理:
    IOC将创建对象和管理对象交给Spring容器来管理,动态注入,原理是反射实现
    AOP面向切面编程,对某一类对象或方法来进行监督和控制,来扩充模块的功能
    Spring目的就是让对象与对象之间不通过代码来关联,通过配置来管理。


    【SpringMVC】
    1.SpringMVC工作流程(回顾那张图即可)
    请求 → 前端控制器 DispatcherServlet → 处理器映射器 → 适配器 → 匹配处理器 → 返回modelAndView → 前端控制器转给视图解析器 → 解析视图 → 将视图返回响应给用户
    2.Spring中IOC容器:BeanFactory与ApplicationContext
    BeanFactory:Spring原始的Factory,无法支持AOP,web应用等
    AppliationContext:具备资源访问、事件传播、国际化消息访问等...
    3.IOC方式:构造、set、注解 (IOC控制反转,DI依赖注入,依赖注入实现了控制反转)
    4.IOC:Spring创建/管理/装配/配置对象,并管理对象的生命周期
    5.AOP:静态代理、动态代理(JDK动态代理-实现接口,CGLIB动态代理-继承)
    6.Bean生命周期:实例化 → 属性赋值 → 初始化init方法 → 销毁destory方法
    7.Bean作用范围:Singleton,Prototype,Request,Session,Globalsession
    8.Spring用到的设计模式:代理/单例/工厂/模板方法...
    9.Spring自动装配:xml bean标签 autowire="byType", byName, autodetect
    10.Spring事务:编程式事务,声明式事务(事务管理与业务代码分离,使用注解或xml配置来管理)
    11.AOP原理和通知类型:
    面向切面编程的思想。一系列方法和通知的组合。
    针对业务处理过程进行横向切面提取,优化重复代码。
    如日志、事务、权限等...形成日志切面、事务切面、权限切面...
    场景:缓存、权限、内容传递、错误、懒加载、日志记录、优化、同步管理、事务...
    ● 通知类型:方法执行的之前、之后、之后未出现异常、之后出现异常、之前和之后 5种。
    12.SpringMVC和Struts2的区别:
    拦截机制:SpringMVC是方法级别拦截,Struts2是类级别拦截
    底层框架:SpringMVC底层是Servlet实现,Struts2底层是Filter实现
    性能方面:SpringMVC效率高于Struts2
    配置方面:SpringMVC和Spring是无缝集成的,项目管理和安全上比Struts2高
    13.@Autowire和@Resource:@Autowire按照类型装配,@Resource按照名称装配
    14.spring事务传播行为:7种。①不存在事务就新建 ②不存在事务就不使用 ③不存在事务就抛异常 ④存在事务则挂起创建新的 ⑤存在事务挂起当前事务 ⑥存在事务则抛异常 ⑦存在事务则嵌套事务
    15.spring事务隔离级别:5种。①默认数据库的隔离级别 ②读未提交 ③读已提交 ④可重复读 ⑤序列化
    MySQL 默认 可重复读,生产中使用的是读已提交,原因:死锁/锁表概率低以及update并发高;Oracle 默认 读已提交
    16.脏读:1个事务读到另1个事务未提交的数据
    不可重复读:1个事务读到另1个事务已经提交的数据,导致事务多次查询不一致
    幻读/虚读:select 某记录是否存在,结果是不存在,准备插入此记录,但执行 insert 时发现此记录已存在(因刚好被另一个事务 insert),无法插入,此时 select 过程发生幻读。
    17.spring启用注解自动装配:<context:annotation-config />
    18.@Required 修饰实体类的 setter 方法,xml中必须提供注入该方法的配置,否则会抛异常
    19.@Autowaire & @Qualifie & @Resource
    @Autowaire 自动按照类型注入
    @Qualifie 自动按照类型注入并可以起别名,按照别名注入
    @Resource 直接按照bean的id注入,等价于 @Autowaire + @Qualifie

    【SpringBoot】
    1.SpringBoot自动装配原理
    启动类上的 @SpringBootApplication 注解:
    ● @EnableAutoConfiguration 中
    @AutoConfigurationPackage 自动配置包,可以找到调用的 Registrar.class 然后在这个类里加断点,可以看到扫描的包的路径。
    @Import 导入组件使用,该注解会在启动时将 META-INF/spring.factories 文件中指定的约定配置的值全部导入到容器中,自动配置类就能生效了。
    ● @SpringBootConfiguration 即 SpringBoot 的配置类,本质也只是一个 @Configuration 注解
    ● @ComponentScan 即 开启注解扫描。
    2.SpringBoot运行流程
    SpringApplication 的 run 方法 →
    新建 SpringApplication 对象,并启动计时监控类 →
    调用 createApplicationContext 方法创建应用上下文 → 刷新上下文 → 发布应用上下文 →
    最后返回应用上下文
    3.SpringBoot优点
    独立运行、简化配置、自动配置、无代码生成和XML配置、应用监控
    4.SpringBoot日志框架
    默认日志框架:logback
    支持日志框架:logging、log4j2、logback
    5.SpringBoot打包war包:
    ① maven编译插件 ②packaging打包为war


    【Mybatis】
    0.Mybatis原理:
    Mabatis使用XML或注解用于配置和原始映射,主要都是使用SqlSessionFactory实例的,一个SqlSessionFactory实例可以通过 SqlSessionFactoryBuilder 获得。用xml文件构建SqlSessionFactory实例。
    MyBatis有一个实用类----Resources,它有很多方法,可以方便地从类路径及其它位置加载资源。
    1.Mybatis与Hibernate区别:
    Mybatis:面向sql,半自动ORM,xml动态配置sql更灵活
    Hibernate:面向对象,全自动ORM(根据对象关系模型直接映射字段),支持级联操作,完整的日志系统
    2.Mybatis优点:基于SQL语句,消除JDBC冗余,数据库兼容,与Spring继承,提供灵活的映射标签
    3.Mybatis缺点:依赖数据库,移植性差,sql语句工作量大
    4.resultMap和resultType:
    resultMap:标签映射sql查询结果字段到dto自定义的类,适用于连表查询
    resultType:指定映射返回值的类的类型对应实体类,适用于单表查询
    5.#{}和${}区别:#{}是预编译-可以防止SQL注入攻击,${}是字符串替换
    6.Mybatis缓存:
    一级缓存:sqlSession 默认开启
    二级缓存:sqlSessionFactory 需要配置开启 <setting name="cacheEnabled" value="true"/>
    7.Mybatis主键回填:
    ① xml usegeneratedkeys="true" keyproperty="id"
    ② @Option(usegeneratedkeys="true", keyproperty="id")
    8.Mybatis主键生成:
    @SelectKey(keyProperty = "userBase.id",resultType = String.class, before = true,
    statement = "select replace(uuid(), '-', '')")
    9.Mybatis对象映射:<association ...> 即11关系映射
    Mybatis集合映射:<conllection ...> 即1对多关系映射

    【Mybatis-Plus】
    1.Mybatis-Plus是对Mybatis框架的一种增强,简化开发、提高效率。
    2.核心注解:
    @TableName @TableId @TableField
    @Version-乐观锁标记 @EnumValue-枚举 @TableLogic-逻辑删除

    【Dubbo】
    1.什么是Dubbo?
    Dubbo是Alibaba开源的分布式服务框架,它最大的特点是按照分层的方式来架构,使用这种方式可以使各个层之间解耦合。一款高性能、轻量级的开源Java RPC框架。
    三大核心:面向接口的远程方法调用、智能容错和负载均衡、以及服务自动注册和发现。
    2.Dubbo角色:
    ①Provider暴露服务
    ②Consumer调用远程服务的消费者
    ③Registry服务注册与发现
    ④Monitor监控中心
    ⑤Container服务运行容器
    3.Dubbo负载均衡策略:随机(默认-按权重设置随机概率)、轮询、最少活跃调用、一致性hash
    4.Dubbo默认通信框架:Netty
    5.Dubbo服务调用是阻塞的吗?
    Dubbo是基于NIO的同步非阻塞方式实现的,客户端不需要启动多线程即可并行调用多个远程服务。
    6.Dubbo的核心配置有哪些?
    服务、引用、协议、应用、模块、注册中心、监控中心、提供者、消费者、方法、参数 配置。
    7.Dubbo流程协议:
    提供者 → 注册到注册中心 → 注册中心通知消费者 → 消费者远程调用提供者 → 服务运行容器监控消费者和提供者
    8.Dubbo遇到过的问题:
    1.注册不上nacos,检查所有扫描路径的地方
    2.消费者与服务者均需要打包为 war 包
    3.所有传输的类对象,如 pojo,dto,R结果集 等都需要实现 序列化
    4.公共模块 maven 编译插件

    【WebScoket】
    一种计算机通信协议,单个 tcp 连接提供前双攻通信信道。
    双向+全双工,可以实现客户端与服务器的双向消息发送。
    初始连接使用 HTTP 然后将此连接升级到基于套接字的连接,然后用于未来的通信。
  3. 精通 Git、SVN、禅道等版本控制、敏捷开发服务的部署和使用;

  4. 熟练 MySql、Oracle 关系型数据库,具备常见 SQL 优化相关经验;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Mysql 与 Oracle 区别:
    MySQL是轻量型数据库,并且免费,没有服务恢复数据。分页 limit
    Oracle是重量型数据库,收费,Oracle公司对Oracle数据库有任何服务。分页 rownum 嵌套查询

    SQL调优】
    1.SQL优化方法
    步骤:定位、启用慢查询日志(slow_query_log)、Druid SQL监控
    mysql> set global slow_query_log='ON';
    细节:不*、不子查询、不IN/NOT IN、不OR、不!=、不null判断、DISTINCTORDER BY结合、limit 1explain要控制带range和以内
    2.SQL优化工具
    SQL Tuning Expert for MySQL ● EverSQL
  5. 熟练 Redis 非关系型数据库,包括 Redis 持久化、主从复制、哨兵模式、雪崩与击穿、分布式锁;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    【Redis】
    1.Redis为什么快?C语言实现、单线程NIO、内存数据库、RESP客户端与服务器通信协议、string使用SDS更快
    2.Reids持久化?RDB-快照一定时间多少次请求/AOF-每修改/每秒-容灾性好
    3.Redis数据结构和应用场景?
    str-token, list-消息队列/分页, hash-单点登录, set-去重, zset-排序, bitmap, geo...
    4.Redis事务实现?mutil、exec、discard
    5.Redis淘汰策略?过期时间:1.接近 2.次数最少 3.随机 没有过期时间:1.次数最少 2.随机 最严:禁止驱逐
    6.Redis缓存穿透、击穿、雪崩、倾斜和解决方案?
    ● 穿透, 查不到————布隆过滤器/缓存空对象
    ● 击穿, 瞬时访问量大穿过缓存————热点永不过期/添加互斥锁
    ● 雪崩, 同一时间大面积失效————集群/限流降级/数据预热/过期时随机时间范围失效
    ● 倾斜, 某个服务器压力大导致宕机————集群
    7.Redis如何实现高并发?高可用?
    高并发:一主多从,主从复制,主写从读
    高可用:集群、哨兵、主备切换
    8.Redis的操作为什么是原子性的,怎么保证原子性?————单线程
    9.Redis主从复制的过程?————sync同步命令、bgsave(非阻塞)、offset同步新增的数据不需全量同步
    10.Redis哨兵机制和作用?————高可用,监控、通知、自动故障转移
    11.Redis性能问题和解决方案?————主机不做持久化备份,从机做AOF备份每秒同步,主从在局域网内
    12.Redis的String底层结构
    SDS(simple dynamic string) buff+free+len
    获取字符串复杂度为 O(1), 不会造成缓冲器溢出, 减少修改字符串的内存重新分配次数, 内存预分配机制
    13.Redis的RESP协议
    客户端与服务端的序列化通信协议,Redis Serialization Protocol (Redis序列化协议)
  6. 熟练 Nginx、Linux、Docker 常用环境搭建,如集群搭建等;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    【Nginx】
    1.Apache、Tomcat配置
    2.Nginx负载均衡策略
    轮询、权重、ip hash、最少连接、最小响应时间
    3.Nginx的原理
    master 进程:
    ● worker 进程:
    4.Nginx常用配置
    压缩、统一错误处理、C10K问题(如何处理大量he护短连接<10000+>
  7. 熟练常用的开发工具和项目构建,如 IDEA、Eclipse、Maven 等,以及常见设计模式和JVM调优;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    【设计模式】
    1.单例模式:
    2.代理模式:
    能够创建一个增强方法的类的对象,增强一些指定的方法逻辑功能。
    3.工厂模式
    ● 简单工厂:传入不同的参数实现多态,实例化不同的对象。
    ● 抽象工厂:工厂模式的一层抽象,可以生产一系列产品(对象),灵活配置给各工厂。

    【JVM调优】
    0.JVM监视工具:jdk下自带的 jvisualvm.exe
    1.JVM加载器多少种
    ● 加载过程:加载 → 链接(验证<文件格式,元数据,字节码,符号引用验证>) → 初始化
    ● 加载器:默认3个类加载器 BootstrapClass / ExtClassLoader / SystemClassLoader / 自定义加载器
    URL[] urls = Launcher.getBootstrapClassPath().getURLs(); //获取根目录下所有的依赖jar路径
    URL url = ClassLoader.getSystemClassLoader().getResource(pathName);
    ● 双亲委派模型:
    类加载的时候,递归加载父类,当父类加载器无法完成这个请求时,子类才会尝试加载。保证不重复加载类
    2.JVM的构成(五大区域)
    PC程序计数器、堆、栈、本地方法栈、元空间
    3.JVM堆:
    ● 年轻代(伊甸区=默认为堆大小的1/15,幸存者区=FromSpace + ToSpace)
    ● 老年代(幸存者区每熬过一次GC年龄+115岁时成为老年代)
    ● 永久代(jdk1.8遗弃,改为元空间)
    新创建对象先存到年轻代 → 伊甸区:
    1>如果伊甸区满了,会触发GC(Minor GC)会对伊甸区进行回收,如果存活下来,会转移到幸存者区,依次类推。
    2>如果幸存者区满了,会触发GC(Minor GC)进入幸存者区的 ToSpace。
    3>如果 ToSpace 满了触发GC(Minor GC),会将存活的对象转移到老年代。
    4>老年代满了,触发GC(Major GC)清理。
    5>Full GC,清理整个堆空间,包括年轻代+老年代。(如果触发了 Full GC 说明程序就出了问题)
    a.调用System.gc() 建议但不一定执行
    b.未指定老年代和新生代大小,堆伸缩时
    c.老年代空间不足时
    整体策略:尽量把对象在年轻代使用回收,减少晋升老年代的几率
    4.JVM幸存者区为什么是2块? 内存有碎片化可以充分利用内存 + 便于管理年轻代的对象
    5.强引用+软引用+弱引用+虚引用
    强引用:Object o = new Object();
    ————只要引用还在就不会被GC回收
    软引用:SoftReference<T> sr = new SoftReference<>(new Object()); Object o = sr.get();
    ————内存不足时,GC强制清理软引用
    弱引用:WeakReference<T> wr = new WeakReference<>(new Object(), new ReferenceQueue()); Object o = wr.get();
    ————只要GC运行就清理弱引用对象
    虚引用:PhantomReference<T> pr = new PhantomReference<>(new Object(), new ReferenceQueue()); Object o = pr.get();
    ————幽灵引用,幻影引用:最弱引用,无法获取对象实例。虚可达对象
    ————直接重写finalize方法,方法中记录时间,用于标记GC运行状态和次数,验证JVM调优
    6.final, finally, finalize
    final常量,finally配合try用于释放资源,finalize配合虚引用来触发GC回收对象
    7.GC回收原则
    1>引用计数法 - 简单,但如果对象相互引用,计数不准确,会导致内存泄露
    2>可达性分析算法 - GC ROOT,根节点向下分析引用关系,找到没有引用的对象,不能包含本地方法栈
    8.GC回收算法
    ● 标记-清除:标记并统一回收
    ● 复制:内存分为2块,存活的复制到另一块上,清理掉
    ● 标记-整理:存活的向一端移动,清理到边界以外的
    ● 分代收集:新生代使用复制、老年代使用标记-清除
    9.GC垃圾回收器
    ● 串行 ● 并行 ● CMS ● G1
    10.JVM调优参数
    ● -Xms 初始堆大小 ● -Xmx 最大堆大小 ● -Xmn 年轻代大小 ...
    通过调整参数降低 Full GC 的触发。
    11.为什么出现OOM?
    1> 老年代存储不下幸存者区的内容
    2> 永久代(元数据空间)内存不足
    3> bug GC 无法回收内存 -XX:+HeapDumpOnOutOfMemoryError 堆栈快照 -XX:+PrintGCDetails GC日志
  8. 熟悉 RabbitMQ、Quartz/SpringTask、WebMagic、EasyExcel、ECharts、Elasticsearch 的使用;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    【RabbitMQ】
    1.virtual-host:
    broker逻辑分组,虚拟的 Broker,独立:queue、exchange、binding 权限隔离
    2.RabbitMQ 消息如何传输?
    信道 channel 进行数据传输,没有使用TCP
    是建立在真实 tcp 上的虚拟连接,每个 tcp 上的信道没有限制
    3.RabbitMQ 如何保证消息不丢失?
    生产者发送丢失、消费者接收丢失
    ● 消息确认机制
    生产者发送:转发到队列,返回ACK+消息唯一ID,生产者收到ACK,消息发送成功,如果是NACK,则重新发送
    消费者接收:1.自动确认-发送ACK给生产者 2.手动确认-手动ACK给生产者 3.不确认-ackXXX=none
    ● 事务,确保消息一致性
    ● 开启消息持久化
    4.RabbitMQ 如何保证消息不重复消费?
    ● 消息唯一ID(内部生成/外部生成)
    ● Redis set(value不重复),保证消息的幂等性
    5.RabbitMQ 死信队列和延迟
    ● 死信机制实现延迟消息处理:延迟队列 → 消息超时 → DLX死信交换器 → 路由Key → 死信队列1,2...

    【Quartz】
    两个注解:@EnableScheduling @Scheduled(cron = 定时器表达式)
  9. 熟悉接口开发规范和测试经验,如 Swagger2、Jmeter 等。

  10. 熟悉微服务 Spring Cloud Alibaba 微服务框架各种组件和分布式中间件(缓存/锁/消息队列)的基本使用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    【微服务】
    1.微服务的认知
    高扩展性,公司对并发和扩展性有要求(总用户数百万级,日增5W),所以最终选择了 SpringCloudAlibaba
    2.SpringCloud的各种区别
    dubbo、gRPC、SpringCloudNetflex、SpringCloudAlibaba主流-阿里双11验证、Servicecomb
    3.SpringCloud的常用组件
    Nacos服务注册与发现和配置中心、Ribbon负载均衡(+远程调用)、Feign远程调用、Sentinel服务容错、RocketMQ消息队列、GateWay网关、链路监控
    ● Nacos 服务注册中心+统一配置中心
    ● Ribbon和Fegin区别:都可以做远程服务调用,Ribbon可以做负载匀衡/限流降级,实现类与否
    4.服务间通信
    声明式 REST 调用服务。
    5.最擅长/印象最深的两个模块
    Nacos & Ribbon
    Nacos 调试和测试必然会用到,而且统一配置中心也是它最强大的动能之一。
    Ribbon 使用 RestTemplate 【模板方法】来进行远程服务调用。
    6.微服务项目中遇到的问题:
    ● 服务注册不上————检查IP地址或者配置服务的Nacos IP 和 port
    ● 服务有注册但是请求发送不成功的问题————请求方式不对,HTTP头加入消息类型为JSON

00-面试题杂项整理(新)
https://janycode.github.io/2017/05/03/20_收藏整理/01_面试题/00-面试题杂项整理 (新)/
作者
Jerry(姜源)
发布于
2017年5月3日
许可协议