一、Java 基础知识
1、Object 类相干办法
- getClass
获取以后运行时对象的 Class 对象。 - hashCode
返回对象的 hash 码。 - clone
拷贝以后对象, 必须实现 Cloneable 接口。浅拷贝对根本类型进行值拷贝,对援用类型拷贝援用;深拷贝对根本类型进行值拷贝,对援用类型对象岂但拷贝对象的援用还拷贝对象的相干属性和办法。两者不同在于深拷贝创立了一个新的对象。 - equals
通过内存地址比拟两个对象是否相等,String 类重写了这个办法应用值来比拟是否相等。 - toString
返回类名@哈希码的 16 进制。 - notify
唤醒以后对象监视器的任一个线程。 - notifyAll
唤醒以后对象监视器上的所有线程。 - wait
1、暂停线程的执行;2、三个不同参数办法(期待多少毫秒;额定期待多少毫秒;始终期待)3、与Thread.sleep(long time)
相比,sleep 使以后线程休眠一段时间,并没有开释该对象的锁,wait 开释了锁。 - finalize
对象被垃圾回收器回收时执行的办法。
2、根本数据类型
- 整型:byte(8)、short(16)、int(32)、long(64)
- 浮点型:float(32)、double(64)
- 布尔型:boolean(8)
- 字符型:char(16)
3、序列化
Java 对象实现序列化要实现 Serializable 接口。
- 反序列化并不会调用构造方法。反序列的对象是由 JVM 本人生成的对象,不通过构造方法生成。
- 序列化对象的援用类型成员变量,也必须是可序列化的,否则,会报错。
- 如果想让某个变量不被序列化,应用 transient 润饰。
- 单例类序列化,须要重写 readResolve() 办法。
4、String、StringBuffer、StringBuilder
- String
由 char[] 数组形成,应用了 final 润饰,是不可变对象,能够了解为常量,线程平安;对 String 进行扭转时每次都会新生成一个 String 对象,而后把指针指向新的援用对象。 - StringBuffer 线程平安;StringBuiler 线程不平安。
- 操作大量字符数据用 String;单线程操作大量数据用 StringBuilder;多线程操作大量数据用 StringBuffer。
5、重载与重写
- 重载
产生在同一个类中,办法名雷同,参数的类型、个数、程序不同,办法的返回值和修饰符能够不同。 - 重写
产生在父子类中,办法名和参数雷同,返回值范畴小于等于父类,抛出的异样范畴小于等于父类,拜访修饰符范畴大于等于父类;如果父类办法拜访修饰符为 private 或者 final 则子类就不能重写该办法。
6、final
- 润饰根本类型变量,一经出初始化后就不可能对其进行批改。
- 润饰援用类型变量,不可能指向另一个援用。
- 润饰类或办法,不能被继承或重写。
7、反射
- 在运行时动静的获取类的残缺信息
- 减少程序的灵活性
- JDK 动静代理应用了反射
8、JDK 动静代理
应用步骤
- 创立接口及实现类
- 实现代理处理器:实现 InvokationHandler ,实现 invoke(Proxy proxy,Method method,Object[] args) 办法
- 通过 Proxy.newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h) 取得代理类
- 通过代理类调用办法。
9、Java IO
- 一般 IO ,面向流,同步阻塞线程。
- NIO,面向缓冲区,同步非阻塞。
二、Java 汇合框架
1、List(线性构造)
- ArrayList
Object[] 数组实现,默认大小为 10 ,反对随机拜访,间断内存空间,插入开端工夫复杂度 o(1),插入第 i 个地位工夫复杂度 o(n - i)。扩容,大小变为 1.5 倍,Arrays.copyOf(底层 System.ArrayCopy),复制到新数组,指针指向新数组。 - Vector
相似 ArrayList,线程平安,扩容默认增长为原来的 2 倍,还能够指定增长空间长度。 - LinkedList
基于链表实现,1.7 为双向链表,1.6 为双向循环链表,勾销循环更能分清头尾。
2、Map(K,V 对)
HashMap
- 底层数据结构,JDK 1.8 是数组 + 链表 + 红黑树,JDK 1.7 无红黑树。链表长度大于 8 时,转化为红黑树,优化查问效率。
- 初始容量为 16,通过 tableSizeFor 保障容量为 2 的幂次方。寻址形式,高位异或,(n-1)&h 取模,优化速度。
- 扩容机制,当元素数量大于容量 x 负载因子 0.75 时,容量扩充为原来的 2 倍,新建一个数组,而后转移到新数组。
- 基于 Map 实现。
- 线程不平安。
HashMap (1.7) 多线程循环链表问题
- 在多线程环境下,进行扩容时,1.7 下的 HashMap 会造成循环链表。
- 怎么造成循环链表:
假如有一 HashMap 容量为 2 , 在数组下标 1 地位以 A -> B 链表模式存储。有一线程对该 map 做 put 操作,因为触发扩容条件,须要进行扩容。这时另一个线程也 put 操作,同样须要扩容,并实现了扩容操作,因为复制到新数组是头部插入,所以 1 地位变为 B -> A 。这时第一个线程持续做扩容操作,首先复制 A ,而后复制 B ,再判断 B.next 是否为空时,因为第二个线程做了扩容操作,导致 B.next = A,所以在将 A 放到 B 前,A.next 又等于 B ,导致循环链表呈现。
HashTable
- 线程平安,办法根本全用 Synchronized 润饰。
- 初始容量为 11 ,扩容为 2n + 1 。
- 继承 Dictionary 类。
ConcurrentHashMap
- 线程平安的 HashMap。
- 1.7 采纳分段锁的模式加锁;1.8 应用 Synchronized 和 CAS 实现同步,若数组的 Node 为空,则通过 CAS 的形式设置值,不为空则加在链表的第一个节点。获取第一个元素是否为空应用 Unsafe 类提供的 getObjectVolatile 保障可见性。
- 对于读操作,数组由 volatile 润饰,同时数组的元素为 Node,Node 的 K 应用 final 润饰,V 应用 volatile 润饰,下一个节点也用 volatile 润饰,保障多线程的可见性。
- LinkedHashMap
LinkedHashMap 继承自 HashMap,所以它的底层依然是基于拉链式散列构造即由数组和链表或红黑树组成。另外,LinkedHashMap 在下面构造的根底上,减少了一条双向链表,使得下面的构造能够放弃键值对的插入程序。 - TreeMap
有序的 Map,红黑树结构,能够自定义比拟器来进行排序。 - Collections.synchronizedMap 如何实现 Map 线程平安?
基于 Synchronized ,实际上就是锁住了以后传入的 Map 对象。
3、Set(惟一值)
- HashSet
基于 HashMap 实现,应用了 HashMap 的 K 作为元素存储,V 为 new Object() ,在 add() 办法中如果两个元素的 Hash 值雷同,则通过 equals 办法比拟是否相等。 - LinkedHashSet
LinkedHashSet 继承于 HashSet,并且其外部是通过 LinkedHashMap 来实现的。 - TreeSet
红黑树实现有序惟一。
三、Java 多线程
1、synchronized
- 润饰代码块
底层实现,通过 monitorenter & monitorexit 标记代码块为同步代码块。 - 润饰办法
底层实现,通过 ACC_SYNCHRONIZED 标记办法是同步办法。 - 润饰类 class 对象时,理论锁在类的实例下面。
- 单例模式
public class Singleton { private static volatile Singleton instance = null; private Singleton(){} public static Singleton getInstance(){ if (null == instance) { synchronized (Singleton.class) { if (null == instance) { instance = new Singleton(); } } } return instance; }}
偏差锁,自旋锁,轻量级锁,重量级锁
- 通过 synchronized 加锁,第一个线程获取的锁为偏差锁,这时有其余线程参加锁竞争,降级为轻量级锁,其余线程通过循环的形式尝试取得锁,称自旋锁。若果自旋的次数达到肯定的阈值,则降级为重量级锁。
- 须要留神的是,在第二个线程获取锁时,会先判断第一个线程是否依然存活,如果不存活,不会降级为轻量级锁。
2、Lock
ReentrantLock
- 基于 AQS (AbstractQueuedSynchronizer)实现,次要有 state (资源) + FIFO (线程期待队列) 组成。
- 偏心锁与非偏心锁:区别在于在获取锁时,偏心锁会判断以后队列是否有正在期待的线程,如果有则进行排队。
- 应用 lock() 和 unLock() 办法来加锁解锁。
ReentrantReadWriteLock
- 同样基于 AQS 实现,外部采纳外部类的模式实现了读锁(共享锁)和写锁 (排它锁)。
- 非偏心锁吞吐量高
在获取锁的阶段来剖析,当某一线程要获取锁时,非偏心锁能够间接尝试获取锁,而不是判断以后队列中是否有线程在期待。肯定状况下能够防止线程频繁的上下文切换,这样,沉闷的线程有可能取得锁,而在队列中的锁还要进行唤醒能力持续尝试获取锁,而且线程的执行程序一般来说不影响程序的运行。
3、volatile
- Java 内存模型
- 在多线程环境下,保障变量的可见性。应用了 volatile 润饰变量后,在变量批改后会立刻同步到主存中,每次用这个变量前会从主存刷新。
- 禁止 JVM 指令重排序。
- 单例模式双重校验锁变量为什么应用 volatile 润饰?
禁止 JVM 指令重排序,new Object()分为三个步骤:申请内存空间,将内存空间援用赋值给变量,变量初始化。如果不禁止重排序,有可能失去一个未经初始化的变量。
4、线程的五种状态
1). New
一个新的线程被创立,还没开始运行。
2). Runnable
一个线程准备就绪,随时能够运行的时候就进入了 Runnable 状态。
Runnable 状态能够是理论正在运行的线程,也能够是随时能够运行的线程。
多线程环境下,每个线程都会被调配一个固定长度的 CPU 计算工夫,每个线程运行一会儿就会进行让其余线程运行,这样能力让每个线程偏心的运行。这些期待 CPU 和正在运行的线程就处于 Runnable 状态。
3). Blocked
例如一个线程在期待 I/O 资源,或者它要拜访的被爱护代码曾经被其余线程锁住了,那么它就在阻塞 Blocked 状态,这个线程所需的资源到位后就转入 Runnable 状态。
4). Waiting(无限期期待)
如果一个线程在期待其余线程的唤醒,那么它就处于 Waiting 状态。以下办法会让线程进入期待状态:
- Object.wait()
- Thread.join()
- LockSupport.park()
5). Timed Waiting(有期限期待)
无需期待被其余线程显示唤醒,在肯定工夫后有零碎主动唤醒。
以下办法会让线程进入无限期待状态:
- Thread.sleep(sleeptime)
- Object.wait(timeout)
- Thread.join(timeout)
- LockSupport.parkNanos(timeout)
- LockSupport.parkUntil(timeout)
6). Terminated
一个线程失常执行结束,或者意外失败,那么就完结了。
5、 wait() 与 sleep()
- 调用后线程进入 waiting 状态。
- wait() 开释锁,sleep() 没有开释锁。
- 调用 wait() 后须要调用 notify() 或 notifyAll() 办法唤醒线程。
- wait() 办法申明在 Object 中,sleep() 办法申明在 Thread 中。
6、 yield()
- 调用后线程进入 runnable 状态。
- 让出 CPU 工夫片,之后有可能其余线程取得执行权,也有可能这个线程继续执行。
7、 join()
- 在线程 B 中调用了线程 A 的 Join()办法,直到线程 A 执行结束后,才会继续执行线程 B。
- 能够保障线程的程序执行。
- join() 办法必须在 线程启动后调用才有意义。
- 应用 wait() 办法实现。
9、线程应用形式
- 继承 Tread 类
- 实现 Runnable 接口
- 实现 Callable 接口:带有返回值
10、Runnable 和 Callable 比拟
- 办法签名不同,
void Runnable.run()
,V Callable.call() throws Exception
- 是否容许有返回值,
Callable
容许有返回值 - 是否容许抛出异样,
Callable
容许抛出异样。 - 提交工作形式,
Callable
应用Future<T> submit(Callable<T> task)
返回 Future 对象,调用其 get() 办法能够取得返回值,Runnable
应用void execute(Runnable command)
。
11、hapens-before
如果一个操作 happens-before 另一个操作,那么第一个操作的执行后果将对第二个操作可见,而且第一个操作的执行程序排在第二个操作之前。
12、ThreadLocal
- 场景
主要用途是为了放弃线程本身对象和防止参数传递,次要实用场景是按线程多实例(每个线程对应一个实例)的对象的拜访,并且这个对象很多中央都要用到。 - 原理
为每个线程创立变量正本,不同线程之间不可见,保障线程平安。应用 ThreadLocalMap 存储变量正本,以 ThreadLocal 为 K,这样一个线程能够领有多个 ThreadLocal 对象。 - 理论
应用多数据源时,须要依据数据源的名字切换数据源,假如一个线程设置了一个数据源,这个时候就有可能有另一个线程去批改数据源,能够应用 ThreadLocal 保护这个数据源名字,使每个线程持有数据源名字的正本,防止线程平安问题。
8、线程池
1)、分类
- FixThreadPool 固定数量的线程池,实用于对线程治理,高负载的零碎
- SingleThreadPool 只有一个线程的线程池,实用于保障工作程序执行
- CacheThreadPool 创立一个不限度线程数量的线程池,实用于执行短期异步工作的小程序,低负载零碎
- ScheduledThreadPool 定时工作应用的线程池,实用于定时工作
2)、线程池的几个重要参数
- int corePoolSize, 外围线程数
- int maximumPoolSize, 最大线程数
- long keepAliveTime, TimeUnit unit, 超过 corePoolSize 的线程的存活时长,超过这个工夫,多余的线程会被回收。
- BlockingQueue<Runnable> workQueue, 工作的排队队列
- ThreadFactory threadFactory, 新线程的产生形式
- RejectedExecutionHandler handler) 回绝策略
3)、线程池线程工作过程
corePoolSize -> 工作队列 -> maximumPoolSize -> 回绝策略
外围线程在线程池中始终存活,当有工作须要执行时,间接应用外围线程执行工作。当工作数量大于外围线程数时,退出期待队列。当工作队列数量达到队列最大长度时,持续创立线程,最多达到最大线程数。当设置回收工夫时,外围线程以外的闲暇线程会被回收。如果达到了最大线程数还不可能满足工作执行需要,则依据回绝策略做回绝解决。
4)、线程池回绝策略(默认抛出异样)
AbortPolicy | 抛出 RejectedExecutionException |
DiscardPolicy | 什么也不做,间接疏忽 |
DiscardOldestPolicy | 抛弃执行队列中最老的工作,尝试为以后提交的工作腾出地位 |
CallerRunsPolicy | 间接由提交工作者执行这个工作 |
5)、如何依据 CPU 外围数设计线程池线程数量
- IO 密集型 2nCPU
计算密集型 nCPU+1
- 其中 n 为 CPU 外围数量,可通过
Runtime.getRuntime().availableProcessors()
取得外围数:。 - 为什么加 1:即便当计算密集型的线程偶然因为缺失故障或者其余起因而暂停时,这个额定的线程也能确保 CPU 的时钟周期不会被节约。
- 其中 n 为 CPU 外围数量,可通过
四、Java 虚拟机
1、Java 内存构造
- 堆
由线程共享,寄存 new 进去的对象,是垃圾回收器的次要工作区域。 - 栈
线程公有,分为 Java 虚拟机栈和本地办法栈,寄存局部变量表、操作栈、动静链接、办法进口等信息,办法的执行对应着入栈到出栈的过程。 - 办法区
线程共享,寄存已被加载的类信息、常量、动态变量、即时编译器编译后的代码等信息,JDK 1.8 中办法区被元空间取代,应用间接内存。
2、Java 类加载机制
- 加载
加载字节码文件。 链接
- 验证
验证字节码文件的正确性。 - 筹备
为动态变量分配内存。 - 解析
将符号援用(如类的全限定名)解析为间接援用(类在理论内存中的地址)。
- 验证
- 初始化
为动态变量赋初值。
双亲委派模式当一个类须要加载时,判断以后类是否被加载过。曾经被加载的类会间接返回,否则才会尝试加载。加载的时候,首先会把该申请委派该父类加载器的
loadClass()
解决,因而所有的申请最终都应该传送到顶层的启动类加载器BootstrapClassLoader
中。当父类加载器无奈解决时,才由本人来解决。当父类加载器为 null 时,会应用启动类加载器BootstrapClassLoader
作为父类加载器。
3、垃圾回收算法
- Mark-Sweep(标记-革除)算法
标记须要回收的对象,而后革除,会造成许多内存碎片。 - Copying(复制)算法
将内存分为两块,只应用一块,进行垃圾回收时,先将存活的对象复制到另一块区域,而后清空之前的区域。 - Mark-Compact(标记-整顿)算法(压缩法)
与标记革除算法相似,然而在标记之后,将存活对象向一端挪动,而后革除边界外的垃圾对象。 - Generational Collection(分代收集)算法
分为年老代和老年代,年老代时比拟沉闷的对象,应用复制算法做垃圾回收。老年代每次回收只回收大量对象,应用标记整顿法。
4、典型垃圾回收器
CMS
- 简介
以获取最短回收进展工夫为指标的收集器,它是一种并发收集器,采纳的是 Mark-Sweep 算法。 - 场景
如果你的利用须要更快的响应,不心愿有长时间的进展,同时你的 CPU 资源也比拟丰盛,就适宜实用 CMS 收集器。 - 垃圾回收步骤
- 初始标记 (Stop the World 事件 CPU 进展, 很短) 初始标记仅标记一下 GC Roots 能间接关联到的对象,速度很快;
- 并发标记 (收集垃圾跟用户线程一起执行) 并发标记过程就是进行 GC Roots 查找的过程;
- 从新标记 (Stop the World 事件 CPU 进展,比初始标记略微长,远比并发标记短) 修改因为并发标记时利用运行产生变动的标记。
- 并发清理,标记革除算法;
毛病
- 并发标记时和应用程序同时进行,占用一部分线程,所以吞吐量有所降落。
- 并发革除时和应用程序同时进行,这段时间产生的垃圾就要等下一次 GC 再革除。
- 采纳的标记革除算法,产生内存碎片,如果要新建大对象,会提前触发 Full GC 。
- 简介
G1
- 简介
是一款面向服务端利用的收集器,它能充分利用多 CPU、多核环境。因而它是一款并行与并发收集器,并且它能建设可预测的进展工夫模型,即能够设置 STW 的工夫。 - 垃圾回收步骤
1、初始标记(stop the world 事件 CPU 进展只解决垃圾);
2、并发标记(与用户线程并发执行);
3、最终标记(stop the world 事件 ,CPU 进展解决垃圾);
4、筛选回收(stop the world 事件 依据用户冀望的 GC 进展工夫回收) 特点
- 并发与并行
充分利用多核 CPU ,应用多核来缩短 STW 工夫,局部须要进展利用线程的操作,依然能够通过并发保障应用程序的执行。 - 分代回收
新生代,幸存带,老年代 - 空间整合
总体看是采纳标记整顿算法回收,每个 Region 大小相等,通过复制来回收。 - 可预测的进展工夫
应用 -XX:MaxGCPauseMillis=200 设置最长指标暂停值。
- 并发与并行
- 简介
在 Java 语言中,可作为 GC Roots 的对象包含 4 种状况:a) 虚拟机栈中援用的对象(栈帧中的本地变量表);
b) 办法区中类动态属性援用的对象;
c) 办法区中常量援用的对象;
d) 本地办法栈中 Native 办法援用的对象。
五、MySQL (Inno DB)
1、聚簇索引与非聚簇索引
- 都应用 B+ 树作为数据结构
- 聚簇索引中数据存在主键索引的叶子结点中,失去 key 即失去 data ;非聚簇索引的数据存在独自的空间。
- 聚簇索引中辅助索引的叶子结点存的是主键;非聚簇索引中叶子结点存的是数据的地址;
- 聚簇索引的劣势是找到主键就找到数据,只需一次磁盘 IO ;当 B+ 树的结点发生变化时,地址也会发生变化,这时非聚簇索引须要更新所有的地址,减少开销。
2、为何应用 B 树做索引而不是红黑树?
索引很大,通常作为文件存储在磁盘下面,每次检索索引都须要把索引文件加载进内存,所以磁盘 IO 的次数是掂量索引数据结构好坏的重要指标。应用程序在从磁盘读取数据时,不只是读取须要的数据,还会连同其余数据以页的模式做预读来缩小磁盘 IO 的次数。数据库的设计者将每个节点的大小设置为一页的大小,同时每次新建节点时都从新申请一个页,这样检索一个节点只须要一次 IO,依据索引定位到数据只须要 h- 1(h 为 B 树高度,根节点常驻内存) 次 IO,而 d (度,能够了解为宽度)与 h 称正比,即 d 越大,高度就越小,所以树越扁,磁盘 IO 次数越少,即渐进复杂度为 logdN ,这也是为什么不抉择红黑树做索引的起因。后面能够得出结论,d 越大,索引的性能越好。节点由 key 和 data 组成,页的大小肯定,key 和 data 越小,d 越大。B + 树去掉了节点内的 data 域,所以有更大的 d , 性能更好。
3、最左前缀准则
在 MySQL 中,能够指定多个列为索引,即联结索引。比方 index(name,age) ,最左前缀准则是指查问时准确匹配到从最右边开始的一列或几列(name;name&age),就能够命中索引。如果所有列都用到了,程序不同,查问引擎会主动优化为匹配联结索引的程序,这样是可能命中索引的。
4、什么状况下能够用到 B 树索引
(1) 定义有主键的列肯定要建设索引。因为主键能够减速定位到表中的某行
(2) 定义有外键的列肯定要建设索引。外键列通常用于表与表之间的连贯,在其上创立索引能够放慢表间的连贯
(3) 对于常常查问的数据列最好建设索引。
① 对于须要在指定范畴内疾速或频繁查问的数据列,因为索引曾经排序,其指定的范畴是间断的,查问能够利用索引的排序,放慢查问的工夫
② 常常用在 where
子句中的数据列,将索引建设在 where
子句的汇合过程中,对于须要减速或频繁检索的数据列,能够让这些常常参加查问的数据列依照索引的排序进行查问,放慢查问的工夫。
5、事务隔离级别
- Read uncommitted
读未提交,可能呈现脏读,不可反复读,幻读。 - Read committed
读提交,可能呈现不可反复读,幻读。 - Repeatable read
可反复读,可能呈现脏读。 - Serializable
可串行化,同一数据读写都加锁,防止脏读,性能不忍直视。
Inno DB 默认隔离级别为可反复读级别,分为快照度和以后读,并且通过行锁和间隙锁解决了幻读问题。
6、MVCC (多版本并发管制)
实现细节
- 每行数据都存在一个版本,每次数据更新时都更新该版本。
- 批改时 Copy 出以后版本随便批改,各个事务之间互不烦扰。
- 保留时比拟版本号,如果胜利(commit),则笼罩原记录;失败则放弃 copy(rollback)。
- Inno DB 实现
在 InnoDB 中为每行减少两个暗藏的字段,别离是该行数据创立时的版本号和删除时的版本号,这里的版本号是零碎版本号(能够简略了解为事务的 ID),每开始一个新的事务,零碎版本号就主动递增,作为事务的 ID 。通常这两个版本号别离叫做创立工夫和删除工夫。
具体参考:《 脏读、幻读和不可反复读》
六、Spring 相干
1、Bean 的作用域
类别 | 阐明 |
singleton | 默认在 Spring 容器中仅存在一个实例 |
prototype | 每次调用 getBean() 都从新生成一个实例 |
request | 为每个 HTTP 申请生成一个实例 |
session | 同一个 HTTP session 应用一个实例,不同 session 应用不同实例 |
2、Bean 生命周期
简略来说四步:
- 实例化 Instantiation
- 属性赋值 Populate
- 初始化 Initialization
- 销毁 Destruction
在这四步的根底下面,Spring 提供了一些拓展点:
- Bean 本身的办法: 这个包含了 Bean 自身调用的办法和通过配置文件中 %3Cbean %3E 的 init-method 和 destroy-method 指定的办法
- Bean 级生命周期接口办法: 这个包含了 BeanNameAware、BeanFactoryAware、InitializingBean 和 DiposableBean 这些接口的办法
- 容器级生命周期接口办法:这个包含了 InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 这两个接口实现,个别称它们的实现类为“后处理器”。
- 工厂后处理器接口办法: 这个包含了 AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer 等等十分有用的工厂后处理器接口的办法。工厂后处理器也是容器级的。在利用上下文拆卸配置文件之后立刻调用。
3、Spring AOP
实现形式两种:
- JDK 动静代理:带有接口的对象,在运行期实现
- CGlib 动态代理:在编译期实现。
4、Spring 事务流传行为
默认 PROPAGATION_REQUIRED ,如果存在一个事务,则反对以后事务。如果没有事务则开启一个新的事务。
5、Spring IoC
6、Spring MVC 工作流程
七、计算机网络
1、TCP/IP 五层模型
2、浏览器输出地址后做了什么?
3、三次握手与四次挥手
- 三次握手
- 四次挥手
4、TIME_WAIT 与 CLOSE_WAIT
5、TCP 滑动窗口
TCP 流量管制,次要应用滑动窗口协定,滑动窗口是承受数据端应用的窗口大小,用来通知发送端接收端的缓存大小,以此能够管制发送端发送数据的大小,从而达到流量管制的目标。这个窗口大小就是咱们一次传输几个数据。对所有数据帧按程序赋予编号,发送方在发送过程中始终保持着一个发送窗口,只有落在发送窗口内的帧才容许被发送;同时接管方也维持着一个接管窗口,只有落在接管窗口内的帧才容许接管。
6、TCP 粘包和拆包
- 景象
- 产生起因
1、要发送的数据大于 TCP 发送缓冲区残余空间大小,将会产生拆包。
2、待发送数据大于 MSS(最大报文长度),TCP 在传输前将进行拆包。
3、要发送的数据小于 TCP 发送缓冲区的大小,TCP 将屡次写入缓冲区的数据一次发送进来,将会产生粘包。
4、接收数据端的应用层没有及时读取接收缓冲区中的数据,将产生粘包。 - 解决形式
1、发送端给每个数据包增加包首部,首部中应该至多蕴含数据包的长度,这样接收端在接管到数据后,通过读取包首部的长度字段,便晓得每一个数据包的理论长度了。
2、发送端将每个数据包封装为固定长度(不够的能够通过补 0 填充),这样接收端每次从接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。
3、能够在数据包之间设置边界,如增加特殊符号,这样,接收端通过这个边界就能够将不同的数据包拆离开。
八、MQ 音讯队列
1、场景作用
削峰填谷,异步解耦。
2、如何保障音讯不被反复生产呢?
这个问题能够换个思路,保障音讯反复生产,其实是保障程序的幂等性。无论音讯如何反复,程序运行的后果是统一的。比方生产音讯后做数据库插入操作,为了避免音讯反复生产,能够在插入前先查问一下有没有对应的数据。
3、怎么保障从音讯队列里拿到的数据按程序执行?
生产端在接管到音讯后放入内存队列,而后对队列中的音讯进行有序生产。
4、如何解决音讯队列的延时以及过期生效问题?音讯队列满了当前该怎么解决?有几百万音讯继续积压几小时,说说怎么解决?
音讯过期生效问题,如果音讯一段时间不生产,导致过期生效了,音讯就失落了,只能从新查出失落的音讯,从新发送。
再来说音讯积压的问题:(思路是疾速生产掉积压的音讯)
- 首先排查生产端问题,复原生产端失常生产速度。
而后着手解决队列中的积压音讯。
- 停掉现有的 consumer。
- 新建一个 topic ,设置之前 10 倍的 partation,之前 10 倍的队列。
- 写一个散发程序,将积压的音讯平均的轮询写入这些队列。
- 而后长期用 10 倍的机器部署 consumer,每一批 consumer 生产 1 个长期的队列。
- 生产结束后,复原原有架构。
音讯队列满了:只能边接管边抛弃,而后从新补回失落的音讯,再做生产。
4、如何保障音讯的可靠性传输(如何解决音讯失落的问题)?
kafka 为例:
- 消费者丢了数据:
每次音讯生产后,由主动提交 offset 改为手动提交 offset 。 kafka 丢了音讯:
比拟常见的一个场景,就是 kafka 某个 broker 宕机,而后从新选举 partition 的 leader 时。要是此时其余的 follower 刚好还有些数据没有同步,后果此时 leader 挂了,而后大家选举某个 follower 成为 leader 之后,不就少了一些数据。- 给 topic 设置replication.factor参数:这个值必须大于 1,要求每个 partition 必须有至多两个正本。
- 在 kafka 服务端设置min.insync.replicas参数:这个值必须大于 1,这个是要求一个 leader 至多感知到有至多一个 follower 还跟本人保持联系,没落伍,这样能力确保 leader 挂了还有一个 follower。
- 在 producer 端设置acks=all:这个是要求每条数据,必须是写入所有 replica 之后,能力认为是写胜利了。
- 在 producer 端设置retries=MAX(很大很大很大的一个值,有限次重试的意思):这个是要求一旦写入失败,就有限重试,卡在这里。
- 生产者丢了音讯:
如果依照上述的思路设置了 ack=all,肯定不会丢,要求是,你的 leader 接管到音讯,所有的 follower 都同步到了音讯之后,才认为本次写胜利了。如果没满足这个条件,生产者会主动一直的重试,重试有限次。
九、Redis
1、数据类型
- String
常用命令: set,get,decr,incr,mget 等。
- Hash
常用命令: hget,hset,hgetall 等
- List
常用命令: lpush,rpush,lpop,rpop,lrange 等
能够通过 lrange 命令,就是从某个元素开始读取多少个元素,能够基于 list 实现分页查问。
- Set
常用命令: sadd,spop,smembers,sunion 等
- Sort Set
常用命令: zadd,zrange,zrem,zcard 等
2、Redis 如何实现 key 的过期删除?
定期删除和惰性删除的模式。
- 定期删除
Redis 每隔一段时间从设置过期工夫的 key 汇合中,随机抽取一些 key ,查看是否过期,如果曾经过期做删除解决。 - 惰性删除
Redis 在 key 被拜访的时候查看 key 是否过期,如果过期则删除。
3、Redis 的长久化机制
数据快照(RDB)+ 批改数据语句文件(AOF)
4、如何解决 Redis 缓存雪崩和缓存穿透?
缓存雪崩
缓存同一时间大面积的生效,所以,前面的申请都会落到数据库上,造成数据库短时间内接受大量申请而崩掉。解决形式
- 事先:保障 Redis 集群的稳定性,发现机器宕机尽快补上,设置适合的内存淘汰策略。
- 事中:本地缓存 + 限流降级,防止大量申请落在数据库上。
- 预先:利用 Redis 长久化机制尽快恢复缓存。
缓存穿透
个别是黑客成心去申请缓存中不存在的数据,导致所有的申请都落到数据库上,造成数据库短时间内接受大量申请而崩掉。- 解决形式
将不存在的数据列举到一个足够大的 map 上,这样受到攻打时,间接拦挡 map 中的申请,申请到数据库下面。或是把不存在的也做缓存,值为 null ,设置过期工夫。
- 解决形式
5、如何应用 Redis 实现音讯队列?
Redis 实现音讯队列依赖于 Redis 集群的稳定性,通常不倡议应用。
- Redis 自带公布订阅性能,基于 publish 和 subscribe 命令。
- 应用 List 存储音讯,lpush,rpop 别离发送接管音讯。
十、Nginx
Nginx 是一款轻量级的 Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器。 Nginx 次要提供反向代
理、负载平衡、动静拆散(动态资源服务)等服务。
1、正向代理和反向代理
- 正向代理
代理客户端拜访服务器。典型:VPN - 反向代理
代替服务器接管客户端申请,而后转发给服务器,服务器接管申请并将解决的后果通过代理服务器转发给客户端。
2、负载平衡
将申请摊派到多台机器下来,高并发,减少吞吐量。
负载平衡算法
- 权重轮询
- fair
- ip_hash
- url_hash
3、动静拆散
动静拆散是让动静网站里的动静网页依据肯定规定把不变的资源和常常变的资源辨别开来,动静资源做好了拆分当前,咱们就能够依据动态资源的特点将其做缓存操作,这就是网站动态化解决的外围思路。
4、Nginx 四个组成部分
- Nginx 二进制可执行文件:由各模块源码编译出一个文件
- Nginx.conf 配置文件:管制 Nginx 行为
- acess.log 拜访日志: 记录每一条 HTTP 申请信息
- error.log 谬误日志:定位问题
欢送拜访集体博客 获取更多常识分享。