From a3d77949ed8cfa668c9cf08825d2e409cd8292d8 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 24 Oct 2021 17:25:34 +0800
Subject: [PATCH 001/112] =?UTF-8?q?add=20Spring=E9=AB=98=E9=A2=91=E9=9D=A2?=
=?UTF-8?q?=E8=AF=95=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...21\351\235\242\350\257\225\351\242\230.md" | 4 +-
README.md | 11 +-
...21\351\235\242\350\257\225\351\242\230.md" | 2 +-
...ng\351\235\242\350\257\225\351\242\230.md" | 639 ++++++++++++++++++
...21\351\235\242\350\257\225\351\242\230.md" | 2 +-
5 files changed, 648 insertions(+), 10 deletions(-)
create mode 100644 "\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
diff --git "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 47d6786..8db6462 100644
--- "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -28,7 +28,7 @@
- [Full GC 的触发条件?](#full-gc-%E7%9A%84%E8%A7%A6%E5%8F%91%E6%9D%A1%E4%BB%B6)
- [垃圾回收算法有哪些?](#%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E6%9C%89%E5%93%AA%E4%BA%9B)
- [有哪些垃圾回收器?](#%E6%9C%89%E5%93%AA%E4%BA%9B%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8)
-- [常用的 JVM 调优的参数都有哪些?](#%E5%B8%B8%E7%94%A8%E7%9A%84-jvm-%E8%B0%83%E4%BC%98%E7%9A%84%E5%8F%82%E6%95%B0%E9%83%BD%E6%9C%89%E5%93%AA%E4%BA%9B)
+- [常用的 JVM 调优的命令都有哪些?](#%E5%B8%B8%E7%94%A8%E7%9A%84-jvm-%E8%B0%83%E4%BC%98%E7%9A%84%E5%8F%82%E6%95%B0%E9%83%BD%E6%9C%89%E5%93%AA%E4%BA%9B)
- [对象头了解吗?](#%E5%AF%B9%E8%B1%A1%E5%A4%B4%E4%BA%86%E8%A7%A3%E5%90%97)
- [如何排查 OOM 的问题?](#%E5%A6%82%E4%BD%95%E6%8E%92%E6%9F%A5-oom-%E7%9A%84%E9%97%AE%E9%A2%98)
- [GC是什么?为什么要GC?](#gc%E6%98%AF%E4%BB%80%E4%B9%88%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81gc)
@@ -523,7 +523,7 @@ G1 收集器的回收过程分为以下几个步骤:
- **最终标记**。需对其他线程做短暂的暂停,用于处理并发标记阶段对象引用出现变动的区域。
- **筛选回收**。对各个分区的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,然后把决定回收的分区的存活对象复制到空的分区中,再清理掉整个旧的分区的全部空间。这里的操作涉及存活对象的移动,会暂停用户线程,由多条收集器线程并行完成。
-## 常用的 JVM 调优的参数都有哪些?
+## 常用的 JVM 调优的命令都有哪些?
**jps**:列出本机所有 Java 进程的**进程号**。
diff --git a/README.md b/README.md
index 714e7d4..6c1888c 100644
--- a/README.md
+++ b/README.md
@@ -65,14 +65,14 @@
## MySQL
-1. [【大厂面试】—— MySQL高频面试题50道](数据库/MySQL高频面试题.md)(推荐 :+1:)
+1. [【大厂面试】—— MySQL高频面试题50道](数据库/MySQL高频面试题.md)(知乎高赞,推荐 :+1:)
2. [MySQL基础知识总结](数据库/MySQL基础.md)
3. [MySQL执行计划](数据库/MySQL执行计划.md)(推荐 :+1:)
## Redis
-1. [【大厂面试】——Redis30问](中间件/Redis面试题.md)(推荐 :+1:)
-2. [Redis基础](中间件/Redis入门指南总结.md)
+1. [【大厂面试】——Redis30问](中间件/Redis面试题.md)(知乎高赞,推荐 :+1:)
+2. [Redis基础总结](中间件/Redis入门指南总结.md)
3. [Redis分布式锁(推荐 :+1:)](中间件/Redis分布式锁.md)
4. [缓存穿透、缓存雪崩、缓存击穿](中间件/缓存穿透、缓存雪崩、缓存击穿.md)
@@ -80,10 +80,9 @@
## Spring
-1. [Spring知识点总结](框架/Spring总结.md)(推荐 :+1:)
-2. [Spring实战笔记](框架/Spring实战.md)
+1. [Spring高频面试题30道](框架/Spring面试题.md)(推荐 :+1:)
+2. [Spring知识点总结](框架/Spring总结.md)
3. [Spring用到哪些设计模式?](框架/Spring用到哪些设计模式.md)
-4. [Spring自动装配](框架/Spring自动装配.md)
## Spring Boot
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index c432724..c6850cb 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -168,7 +168,7 @@ B+ 树是基于B 树和叶子节点顺序访问指针进行实现,它具有B
在 B+ 树中,节点中的 `key` 从左到右递增排列,如果某个指针的左右相邻 `key` 分别是 keyi 和 keyi+1,则该指针指向节点的所有 `key` 大于等于 keyi 且小于等于 keyi+1。
-
+
进行查找操作时,首先在根节点进行二分查找,找到`key`所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出`key`所对应的数据项。
diff --git "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md" "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
new file mode 100644
index 0000000..d76a9ef
--- /dev/null
+++ "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
@@ -0,0 +1,639 @@
+
+
+
+
+- [Spring的优点](#spring%E7%9A%84%E4%BC%98%E7%82%B9)
+- [Spring 用到了哪些设计模式?](#spring-%E7%94%A8%E5%88%B0%E4%BA%86%E5%93%AA%E4%BA%9B%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F)
+- [什么是AOP?](#%E4%BB%80%E4%B9%88%E6%98%AFaop)
+- [AOP有哪些实现方式?](#aop%E6%9C%89%E5%93%AA%E4%BA%9B%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F)
+- [JDK动态代理和CGLIB动态代理的区别?](#jdk%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E5%92%8Ccglib%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E7%9A%84%E5%8C%BA%E5%88%AB)
+- [Spring AOP相关术语](#spring-aop%E7%9B%B8%E5%85%B3%E6%9C%AF%E8%AF%AD)
+- [Spring通知有哪些类型?](#spring%E9%80%9A%E7%9F%A5%E6%9C%89%E5%93%AA%E4%BA%9B%E7%B1%BB%E5%9E%8B)
+- [什么是IOC?](#%E4%BB%80%E4%B9%88%E6%98%AFioc)
+- [IOC的优点是什么?](#ioc%E7%9A%84%E4%BC%98%E7%82%B9%E6%98%AF%E4%BB%80%E4%B9%88)
+- [什么是依赖注入?](#%E4%BB%80%E4%B9%88%E6%98%AF%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5)
+- [IOC容器初始化过程?](#ioc%E5%AE%B9%E5%99%A8%E5%88%9D%E5%A7%8B%E5%8C%96%E8%BF%87%E7%A8%8B)
+- [Bean的生命周期](#bean%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F)
+- [BeanFactory和FactoryBean的区别?](#beanfactory%E5%92%8Cfactorybean%E7%9A%84%E5%8C%BA%E5%88%AB)
+- [Bean注入容器有哪些方式?](#bean%E6%B3%A8%E5%85%A5%E5%AE%B9%E5%99%A8%E6%9C%89%E5%93%AA%E4%BA%9B%E6%96%B9%E5%BC%8F)
+- [Bean的作用域](#bean%E7%9A%84%E4%BD%9C%E7%94%A8%E5%9F%9F)
+- [Spring自动装配的方式有哪些?](#spring%E8%87%AA%E5%8A%A8%E8%A3%85%E9%85%8D%E7%9A%84%E6%96%B9%E5%BC%8F%E6%9C%89%E5%93%AA%E4%BA%9B)
+- [@Autowired和@Resource的区别?](#autowired%E5%92%8Cresource%E7%9A%84%E5%8C%BA%E5%88%AB)
+- [@Qualifier 注解有什么作用](#qualifier-%E6%B3%A8%E8%A7%A3%E6%9C%89%E4%BB%80%E4%B9%88%E4%BD%9C%E7%94%A8)
+- [@Bean和@Component有什么区别?](#bean%E5%92%8Ccomponent%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB)
+- [@Component、@Controller、@Repositor和@Service 的区别?](#componentcontrollerrepositor%E5%92%8Cservice-%E7%9A%84%E5%8C%BA%E5%88%AB)
+- [Spring 事务实现方式有哪些?](#spring-%E4%BA%8B%E5%8A%A1%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F%E6%9C%89%E5%93%AA%E4%BA%9B)
+- [有哪些事务传播行为?](#%E6%9C%89%E5%93%AA%E4%BA%9B%E4%BA%8B%E5%8A%A1%E4%BC%A0%E6%92%AD%E8%A1%8C%E4%B8%BA)
+- [Spring怎么解决循环依赖的问题?](#spring%E6%80%8E%E4%B9%88%E8%A7%A3%E5%86%B3%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96%E7%9A%84%E9%97%AE%E9%A2%98)
+- [Spring启动过程](#spring%E5%90%AF%E5%8A%A8%E8%BF%87%E7%A8%8B)
+- [Spring 的单例 Bean 是否有线程安全问题?](#spring-%E7%9A%84%E5%8D%95%E4%BE%8B-bean-%E6%98%AF%E5%90%A6%E6%9C%89%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E9%97%AE%E9%A2%98)
+
+
+
+## Spring的优点
+
+- **轻量**,基本版本大约2MB。
+- 通过控制反转和依赖注入实现**松耦合**。
+- 支持**面向切面**的编程,并且把应用业务逻辑和系统服务分开。
+- 通过切面和模板减少样板式代码。
+- 方便集成各种优秀框架。内部提供了对各种优秀框架的直接支持(如:Hibernate、MyBatis等)。
+- 方便程序的测试。Spring支持Junit4,添加注解便可以测试Spring程序。
+
+## Spring 用到了哪些设计模式?
+
+1、简单工厂模式:`BeanFactory`就是简单工厂模式的体现,根据传入一个唯一标识来获得 Bean 对象。
+
+```java
+@Override
+public Object getBean(String name) throws BeansException {
+ assertBeanFactoryActive();
+ return getBeanFactory().getBean(name);
+}
+```
+
+2、工厂方法模式:`FactoryBean`就是典型的工厂方法模式。spring在使用`getBean()`调用获得该bean时,会自动调用该bean的`getObject()`方法。每个 Bean 都会对应一个 `FactoryBean`,如 `SqlSessionFactory` 对应 `SqlSessionFactoryBean`。
+
+3、单例模式:一个类仅有一个实例,提供一个访问它的全局访问点。Spring 创建 Bean 实例默认是单例的。
+
+4、适配器模式:SpringMVC中的适配器`HandlerAdatper`。由于应用会有多个Controller实现,如果需要直接调用Controller方法,那么需要先判断是由哪一个Controller处理请求,然后调用相应的方法。当增加新的 Controller,需要修改原来的逻辑,违反了开闭原则(对修改关闭,对扩展开放)。
+
+为此,Spring提供了一个适配器接口,每一种 Controller 对应一种 `HandlerAdapter` 实现类,当请求过来,SpringMVC会调用`getHandler()`获取相应的Controller,然后获取该Controller对应的 `HandlerAdapter`,最后调用`HandlerAdapter`的`handle()`方法处理请求,实际上调用的是Controller的`handleRequest()`。每次添加新的 Controller 时,只需要增加一个适配器类就可以,无需修改原有的逻辑。
+
+常用的处理器适配器:`SimpleControllerHandlerAdapter`,`HttpRequestHandlerAdapter`,`AnnotationMethodHandlerAdapter`。
+
+```java
+// Determine handler for the current request.
+mappedHandler = getHandler(processedRequest);
+
+HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
+
+// Actually invoke the handler.
+mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
+
+public class HttpRequestHandlerAdapter implements HandlerAdapter {
+
+ @Override
+ public boolean supports(Object handler) {//handler是被适配的对象,这里使用的是对象的适配器模式
+ return (handler instanceof HttpRequestHandler);
+ }
+
+ @Override
+ @Nullable
+ public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
+ throws Exception {
+
+ ((HttpRequestHandler) handler).handleRequest(request, response);
+ return null;
+ }
+}
+```
+
+5、代理模式:spring 的 aop 使用了动态代理,有两种方式`JdkDynamicAopProxy`和`Cglib2AopProxy`。
+
+6、观察者模式:spring 中 observer 模式常用的地方是 listener 的实现,如`ApplicationListener`。
+
+7、模板模式: Spring 中 `jdbcTemplate`、`hibernateTemplate` 等,就使用到了模板模式。
+
+## 什么是AOP?
+
+面向切面编程,作为面向对象的一种补充,将公共逻辑(事务管理、日志、缓存等)封装成切面,跟业务代码进行分离,可以减少系统的重复代码和降低模块之间的耦合度。切面就是那些与业务无关,但所有业务模块都会调用的公共逻辑。
+
+## AOP有哪些实现方式?
+
+AOP有两种实现方式:静态代理和动态代理。
+
+**静态代理**
+
+静态代理:代理类在编译阶段生成,在编译阶段将通知织入Java字节码中,也称编译时增强。AspectJ使用的是静态代理。
+
+缺点:代理对象需要与目标对象实现一样的接口,并且实现接口的方法,会有冗余代码。同时,一旦接口增加方法,目标对象与代理对象都要维护。
+
+**动态代理**
+
+动态代理:代理类在程序运行时创建,AOP框架不会去修改字节码,而是在内存中临时生成一个代理对象,在运行期间对业务方法进行增强,不会生成新类。
+
+## JDK动态代理和CGLIB动态代理的区别?
+
+Spring AOP中的动态代理主要有两种方式:JDK动态代理和CGLIB动态代理。
+
+**JDK动态代理**
+
+如果目标类实现了接口,Spring AOP会选择使用JDK动态代理目标类。代理类根据目标类实现的接口动态生成,不需要自己编写,生成的动态代理类和目标类都实现相同的接口。JDK动态代理的核心是`InvocationHandler`接口和`Proxy`类。
+
+缺点:目标类必须有实现的接口。如果某个类没有实现接口,那么这个类就不能用JDK动态代理。
+
+**CGLIB来动态代理**
+
+通过继承实现。如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library)可以在运行时动态生成类的字节码,动态创建目标类的子类对象,在子类对象中增强目标类。
+
+CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为`final`,那么它是无法使用CGLIB做动态代理的。
+
+优点:目标类不需要实现特定的接口,更加灵活。
+
+什么时候采用哪种动态代理?
+
+1. 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
+2. 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
+3. 如果目标对象没有实现了接口,必须采用CGLIB库
+
+**两者的区别**:
+
+1. jdk动态代理使用jdk中的类Proxy来创建代理对象,它使用反射技术来实现,不需要导入其他依赖。cglib需要引入相关依赖:asm.jar,它使用字节码增强技术来实现。
+2. 当目标类实现了接口的时候Spring Aop默认使用jdk动态代理方式来增强方法,没有实现接口的时候使用cglib动态代理方式增强方法。
+
+## Spring AOP相关术语
+
+(1)切面(Aspect):切面是通知和切点的结合。通知和切点共同定义了切面的全部内容。
+
+(2)连接点(Join point):指方法,在Spring AOP中,一个连接点总是代表一个方法的执行。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
+
+(3)通知(Advice):在AOP术语中,切面的工作被称为通知。
+
+(4)切入点(Pointcut):切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。
+
+(5)引入(Introduction):引入允许我们向现有类添加新方法或属性。
+
+(6)目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。它通常是一个代理对象。
+
+(7)织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。在目标对象的生命周期里有以下时间点可以进行织入:
+
+- 编译期:切面在目标类编译时被织入。AspectJ的织入编译器是以这种方式织入切面的。
+- 类加载期:切面在目标类加载到JVM时被织入。需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入就支持以这种方式织入切面。
+- 运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。SpringAOP就是以这种方式织入切面。
+
+## Spring通知有哪些类型?
+
+在AOP术语中,切面的工作被称为通知。通知实际上是程序运行时要通过Spring AOP框架来触发的代码段。
+
+Spring切面可以应用5种类型的通知:
+
+1. 前置通知(Before):在目标方法被调用之前调用通知功能;
+2. 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
+3. 返回通知(After-returning ):在目标方法成功执行之后调用通知;
+4. 异常通知(After-throwing):在目标方法抛出异常后调用通知;
+5. 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的逻辑。
+
+## 什么是IOC?
+
+IOC:控制反转,由Spring容器管理bean的整个生命周期。通过反射实现对其他对象的控制,包括初始化、创建、销毁等,解放手动创建对象的过程,同时降低类之间的耦合度。
+
+IOC的好处:降低了类之间的耦合,对象创建和初始化交给Spring容器管理,在需要的时候只需向容器进行申请。
+
+## IOC的优点是什么?
+
+- IOC 和依赖注入降低了应用的代码量。
+- 松耦合。
+- 支持加载服务时的饿汉式初始化和懒加载。
+
+## 什么是依赖注入?
+
+在Spring创建对象的过程中,把对象依赖的属性注入到对象中。依赖注入主要有两种方式:构造器注入和属性注入。
+
+## IOC容器初始化过程?
+
+ioc 容器初始化过程:BeanDefinition 的资源定位、解析和注册。
+
+1. 从XML中读取配置文件。
+2. 将bean标签解析成 BeanDefinition,如解析 property 元素, 并注入到 BeanDefinition 实例中。
+3. 将 BeanDefinition 注册到容器 BeanDefinitionMap 中。
+4. BeanFactory 根据 BeanDefinition 的定义信息创建实例化和初始化 bean。
+
+单例bean的初始化以及依赖注入一般都在容器初始化阶段进行,只有懒加载(lazy-init为true)的单例bean是在应用第一次调用getBean()时进行初始化和依赖注入。
+
+```java
+// AbstractApplicationContext
+// Instantiate all remaining (non-lazy-init) singletons.
+finishBeanFactoryInitialization(beanFactory);
+```
+
+多例bean 在容器启动时不实例化,即使设置 lazy-init 为 false 也没用,只有调用了getBean()才进行实例化。
+
+`loadBeanDefinitions`采用了模板模式,具体加载 `BeanDefinition` 的逻辑由各个子类完成。
+
+## Bean的生命周期
+
+
+
+1.对Bean进行实例化
+
+2.依赖注入
+
+3.如果Bean实现了`BeanNameAware`接口,Spring将调用`setBeanName`(),设置 `Bean`的 id(xml文件中bean标签的id)
+
+4.如果Bean实现了`BeanFactoryAware`接口,Spring将调用`setBeanFactory()`
+
+5.如果Bean实现了`ApplicationContextAware`接口,Spring容器将调用`setApplicationContext()`
+
+6.如果存在`BeanPostProcessor`,Spring将调用它们的`postProcessBeforeInitialization`(预初始化)方法,在Bean初始化前对其进行处理
+
+7.如果Bean实现了`InitializingBean`接口,Spring将调用它的`afterPropertiesSet`方法,然后调用xml定义的 init-method 方法,两个方法作用类似,都是在初始化 bean 的时候执行
+
+8.如果存在`BeanPostProcessor`,Spring将调用它们的`postProcessAfterInitialization`(后初始化)方法,在Bean初始化后对其进行处理
+
+9.Bean初始化完成,供应用使用,直到应用被销毁
+
+10.如果Bean实现了`DisposableBean`接口,Spring将调用它的`destory`方法,然后调用在xml中定义的 `destory-method`方法,这两个方法作用类似,都是在Bean实例销毁前执行。
+
+```java
+public interface BeanPostProcessor {
+ @Nullable
+ default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ return bean;
+ }
+
+ @Nullable
+ default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+ return bean;
+ }
+
+}
+
+public interface InitializingBean {
+ void afterPropertiesSet() throws Exception;
+}
+```
+
+## BeanFactory和FactoryBean的区别?
+
+BeanFactory:管理Bean的容器,Spring中生成的Bean都是由这个接口的实现来管理的。
+
+FactoryBean:通常是用来创建比较复杂的bean,一般的bean 直接用xml配置即可,但如果一个bean的创建过程中涉及到很多其他的bean 和复杂的逻辑,直接用xml配置比较麻烦,这时可以考虑用FactoryBean,可以隐藏实例化复杂Bean的细节。
+
+当配置文件中bean标签的class属性配置的实现类是FactoryBean时,通过 getBean()方法返回的不是FactoryBean本身,而是调用FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。如果想得到FactoryBean必须使用 '&' + beanName 的方式获取。
+
+Mybatis 提供了 SqlSessionFactoryBean,可以简化 SqlSessionFactory 的配置:
+
+```java
+public class SqlSessionFactoryBean implements FactoryBean, InitializingBean, ApplicationListener {
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ notNull(dataSource, "Property 'dataSource' is required");
+ notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
+ state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
+ "Property 'configuration' and 'configLocation' can not specified with together");
+
+ this.sqlSessionFactory = buildSqlSessionFactory();
+ }
+
+ protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
+ //复杂逻辑
+ }
+
+ @Override
+ public SqlSessionFactory getObject() throws Exception {
+ if (this.sqlSessionFactory == null) {
+ afterPropertiesSet();
+ }
+
+ return this.sqlSessionFactory;
+ }
+}
+```
+
+在 xml 配置 SqlSessionFactoryBean:
+
+```xml
+
+
+
+
+
+
+```
+
+Spring 将会在应用启动时创建 `SqlSessionFactory`,并使用 `sqlSessionFactory` 这个名字存储起来。
+
+## Bean注入容器有哪些方式?
+
+将普通类交给Spring容器管理,通常有以下方法:
+
+1、使用`@Configuration`与`@Bean`注解
+
+2、使用`@Controller`、`@Service`、`@Repository`、`@Component` 注解标注该类,然后启用`@ComponentScan`自动扫描
+
+3、使用`@Import` 方法。使用@Import注解把bean导入到当前容器中,代码如下:
+
+```java
+//@SpringBootApplication
+@ComponentScan
+/*把用到的资源导入到当前容器中*/
+@Import({Dog.class, Cat.class})
+public class App {
+
+ public static void main(String[] args) throws Exception {
+
+ ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
+ System.out.println(context.getBean(Dog.class));
+ System.out.println(context.getBean(Cat.class));
+ context.close();
+ }
+}
+```
+
+
+
+## Bean的作用域
+
+1、singleton:单例,Spring中的bean默认都是单例的。
+
+2、prototype:每次请求都会创建一个新的bean实例。
+
+3、request:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。
+
+4、session:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP session内有效。
+
+5、global-session:全局session作用域。
+
+## Spring自动装配的方式有哪些?
+
+Spring的自动装配有三种模式:byType(根据类型),byName(根据名称)、constructor(根据构造函数)。
+
+**byType**
+
+找到与依赖类型相同的bean注入到另外的bean中,这个过程需要借助setter注入来完成,因此必须存在set方法,否则注入失败。
+
+当xml文件中存在多个相同类型名称不同的实例Bean时,Spring容器依赖注入仍然会失败,因为存在多种适合的选项,Spring容器无法知道该注入那种,此时我们需要为Spring容器提供帮助,指定注入那个Bean实例。可以通过<bean>标签的autowire-candidate设置为false来过滤那些不需要注入的实例Bean
+
+```xml
+
+
+
+
+
+
+
+```
+
+**byName**
+
+将属性名与bean名称进行匹配,如果找到则注入依赖bean。
+
+```xml
+
+
+
+
+
+```
+
+**constructor**
+
+存在单个实例则优先按类型进行参数匹配(无论名称是否匹配),当存在多个类型相同实例时,按名称优先匹配,如果没有找到对应名称,则注入失败,此时可以使用autowire-candidate=”false” 过滤来解决。
+
+```xml
+
+
+
+
+
+
+```
+
+@Autowired 可以传递了一个required=false的属性,false指明当userDao实例存在就注入不存就忽略,如果为true,就必须注入,若userDao实例不存在,就抛出异常。由于默认情况下@Autowired是按类型匹配的(byType),如果需要按名称(byName)匹配的话,可以使用@Qualifier注解与@Autowired结合。
+
+```java
+public class UserServiceImpl implements UserService {
+ //标注成员变量
+ @Autowired
+ @Qualifier("userDao1")
+ private UserDao userDao;
+ }
+```
+
+byName模式 xml 配置:
+
+```xml
+
+
+
+
+
+```
+
+@Resource,默认按 byName模式自动注入。@Resource有两个中重要的属性:name和type。Spring容器对于@Resource注解的name属性解析为bean的名字,type属性则解析为bean的类型。因此使用name属性,则按byName模式的自动注入策略,如果使用type属性则按 byType模式自动注入策略。倘若既不指定name也不指定type属性,Spring容器将通过反射技术默认按byName模式注入。
+
+```java
+@Resource(name=“userDao”)
+private UserDao userDao;//用于成员变量
+
+//也可以用于set方法标注
+@Resource(name=“userDao”)
+public void setUserDao(UserDao userDao) {
+ this.userDao= userDao;
+}
+```
+
+上述两种自动装配的依赖注入并不适合简单值类型,如int、boolean、long、String以及Enum等,对于这些类型,Spring容器也提供了@Value注入的方式。@Value接收一个String的值,该值指定了将要被注入到内置的java类型属性值,Spring 容器会做好类型转换。一般情况下@Value会与properties文件结合使用。
+
+jdbc.properties文件如下:
+
+```properties
+jdbc.driver=com.mysql.jdbc.Driver
+jdbc.url=jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF-8&allowMultiQueries=true
+jdbc.username=root
+jdbc.password=root
+```
+
+利用注解@Value获取jdbc.url和jdbc.username的值,实现如下:
+
+```java
+public class UserServiceImpl implements UserService {
+ //占位符方式
+ @Value("${jdbc.url}")
+ private String url;
+ //SpEL表达方式,其中代表xml配置文件中的id值configProperties
+ @Value("#{configProperties['jdbc.username']}")
+ private String userName;
+
+}
+```
+
+xml配置文件:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+ classpath:/conf/jdbc.properties
+
+
+
+```
+
+
+
+## @Autowired和@Resource的区别?
+
+@Autowired注解是按照类型(byType)装配依赖对象的,但是存在多个类型⼀致的bean,⽆法通过byType注⼊时,就会再使⽤byName来注⼊,如果还是⽆法判断注⼊哪个bean则会UnsatisfiedDependencyException。
+@Resource会⾸先按照byName来装配,如果找不到bean,会⾃动byType再找⼀次。
+
+## @Qualifier 注解有什么作用
+
+当需要创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时,可以使用@Qualifier 注解和 @Autowired 通过指定应该装配哪个 bean 来消除歧义。
+
+## @Bean和@Component有什么区别?
+
+都是使用注解定义 Bean。@Bean 是使用 Java 代码装配 Bean,@Component 是自动装配 Bean。
+
+@Component 注解用在类上,表明一个类会作为组件类,并告知Spring要为这个类创建bean,每个类对应一个 Bean。
+
+@Bean 注解用在方法上,表示这个方法会返回一个 Bean。@Bean 需要在配置类中使用,即类上需要加上@Configuration注解。
+
+```java
+@Component
+public class Student {
+ private String name = "lkm";
+
+ public String getName() {
+ return name;
+ }
+}
+
+@Configuration
+public class WebSocketConfig {
+ @Bean
+ public Student student(){
+ return new Student();
+ }
+}
+```
+
+@Bean 注解更加灵活。当需要将第三方类装配到 Spring 容器中,因为没办法源代码上添加@Component注解,只能使用@Bean 注解的方式,当然也可以使用 xml 的方式。
+
+## @Component、@Controller、@Repositor和@Service 的区别?
+
+@Component:最普通的组件,可以被注入到spring容器进行管理。
+
+@Controller:将类标记为 Spring Web MVC 控制器。
+
+@Service:将类标记为业务层组件。
+
+@Repository:将类标记为数据访问组件,即DAO组件。
+
+## Spring 事务实现方式有哪些?
+
+事务就是一系列的操作原子执行。Spring事务机制主要包括声明式事务和编程式事务。
+
+- 编程式事务:通过编程的方式管理事务,这种方式带来了很大的灵活性,但很难维护。
+- 声明式事务:将事务管理代码从业务方法中分离出来,通过aop进行封装。Spring声明式事务使得我们无需要去处理获得连接、关闭连接、事务提交和回滚等这些操作。使用 `@Transactional` 注解开启声明式事务。
+
+`@Transactional`相关属性如下:
+
+| 属性 | 类型 | 描述 |
+| :--------------------- | ---------------------------------- | -------------------------------------- |
+| value | String | 可选的限定描述符,指定使用的事务管理器 |
+| propagation | enum: Propagation | 可选的事务传播行为设置 |
+| isolation | enum: Isolation | 可选的事务隔离级别设置 |
+| readOnly | boolean | 读写或只读事务,默认读写 |
+| timeout | int (in seconds granularity) | 事务超时时间设置 |
+| rollbackFor | Class对象数组,必须继承自Throwable | 导致事务回滚的异常类数组 |
+| rollbackForClassName | 类名数组,必须继承自Throwable | 导致事务回滚的异常类名字数组 |
+| noRollbackFor | Class对象数组,必须继承自Throwable | 不会导致事务回滚的异常类数组 |
+| noRollbackForClassName | 类名数组,必须继承自Throwable | 不会导致事务回滚的异常类名字数组 |
+
+## 有哪些事务传播行为?
+
+在TransactionDefinition接口中定义了七个事务传播行为:
+
+1. `PROPAGATION_REQUIRED`如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。如果嵌套调用的两个方法都加了事务注解,并且运行在相同线程中,则这两个方法使用相同的事务中。如果运行在不同线程中,则会开启新的事务。
+2. `PROPAGATION_SUPPORTS` 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。
+3. `PROPAGATION_MANDATORY` 如果已经存在一个事务,支持当前事务。如果不存在事务,则抛出异常`IllegalTransactionStateException`。
+4. `PROPAGATION_REQUIRES_NEW` 总是开启一个新的事务。需要使用JtaTransactionManager作为事务管理器。
+5. `PROPAGATION_NOT_SUPPORTED` 总是非事务地执行,并挂起任何存在的事务。需要使用JtaTransactionManager作为事务管理器。
+6. `PROPAGATION_NEVER` 总是非事务地执行,如果存在一个活动事务,则抛出异常。
+7. `PROPAGATION_NESTED` 如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务, 则按PROPAGATION_REQUIRED 属性执行。
+
+**PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别:**
+
+使用`PROPAGATION_REQUIRES_NEW`时,内层事务与外层事务是两个独立的事务。一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。
+
+使用`PROPAGATION_NESTED`时,外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。
+
+
+
+## Spring怎么解决循环依赖的问题?
+
+构造器注入的循环依赖:Spring处理不了,直接抛出`BeanCurrentlylnCreationException`异常。
+
+单例模式下属性注入的循环依赖:通过三级缓存处理循环依赖。
+
+非单例循环依赖:无法处理。
+
+下面分析单例模式下属性注入的循环依赖是怎么处理的:
+
+首先,Spring单例对象的初始化大略分为三步:
+
+1. `createBeanInstance`:实例化bean,使用构造方法创建对象,为对象分配内存。
+2. `populateBean`:进行依赖注入。
+3. `initializeBean`:初始化bean。
+
+Spring为了解决单例的循环依赖问题,使用了三级缓存:
+
+`singletonObjects`:完成了初始化的单例对象map,bean name --> bean instance
+
+`earlySingletonObjects `:完成实例化未初始化的单例对象map,bean name --> bean instance
+
+`singletonFactories `: 单例对象工厂map,bean name --> ObjectFactory,单例对象实例化完成之后会加入singletonFactories。
+
+在调用createBeanInstance进行实例化之后,会调用addSingletonFactory,将单例对象放到singletonFactories中。
+
+```java
+protected void addSingletonFactory(String beanName, ObjectFactory> singletonFactory) {
+ Assert.notNull(singletonFactory, "Singleton factory must not be null");
+ synchronized (this.singletonObjects) {
+ if (!this.singletonObjects.containsKey(beanName)) {
+ this.singletonFactories.put(beanName, singletonFactory);
+ this.earlySingletonObjects.remove(beanName);
+ this.registeredSingletons.add(beanName);
+ }
+ }
+}
+```
+
+假如A依赖了B的实例对象,同时B也依赖A的实例对象。
+
+1. A首先完成了实例化,并且将自己添加到singletonFactories中
+2. 接着进行依赖注入,发现自己依赖对象B,此时就尝试去get(B)
+3. 发现B还没有被实例化,对B进行实例化
+4. 然后B在初始化的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects和二级缓存earlySingletonObjects没找到,尝试三级缓存singletonFactories,由于A初始化时将自己添加到了singletonFactories,所以B可以拿到A对象,然后将A从三级缓存中移到二级缓存中
+5. B拿到A对象后顺利完成了初始化,然后将自己放入到一级缓存singletonObjects中
+6. 此时返回A中,A此时能拿到B的对象顺利完成自己的初始化
+
+由此看出,属性注入的循环依赖主要是通过将实例化完成的bean添加到singletonFactories来实现的。而使用构造器依赖注入的bean在实例化的时候会进行依赖注入,不会被添加到singletonFactories中。比如A和B都是通过构造器依赖注入,A在调用构造器进行实例化的时候,发现自己依赖B,B没有被实例化,就会对B进行实例化,此时A未实例化完成,不会被添加到singtonFactories。而B依赖于A,B会去三级缓存寻找A对象,发现不存在,于是又会实例化A,A实例化了两次,从而导致抛异常。
+
+总结:1、利用缓存识别已经遍历过的节点; 2、利用Java引用,先提前设置对象地址,后完善对象。
+
+
+
+## Spring启动过程
+
+1. 读取web.xml文件。
+2. 创建 ServletContext,为 ioc 容器提供宿主环境。
+3. 触发容器初始化事件,调用 contextLoaderListener.contextInitialized()方法,在这个方法会初始化一个应用上下文WebApplicationContext,即 Spring 的 ioc 容器。ioc 容器初始化完成之后,会被存储到 ServletContext 中。
+
+4. 初始化web.xml中配置的Servlet。如DispatcherServlet,用于匹配、处理每个servlet请求。
+
+
+
+## Spring 的单例 Bean 是否有线程安全问题?
+
+当多个用户同时请求一个服务时,容器会给每一个请求分配一个线程,这时多个线程会并发执行该请求对应的业务逻辑,如果业务逻辑有对单例状态的修改(体现为此单例的成员属性),则必须考虑线程安全问题。
+
+若每个线程中对全局变量、静态变量只有读操作,而无写操作,那么不会有线程安全问题;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。
+
+**无状态bean和有状态bean**
+
+- 有实例变量的bean,可以保存数据,是非线程安全的。
+- 没有实例变量的对象。不能保存数据,是线程安全的。
+
+在Spring中无状态的Bean适合用单例模式,这样可以共享实例提高性能。有状态的Bean在多线程环境下不安全,一般用Prototype模式或者使用ThreadLocal解决线程安全问题。
+
diff --git "a/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 9c01024..fc7a5a6 100644
--- "a/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -52,7 +52,7 @@ TCP/IP五层模型:应用层、传输层、网络层、数据链路层、物
假设发送端为客户端,接收端为服务端。开始时客户端和服务端的状态都是`CLOSED`。
-
+
1. 第一次握手:客户端向服务端发起建立连接请求,客户端会随机生成一个起始序列号x,客户端向服务端发送的字段中包含标志位`SYN=1`,序列号`seq=x`。第一次握手前客户端的状态为`CLOSE`,第一次握手后客户端的状态为`SYN-SENT`。此时服务端的状态为`LISTEN`。
2. 第二次握手:服务端在收到客户端发来的报文后,会随机生成一个服务端的起始序列号y,然后给客户端回复一段报文,其中包括标志位`SYN=1`,`ACK=1`,序列号`seq=y`,确认号`ack=x+1`。第二次握手前服务端的状态为`LISTEN`,第二次握手后服务端的状态为`SYN-RCVD`,此时客户端的状态为`SYN-SENT`。(其中`SYN=1`表示要和客户端建立一个连接,`ACK=1`表示确认序号有效)
From 726c05f44d61ef09568e2f51d7c4dc73e776c247 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 31 Oct 2021 16:10:31 +0800
Subject: [PATCH 002/112] =?UTF-8?q?=E8=B5=9E=E8=B5=8F=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 6c1888c..83e9509 100644
--- a/README.md
+++ b/README.md
@@ -157,13 +157,24 @@
-PS:如果想进**技术、面试交流群**,可以扫描下方二维码加我微信,**备注加群**,我拉你进群,群里有BAT大佬,互相学习~
+# 交流
+
+如果想进**技术、面试交流群**,可以扫描下方二维码加我微信,**备注加群**,我拉你进群,群里有BAT大佬,互相学习~
+# 赞赏
+
+如果觉得**本仓库**对您有帮助的话,可以请大彬**喝一杯咖啡**(小伙伴们赞赏的时候可以备注下哦~)
+
+| 微信 | 支付宝 |
+| ----------------------------------------------------------- | ------------------------------------------------------------ |
+|  |  |
+
+
> 最后给大家分享另一个github仓库,上面放了**200多本经典的计算机书籍**,包括C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习等,可以star一下,下次找书直接在上面搜索,仓库持续更新中~
>
From 502966a0668c3d097facf894e54060cdf76ff967 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 14 Nov 2021 11:43:36 +0800
Subject: [PATCH 003/112] update
---
"\345\267\245\345\205\267/docker.md" | 77 ++++++++++++++++++-
...50\347\275\262\351\241\271\347\233\256.md" | 7 +-
...ng\351\235\242\350\257\225\351\242\230.md" | 12 +--
3 files changed, 85 insertions(+), 11 deletions(-)
diff --git "a/\345\267\245\345\205\267/docker.md" "b/\345\267\245\345\205\267/docker.md"
index 8e49470..e6085cf 100644
--- "a/\345\267\245\345\205\267/docker.md"
+++ "b/\345\267\245\345\205\267/docker.md"
@@ -107,6 +107,12 @@ yum install docker-ce
systemctl start docker
```
+### 重启docker服务
+
+```java
+systemctl restart docker.service
+```
+
### 搜索镜像
```
@@ -313,6 +319,18 @@ docker run -p 33055:33055 --name nginx \
使用-it,此时如果使用exit退出,则容器的状态处于Exit,而不是后台运行。如果想让容器一直运行,而不是停止,可以使用快捷键 ctrl+p ctrl+q 退出,此时容器的状态为Up。
+**创建MySQL容器**:
+
+```java
+docker run --name mysql4blog -e MYSQL_ROOT_PASSWORD=123456 -p 3307:3307 -d mysql:8.0.20
+```
+
+- `-name`: 给新创建的容器命名,此处命名为`mysql4blog`
+- `-e`: 配置信息,此处配置MySQL的 root 用户的登录密码
+- `-p`: 端口映射,此处映射主机的3307端口到容器的3307端口
+- -d: 成功启动同期后输出容器的完整ID
+- 最后一个`mysql:8.0.20`指的是`mysql`镜像
+
### 查看容器
列出运行中的容器,`-a`参数可以列出所有容器:
@@ -344,7 +362,7 @@ docker start $ContainerName
### 重启容器
```
-systemctl restart docker
+docker restart nginx
```
### 进入容器
@@ -760,3 +778,60 @@ docker run -p 8080:8080 --name mall-tiny-docker \
-d mall-tiny/mall-tiny-docker:0.0.1-SNAPSHOT
```
+
+
+## 其他
+
+### 给nginx增加端口映射
+
+nginx一开始只映射了80端口,后面载部署项目的时候,需要用到其他端口,不想重新部署容器,所以通过修改配置文件的方式给容器添加其他端口。
+
+1、执行命令`docker inspect nginx`,找到容器id
+
+2、停止容器`docker stop nginx`,不然修改了配置会自动还原
+
+3、修改hostconfig.json
+
+```java
+cd /var/lib/docker/containers/135254e3429d1e75aa68569137c753b789416256f2ced52b4c5a85ec3849db87 # container id
+vim hostconfig.json
+```
+
+添加端口:
+
+```java
+"PortBindings": {
+ "80/tcp": [
+ {
+ "HostIp": "",
+ "HostPort": "80"
+ }
+ ],
+ "8080/tcp": [
+ {
+ "HostIp": "",
+ "HostPort": "8080"
+ }
+ ]
+},
+```
+
+4、修改同目录下 config.v2.json
+
+```java
+"ExposedPorts": {
+ "80/tcp": {},
+ "8080/tcp": {},
+ "8189/tcp": {}
+},
+```
+
+5、重启容器
+
+```java
+systemctl restart docker.service # 重启docker服务
+docker start nginx
+```
+
+
+
diff --git "a/\345\267\245\345\205\267/nginx\351\203\250\347\275\262\351\241\271\347\233\256.md" "b/\345\267\245\345\205\267/nginx\351\203\250\347\275\262\351\241\271\347\233\256.md"
index df09653..2b9f46f 100644
--- "a/\345\267\245\345\205\267/nginx\351\203\250\347\275\262\351\241\271\347\233\256.md"
+++ "b/\345\267\245\345\205\267/nginx\351\203\250\347\275\262\351\241\271\347\233\256.md"
@@ -160,7 +160,7 @@ server{ } 其实是包含在 http{ } 内部的。每一个 server{ } 是一个
nginx 配置:
-1. /usr/local/nginx/conf/nginx.conf 中 http 节点增加`include /usr/local/nginx/my-conf/*.conf` 不同站点使用不同的配置文件。
+1. /etc/nginx/nginx.conf 中 http 节点增加`include /etc/nginx/conf.d/*.conf` 不同站点使用不同的配置文件。
```
http {
@@ -168,7 +168,7 @@ nginx 配置:
default_type application/octet-stream;
keepalive_timeout 65;
- include /usr/local/nginx/my-conf/*.conf; #配置多个站点
+ include /etc/nginx/conf.d/*.conf; #配置多个站点
server {
xxx
@@ -177,7 +177,7 @@ nginx 配置:
}
```
-2. 新建usr/local/nginx/my-conf/blog.conf,配置nginx:
+2. 新建/etc/nginx/conf.d/blog.conf,配置nginx:
```
#blog
@@ -202,7 +202,6 @@ nginx 配置:
proxy_pass http://129.204.179.3:8001/img/;
}
}
-
```
后端部署:
diff --git "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md" "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
index d76a9ef..56c51c8 100644
--- "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
@@ -214,15 +214,15 @@ finishBeanFactoryInitialization(beanFactory);

-1.对Bean进行实例化
+1.调用bean的构造方法创建Bean
-2.依赖注入
+2.通过反射调用setter方法进行属性的依赖注入
-3.如果Bean实现了`BeanNameAware`接口,Spring将调用`setBeanName`(),设置 `Bean`的 id(xml文件中bean标签的id)
+3.如果Bean实现了`BeanNameAware`接口,Spring将调用`setBeanName`(),设置 `Bean`的name(xml文件中bean标签的id)
-4.如果Bean实现了`BeanFactoryAware`接口,Spring将调用`setBeanFactory()`
+4.如果Bean实现了`BeanFactoryAware`接口,Spring将调用`setBeanFactory()`把bean factory设置给Bean
-5.如果Bean实现了`ApplicationContextAware`接口,Spring容器将调用`setApplicationContext()`
+5.如果Bean实现了`ApplicationContextAware`接口,Spring容器将调用`setApplicationContext()`给Bean设置ApplictionContext
6.如果存在`BeanPostProcessor`,Spring将调用它们的`postProcessBeforeInitialization`(预初始化)方法,在Bean初始化前对其进行处理
@@ -232,7 +232,7 @@ finishBeanFactoryInitialization(beanFactory);
9.Bean初始化完成,供应用使用,直到应用被销毁
-10.如果Bean实现了`DisposableBean`接口,Spring将调用它的`destory`方法,然后调用在xml中定义的 `destory-method`方法,这两个方法作用类似,都是在Bean实例销毁前执行。
+10.如果Bean实现了`DisposableBean`接口,Spring将调用它的`destory`方法,然后调用在xml中定义的 `destory-method`方法,这两个方法作用类似,都是在Bean实例销毁前执行
```java
public interface BeanPostProcessor {
From 58fb72646705dd810ad5e10b3200eabe4faba9f8 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sat, 20 Nov 2021 14:49:37 +0800
Subject: [PATCH 004/112] update
---
README.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 83e9509..067c081 100644
--- a/README.md
+++ b/README.md
@@ -45,7 +45,7 @@
## 集合
-[Java集合高频面试题,牛客高赞!](Java/Java集合面试题.md)(推荐 :+1:)
+[Java集合高频面试题](Java/Java集合面试题.md)(牛客点赞200+!推荐 :+1:)
[Java常见集合总结](Java/集合.md)
@@ -65,13 +65,13 @@
## MySQL
-1. [【大厂面试】—— MySQL高频面试题50道](数据库/MySQL高频面试题.md)(知乎高赞,推荐 :+1:)
+1. [【大厂面试】—— MySQL高频面试题50道](数据库/MySQL高频面试题.md)(知乎1k+收藏,推荐 :+1:)
2. [MySQL基础知识总结](数据库/MySQL基础.md)
3. [MySQL执行计划](数据库/MySQL执行计划.md)(推荐 :+1:)
## Redis
-1. [【大厂面试】——Redis30问](中间件/Redis面试题.md)(知乎高赞,推荐 :+1:)
+1. [【大厂面试】——Redis30问](中间件/Redis面试题.md)(牛客高赞,推荐 :+1:)
2. [Redis基础总结](中间件/Redis入门指南总结.md)
3. [Redis分布式锁(推荐 :+1:)](中间件/Redis分布式锁.md)
4. [缓存穿透、缓存雪崩、缓存击穿](中间件/缓存穿透、缓存雪崩、缓存击穿.md)
@@ -115,7 +115,7 @@
# 计算机网络
-[【大厂面试】—— 计算机网络常见面试题总结](网络/计算机网络高频面试题.md) (精选30题,非常适合面试前阅读!推荐 :+1:)
+[【大厂面试】—— 计算机网络常见面试题总结](网络/计算机网络高频面试题.md) (知乎1k+收藏!推荐 :+1:)
[session和cookie详解](网络/session和cookie.md)
From 3960e09dd178aad0cdf39689bd6949ffb4d6d8b8 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sat, 20 Nov 2021 14:50:46 +0800
Subject: [PATCH 005/112] update
---
README.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index 067c081..c2a5a7b 100644
--- a/README.md
+++ b/README.md
@@ -45,7 +45,7 @@
## 集合
-[Java集合高频面试题](Java/Java集合面试题.md)(牛客点赞200+!推荐 :+1:)
+[Java集合高频面试题](Java/Java集合面试题.md)(**牛客点赞200+!推荐** :+1:)
[Java常见集合总结](Java/集合.md)
@@ -65,7 +65,7 @@
## MySQL
-1. [【大厂面试】—— MySQL高频面试题50道](数据库/MySQL高频面试题.md)(知乎1k+收藏,推荐 :+1:)
+1. [【大厂面试】—— MySQL高频面试题50道](数据库/MySQL高频面试题.md)(**知乎1k+收藏,推荐** :+1:)
2. [MySQL基础知识总结](数据库/MySQL基础.md)
3. [MySQL执行计划](数据库/MySQL执行计划.md)(推荐 :+1:)
@@ -115,7 +115,7 @@
# 计算机网络
-[【大厂面试】—— 计算机网络常见面试题总结](网络/计算机网络高频面试题.md) (知乎1k+收藏!推荐 :+1:)
+[【大厂面试】—— 计算机网络常见面试题总结](网络/计算机网络高频面试题.md) (**知乎1k+收藏!推荐 :+1:**)
[session和cookie详解](网络/session和cookie.md)
From 30c124459fd93ca52e11b0132c0023ef4e811788 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 28 Nov 2021 11:38:48 +0800
Subject: [PATCH 006/112] =?UTF-8?q?Java=E9=AB=98=E9=A2=91=E9=9D=A2?=
=?UTF-8?q?=E8=AF=95=E9=A2=98=E6=95=B4=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index c2a5a7b..439e093 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,8 @@
**本仓库用于分享互联网大厂高频面试题、Java核心知识总结,包括Java基础、并发、MySQL、Springboot、MyBatis、Redis、RabbitMQ等等,面试必备!**
+**面试专题相关的文章已经整理成PDF,需要的小伙伴可以自行下载**:[Java高频面试题PDF](http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd)
+
如果你是Java初学者,不知道下一步该学什么内容,可以看下我总结的**2021年最新的[Java学习路线](https://zhuanlan.zhihu.com/p/395162772)**。如果喜欢看视频学习,可以参考这个:[播放量1000万+!B站最值得学习的Java视频教程](https://zhuanlan.zhihu.com/p/397533240)。
如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的**公众号『 程序员大彬 』**,后台回复『 PDF 』可以**下载最新版本的大厂高频面试题目PDF版本**。
From 7f1a1d8406ce0e52d4bd442be09497970c398065 Mon Sep 17 00:00:00 2001
From: tyson
Date: Fri, 10 Dec 2021 21:52:30 +0800
Subject: [PATCH 007/112] update
---
README.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/README.md b/README.md
index 439e093..508305e 100644
--- a/README.md
+++ b/README.md
@@ -176,7 +176,13 @@
| ----------------------------------------------------------- | ------------------------------------------------------------ |
|  |  |
+每笔赞赏我会在下面记录下来,感谢你们,我会更加努力,砥砺前行~
+| 日期 | 来源 | **用户** | **金额** | 备注 |
+| ---------- | ------------ | -------- | -------- | ------ |
+| 2021.11.19 | 微信收款码 | *张 | 6.66元 | 支持! |
+| 2021.11.25 | 支付宝收款码 | *海 | 1元 | |
+| 2021.12.10 | 微信收款码 | 浩*y | 10元 | |
> 最后给大家分享另一个github仓库,上面放了**200多本经典的计算机书籍**,包括C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习等,可以star一下,下次找书直接在上面搜索,仓库持续更新中~
>
From 8f3014a19f7173dbb7fa715a77bc37bab6cf18d4 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 19 Dec 2021 09:09:12 +0800
Subject: [PATCH 008/112] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=85=A8=E6=96=87?=
=?UTF-8?q?=E7=B4=A2=E5=BC=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 9 ++-------
...0\351\242\221\351\235\242\350\257\225\351\242\230.md" | 2 +-
2 files changed, 3 insertions(+), 8 deletions(-)
diff --git a/README.md b/README.md
index 508305e..98b7b7a 100644
--- a/README.md
+++ b/README.md
@@ -183,12 +183,7 @@
| 2021.11.19 | 微信收款码 | *张 | 6.66元 | 支持! |
| 2021.11.25 | 支付宝收款码 | *海 | 1元 | |
| 2021.12.10 | 微信收款码 | 浩*y | 10元 | |
+| 2021.12.15 | 微信收款码 | biubiu* | 6.66元 | 好 |
+
-> 最后给大家分享另一个github仓库,上面放了**200多本经典的计算机书籍**,包括C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习等,可以star一下,下次找书直接在上面搜索,仓库持续更新中~
->
-> github地址:https://github.com/Tyson0314/java-books
->
-> 如果github访问不了,可以访问gitee仓库。
->
-> gitee地址:https://gitee.com/tysondai/java-books
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index c6850cb..2b3f0e7 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -226,7 +226,7 @@ ADD CONSTRAINT constraint_name UNIQUE KEY(column_1,column_2,...);
3、**组合索引**:在表中的多个字段组合上创建的索引,只有在查询条件中使用了这些字段的左边字段时,索引才会被使用,使用组合索引时需遵循最左前缀原则。
-4、**全文索引**:只有在`MyISAM`引擎上才能使用,只能在`CHAR`、`VARCHAR`和`TEXT`类型字段上使用全文索引。
+4、**全文索引**:只能在`CHAR`、`VARCHAR`和`TEXT`类型字段上使用全文索引。
### 什么是最左匹配原则?
From de7997162bfc9305046e4db0c1147cc226d280ca Mon Sep 17 00:00:00 2001
From: tyson
Date: Sat, 12 Feb 2022 00:18:44 +0800
Subject: [PATCH 009/112] =?UTF-8?q?=E6=9B=B4=E6=96=B0MySQL=E5=B9=BB?=
=?UTF-8?q?=E8=AF=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...53\230\351\242\221\351\235\242\350\257\225\351\242\230.md" | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 2b3f0e7..f9e4b94 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -92,12 +92,10 @@
- **脏读**是指在一个事务处理过程里读取了另一个未提交的事务中的数据。
- **不可重复读**是指在对于数据库中的某行记录,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,另一个事务修改了数据并提交了。
-- **幻读**是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行,就像产生幻觉一样,这就是发生了幻读。
+- **幻读**是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录。对幻读的正确理解是一个事务内的读取操作的结论不能支撑之后业务的执行。假设事务要新增一条记录,主键为id,在新增之前执行了select,没有发现id为xxx的记录,但插入时出现主键冲突,这就属于幻读,读取不到记录却发现主键冲突是因为记录实际上已经被其他的事务插入了,但当前事务不可见。
**不可重复读和脏读的区别**是,脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。
-幻读和不可重复读都是读取了另一条已经提交的事务,不同的是不可重复读的重点是修改,幻读的重点在于新增或者删除。
-
事务隔离就是为了解决上面提到的脏读、不可重复读、幻读这几个问题。
MySQL数据库为我们提供的四种隔离级别:
From 59267fe8354383a6fbe3578f6e3e46c983ee19a9 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sat, 12 Feb 2022 00:19:21 +0800
Subject: [PATCH 010/112] =?UTF-8?q?=E6=9B=B4=E6=96=B0jvm=20jinfo?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...21\351\235\242\350\257\225\351\242\230.md" | 31 +++++++++++++++++++
README.md | 4 +--
2 files changed, 33 insertions(+), 2 deletions(-)
diff --git "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 8db6462..2ee83cb 100644
--- "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -644,6 +644,37 @@ PS Old Generation
27776 interned Strings occupying 3262336 bytes.
```
+**jinfo**:`jinfo -flags 1`。查看当前的应用JVM参数配置。
+
+```java
+Attaching to process ID 1, please wait...
+Debugger attached successfully.
+Server compiler detected.
+JVM version is 25.111-b14
+Non-default VM flags: -XX:CICompilerCount=2 -XX:InitialHeapSize=31457280 -XX:MaxHeapSize=480247808 -XX:MaxNewSize=160038912 -XX:MinHeapDeltaBytes=196608 -XX:NewSize=10485760 -XX:OldSize=20971520 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops
+Command line:
+```
+
+**查看所有参数**:`java -XX:+PrintFlagsFinal -version`。用于查看最终值,初始值可能被修改掉(查看初始值可以使用java -XX:+PrintFlagsInitial)。
+
+```java
+[Global flags]
+ uintx AdaptiveSizeDecrementScaleFactor = 4 {product}
+ uintx AdaptiveSizeMajorGCDecayTimeScale = 10 {product}
+ uintx AdaptiveSizePausePolicy = 0 {product}
+ uintx AdaptiveSizePolicyCollectionCostMargin = 50 {product}
+ uintx AdaptiveSizePolicyInitializingSteps = 20 {product}
+ uintx AdaptiveSizePolicyOutputInterval = 0 {product}
+ uintx AdaptiveSizePolicyWeight = 10 {product}
+ uintx AdaptiveSizeThroughPutPolicy = 0 {product}
+ uintx AdaptiveTimeWeight = 25 {product}
+ bool AdjustConcurrency = false {product}
+ bool AggressiveOpts = false {product}
+ ....
+```
+
+
+
## 对象头了解吗?
Java 内存中的对象由以下三部分组成:**对象头**、**实例数据**和**对齐填充字节**。
diff --git a/README.md b/README.md
index 98b7b7a..6dc68d2 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,9 @@
# 作者简介😜
-**大彬**,**非科班转码**,上学时玩过单片机、搭过服务器、参加过各种比赛。**大三开始自学 Java 8个月,校招时拿了多家互联网中大厂offer。**
+大彬,**非科班自学Java**,校招斩获京东、携程、华为等多家互联网中大厂offer。作为一名转码选手,深感这一路的不易,**半年的自学经历**,彻底改变了我的职业生涯。坚持分享自学Java经历、计算机知识、Java后端技术和面试经验等,希望能帮助到更多的小伙伴,**我踩过的坑你们不要再踩**。
-[点击此处](https://zhuanlan.zhihu.com/p/395162772) 查看我的**自学路线**,一路走来,说难不难,说简单也不简单,只是比别人多花了点时间。自学路上很不容易,希望像我一样转行的小伙伴可以少踩坑!
+[点击此处](https://zhuanlan.zhihu.com/p/395162772) 查看我的**自学路线**。
# 仓库简介
From 8b08743b38340b9b8fce41dc9dbcb583cc5ab7fa Mon Sep 17 00:00:00 2001
From: tyson
Date: Sat, 26 Feb 2022 18:22:13 +0800
Subject: [PATCH 011/112] =?UTF-8?q?=E5=B9=B6=E5=8F=91=E7=9F=A5=E8=AF=86?=
=?UTF-8?q?=E7=82=B9=E6=95=B4=E7=90=86=E8=A1=A5=E5=85=85?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...21\351\235\242\350\257\225\351\242\230.md" | 2 +-
...21\351\235\242\350\257\225\351\242\230.md" | 414 ++++++++----------
"\345\267\245\345\205\267/docker.md" | 3 +
...47\350\241\214\350\256\241\345\210\222.md" | 1 +
...21\351\235\242\350\257\225\351\242\230.md" | 1 -
5 files changed, 198 insertions(+), 223 deletions(-)
diff --git "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 2ee83cb..28e75d2 100644
--- "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -28,7 +28,7 @@
- [Full GC 的触发条件?](#full-gc-%E7%9A%84%E8%A7%A6%E5%8F%91%E6%9D%A1%E4%BB%B6)
- [垃圾回收算法有哪些?](#%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E6%9C%89%E5%93%AA%E4%BA%9B)
- [有哪些垃圾回收器?](#%E6%9C%89%E5%93%AA%E4%BA%9B%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8)
-- [常用的 JVM 调优的命令都有哪些?](#%E5%B8%B8%E7%94%A8%E7%9A%84-jvm-%E8%B0%83%E4%BC%98%E7%9A%84%E5%8F%82%E6%95%B0%E9%83%BD%E6%9C%89%E5%93%AA%E4%BA%9B)
+- [常用的 JVM 调优的命令都有哪些?](#%E5%B8%B8%E7%94%A8%E7%9A%84-jvm-%E8%B0%83%E4%BC%98%E7%9A%84%E5%91%BD%E4%BB%A4%E9%83%BD%E6%9C%89%E5%93%AA%E4%BA%9B)
- [对象头了解吗?](#%E5%AF%B9%E8%B1%A1%E5%A4%B4%E4%BA%86%E8%A7%A3%E5%90%97)
- [如何排查 OOM 的问题?](#%E5%A6%82%E4%BD%95%E6%8E%92%E6%9F%A5-oom-%E7%9A%84%E9%97%AE%E9%A2%98)
- [GC是什么?为什么要GC?](#gc%E6%98%AF%E4%BB%80%E4%B9%88%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81gc)
diff --git "a/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
index 1895591..a9a186f 100644
--- "a/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
@@ -10,41 +10,36 @@
- [线程池的类型有哪些?适用场景?](#%E7%BA%BF%E7%A8%8B%E6%B1%A0%E7%9A%84%E7%B1%BB%E5%9E%8B%E6%9C%89%E5%93%AA%E4%BA%9B%E9%80%82%E7%94%A8%E5%9C%BA%E6%99%AF)
- [进程线程](#%E8%BF%9B%E7%A8%8B%E7%BA%BF%E7%A8%8B)
- [线程的生命周期](#%E7%BA%BF%E7%A8%8B%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F)
- - [讲一下线程中断?](#%E8%AE%B2%E4%B8%80%E4%B8%8B%E7%BA%BF%E7%A8%8B%E4%B8%AD%E6%96%AD)
+ - [讲讲线程中断?](#%E8%AE%B2%E8%AE%B2%E7%BA%BF%E7%A8%8B%E4%B8%AD%E6%96%AD)
- [创建线程有哪几种方式?](#%E5%88%9B%E5%BB%BA%E7%BA%BF%E7%A8%8B%E6%9C%89%E5%93%AA%E5%87%A0%E7%A7%8D%E6%96%B9%E5%BC%8F)
- [什么是线程死锁?](#%E4%BB%80%E4%B9%88%E6%98%AF%E7%BA%BF%E7%A8%8B%E6%AD%BB%E9%94%81)
- [线程死锁怎么产生?怎么避免?](#%E7%BA%BF%E7%A8%8B%E6%AD%BB%E9%94%81%E6%80%8E%E4%B9%88%E4%BA%A7%E7%94%9F%E6%80%8E%E4%B9%88%E9%81%BF%E5%85%8D)
- [线程run和start的区别?](#%E7%BA%BF%E7%A8%8Brun%E5%92%8Cstart%E7%9A%84%E5%8C%BA%E5%88%AB)
- [线程都有哪些方法?](#%E7%BA%BF%E7%A8%8B%E9%83%BD%E6%9C%89%E5%93%AA%E4%BA%9B%E6%96%B9%E6%B3%95)
- [volatile底层原理](#volatile%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86)
-- [AQS原理](#aqs%E5%8E%9F%E7%90%86)
- [synchronized的用法有哪些?](#synchronized%E7%9A%84%E7%94%A8%E6%B3%95%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [Synchronized的作用有哪些?](#synchronized%E7%9A%84%E4%BD%9C%E7%94%A8%E6%9C%89%E5%93%AA%E4%BA%9B)
+- [synchronized的作用有哪些?](#synchronized%E7%9A%84%E4%BD%9C%E7%94%A8%E6%9C%89%E5%93%AA%E4%BA%9B)
- [synchronized 底层实现原理?](#synchronized-%E5%BA%95%E5%B1%82%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86)
-- [ReentrantLock 是如何实现可重入性的?](#reentrantlock-%E6%98%AF%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E5%8F%AF%E9%87%8D%E5%85%A5%E6%80%A7%E7%9A%84)
-- [ReentrantLock和synchronized区别](#reentrantlock%E5%92%8Csynchronized%E5%8C%BA%E5%88%AB)
-- [wait()和sleep()的区别](#wait%E5%92%8Csleep%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [wait(),notify()和suspend(),resume()之间的区别](#waitnotify%E5%92%8Csuspendresume%E4%B9%8B%E9%97%B4%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [Runnable和 Callable有什么区别?](#runnable%E5%92%8C-callable%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB)
- [volatile和synchronized的区别是什么?](#volatile%E5%92%8Csynchronized%E7%9A%84%E5%8C%BA%E5%88%AB%E6%98%AF%E4%BB%80%E4%B9%88)
+- [ReentrantLock和synchronized区别](#reentrantlock%E5%92%8Csynchronized%E5%8C%BA%E5%88%AB)
+- [wait()和sleep()的异同点?](#wait%E5%92%8Csleep%E7%9A%84%E5%BC%82%E5%90%8C%E7%82%B9)
+- [Runnable和Callable有什么区别?](#runnable%E5%92%8Ccallable%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB)
- [线程执行顺序怎么控制?](#%E7%BA%BF%E7%A8%8B%E6%89%A7%E8%A1%8C%E9%A1%BA%E5%BA%8F%E6%80%8E%E4%B9%88%E6%8E%A7%E5%88%B6)
-- [乐观锁一定就是好的吗?](#%E4%B9%90%E8%A7%82%E9%94%81%E4%B8%80%E5%AE%9A%E5%B0%B1%E6%98%AF%E5%A5%BD%E7%9A%84%E5%90%97)
- [守护线程是什么?](#%E5%AE%88%E6%8A%A4%E7%BA%BF%E7%A8%8B%E6%98%AF%E4%BB%80%E4%B9%88)
- [线程间通信方式](#%E7%BA%BF%E7%A8%8B%E9%97%B4%E9%80%9A%E4%BF%A1%E6%96%B9%E5%BC%8F)
- - [volatile](#volatile)
- - [synchronized](#synchronized)
- - [等待通知机制](#%E7%AD%89%E5%BE%85%E9%80%9A%E7%9F%A5%E6%9C%BA%E5%88%B6)
- [ThreadLocal](#threadlocal)
- [ThreadLocal原理](#threadlocal%E5%8E%9F%E7%90%86)
- [ThreadLocal内存泄漏的原因?](#threadlocal%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F%E7%9A%84%E5%8E%9F%E5%9B%A0)
- [ThreadLocal使用场景有哪些?](#threadlocal%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF%E6%9C%89%E5%93%AA%E4%BA%9B)
+- [AQS原理](#aqs%E5%8E%9F%E7%90%86)
+- [ReentrantLock 是如何实现可重入性的?](#reentrantlock-%E6%98%AF%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E5%8F%AF%E9%87%8D%E5%85%A5%E6%80%A7%E7%9A%84)
- [锁的分类](#%E9%94%81%E7%9A%84%E5%88%86%E7%B1%BB)
- [公平锁与非公平锁](#%E5%85%AC%E5%B9%B3%E9%94%81%E4%B8%8E%E9%9D%9E%E5%85%AC%E5%B9%B3%E9%94%81)
- [共享式与独占式锁](#%E5%85%B1%E4%BA%AB%E5%BC%8F%E4%B8%8E%E7%8B%AC%E5%8D%A0%E5%BC%8F%E9%94%81)
- [悲观锁与乐观锁](#%E6%82%B2%E8%A7%82%E9%94%81%E4%B8%8E%E4%B9%90%E8%A7%82%E9%94%81)
-- [CAS](#cas)
- - [什么是CAS?](#%E4%BB%80%E4%B9%88%E6%98%AFcas)
- - [CAS存在的问题?](#cas%E5%AD%98%E5%9C%A8%E7%9A%84%E9%97%AE%E9%A2%98)
+- [乐观锁有什么问题?](#%E4%B9%90%E8%A7%82%E9%94%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98)
+- [什么是CAS?](#%E4%BB%80%E4%B9%88%E6%98%AFcas)
+- [CAS存在的问题?](#cas%E5%AD%98%E5%9C%A8%E7%9A%84%E9%97%AE%E9%A2%98)
- [并发工具](#%E5%B9%B6%E5%8F%91%E5%B7%A5%E5%85%B7)
- [CountDownLatch](#countdownlatch)
- [CyclicBarrier](#cyclicbarrier)
@@ -57,14 +52,6 @@
-> 首先给大家分享一个github仓库,上面放了**200多本经典的计算机书籍**,包括C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生等,可以star一下,下次找书直接在上面搜索,仓库持续更新中~
->
-> github地址:https://github.com/Tyson0314/java-books
->
-> 如果github访问不了,可以访问gitee仓库。
->
-> gitee地址:https://gitee.com/tysondai/java-books
-
## 线程池
线程池:一个管理线程的池子。
@@ -77,18 +64,12 @@
### 线程池执行原理?
-创建新的线程需要获取全局锁,通过这种设计可以尽量避免获取全局锁,当 ThreadPoolExecutor 完成预热之后(当前运行的线程数大于等于 corePoolSize),提交的大部分任务都会被放到 BlockingQueue。
+
-
-
-为了形象描述线程池执行,打个比喻:
-
-- 核心线程比作公司正式员工
-- 非核心线程比作外包员工
-- 阻塞队列比作需求池
-- 提交任务比作提需求
-
-
+1. 当线程池里存活的线程数小于核心线程数`corePoolSize`时,这时对于一个新提交的任务,线程池会创建一个线程去处理任务。当线程池里面存活的线程数小于等于核心线程数`corePoolSize`时,线程池里面的线程会一直存活着,就算空闲时间超过了`keepAliveTime`,线程也不会被销毁,而是一直阻塞在那里一直等待任务队列的任务来执行。
+2. 当线程池里面存活的线程数已经等于corePoolSize了,这是对于一个新提交的任务,会被放进任务队列workQueue排队等待执行。
+3. 当线程池里面存活的线程数已经等于`corePoolSize`了,并且任务队列也满了,假设`maximumPoolSize>corePoolSize`,这时如果再来新的任务,线程池就会继续创建新的线程来处理新的任务,知道线程数达到`maximumPoolSize`,就不会再创建了。
+4. 如果当前的线程数达到了`maximumPoolSize`,并且任务队列也满了,如果还有新的任务过来,那就直接采用拒绝策略进行处理。默认的拒绝策略是抛出一个RejectedExecutionException异常。
### 线程池参数有哪些?
@@ -98,64 +79,64 @@ ThreadPoolExecutor 的通用构造函数:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler);
```
-- corePoolSize:当有新任务时,如果线程池中线程数没有达到线程池的基本大小,则会创建新的线程执行任务,否则将任务放入阻塞队列。当线程池中存活的线程数总是大于 corePoolSize 时,应该考虑调大 corePoolSize。
+1、`corePoolSize`:当有新任务时,如果线程池中线程数没有达到线程池的基本大小,则会创建新的线程执行任务,否则将任务放入阻塞队列。当线程池中存活的线程数总是大于 corePoolSize 时,应该考虑调大 corePoolSize。
-- maximumPoolSize:当阻塞队列填满时,如果线程池中线程数没有超过最大线程数,则会创建新的线程运行任务。否则根据拒绝策略处理新任务。非核心线程类似于临时借来的资源,这些线程在空闲时间超过 keepAliveTime 之后,就应该退出,避免资源浪费。
+2、`maximumPoolSize`:当阻塞队列填满时,如果线程池中线程数没有超过最大线程数,则会创建新的线程运行任务。否则根据拒绝策略处理新任务。非核心线程类似于临时借来的资源,这些线程在空闲时间超过 keepAliveTime 之后,就应该退出,避免资源浪费。
-- BlockingQueue:存储等待运行的任务。
+3、`BlockingQueue`:存储等待运行的任务。
-- keepAliveTime:**非核心线程**空闲后,保持存活的时间,此参数只对非核心线程有效。设置为0,表示多余的空闲线程会被立即终止。
+4、`keepAliveTime`:**非核心线程**空闲后,保持存活的时间,此参数只对非核心线程有效。设置为0,表示多余的空闲线程会被立即终止。
-- TimeUnit:时间单位
+5、`TimeUnit`:时间单位
- ```java
- TimeUnit.DAYS
- TimeUnit.HOURS
- TimeUnit.MINUTES
- TimeUnit.SECONDS
- TimeUnit.MILLISECONDS
- TimeUnit.MICROSECONDS
- TimeUnit.NANOSECONDS
- ```
+```java
+TimeUnit.DAYS
+TimeUnit.HOURS
+TimeUnit.MINUTES
+TimeUnit.SECONDS
+TimeUnit.MILLISECONDS
+TimeUnit.MICROSECONDS
+TimeUnit.NANOSECONDS
+```
-- ThreadFactory:每当线程池创建一个新的线程时,都是通过线程工厂方法来完成的。在 ThreadFactory 中只定义了一个方法 newThread,每当线程池需要创建新线程就会调用它。
+6、`ThreadFactory`:每当线程池创建一个新的线程时,都是通过线程工厂方法来完成的。在 ThreadFactory 中只定义了一个方法 newThread,每当线程池需要创建新线程就会调用它。
- ```java
- public class MyThreadFactory implements ThreadFactory {
- private final String poolName;
-
- public MyThreadFactory(String poolName) {
- this.poolName = poolName;
- }
-
- public Thread newThread(Runnable runnable) {
- return new MyAppThread(runnable, poolName);//将线程池名字传递给构造函数,用于区分不同线程池的线程
- }
- }
- ```
+```java
+public class MyThreadFactory implements ThreadFactory {
+ private final String poolName;
+
+ public MyThreadFactory(String poolName) {
+ this.poolName = poolName;
+ }
+
+ public Thread newThread(Runnable runnable) {
+ return new MyAppThread(runnable, poolName);//将线程池名字传递给构造函数,用于区分不同线程池的线程
+ }
+}
+```
-- RejectedExecutionHandler:当队列和线程池都满了的时候,根据拒绝策略处理新任务。
+7、`RejectedExecutionHandler`:当队列和线程池都满了的时候,根据拒绝策略处理新任务。
- ```java
- AbortPolicy:默认的策略,直接抛出RejectedExecutionException
- DiscardPolicy:不处理,直接丢弃
- DiscardOldestPolicy:将等待队列队首的任务丢弃,并执行当前任务
- CallerRunsPolicy:由调用线程处理该任务
- ```
+```java
+AbortPolicy:默认的策略,直接抛出RejectedExecutionException
+DiscardPolicy:不处理,直接丢弃
+DiscardOldestPolicy:将等待队列队首的任务丢弃,并执行当前任务
+CallerRunsPolicy:由调用线程处理该任务
+```
### 线程池大小怎么设置?
-如果线程池线程数量太小,当有大量请求需要处理,系统响应比较慢影响体验,甚至会出现任务队列大量堆积任务导致OOM。
+如果线程池线程数量太小,当有大量请求需要处理,系统响应比较慢,会影响用户体验,甚至会出现任务队列大量堆积任务导致OOM。
-如果线程池线程数量过大,大量线程可能会同时在争取 CPU 资源,这样会导致大量的上下文切换(cpu给线程分配时间片,当线程的cpu时间片用完后保存状态,以便下次继续运行),从 而增加线程的执行时间,影响了整体执行效率。
+如果线程池线程数量过大,大量线程可能会同时抢占 CPU 资源,这样会导致大量的上下文切换,从而增加线程的执行时间,影响了执行效率。
-**CPU 密集型任务(N+1)**: 这种任务消耗的主要是 CPU 资源,可以将线程数设置为 N(CPU 核心数)+1,比 CPU 核心数多出来的一个线程是为了防止某些原因导致的任务暂停(线程阻塞,如io操作,等待锁,线程sleep)而带来的影响。一旦某个线程被阻塞,释放了cpu资源,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。
+**CPU 密集型任务(N+1)**: 这种任务消耗的主要是 CPU 资源,可以将线程数设置为` N(CPU 核心数)+1`,多出来的一个线程是为了防止某些原因导致的线程阻塞(如IO操作,线程sleep,等待锁)而带来的影响。一旦某个线程被阻塞,释放了CPU资源,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。
-**I/O 密集型任务(2N)**: 系统会用大部分的时间来处理 I/O 操作,而线程等待 I/O 操作会被阻塞,释放 cpu资源,这时就可以将 CPU 交出给其它线程使用。因此在 I/O 密集型任务的应用中,我们可以多配置一些线程,具体的计算方法:最佳线程数 = CPU核心数 * (1/CPU利用率) = CPU核心数 * (1 + (I/O耗时/CPU耗时)),一般可设置为2N
+**I/O 密集型任务(2N)**: 系统的大部分时间都在处理 IO 操作,此时线程可能会被阻塞,释放CPU资源,这时就可以将 CPU 交出给其它线程使用。因此在 IO 密集型任务的应用中,可以多配置一些线程,具体的计算方法:`最佳线程数 = CPU核心数 * (1/CPU利用率) = CPU核心数 * (1 + (IO耗时/CPU耗时))`,一般可设置为2N。
### 线程池的类型有哪些?适用场景?
-常见的线程池有 FixedThreadPool、SingleThreadExecutor、CachedThreadPool 和 ScheduledThreadPool。这几个都是 ExecutorService (线程池)实例。
+常见的线程池有 `FixedThreadPool`、`SingleThreadExecutor`、`CachedThreadPool` 和 `ScheduledThreadPool`。这几个都是 `ExecutorService` 线程池实例。
**FixedThreadPool**
@@ -226,28 +207,29 @@ public static ExecutorService newCachedThreadPool() {
## 进程线程
-进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。
+进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间。
+
线程是比进程更小的执行单位,它是在一个进程中独立的控制流,一个进程可以启动多个线程,每条线程并行执行不同的任务。
### 线程的生命周期
-初始(NEW):线程被构建,还没有调用 start()。
+**初始(NEW)**:线程被构建,还没有调用 start()。
-运行(RUNNABLE):包括操作系统的就绪和运行两种状态。
+**运行(RUNNABLE)**:包括操作系统的就绪和运行两种状态。
-阻塞(BLOCKED):一般是被动的,在抢占资源中得不到资源,被动的挂起在内存,等待资源释放将其唤醒。线程被阻塞会释放CPU,不释放内存。
+**阻塞(BLOCKED)**:一般是被动的,在抢占资源中得不到资源,被动的挂起在内存,等待资源释放将其唤醒。线程被阻塞会释放CPU,不释放内存。
-等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
+**等待(WAITING)**:进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
-超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
+**超时等待(TIMED_WAITING)**:该状态不同于WAITING,它可以在指定的时间后自行返回。
-终止(TERMINATED):表示该线程已经执行完毕。
+**终止(TERMINATED)**:表示该线程已经执行完毕。

> 图片来源:Java并发编程的艺术
-### 讲一下线程中断?
+### 讲讲线程中断?
线程中断即线程运行过程中被其他线程给打断了,它与 stop 最大的区别是:stop 是由系统强制终止线程,而线程中断则是给目标线程发送一个中断信号,如果目标线程没有接收线程中断的信号并结束线程,线程则不会终止,具体是否退出或者执行其他逻辑取决于目标线程。
@@ -255,7 +237,7 @@ public static ExecutorService newCachedThreadPool() {
**1、java.lang.Thread#interrupt**
-调用目标线程的interrupt()方法,给目标线程发一个中断信号,线程被打上中断标记。
+调用目标线程的`interrupt()`方法,给目标线程发一个中断信号,线程被打上中断标记。
**2、java.lang.Thread#isInterrupted()**
@@ -285,10 +267,10 @@ private static void test2() {
### 创建线程有哪几种方式?
-- 通过扩展Thread类来创建多线程
-- 通过实现Runnable接口来创建多线程
-- 实现Callable接口,通过FutureTask接口创建线程。
-- 使用Executor框架来创建线程池。
+- 通过扩展`Thread`类来创建多线程
+- 通过实现`Runnable`接口来创建多线程
+- 实现`Callable`接口,通过`FutureTask`接口创建线程。
+- 使用`Executor`框架来创建线程池。
**继承 Thread 创建线程**代码如下。run()方法是由jvm创建完操作系统级线程后回调的方法,不可以手动调用,手动调用相当于调用普通方法。
@@ -419,7 +401,7 @@ class RunnableDemo implements Runnable {
如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。
-
+
下面通过例子说明线程死锁,代码来自并发编程之美。
@@ -471,31 +453,31 @@ Thread[线程 1,5,main]waiting get resource2
Thread[线程 2,5,main]waiting get resource1
```
-线程 A 通过 synchronized (resource1) 获得 resource1 的监视器锁,然后通过 Thread.sleep(1000);让线程 A 休眠 1s 为的是让线程 B 得到执行然后获取到 resource2 的监视器锁。线程 A 和线程 B 休眠结束了都开始企图请求获取对方的资源,然后这两个线程就会陷入互相等待的状态,这也就产生了死锁。
+线程 A 通过 `synchronized` (resource1) 获得 resource1 的监视器锁,然后通过 `Thread.sleep(1000)`。让线程 A 休眠 1s 为的是让线程 B 得到执行然后获取到 resource2 的监视器锁。线程 A 和线程 B 休眠结束了都开始企图请求获取对方的资源,然后这两个线程就会陷入互相等待的状态,这也就产生了死锁。
### 线程死锁怎么产生?怎么避免?
**死锁产生的四个必要条件**:
-- 互斥:一个资源每次只能被一个进程使用(资源独立)
+- 互斥:一个资源每次只能被一个进程使用
-- 请求与保持:一个进程因请求资源而阻塞时,对已获得的资源保持不放(不释放锁)
+- 请求与保持:一个进程因请求资源而阻塞时,不释放获得的资源
-- 不剥夺:进程已获得的资源,在未使用之前,不能强行剥夺(抢夺资源)
+- 不剥夺:进程已获得的资源,在未使用之前,不能强行剥夺
-- 循环等待:若干进程之间形成一种头尾相接的循环等待的资源关闭(死循环)
+- 循环等待:进程之间循环等待着资源
-避免死锁的方法:
+**避免死锁的方法**:
-- 第一个条件 "互斥" 是不能破坏的,因为加锁就是为了保证互斥
-- 一次性申请所有的资源,破坏 "占有且等待" 条件
-- 占有部分资源的线程进一步申请其他资源时,如果申请不到,主动释放它占有的资源,破坏 "不可抢占" 条件
-- 按序申请资源,破坏 "循环等待" 条件
+- 互斥条件不能破坏,因为加锁就是为了保证互斥
+- 一次性申请所有的资源,避免线程占有资源而且在等待其他资源
+- 占有部分资源的线程进一步申请其他资源时,如果申请不到,主动释放它占有的资源
+- 按序申请资源
### 线程run和start的区别?
-调用 start() 方法是用来启动线程的,轮到该线程执行时,会自动调用 run();直接调用 run() 方法,无法达到启动多线程的目的,相当于主线程线性执行 Thread 对象的 run() 方法。
-一个线程对线的 start() 方法只能调用一次,多次调用会抛出 java.lang.IllegalThreadStateException 异常;run() 方法没有限制。
+- 当程序调用`start()`方法,将会创建一个新线程去执行`run()`方法中的代码。`run()`就像一个普通方法一样,直接调用`run()`的话,不会创建新线程。
+- 一个线程的 `start()` 方法只能调用一次,多次调用会抛出 java.lang.IllegalThreadStateException 异常。`run()` 方法则没有限制。
### 线程都有哪些方法?
@@ -529,104 +511,79 @@ public static native void sleep(long millis) throws InterruptedException;//stati
## volatile底层原理
-volatile是轻量级的同步机制,volatile保证变量对所有线程的可见性,不保证原子性。
+`volatile`是轻量级的同步机制,`volatile`保证变量对所有线程的可见性,不保证原子性。
-1. 当对volatile变量进行写操作的时候,JVM会向处理器发送一条LOCK前缀的指令,将该变量所在缓存行的数据写回系统内存。
+1. 当对`volatile`变量进行写操作的时候,JVM会向处理器发送一条`LOCK`前缀的指令,将该变量所在缓存行的数据写回系统内存。
2. 由于缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己的缓存是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行置为无效状态,当处理器对这个数据进行修改操作的时候,会重新从系统内存中把数据读到处理器缓存中。
-MESI(缓存一致性协议):当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,就会从内存重新读取。
-
-volatile关键字的两个作用:
-
-1. 保证了不同线程对共享变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
-2. 禁止进行指令重排序。
-
-指令重排序是JVM为了优化指令,提高程序运行效率,在不影响单线程程序执行结果的前提下,尽可能地提高并行度。Java编译器会在生成指令系列时在适当的位置会插入`内存屏障`指令来禁止处理器重排序。插入一个内存屏障,相当于告诉CPU和编译器先于这个命令的必须先执行,后于这个命令的必须后执行。对一个volatile字段进行写操作,Java内存模型将在写操作后插入一个写屏障指令,这个指令会把之前的写入值都刷新到内存。
-
-## AQS原理
-
-AQS,AbstractQueuedSynchronizer,抽象队列同步器,定义了一套多线程访问共享资源的同步器框架,许多并发工具的实现都依赖于它,如常用的ReentrantLock/Semaphore/CountDownLatch。
+> 来看看缓存一致性协议是什么。
+>
+> **缓存一致性协议**:当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,就会从内存重新读取。
-AQS使用一个volatile的int类型的成员变量state来表示同步状态,通过CAS修改同步状态的值。当线程调用 lock 方法时 ,如果 state=0,说明没有任何线程占有共享资源的锁,可以获得锁并将 state加1。如果 state不为0,则说明有线程目前正在使用共享变量,其他线程必须加入同步队列进行等待。
+`volatile`关键字的两个作用:
-```java
-private volatile int state;//共享变量,使用volatile修饰保证线程可见性
-```
+1. 保证了不同线程对共享变量进行操作时的**可见性**,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
+2. 禁止进行**指令重排序**。
-同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态(独占或共享 )构造成为一个节点(Node)并将其加入同步队列并进行自旋,当同步状态释放时,会把首节点中的后继节点对应的线程唤醒,使其再次尝试获取同步状态。
-
-
+> 指令重排序是JVM为了优化指令,提高程序运行效率,在不影响单线程程序执行结果的前提下,尽可能地提高并行度。Java编译器会在生成指令系列时在适当的位置会插入`内存屏障`指令来禁止处理器重排序。插入一个内存屏障,相当于告诉CPU和编译器先于这个命令的必须先执行,后于这个命令的必须后执行。对一个volatile字段进行写操作,Java内存模型将在写操作后插入一个写屏障指令,这个指令会把之前的写入值都刷新到内存。
+## synchronized的用法有哪些?
+1. **修饰普通方法**:作用于当前对象实例,进入同步代码前要获得当前对象实例的锁
+2. **修饰静态方法**:作用于当前类,进入同步代码前要获得当前类对象的锁,synchronized关键字加到static 静态方法和 synchronized(class)代码块上都是是给 Class 类上锁
+3. **修饰代码块**:指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁
-## synchronized的用法有哪些?
+## synchronized的作用有哪些?
-1. 修饰普通方法:作用于当前对象实例,进入同步代码前要获得当前对象实例的锁
-2. 修饰静态方法:作用于当前类,进入同步代码前要获得当前类对象的锁,synchronized关键字加到static 静态方法和 synchronized(class)代码块上都是是给 Class 类上锁
-3. 修饰代码块:指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁
+**原子性**:确保线程互斥的访问同步代码;
-## Synchronized的作用有哪些?
+**可见性**:保证共享变量的修改能够及时可见;
-原子性:确保线程互斥的访问同步代码;
-可见性:保证共享变量的修改能够及时可见,其实是通过Java内存模型中的 “对一个变量unlock 操作之前,必须要同步到主内存中;如果对一个变量进行lock操作,则将会清空工作内存中此变量的值,在执行引擎使用此变量前,需要重新从主内存中load操作或assign操作初始化变量值” 来保证的;
-有序性:有效解决重排序问题,即 “一个unlock操作先行发生(happen-before)于后面对同一个锁的lock操作”。
+**有序性**:有效解决重排序问题。
## synchronized 底层实现原理?
-synchronized 同步代码块的实现是通过 monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。当执行 monitorenter 指令时,线程试图获取锁也就是获取 monitor(monitor对象存在于每个Java对象的对象头中, synchronized 锁便是通过这种方式获取锁的,也是为什么Java中任意对象可以作为锁的原因) 的持有权。
+synchronized 同步代码块的实现是通过 `monitorenter` 和 `monitorexit` 指令,其中 `monitorenter` 指令指向同步代码块的开始位置,`monitorexit` 指令则指明同步代码块的结束位置。当执行 `monitorenter` 指令时,线程试图获取锁也就是获取 `monitor`的持有权(monitor对象存在于每个Java对象的对象头中, synchronized 锁便是通过这种方式获取锁的,也是为什么Java中任意对象可以作为锁的原因)。
-其内部包含一个计数器,当计数器为0则可以成功获取,获取后将锁计数器设为1也就是加1。相应的在 执行 monitorexit 指令后,将锁计数器设为0
+其内部包含一个计数器,当计数器为0则可以成功获取,获取后将锁计数器设为1也就是加1。相应的在执行 `monitorexit` 指令后,将锁计数器设为0
,表明锁被释放。如果获取对象锁失败,那当前线程就要阻塞等待,直到锁被另外一个线程释放为止
-synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法,JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。
+synchronized 修饰的方法并没有 `monitorenter` 指令和 `monitorexit` 指令,取得代之的确实是`ACC_SYNCHRONIZED` 标识,该标识指明了该方法是一个同步方法,JVM 通过该 `ACC_SYNCHRONIZED` 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。
-## ReentrantLock 是如何实现可重入性的?
+## volatile和synchronized的区别是什么?
-ReentrantLock 内部自定义了同步器 Sync,在加锁的时候通过 CAS 算法,将线程对象放到一个双向链表中,每次获取锁的时候,检查当前维护的那个线程 ID 和当前请求的线程 ID 是否 一致,如果一致,同步状态加1,表示锁被当前线程获取了多次。
+1. `volatile`只能使用在变量上;而`synchronized`可以在类,变量,方法和代码块上。
+2. `volatile`至保证可见性;`synchronized`保证原子性与可见性。
+3. `volatile`禁用指令重排序;`synchronized`不会。
+4. `volatile`不会造成阻塞;`synchronized`会。
## ReentrantLock和synchronized区别
-1. 使用synchronized关键字实现同步,线程执行完同步代码块会自动释放锁,而ReentrantLock需要手动释放锁。
-2. synchronized是非公平锁,ReentrantLock可以设置为公平锁。
-3. ReentrantLock上等待获取锁的线程是可中断的,线程可以放弃等待锁。而synchonized会无限期等待下去。
-4. ReentrantLock 可以设置超时获取锁。在指定的截止时间之前获取锁,如果截止时间到了还没有获取到锁,则返回。
-5. ReentrantLock 的 tryLock() 方法可以尝试非阻塞的获取锁,调用该方法后立刻返回,如果能够获取则返回true,否则返回false。
+1. 使用synchronized关键字实现同步,线程执行完同步代码块会**自动释放锁**,而ReentrantLock需要手动释放锁。
+2. synchronized是**非公平锁**,ReentrantLock可以设置为公平锁。
+3. ReentrantLock上等待获取锁的线程是**可中断的**,线程可以放弃等待锁。而synchonized会无限期等待下去。
+4. ReentrantLock **可以设置超时获取锁**。在指定的截止时间之前获取锁,如果截止时间到了还没有获取到锁,则返回。
+5. ReentrantLock 的 tryLock() 方法**可以尝试非阻塞的获取锁**,调用该方法后立刻返回,如果能够获取则返回true,否则返回false。
-## wait()和sleep()的区别
+## wait()和sleep()的异同点?
-相同点:
+**相同点**:
1. 使当前线程暂停运行,把机会交给其他线程
-2. 任何线程在等待期间被中断都会抛出InterruptedException
-
-不同点:
-
-1. wait() 是Object超类中的方法;而sleep()是线程Thread类中的方法
-2. 对锁的持有不同,wait()会释放锁,而sleep()并不释放锁
-3. 唤醒方法不完全相同,wait() 依靠notify或者notifyAll 、中断、达到指定时间来唤醒;而sleep()到达指定时间被唤醒
-4. 调用obj.wait()需要先获取对象的锁,而 Thread.sleep()不用
+2. 任何线程在等待期间被中断都会抛出`InterruptedException`
-## wait(),notify()和suspend(),resume()之间的区别
+**不同点**:
-- wait() 使得线程进入阻塞等待状态,并且释放锁
-- notify()唤醒一个处于等待状态的线程,它一般跟wait()方法配套使用。
-- suspend()使得线程进入阻塞状态,并且不会自动恢复,必须对应的resume() 被调用,才能使得线程重新进入可执行状态。suspend()方法很容易引起死锁问题。
-- resume()方法跟suspend()方法配套使用。
+1. `wait()`是Object超类中的方法;而`sleep()`是线程Thread类中的方法
+2. 对锁的持有不同,`wait()`会释放锁,而`sleep()`并不释放锁
+3. 唤醒方法不完全相同,`wait()`依靠`notify`或者`notifyAll `、中断、达到指定时间来唤醒;而`sleep()`到达指定时间被唤醒
+4. 调用`wait()`需要先获取对象的锁,而`Thread.sleep()`不用
-**suspend()不建议使用**,suspend()方法在调用后,线程不会释放已经占有的资 源(比如锁),而是占有着资源进入睡眠状态,这样容易引发死锁问题。
+## Runnable和Callable有什么区别?
-## Runnable和 Callable有什么区别?
-
-- Callable接口方法是call(),Runnable的方法是run();
+- Callable接口方法是`call()`,Runnable的方法是`run()`;
- Callable接口call方法有返回值,支持泛型,Runnable接口run方法无返回值。
-- Callable接口call()方法允许抛出异常;而Runnable接口run()方法不能继续上抛异常;
-
-## volatile和synchronized的区别是什么?
-
-1. volatile只能使用在变量上;而synchronized可以在类,变量,方法和代码块上。
-2. volatile至保证可见性;synchronized保证原子性与可见性。
-3. volatile禁用指令重排序;synchronized不会。
-4. volatile不会造成阻塞;synchronized会。
+- Callable接口`call()`方法允许抛出异常;而Runnable接口`run()`方法不能继续上抛异常。
## 线程执行顺序怎么控制?
@@ -703,21 +660,9 @@ class SeasonThreadTask implements Runnable{
秋天来了: 3次
```
-
-
-## 乐观锁一定就是好的吗?
-
-乐观锁避免了悲观锁独占对象的现象,提高了并发性能,但它也有缺点:
-
-- 乐观锁只能保证一个共享变量的原子操作。如果多一个或几个变量,乐观锁将变得力不从心,但互斥锁能轻易解决,不管对象数量多少及对象颗粒度大小。
-- 长时间自旋可能导致开销大。假如 CAS 长时间不成功而一直自旋,会 给 CPU 带来很大的开销。
-- ABA 问题。CAS 的核心思想是通过比对内存值与预期值是否一样而判 断内存值是否被改过,但这个判断逻辑不严谨,假如内存值原来是 A, 后来被一条线程改为 B,最后又被改成了 A,则 CAS 认为此内存值并 没有发生改变,但实际上是有被其他线程改过的,这种情况对依赖过程值的情景的运算结果影响很大。解决的思路是引入版本号,每次变量更新都把版本号加一。
-
## 守护线程是什么?
-守护线程是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些 发生的事件。在 Java 中垃圾回收线程就是特殊的守护线程。
-
-
+守护线程是**运行在后台的一种特殊进程**。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。在 Java 中垃圾回收线程就是特殊的守护线程。
## 线程间通信方式
@@ -725,27 +670,25 @@ class SeasonThreadTask implements Runnable{
**volatile** 使用共享内存实现线程间相互通信。多个线程同时监听一个变量,当这个变量被某一个线程修改的时候,其他线程可以感知到这个变化。
-### wait() 和 notify()
-
-wait/notify为 Object 对象的方法,调用wait/notify需要先获得对象的锁。对象调用wait之后线程释放锁,将线程放到对象的等待队列,当通知线程调用此对象的notify()方法后,等待线程并不会立即从wait返回,需要等待通知线程释放锁(通知线程执行完同步代码块),等待队列里的线程获取锁,获取锁成功才能从wait()方法返回,即从wait方法返回前提是线程获得锁。
+### wait 和 notify
-### Thread.join()
-
-当在一个线程调用另一个线程的join方法时,当前线程阻塞等待被调用join方法的线程执行完毕才能继续执行。join的实现其实是基于等待通知机制的。
+`wait/notify`为Object对象的方法,调用`wait/notify`需要先获得对象的锁。对象调用`wait()`之后线程释放锁,将线程放到对象的等待队列,当通知线程调用此对象的`notify()`方法后,等待线程并不会立即从`wait()`返回,需要等待通知线程释放锁(通知线程执行完同步代码块),等待队列里的线程获取锁,获取锁成功才能从`wait()`方法返回,即从`wait()`方法返回前提是线程获得锁。
+### join
+当在一个线程调用另一个线程的`join()`方法时,当前线程阻塞等待被调用join方法的线程执行完毕才能继续执行。`join()`是基于等待通知机制实现的。
## ThreadLocal
-线程本地变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程。
+线程本地变量。当使用`ThreadLocal`维护变量时,`ThreadLocal`为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程。
### ThreadLocal原理
-每个线程都有一个ThreadLocalMap(ThreadLocal内部类),Map中元素的键为ThreadLocal,而值对应线程的变量副本。
+每个线程都有一个`ThreadLocalMap`(`ThreadLocal`内部类),Map中元素的键为`ThreadLocal`,而值对应线程的变量副本。

-调用threadLocal.set()-->调用getMap(Thread)-->返回当前线程的ThreadLocalMap-->map.set(this, value),this是ThreadLocal
+调用`threadLocal.set()`-->调用`getMap(Thread)`-->返回当前线程的`ThreadLocalMap`-->`map.set(this, value)`,this是`threadLocal`本身。源码如下:
```java
public void set(T value) {
@@ -766,7 +709,7 @@ void createMap(Thread t, T firstValue) {
}
```
-调用get()-->调用getMap(Thread)-->返回当前线程的ThreadLocalMap-->map.getEntry(this),返回value
+调用`get()`-->调用`getMap(Thread)`-->返回当前线程的`ThreadLocalMap`-->`map.getEntry(this)`,返回`value`。源码如下:
```java
public T get() {
@@ -784,7 +727,7 @@ void createMap(Thread t, T firstValue) {
}
```
-threadLocals的类型ThreadLocalMap的键为ThreadLocal对象,因为每个线程中可有多个threadLocal变量,如longLocal和stringLocal。
+`threadLocals`的类型`ThreadLocalMap`的键为`ThreadLocal`对象,因为每个线程中可有多个`threadLocal`变量,如`longLocal`和`stringLocal`。
```
public class ThreadLocalDemo {
@@ -816,34 +759,64 @@ public class ThreadLocalDemo {
}
```
-ThreadLocal 并不是用来解决共享资源的多线程访问的问题,因为每个线程中的资源只是副本,并不共享。因此ThreadLocal适合作为线程上下文变量,简化线程内传参。
+`ThreadLocal`并不是用来解决共享资源的多线程访问问题,因为每个线程中的资源只是副本,不会共享。因此`ThreadLocal`适合作为线程上下文变量,简化线程内传参。
### ThreadLocal内存泄漏的原因?
-每个Thread都有⼀个ThreadLocalMap的内部属性,map的key是ThreaLocal,定义为弱引用,value是强引用类型。GC的时候会⾃动回收key,而value的回收取决于Thread对象的生命周期。一般会通过线程池的方式复用Thread对象节省资源,这也就导致了Thread对象的生命周期比较长,这样便一直存在一条强引用链的关系:Thread --> ThreadLocalMap-->Entry-->Value,随着任务的执行,value就有可能越来越多且无法释放,最终导致内存泄漏。
+每个线程都有⼀个`ThreadLocalMap`的内部属性,map的key是`ThreaLocal`,定义为弱引用,value是强引用类型。垃圾回收的时候会⾃动回收key,而value的回收取决于Thread对象的生命周期。一般会通过线程池的方式复用线程节省资源,这也就导致了线程对象的生命周期比较长,这样便一直存在一条强引用链的关系:`Thread` --> `ThreadLocalMap`-->`Entry`-->`Value`,随着任务的执行,value就有可能越来越多且无法释放,最终导致内存泄漏。
+
+解决⽅法:每次使⽤完`ThreadLocal`就调⽤它的`remove()`⽅法,手动将对应的键值对删除,从⽽避免内存泄漏。
+
+### ThreadLocal使用场景有哪些?
+
+`ThreadLocal`适用场景:每个线程需要有自己单独的实例,且需要在多个方法中共享实例,即同时满足实例在线程间的隔离与方法间的共享,这种情况适合使用`ThreadLocal`。比如Java web应用中,每个线程有自己单独的`Session`实例,就可以使用`ThreadLocal`来实现。
+
+## AQS原理
-
+AQS,`AbstractQueuedSynchronizer`,抽象队列同步器,定义了一套多线程访问共享资源的同步器框架,许多并发工具的实现都依赖于它,如常用的`ReentrantLock/Semaphore/CountDownLatch`。
-解决⽅法:每次使⽤完ThreadLocal就调⽤它的remove()⽅法,手动将对应的键值对删除,从⽽避免内存泄漏。
+AQS使用一个`volatile`的int类型的成员变量`state`来表示同步状态,通过CAS修改同步状态的值。当线程调用 lock 方法时 ,如果 `state`=0,说明没有任何线程占有共享资源的锁,可以获得锁并将 `state`加1。如果 `state`不为0,则说明有线程目前正在使用共享变量,其他线程必须加入同步队列进行等待。
```java
-currentTime.set(System.currentTimeMillis());
-result = joinPoint.proceed();
-Log log = new Log("INFO",System.currentTimeMillis() - currentTime.get());
-currentTime.remove();
+private volatile int state;//共享变量,使用volatile修饰保证线程可见性
```
-### ThreadLocal使用场景有哪些?
+同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态(独占或共享 )构造成为一个节点(Node)并将其加入同步队列并进行自旋,当同步状态释放时,会把首节点中的后继节点对应的线程唤醒,使其再次尝试获取同步状态。
+
+
-ThreadLocal 适用场景:每个线程需要有自己单独的实例,且需要在多个方法中共享实例,即同时满足实例在线程间的隔离与方法间的共享,这种情况适合使用ThreadLocal。比如Java web应用中,每个线程有自己单独的 Session 实例,就可以使用ThreadLocal来实现。
+## ReentrantLock 是如何实现可重入性的?
+`ReentrantLock`内部自定义了同步器sync,在加锁的时候通过CAS算法,将线程对象放到一个双向链表中,每次获取锁的时候,检查当前维护的那个线程ID和当前请求的线程ID是否 一致,如果一致,同步状态加1,表示锁被当前线程获取了多次。
+源码如下:
+
+```java
+final boolean nonfairTryAcquire(int acquires) {
+ final Thread current = Thread.currentThread();
+ int c = getState();
+ if (c == 0) {
+ if (compareAndSetState(0, acquires)) {
+ setExclusiveOwnerThread(current);
+ return true;
+ }
+ }
+ else if (current == getExclusiveOwnerThread()) {
+ int nextc = c + acquires;
+ if (nextc < 0) // overflow
+ throw new Error("Maximum lock count exceeded");
+ setState(nextc);
+ return true;
+ }
+ return false;
+}
+```
## 锁的分类
### 公平锁与非公平锁
-按照线程访问顺序获取对象锁。synchronized 是非公平锁, Lock 默认是非公平锁,可以设置为公平锁,公平锁会影响性能。
+按照**线程访问顺序**获取对象锁。`synchronized`是非公平锁,`Lock`默认是非公平锁,可以设置为公平锁,公平锁会影响性能。
```java
public ReentrantLock() {
@@ -857,41 +830,42 @@ public ReentrantLock(boolean fair) {
### 共享式与独占式锁
-共享式与独占式的最主要区别在于:同一时刻独占式只能有一个线程获取同步状态,而共享式在同一时刻可以有多个线程获取同步状态。例如读操作可以有多个线程同时进行,而写操作同一时刻只能有一个线程进行写操作,其他操作都会被阻塞。
+共享式与独占式的最主要**区别**在于:同一时刻独占式只能有**一个线程**获取同步状态,而共享式在同一时刻可以有多个线程获取同步状态。例如读操作可以有多个线程同时进行,而写操作同一时刻只能有一个线程进行写操作,其他操作都会被阻塞。
### 悲观锁与乐观锁
-悲观锁,每次访问资源都会加锁,执行完同步代码释放锁,synchronized 和 ReentrantLock 属于悲观锁。
+悲观锁,**每次访问资源都会加锁**,执行完同步代码释放锁,`synchronized`和`ReentrantLock`属于悲观锁。
-乐观锁,不会锁定资源,所有的线程都能访问并修改同一个资源,如果没有冲突就修改成功并退出,否则就会继续循环尝试。乐观锁最常见的实现就是CAS。
+乐观锁,不会锁定资源,所有的线程都能访问并修改同一个资源,如果没有冲突就修改成功并退出,否则就会继续循环尝试。乐观锁最常见的实现就是`CAS`。
-乐观锁一般来说有以下2种方式:
+适用场景:
-1. 使用数据版本记录机制实现,这是乐观锁最常用的一种实现方式。给数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的version字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。
-2. 使用时间戳。数据库表增加一个字段,字段类型使用时间戳(timestamp),和上面的version类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。
+- 悲观锁适合**写操作多**的场景。
+- 乐观锁适合**读操作多**的场景,不加锁可以提升读操作的性能。
-适用场景:
+## 乐观锁有什么问题?
-- 悲观锁适合写操作多的场景。
-- 乐观锁适合读操作多的场景,不加锁可以提升读操作的性能。
+乐观锁避免了悲观锁独占对象的问题,提高了并发性能,但它也有缺点:
-## CAS
+- 乐观锁只能保证**一个共享变量**的原子操作。
+- 长时间自旋可能导致**开销大**。假如CAS长时间不成功而一直自旋,会给CPU带来很大的开销。
+- **ABA问题**。CAS的原理是通过比对内存值与预期值是否一样而判断内存值是否被改过,但是会有以下问题:假如内存值原来是A, 后来被一条线程改为B,最后又被改成了A,则CAS认为此内存值并没有发生改变。可以引入版本号解决这个问题,每次变量更新都把版本号加一。
-### 什么是CAS?
+## 什么是CAS?
-CAS全称 Compare And Swap,比较与交换,是乐观锁的主要实现方式。CAS 在不使用锁的情况下实现多线程之间的变量同步。ReentrantLock 内部的 AQS 和原子类内部都使用了 CAS。
+CAS全称`Compare And Swap`,比较与交换,是乐观锁的主要实现方式。CAS在不使用锁的情况下实现多线程之间的变量同步。`ReentrantLock`内部的AQS和原子类内部都使用了CAS。
CAS算法涉及到三个操作数:
-- 需要读写的内存值 V。
-- 进行比较的值 A。
-- 要写入的新值 B。
+- 需要读写的内存值V。
+- 进行比较的值A。
+- 要写入的新值B。
-只有当 V 的值等于 A 时,才会使用原子方式用新值B来更新V的值,否则会继续重试直到成功更新值。
+只有当V的值等于A时,才会使用原子方式用新值B来更新V的值,否则会继续重试直到成功更新值。
-以 AtomicInteger 为例,AtomicInteger 的 getAndIncrement()方法底层就是CAS实现,关键代码是 `compareAndSwapInt(obj, offset, expect, update)`,其含义就是,如果`obj`内的`value`和`expect`相等,就证明没有其他线程改变过这个变量,那么就更新它为`update`,如果不相等,那就会继续重试直到成功更新值。
+以`AtomicInteger`为例,`AtomicInteger`的`getAndIncrement()`方法底层就是CAS实现,关键代码是 `compareAndSwapInt(obj, offset, expect, update)`,其含义就是,如果`obj`内的`value`和`expect`相等,就证明没有其他线程改变过这个变量,那么就更新它为`update`,如果不相等,那就会继续重试直到成功更新值。
-### CAS存在的问题?
+## CAS存在的问题?
CAS 三大问题:
@@ -905,8 +879,6 @@ CAS 三大问题:
Java从1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,可以把多个变量放在一个对象里来进行CAS操作。
-
-
## 并发工具
在JDK的并发包里提供了几个非常有用的并发工具类。CountDownLatch、CyclicBarrier和Semaphore工具类提供了一种并发流程控制的手段。
diff --git "a/\345\267\245\345\205\267/docker.md" "b/\345\267\245\345\205\267/docker.md"
index e6085cf..0359ec2 100644
--- "a/\345\267\245\345\205\267/docker.md"
+++ "b/\345\267\245\345\205\267/docker.md"
@@ -8,6 +8,7 @@
- [添加docker仓库位置](#%E6%B7%BB%E5%8A%A0docker%E4%BB%93%E5%BA%93%E4%BD%8D%E7%BD%AE)
- [安装docker服务](#%E5%AE%89%E8%A3%85docker%E6%9C%8D%E5%8A%A1)
- [启动 docker 服务](#%E5%90%AF%E5%8A%A8-docker-%E6%9C%8D%E5%8A%A1)
+ - [重启docker服务](#%E9%87%8D%E5%90%AFdocker%E6%9C%8D%E5%8A%A1)
- [搜索镜像](#%E6%90%9C%E7%B4%A2%E9%95%9C%E5%83%8F)
- [下载镜像:](#%E4%B8%8B%E8%BD%BD%E9%95%9C%E5%83%8F)
- [列出镜像](#%E5%88%97%E5%87%BA%E9%95%9C%E5%83%8F)
@@ -46,6 +47,8 @@
- [重启docker](#%E9%87%8D%E5%90%AFdocker)
- [开放端口](#%E5%BC%80%E6%94%BE%E7%AB%AF%E5%8F%A3)
- [构建docker镜像](#%E6%9E%84%E5%BB%BAdocker%E9%95%9C%E5%83%8F)
+- [其他](#%E5%85%B6%E4%BB%96)
+ - [给nginx增加端口映射](#%E7%BB%99nginx%E5%A2%9E%E5%8A%A0%E7%AB%AF%E5%8F%A3%E6%98%A0%E5%B0%84)
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md"
index 2d8d366..2378225 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md"
@@ -18,6 +18,7 @@
- [all](#all)
- [possible_keys](#possible_keys)
- [key](#key)
+- [ref](#ref-1)
- [rows](#rows)
- [filtered](#filtered)
- [extra](#extra)
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index f9e4b94..c9619ff 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -27,7 +27,6 @@
- [快照读和当前读](#%E5%BF%AB%E7%85%A7%E8%AF%BB%E5%92%8C%E5%BD%93%E5%89%8D%E8%AF%BB)
- [共享锁和排他锁](#%E5%85%B1%E4%BA%AB%E9%94%81%E5%92%8C%E6%8E%92%E4%BB%96%E9%94%81)
- [大表怎么优化?](#%E5%A4%A7%E8%A1%A8%E6%80%8E%E4%B9%88%E4%BC%98%E5%8C%96)
-- [MySQL 执行计划了解吗?](#mysql-%E6%89%A7%E8%A1%8C%E8%AE%A1%E5%88%92%E4%BA%86%E8%A7%A3%E5%90%97)
- [bin log/redo log/undo log](#bin-logredo-logundo-log)
- [bin log和redo log有什么区别?](#bin-log%E5%92%8Credo-log%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB)
- [讲一下MySQL架构?](#%E8%AE%B2%E4%B8%80%E4%B8%8Bmysql%E6%9E%B6%E6%9E%84)
From 59f74b80fa1e2630565993a54d2ea3f5ef603d16 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 27 Feb 2022 00:31:00 +0800
Subject: [PATCH 012/112] =?UTF-8?q?Java=E6=A0=B8=E5=BF=83=E7=9F=A5?=
=?UTF-8?q?=E8=AF=86=E9=9D=A2=E8=AF=95=E9=A2=98=E6=95=B4=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...00\351\235\242\350\257\225\351\242\230.md" | 234 ++++++------------
1 file changed, 75 insertions(+), 159 deletions(-)
diff --git "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
index b165101..0ff8991 100644
--- "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
@@ -1,6 +1,5 @@
-**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [Java的特点](#java%E7%9A%84%E7%89%B9%E7%82%B9)
- [Java 与 C++ 的区别](#java-%E4%B8%8E-c-%E7%9A%84%E5%8C%BA%E5%88%AB)
@@ -16,12 +15,16 @@
- [new String("dabin")会创建几个对象?](#new-stringdabin%E4%BC%9A%E5%88%9B%E5%BB%BA%E5%87%A0%E4%B8%AA%E5%AF%B9%E8%B1%A1)
- [什么是字符串常量池?](#%E4%BB%80%E4%B9%88%E6%98%AF%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%B8%B8%E9%87%8F%E6%B1%A0)
- [object常用方法有哪些?](#object%E5%B8%B8%E7%94%A8%E6%96%B9%E6%B3%95%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [深拷贝和浅拷贝?](#%E6%B7%B1%E6%8B%B7%E8%B4%9D%E5%92%8C%E6%B5%85%E6%8B%B7%E8%B4%9D)
+- [讲讲深拷贝和浅拷贝?](#%E8%AE%B2%E8%AE%B2%E6%B7%B1%E6%8B%B7%E8%B4%9D%E5%92%8C%E6%B5%85%E6%8B%B7%E8%B4%9D)
- [两个对象的hashCode()相同,则 equals()是否也一定为 true?](#%E4%B8%A4%E4%B8%AA%E5%AF%B9%E8%B1%A1%E7%9A%84hashcode%E7%9B%B8%E5%90%8C%E5%88%99-equals%E6%98%AF%E5%90%A6%E4%B9%9F%E4%B8%80%E5%AE%9A%E4%B8%BA-true)
- [Java创建对象有几种方式?](#java%E5%88%9B%E5%BB%BA%E5%AF%B9%E8%B1%A1%E6%9C%89%E5%87%A0%E7%A7%8D%E6%96%B9%E5%BC%8F)
- [说说类实例化的顺序](#%E8%AF%B4%E8%AF%B4%E7%B1%BB%E5%AE%9E%E4%BE%8B%E5%8C%96%E7%9A%84%E9%A1%BA%E5%BA%8F)
- [equals和==有什么区别?](#equals%E5%92%8C%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB)
- [常见的关键字有哪些?](#%E5%B8%B8%E8%A7%81%E7%9A%84%E5%85%B3%E9%94%AE%E5%AD%97%E6%9C%89%E5%93%AA%E4%BA%9B)
+ - [static](#static)
+ - [final](#final)
+ - [this](#this)
+ - [super](#super)
- [final, finally, finalize 的区别](#final-finally-finalize-%E7%9A%84%E5%8C%BA%E5%88%AB)
- [final关键字的作用?](#final%E5%85%B3%E9%94%AE%E5%AD%97%E7%9A%84%E4%BD%9C%E7%94%A8)
- [方法重载和重写的区别?](#%E6%96%B9%E6%B3%95%E9%87%8D%E8%BD%BD%E5%92%8C%E9%87%8D%E5%86%99%E7%9A%84%E5%8C%BA%E5%88%AB)
@@ -31,8 +34,6 @@
- [运行时异常和非运行时异常(checked)的区别?](#%E8%BF%90%E8%A1%8C%E6%97%B6%E5%BC%82%E5%B8%B8%E5%92%8C%E9%9D%9E%E8%BF%90%E8%A1%8C%E6%97%B6%E5%BC%82%E5%B8%B8checked%E7%9A%84%E5%8C%BA%E5%88%AB)
- [throw和throws的区别?](#throw%E5%92%8Cthrows%E7%9A%84%E5%8C%BA%E5%88%AB)
- [BIO/NIO/AIO区别的区别?](#bionioaio%E5%8C%BA%E5%88%AB%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [ThreadLocal的实现原理?](#threadlocal%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86)
-- [什么是fail fast?](#%E4%BB%80%E4%B9%88%E6%98%AFfail-fast)
- [守护线程是什么?](#%E5%AE%88%E6%8A%A4%E7%BA%BF%E7%A8%8B%E6%98%AF%E4%BB%80%E4%B9%88)
- [Java支持多继承吗?](#java%E6%94%AF%E6%8C%81%E5%A4%9A%E7%BB%A7%E6%89%BF%E5%90%97)
- [如何实现对象克隆?](#%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E5%AF%B9%E8%B1%A1%E5%85%8B%E9%9A%86)
@@ -44,14 +45,6 @@
-> 大家好,我是大彬。最近在面试,看了很多面经,这段时间抽空将Java常见的面试题总结了一下,如果对你有帮助,可以**收藏和点赞**,**后续还会继续更新新的面试题目哦!**
-
-**文章目录**
-
-
-
-
-
> 首先给大家分享一个github仓库,上面放了**200多本经典的计算机书籍**,包括C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生等,可以star一下,下次找书直接在上面搜索,仓库持续更新中~
>
> github地址:https://github.com/Tyson0314/java-books
@@ -81,8 +74,6 @@
- Java 支持自动垃圾回收,而 C++ 需要手动回收。
- Java 不支持多重继承,只能通过实现多个接口来达到相同目的,而 C++ 支持多重继承。
-> [What are the main differences between Java and C++?](http://cs-fundamentals.com/tech-interview/java/differences-between-java-and-cpp.php)
-
## 面向对象和面向过程的区别?
面向对象和面向过程是一种软件开发思想。
@@ -92,6 +83,7 @@
- 面向对象是把构成问题事务分解成各个对象,分别设计这些对象,然后将他们组装成有完整功能的系统。面向过程只用函数实现,面向对象是用类实现各个功能模块。
以五子棋为例(来源网络),面向过程的设计思路就是首先分析问题的步骤:
+
1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤2,9、输出最后结果。
把上面每个步骤用分别的函数来实现,问题就解决了。
@@ -117,14 +109,16 @@ JRE是运行基于Java语言编写的程序所不可缺少的运行环境。也
面向对象四大特性:封装,继承,多态,抽象
-- 封装就是将类的信息隐藏在类内部,不允许外部程序直接访问,而是通过该类的方法实现对隐藏信息的操作和访问。 良好的封装能够减少耦合。
-- 继承是从已有的类中派生出新的类,新的类继承父类的属性和行为,并能扩展新的能力,大大增加程序的重用性和易维护性。在Java中是单继承的,也就是说一个子类只有一个父类。
-- 多态是同一个行为具有多个不同表现形式的能力。在不修改程序代码的情况下改变程序运行时绑定的代码。实现多态的三要素:继承、重写、父类引用指向子类对象。
- 静态多态性:通过重载实现,相同的方法有不同的參数列表,可以根据参数的不同,做出不同的处理。
- 动态多态性:在子类中重写父类的方法。运行期间判断所引用对象的实际类型,根据其实际类型调用相应的方法。
-- 抽象。把客观事物用代码抽象出来。
+1、封装就是将类的信息隐藏在类内部,不允许外部程序直接访问,而是通过该类的方法实现对隐藏信息的操作和访问。 良好的封装能够减少耦合。
+
+2、继承是从已有的类中派生出新的类,新的类继承父类的属性和行为,并能扩展新的能力,大大增加程序的重用性和易维护性。在Java中是单继承的,也就是说一个子类只有一个父类。
+
+3、多态是同一个行为具有多个不同表现形式的能力。在不修改程序代码的情况下改变程序运行时绑定的代码。实现多态的三要素:继承、重写、父类引用指向子类对象。
+- 静态多态性:通过重载实现,相同的方法有不同的參数列表,可以根据参数的不同,做出不同的处理。
+- 动态多态性:在子类中重写父类的方法。运行期间判断所引用对象的实际类型,根据其实际类型调用相应的方法。
+3、抽象。把客观事物用代码抽象出来。
## Java的基本数据类型有哪些?
@@ -312,7 +306,7 @@ String类是final的,它的所有成员变量也都是final的。为什么是f
Java面试经常会出现的一道题目,Object的常用方法。下面给大家整理一下。
-object常用方法有:toString()、equals()、hashCode()、clone()等。
+object常用方法有:`toString()`、`equals()`、`hashCode()`、`clone()`等。
**toString**
@@ -504,7 +498,7 @@ public class Person {
obj.notify()唤醒在此对象上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象上等待的所有线程。
-## 深拷贝和浅拷贝?
+## 讲讲深拷贝和浅拷贝?
**浅拷贝**:拷⻉对象和原始对象的引⽤类型引用同⼀个对象。
@@ -572,7 +566,7 @@ equals与hashcode的关系:
1. 如果两个对象调用equals比较返回true,那么它们的hashCode值一定要相同;
2. 如果两个对象的hashCode相同,它们并不一定相同。
-hashcode方法主要是用来提升对象比较的效率,先进行hashcode()的比较,如果不相同,那就不必在进行equals的比较,这样就大大减少了equals比较的次数,当比较对象的数量很大的时候能提升效率。
+hashcode方法主要是用来**提升对象比较的效率**,先进行hashcode()的比较,如果不相同,那就不必在进行equals的比较,这样就大大减少了equals比较的次数,当比较对象的数量很大的时候能提升效率。
之所以重写equals()要重写hashcode(),是为了保证equals()方法返回true的情况下hashcode值也要一致,如果重写了equals()没有重写hashcode(),就会出现两个对象相等但hashcode()不相等的情况。这样,当用其中的一个对象作为键保存到hashMap、hashTable或hashSet中,再以另一个对象作为键值去查找他们的时候,则会查找不到。
@@ -652,7 +646,7 @@ public class LifeCycle {
## 常见的关键字有哪些?
-**static**
+### static
static可以用来修饰类的成员方法、类的成员变量。
@@ -751,7 +745,7 @@ public class OuterClass {
}
```
-**final**
+### final
1. **基本数据**类型用final修饰,则不能修改,是常量;**对象引用**用final修饰,则引用只能指向该对象,不能指向别的对象,但是对象本身可以修改。
@@ -759,7 +753,7 @@ public class OuterClass {
3. final修饰的类不能被继承。
-**this**
+### this
`this.属性名称`指访问类中的成员变量,可以用来区分成员变量和局部变量。如下代码所示,`this.name`访问类Person当前实例的变量。
@@ -803,7 +797,7 @@ public class Person {
}
```
-**super**
+### super
super 关键字用于在子类中访问父类的变量和方法。
@@ -834,13 +828,13 @@ public class B extends A {
}
```
-在子类B中,我们重写了父类的getName()方法,如果在重写的getName()方法中我们要调用父类的相同方法,必须要通过super关键字显式指出。
+在子类B中,我们重写了父类的`getName()`方法,如果在重写的`getName()`方法中我们要调用父类的相同方法,必须要通过super关键字显式指出。
## final, finally, finalize 的区别
-- final 用于修饰属性,方法和类, 分别表示属性不能被重新赋值, 方法不可被覆盖, 类不可被继承。
-- finally 是异常处理语句结构的一部分,一般以ty-catch-finally出现,finally代码块表示总是被执行。
-- finalize 是Object类的一个方法,该方法一般由垃圾回收器来调用,当我们调用System.gc() 方法的时候,由垃圾回收器调用finalize()方法,回收垃圾,JVM并不保证此方法总被调用。
+- final 用于修饰属性、方法和类, 分别表示属性不能被重新赋值,方法不可被覆盖,类不可被继承。
+- finally 是异常处理语句结构的一部分,一般以`try-catch-finally`出现,`finally`代码块表示总是被执行。
+- finalize 是Object类的一个方法,该方法一般由垃圾回收器来调用,当我们调用`System.gc()`方法的时候,由垃圾回收器调用`finalize()`方法,回收垃圾,JVM并不保证此方法总被调用。
## final关键字的作用?
@@ -892,27 +886,29 @@ public class Student extends Person {
## 接口与抽象类区别?
-- 语法层面上
- 1)抽象类可以有方法实现,而接口的方法中只能是抽象方法;
- 2)抽象类中的成员变量可以是各种类型的,接口中的成员变量只能是public static final类型;
- 3)接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
- 4)一个类只能继承一个抽象类,而一个类却可以实现多个接口。
+1、语法层面上
-- 设计层面上的区别
- 1)抽象层次不同。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口只是对类行为进行抽象。继承抽象类是一种"是不是"的关系,而接口实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是具备不具备的关系,比如鸟是否能飞。
- 2) 继承抽象类的是具有相似特点的类,而实现接口的却可以不同的类。
+- 抽象类可以有方法实现,而接口的方法中只能是抽象方法;
+- 抽象类中的成员变量可以是各种类型的,接口中的成员变量只能是public static final类型;
+- 接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
+- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
- 门和警报的例子:
+2、设计层面上的区别
- ```java
- class AlarmDoor extends Door implements Alarm {
- //code
- }
-
- class BMWCar extends Car implements Alarm {
- //code
- }
- ```
+- 抽象层次不同。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口只是对类行为进行抽象。继承抽象类是一种"是不是"的关系,而接口实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是具备不具备的关系,比如鸟是否能飞。
+- 继承抽象类的是具有相似特点的类,而实现接口的却可以不同的类。
+
+门和警报的例子:
+
+```java
+class AlarmDoor extends Door implements Alarm {
+ //code
+}
+
+class BMWCar extends Car implements Alarm {
+ //code
+}
+```
## 常见的Exception有哪些?
@@ -933,16 +929,16 @@ unchecked Exception:
## Error和Exception的区别?
-Error:JVM 无法解决的严重问题,如栈溢出(StackOverflowError)、内存溢出(OOM)等。程序无法处理的错误。
+Error:JVM 无法解决的严重问题,如栈溢出`StackOverflowError`、内存溢出`OOM`等。程序无法处理的错误。
Exception:其它因编程错误或偶然的外在因素导致的一般性问题。可以在代码中进行处理。如:空指针异常、数组下标越界等。
## 运行时异常和非运行时异常(checked)的区别?
-unchecked exception包括RuntimeException和Error类,其他所有异常称为检查(checked)异常。
+`unchecked exception`包括`RuntimeException`和`Error`类,其他所有异常称为检查(checked)异常。
-1. RuntimeException由程序错误导致,应该修正程序避免这类异常发生。
-2. checked Exception由具体的环境(读取的文件不存在或文件为空或sql异常)导致的异常。必须进行处理,不然编译不通过,可以catch或者throws。
+1. `RuntimeException`由程序错误导致,应该修正程序避免这类异常发生。
+2. `checked Exception`由具体的环境(读取的文件不存在或文件为空或sql异常)导致的异常。必须进行处理,不然编译不通过,可以catch或者throws。
## throw和throws的区别?
@@ -954,101 +950,10 @@ unchecked exception包括RuntimeException和Error类,其他所有异常称为
同步阻塞IO : 用户进程发起一个IO操作以后,必须等待IO操作的真正完成后,才能继续运行。
-同步非阻塞IO: 客户端与服务器通过Channel连接,采用多路复用器轮询注册的Channel。提高吞吐量和可靠性。用户进程发起一个IO操作以后,可做其它事情,但用户进程需要轮询IO操作是否完成,这样造成不必要的CPU资源浪费。
+同步非阻塞IO: 客户端与服务器通过Channel连接,采用多路复用器轮询注册的`Channel`。提高吞吐量和可靠性。用户进程发起一个IO操作以后,可做其它事情,但用户进程需要轮询IO操作是否完成,这样造成不必要的CPU资源浪费。
异步非阻塞IO: 非阻塞异步通信模式,NIO的升级版,采用异步通道实现异步通信,其read和write方法均是异步方法。用户进程发起一个IO操作,然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知。类似Future模式。
-
-
-## ThreadLocal的实现原理?
-
-每个线程都有一个ThreadLocalMap(ThreadLocal内部类),Map中元素的键为ThreadLocal,而值对应线程的变量副本。
-
-
-
-调用threadLocal.set()-->调用getMap(Thread)-->返回当前线程的ThreadLocalMap-->map.set(this, value),this是ThreadLocal
-
-```java
-public void set(T value) {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null)
- map.set(this, value);
- else
- createMap(t, value);
-}
-
-ThreadLocalMap getMap(Thread t) {
- return t.threadLocals;
-}
-
-void createMap(Thread t, T firstValue) {
- t.threadLocals = new ThreadLocalMap(this, firstValue);
-}
-```
-
-调用get()-->调用getMap(Thread)-->返回当前线程的ThreadLocalMap-->map.getEntry(this),返回value
-
-```java
- public T get() {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null) {
- ThreadLocalMap.Entry e = map.getEntry(this);
- if (e != null) {
- @SuppressWarnings("unchecked")
- T result = (T)e.value;
- return result;
- }
- }
- return setInitialValue();
- }
-```
-
-threadLocals的类型ThreadLocalMap的键为ThreadLocal对象,因为每个线程中可有多个threadLocal变量,如longLocal和stringLocal。
-
-```java
-public class ThreadLocalDemo {
- ThreadLocal longLocal = new ThreadLocal<>();
-
- public void set() {
- longLocal.set(Thread.currentThread().getId());
- }
- public Long get() {
- return longLocal.get();
- }
-
- public static void main(String[] args) throws InterruptedException {
- ThreadLocalDemo threadLocalDemo = new ThreadLocalDemo();
- threadLocalDemo.set();
- System.out.println(threadLocalDemo.get());
-
- Thread thread = new Thread(() -> {
- threadLocalDemo.set();
- System.out.println(threadLocalDemo.get());
- }
- );
-
- thread.start();
- thread.join();
-
- System.out.println(threadLocalDemo.get());
- }
-}
-```
-
-ThreadLocal 并不是用来解决共享资源的多线程访问的问题,因为每个线程中的资源只是副本,并不共享。因此ThreadLocal适合作为线程上下文变量,简化线程内传参。
-
-## 什么是fail fast?
-
-fast-fail是Java集合的一种错误机制。当多个线程对同一个集合进行操作时,就有可能会产生fast-fail事件。
-例如:当线程a正通过iterator遍历集合时,另一个线程b修改了集合的内容,此时modCount(记录集合操作过程的修改次数)会加1,不等于expectedModCount,那么线程a访问集合的时候,就会抛出ConcurrentModificationException,产生fast-fail事件。边遍历边修改集合也会产生fast-fail事件。
-
-解决方法:
-
-- 使用Colletions.synchronizedList方法或在修改集合内容的地方加上synchronized。这样的话,增删集合内容的同步锁会阻塞遍历操作,影响性能。
-- 使用CopyOnWriteArrayList来替换ArrayList。在对CopyOnWriteArrayList进行修改操作的时候,会拷贝一个新的数组,对新的数组进行操作,操作完成后再把引用移到新的数组。
-
## 守护线程是什么?
- 守护线程是运行在后台的一种特殊进程。
@@ -1059,15 +964,15 @@ fast-fail是Java集合的一种错误机制。当多个线程对同一个集合
java中,**类不支持**多继承。**接口才支持**多继承。接口的作用是拓展对象功能。当一个子接口继承了多个父接口时,说明子接口拓展了多个功能。当一个类实现该接口时,就拓展了多个的功能。
-Java不支持多继承的原因(来源网络):
+Java不支持多继承的原因:
-- 安全性的考虑,如果子类继承的多个父类里面有相同的方法或者属性,子类将不知道具体要继承哪个。
+- 出于安全性的考虑,如果子类继承的多个父类里面有相同的方法或者属性,子类将不知道具体要继承哪个。
- Java提供了接口和内部类以达到实现多继承功能,弥补单继承的缺陷。
## 如何实现对象克隆?
-- 实现 Cloneable 接口,重写 clone() 方法。这种方式是浅拷贝,即如果类中属性有自定义引用类型,只拷贝引用,不拷贝引用指向的对象。如果对象的属性的Class 也实现 Cloneable 接口,那么在克隆对象时也会克隆属性,即深拷贝。
-- 结合序列化(JDK java.io.Serializable 接口、JSON格式、XML格式等),深拷贝。
+- 实现`Cloneable`接口,重写 `clone()` 方法。这种方式是浅拷贝,即如果类中属性有自定义引用类型,只拷贝引用,不拷贝引用指向的对象。如果对象的属性的Class也实现 `Cloneable` 接口,那么在克隆对象时也会克隆属性,即深拷贝。
+- 结合序列化,深拷贝。
- 通过`org.apache.commons`中的工具类`BeanUtils`和`PropertyUtils`进行对象复制。
## 同步和异步的区别?
@@ -1106,7 +1011,27 @@ Java不支持多继承的原因(来源网络):
## 如何实现序列化
-实现Serializable接口即可。序列化的时候(如objectOutputStream.writeObject(user)),会判断user是否实现了Serializable(obj instanceof Serializable),如果对象没有实现Serializable接口,在序列化的时候会抛出NotSerializableException异常。
+实现`Serializable`接口即可。序列化的时候(如`objectOutputStream.writeObject(user)`),会判断user是否实现了`Serializable`,如果对象没有实现`Serializable`接口,在序列化的时候会抛出`NotSerializableException`异常。源码如下:
+
+```java
+// remaining cases
+if (obj instanceof String) {
+ writeString((String) obj, unshared);
+} else if (cl.isArray()) {
+ writeArray(obj, desc, unshared);
+} else if (obj instanceof Enum) {
+ writeEnum((Enum>) obj, desc, unshared);
+} else if (obj instanceof Serializable) {
+ writeOrdinaryObject(obj, desc, unshared);
+} else {
+ if (extendedDebugInfo) {
+ throw new NotSerializableException(
+ cl.getName() + "\n" + debugInfoStack.toString());
+ } else {
+ throw new NotSerializableException(cl.getName());
+ }
+}
+```
@@ -1120,12 +1045,3 @@ Java不支持多继承的原因(来源网络):
>
> gitee地址:https://gitee.com/tysondai/Java-learning
-
-
-我的其他文章推荐:
-
-- [Java并发知识八股文](https://www.nowcoder.com/discuss/749251?source_id=profile_create_nctrack&channel=-1)
-- [一文搞懂RabbitMQ!](https://www.nowcoder.com/discuss/744363?source_id=profile_create_nctrack&channel=-1)
-- [Java虚拟机核心知识总结!](https://www.nowcoder.com/discuss/733750?source_id=profile_create_nctrack&channel=-1)
-- [Redis核心知识总结](https://www.nowcoder.com/discuss/742957?source_id=profile_create_nctrack&channel=-1)
-
From c1f015c2b9102c74e7419bb206c05dc13e94aad1 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 27 Feb 2022 12:29:48 +0800
Subject: [PATCH 013/112] =?UTF-8?q?Java=E9=9B=86=E5=90=88=E9=9D=A2?=
=?UTF-8?q?=E8=AF=95=E9=A2=98=E6=95=B4=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...00\351\235\242\350\257\225\351\242\230.md" | 4 +-
...10\351\235\242\350\257\225\351\242\230.md" | 76 +++++++++----------
2 files changed, 36 insertions(+), 44 deletions(-)
diff --git "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
index 0ff8991..28aacf0 100644
--- "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
@@ -59,12 +59,12 @@
**Java具有平台独立性和移植性。**
-- Java有一句口号:Write once, run anywhere,一次编写、到处运行。这也是Java的魅力所在。而实现这种特性的正是Java虚拟机JVM。已编译的Java程序可以在任何带有JVM的平台上运行。你可以在windows平台编写代码,然后拿到linux上运行。只要你在编写完代码后,将代码编译成.class文件,再把class文件打成Java包,这个jar包就可以在不同的平台上运行了。
+- Java有一句口号:`Write once, run anywhere`,一次编写、到处运行。这也是Java的魅力所在。而实现这种特性的正是Java虚拟机JVM。已编译的Java程序可以在任何带有JVM的平台上运行。你可以在windows平台编写代码,然后拿到linux上运行。只要你在编写完代码后,将代码编译成.class文件,再把class文件打成Java包,这个jar包就可以在不同的平台上运行了。
**Java具有稳健性。**
- Java是一个强类型语言,它允许扩展编译时检查潜在类型不匹配问题的功能。Java要求显式的方法声明,它不支持C风格的隐式声明。这些严格的要求保证编译程序能捕捉调用错误,这就导致更可靠的程序。
-- 异常处理是Java中使得程序更稳健的另一个特征。异常是某种类似于错误的异常条件出现的信号。使用try/catch/finally语句,程序员可以找到出错的处理代码,这就简化了出错处理和恢复的任务。
+- 异常处理是Java中使得程序更稳健的另一个特征。异常是某种类似于错误的异常条件出现的信号。使用`try/catch/finally`语句,程序员可以找到出错的处理代码,这就简化了出错处理和恢复的任务。
## Java 与 C++ 的区别
diff --git "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
index a4a5b39..518c377 100644
--- "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
@@ -46,13 +46,7 @@
-> 首先给大家分享一个github仓库,上面放了**200多本经典的计算机书籍**,包括C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生等,可以star一下,下次找书直接在上面搜索,仓库持续更新中~
->
-> github地址:https://github.com/Tyson0314/java-books
->
-> 如果github访问不了,可以访问gitee仓库。
->
-> gitee地址:https://gitee.com/tysondai/java-books
+
## 常见的集合有哪些?
@@ -66,7 +60,7 @@ Java集合框架图如下:
List代表了有序可重复集合,可直接根据元素的索引来访问;Set代表无序不可重复集合,只能根据元素本身来访问;Queue是队列集合。Map代表的是存储key-value对的集合,可根据元素的key来访问value。
-集合体系中常用的实现类有ArrayList、LinkedList、HashSet、TreeSet、HashMap、TreeMap等实现类。
+集合体系中常用的实现类有`ArrayList、LinkedList、HashSet、TreeSet、HashMap、TreeMap`等实现类。
## List 、Set和Map 的区别
@@ -150,7 +144,7 @@ while(itr.hasNext()) {
1. ArrayList在内存不够时默认是扩展50% + 1个,Vector是默认扩展1倍。
2. Vector属于线程安全级别的,但是大多数情况下不使用Vector,因为操作Vector效率比较低。
-## Arraylist 与 LinkedList 区别
+## Arraylist 与 LinkedList的区别
1. ArrayList基于动态数组实现;LinkedList基于链表实现。
2. 对于随机index访问的get和set方法,ArrayList的速度要优于LinkedList。因为ArrayList直接通过数组下标直接找到元素;LinkedList要移动指针遍历每个元素直到找到为止。
@@ -158,11 +152,11 @@ while(itr.hasNext()) {
## HashMap
-HashMap 使用数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的, 链表长度大于8(TREEIFY_THRESHOLD)时,会把链表转换为红黑树,红黑树节点个数小于6(UNTREEIFY_THRESHOLD)时才转化为链表,防止频繁的转化。
+HashMap 使用数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的, 链表长度大于8(`TREEIFY_THRESHOLD`)时,会把链表转换为红黑树,红黑树节点个数小于6(`UNTREEIFY_THRESHOLD`)时才转化为链表,防止频繁的转化。
### 解决hash冲突的办法有哪些?HashMap用的哪种?
-解决Hash冲突方法有:开放定址法、再哈希法、链地址法。HashMap中采用的是 链地址法 。
+解决Hash冲突方法有:开放定址法、再哈希法、链地址法。HashMap中采用的是 链地址法 。
* 开放定址法基本思想就是,如果`p=H(key)`出现冲突时,则以`p`为基础,再次hash,`p1=H(p)`,如果p1再次出现冲突,则以p1为基础,以此类推,直到找到一个不冲突的哈希地址`pi`。 因此开放定址法所需要的hash表的长度要大于等于所需要存放的元素,而且因为存在再次hash,所以`只能在删除的节点上做标记,而不能真正删除节点。`
* 再哈希法提供多个不同的hash函数,当`R1=H1(key1)`发生冲突时,再计算`R2=H2(key1)`,直到没有冲突为止。 这样做虽然不易产生堆集,但增加了计算的时间。
@@ -178,11 +172,11 @@ h^(h>>>16) //第二步 高位参与运算,减少冲突
return h&(length-1); //第三步 取模运算
```
-在JDK1.8的实现中,优化了高位运算的算法,通过hashCode()的高16位异或低16位实现的:这么做可以在数组比较小的时候,也能保证考虑到高低位都参与到Hash的计算中,可以减少冲突,同时不会有太大的开销。
+在JDK1.8的实现中,优化了高位运算的算法,通过`hashCode()`的高16位异或低16位实现的:这么做可以在数组比较小的时候,也能保证考虑到高低位都参与到Hash的计算中,可以减少冲突,同时不会有太大的开销。
### 扩容过程?
-1.8扩容机制:当元素个数大于threshold时,会进行扩容,使用2倍容量的数组代替原有数组。采用尾插入的方式将原数组元素拷贝到新数组。1.8扩容之后链表元素相对位置没有变化,而1.7扩容之后链表元素会倒置。
+1.8扩容机制:当元素个数大于`threshold`时,会进行扩容,使用2倍容量的数组代替原有数组。采用尾插入的方式将原数组元素拷贝到新数组。1.8扩容之后链表元素相对位置没有变化,而1.7扩容之后链表元素会倒置。
1.7链表新节点采用的是头插法,这样在线程一扩容迁移元素时,会将元素顺序改变,导致两个线程中出现元素的相互指向而形成循环链表,1.8采用了尾插法,避免了这种情况的发生。
@@ -197,13 +191,13 @@ return h&(length-1); //第三步 取模运算
5. 链表的数量大于阈值8,就要转换成红黑树的结构
6. 添加成功后会检查是否需要扩容
-
+
### 红黑树的特点?
- 每个节点或者是黑色,或者是红色。
- 根节点是黑色。
-- 每个叶子节点(NIL)是黑色。
+- 每个叶子节点(`NIL`)是黑色。
- 如果一个节点是红色的,则它的子节点必须是黑色的。
- 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
@@ -213,7 +207,7 @@ ConcurrentHashMap 在put的时候会加锁,使用红黑树插入速度更快
### 在解决 hash 冲突的时候,为什么选择先用链表,再转红黑树?
-因为红黑树需要进行左旋,右旋,变色这些操作来保持平衡,而单链表不需要。当元素小于 8 个的时候,链表结构可以保证查询性能。当元素大于 8 个的时候, 红黑树搜索时间复杂度是 O(logn),而链表是 O(n),此时需要红黑树来加快查询速度,但是插入和删除节点的效率变慢了。如果一开始就用红黑树结构,元素太少,插入和删除节点的效率又比较慢,浪费性能。
+因为红黑树需要进行左旋,右旋,变色这些操作来保持平衡,而单链表不需要。当元素小于 8 个的时候,链表结构可以保证查询性能。当元素大于 8 个的时候, 红黑树搜索时间复杂度是 `O(logn)`,而链表是 `O(n)`,此时需要红黑树来加快查询速度,但是插入和删除节点的效率变慢了。如果一开始就用红黑树结构,元素太少,插入和删除节点的效率又比较慢,浪费性能。
### HashMap 的长度为什么是 2 的幂次方?
@@ -238,10 +232,10 @@ Node[] table的初始化长度length为16,默认的loadFactor是0.75,0.75是
### 一般用什么作为HashMap的key?
-一般用Integer、String 这种不可变类当 HashMap 当 key。String类比较常用。
+一般用`Integer`、`String`这种不可变类当 HashMap 当 key。String类比较常用。
-- 因为 String 是不可变的,所以在它创建的时候 hashcode 就被缓存了,不需要重新计算。这就是 HashMap 中的key经常使用字符串的原因。
-- 获取对象的时候要用到 equals() 和 hashCode() 方法,而Integer、String这些类都已经重写了 hashCode() 以及 equals() 方法,不需要自己去重写这两个方法。
+- 因为 String 是不可变的,所以在它创建的时候`hashcode``就被缓存了,不需要重新计算。这就是 HashMap 中的key经常使用字符串的原因。
+- 获取对象的时候要用到 `equals()` 和 `hashCode()` 方法,而Integer、String这些类都已经重写了 `hashCode()` 以及 `equals()` 方法,不需要自己去重写这两个方法。
### HashMap为什么线程不安全?
@@ -263,8 +257,6 @@ HashMap是无序的,迭代HashMap所得到元素的顺序并不是它们最初
LinkedHashMap继承于HashMap,是HashMap和LinkedList的融合体,具备两者的特性。每次put操作都会将entry插入到双向链表的尾部。
-
-
## 讲一下TreeMap?
TreeMap是一个能比较元素大小的Map集合,会对传入的key进行了大小排序。可以使用元素的自然顺序,也可以使用集合中自定义的比较器来进行排序。
@@ -424,12 +416,12 @@ put 操作流程:
#### ConcurrentHashMap 和 Hashtable 的区别?
-1. Hashtable通过使用synchronized修饰方法的方式来实现多线程同步,因此,Hashtable的同步会锁住整个数组。在高并发的情况下,性能会非常差。ConcurrentHashMap采用了更细粒度的锁来提高在并发情况下的效率。注:Synchronized容器(同步容器)也是通过synchronized关键字来实现线程安全,在使用的时候会对所有的数据加锁。
+1. Hashtable通过使用synchronized修饰方法的方式来实现多线程同步,因此,Hashtable的同步会锁住整个数组。在高并发的情况下,性能会非常差。ConcurrentHashMap采用了更细粒度的锁来提高在并发情况下的效率。注:synchronized容器(同步容器)也是通过synchronized关键字来实现线程安全,在使用的时候会对所有的数据加锁。
2. Hashtable默认的大小为11,当达到阈值后,每次按照下面的公式对容量进行扩充:newCapacity = oldCapacity * 2 + 1。ConcurrentHashMap默认大小是16,扩容时容量扩大为原来的2倍。
### CopyOnWrite
-写时复制。当我们往容器添加元素时,不直接往容器添加,而是先将当前容器进行复制,复制出一个新的容器,然后往新的容器添加元素,添加完元素之后,再将原容器的引用指向新容器。这样做的好处就是可以对CopyOnWrite容器进行并发的读而不需要加锁,因为当前容器不会被修改。
+写时复制。当我们往容器添加元素时,不直接往容器添加,而是先将当前容器进行复制,复制出一个新的容器,然后往新的容器添加元素,添加完元素之后,再将原容器的引用指向新容器。这样做的好处就是可以对`CopyOnWrite`容器进行并发的读而不需要加锁,因为当前容器不会被修改。
```java
public boolean add(E e) {
@@ -448,9 +440,9 @@ put 操作流程:
}
```
-从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。
+从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是`CopyOnWriteArrayList`和`CopyOnWriteArraySet`。
-CopyOnWriteArrayList中add方法添加的时候是需要加锁的,保证同步,避免了多线程写的时候复制出多个副本。读的时候不需要加锁,如果读的时候有其他线程正在向CopyOnWriteArrayList添加数据,还是可以读到旧的数据。
+`CopyOnWriteArrayList`中add方法添加的时候是需要加锁的,保证同步,避免了多线程写的时候复制出多个副本。读的时候不需要加锁,如果读的时候有其他线程正在向`CopyOnWriteArrayList`添加数据,还是可以读到旧的数据。
**缺点:**
@@ -459,29 +451,29 @@ CopyOnWriteArrayList中add方法添加的时候是需要加锁的,保证同步
### ConcurrentLinkedQueue
-非阻塞队列。高效的并发队列,使用链表实现。可以看做一个线程安全的 LinkedList,通过 CAS 操作实现。
+非阻塞队列。高效的并发队列,使用链表实现。可以看做一个线程安全的 `LinkedList`,通过 CAS 操作实现。
-如果对队列加锁的成本较高则适合使用无锁的 ConcurrentLinkedQueue 来替代。适合在对性能要求相对较高,同时有多个线程对队列进行读写的场景。
+如果对队列加锁的成本较高则适合使用无锁的 `ConcurrentLinkedQueue` 来替代。适合在对性能要求相对较高,同时有多个线程对队列进行读写的场景。
**非阻塞队列中的几种主要方法:**
-add(E e) : 将元素e插入到队列末尾,如果插入成功,则返回true;如果插入失败(即队列已满),则会抛出异常;
-remove() :移除队首元素,若移除成功,则返回true;如果移除失败(队列为空),则会抛出异常;
-offer(E e) :将元素e插入到队列末尾,如果插入成功,则返回true;如果插入失败(即队列已满),则返回false;
-poll() :移除并获取队首元素,若成功,则返回队首元素;否则返回null;
-peek() :获取队首元素,若成功,则返回队首元素;否则返回null
+`add(E e)`: 将元素e插入到队列末尾,如果插入成功,则返回true;如果插入失败(即队列已满),则会抛出异常;
+`remove()`:移除队首元素,若移除成功,则返回true;如果移除失败(队列为空),则会抛出异常;
+`offer(E e)`:将元素e插入到队列末尾,如果插入成功,则返回true;如果插入失败(即队列已满),则返回false;
+`poll()`:移除并获取队首元素,若成功,则返回队首元素;否则返回null;
+`peek()`:获取队首元素,若成功,则返回队首元素;否则返回null
对于非阻塞队列,一般情况下建议使用offer、poll和peek三个方法,不建议使用add和remove方法。因为使用offer、poll和peek三个方法可以通过返回值判断操作成功与否,而使用add和remove方法却不能达到这样的效果。
### 阻塞队列
-阻塞队列是java.util.concurrent包下重要的数据结构,BlockingQueue提供了线程安全的队列访问方式:当阻塞队列进行插入数据时,如果队列已满,线程将会阻塞等待直到队列非满;从阻塞队列取数据时,如果队列已空,线程将会阻塞等待直到队列非空。并发包下很多高级同步类的实现都是基于BlockingQueue实现的。BlockingQueue 适合用于作为数据共享的通道。
+阻塞队列是`java.util.concurrent`包下重要的数据结构,`BlockingQueue`提供了线程安全的队列访问方式:当阻塞队列进行插入数据时,如果队列已满,线程将会阻塞等待直到队列非满;从阻塞队列取数据时,如果队列已空,线程将会阻塞等待直到队列非空。并发包下很多高级同步类的实现都是基于`BlockingQueue`实现的。`BlockingQueue` 适合用于作为数据共享的通道。
使用阻塞算法的队列可以用一个锁(入队和出队用同一把锁)或两个锁(入队和出队用不同的锁)等方式来实现。
阻塞队列和一般的队列的区别就在于:
1. 多线程支持,多个线程可以安全的访问队列
-2. 阻塞操作,当队列为空的时候,消费线程会阻塞等待队列不为空;当队列满了的时候,生产线程就会阻塞直到队列不满。
+2. 阻塞操作,当队列为空的时候,消费线程会阻塞等待队列不为空;当队列满了的时候,生产线程就会阻塞直到队列不满
**方法**
@@ -497,7 +489,7 @@ JDK 7 提供了7个阻塞队列,如下
1、**ArrayBlockingQueue**
-有界阻塞队列,底层采用数组实现。ArrayBlockingQueue 一旦创建,容量不能改变。其并发控制采用可重入锁来控制,不管是插入操作还是读取操作,都需要获取到锁才能进行操作。此队列按照先进先出(FIFO)的原则对元素进行排序。默认情况下不能保证线程访问队列的公平性,参数`fair`可用于设置线程是否公平访问队列。为了保证公平性,通常会降低吞吐量。
+有界阻塞队列,底层采用数组实现。`ArrayBlockingQueue` 一旦创建,容量不能改变。其并发控制采用可重入锁来控制,不管是插入操作还是读取操作,都需要获取到锁才能进行操作。此队列按照先进先出(FIFO)的原则对元素进行排序。默认情况下不能保证线程访问队列的公平性,参数`fair`可用于设置线程是否公平访问队列。为了保证公平性,通常会降低吞吐量。
```java
private static ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue(10,true);//fair
@@ -505,29 +497,29 @@ private static ArrayBlockingQueue blockingQueue = new ArrayBlockingQueu
2、**LinkedBlockingQueue**
-LinkedBlockingQueue是一个用单向链表实现的有界阻塞队列,可以当做无界队列也可以当做有界队列来使用。通常在创建 LinkedBlockingQueue 对象时,会指定队列最大的容量。此队列的默认和最大长度为`Integer.MAX_VALUE`。此队列按照先进先出的原则对元素进行排序。与 ArrayBlockingQueue 相比起来具有更高的吞吐量。
+`LinkedBlockingQueue`是一个用单向链表实现的有界阻塞队列,可以当做无界队列也可以当做有界队列来使用。通常在创建 `LinkedBlockingQueue` 对象时,会指定队列最大的容量。此队列的默认和最大长度为`Integer.MAX_VALUE`。此队列按照先进先出的原则对元素进行排序。与 `ArrayBlockingQueue` 相比起来具有更高的吞吐量。
3、**PriorityBlockingQueue**
-支持优先级的**无界**阻塞队列。默认情况下元素采取自然顺序升序排列。也可以自定义类实现`compareTo()`方法来指定元素排序规则,或者初始化PriorityBlockingQueue时,指定构造参数`Comparator`来进行排序。
+支持优先级的**无界**阻塞队列。默认情况下元素采取自然顺序升序排列。也可以自定义类实现`compareTo()`方法来指定元素排序规则,或者初始化`PriorityBlockingQueue`时,指定构造参数`Comparator`来进行排序。
-PriorityBlockingQueue 只能指定初始的队列大小,后面插入元素的时候,如果空间不够的话会**自动扩容**。
+`PriorityBlockingQueue` 只能指定初始的队列大小,后面插入元素的时候,如果空间不够的话会**自动扩容**。
-PriorityQueue 的线程安全版本。不可以插入 null 值,同时,插入队列的对象必须是可比较大小的(comparable),否则报 ClassCastException 异常。它的插入操作 put 方法不会 block,因为它是无界队列(take 方法在队列为空的时候会阻塞)。
+`PriorityQueue` 的线程安全版本。不可以插入 null 值,同时,插入队列的对象必须是可比较大小的(comparable),否则报 ClassCastException 异常。它的插入操作 put 方法不会 block,因为它是无界队列(take 方法在队列为空的时候会阻塞)。
4、**DelayQueue**
-支持延时获取元素的无界阻塞队列。队列使用PriorityBlockingQueue来实现。队列中的元素必须实现Delayed接口,在创建元素时可以指定多久才能从队列中获取当前元素。只有在延迟期满时才能从队列中提取元素。
+支持延时获取元素的无界阻塞队列。队列使用`PriorityBlockingQueue`来实现。队列中的元素必须实现Delayed接口,在创建元素时可以指定多久才能从队列中获取当前元素。只有在延迟期满时才能从队列中提取元素。
5、**SynchronousQueue**
不存储元素的阻塞队列,每一个put必须等待一个take操作,否则不能继续添加元素。支持公平访问队列。
-SynchronousQueue可以看成是一个传球手,负责把生产者线程处理的数据直接传递给消费者线程。队列本身不存储任何元素,非常适合传递性场景。SynchronousQueue的吞吐量高于LinkedBlockingQueue和ArrayBlockingQueue。
+`SynchronousQueue`可以看成是一个传球手,负责把生产者线程处理的数据直接传递给消费者线程。队列本身不存储任何元素,非常适合传递性场景。`SynchronousQueue`的吞吐量高于`LinkedBlockingQueue`和`ArrayBlockingQueue`。
6、**LinkedTransferQueue**
-由链表结构组成的无界阻塞TransferQueue队列。相对于其他阻塞队列,多了tryTransfer和transfer方法。
+由链表结构组成的无界阻塞TransferQueue队列。相对于其他阻塞队列,多了`tryTransfer`和`transfer`方法。
transfer方法:如果当前有消费者正在等待接收元素(take或者待时间限制的poll方法),transfer可以把生产者传入的元素立刻传给消费者。如果没有消费者等待接收元素,则将元素放在队列的tail节点,并等到该元素被消费者消费了才返回。
From da6295e00b59696c99cc810493be2b2df482570e Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 27 Feb 2022 18:02:58 +0800
Subject: [PATCH 014/112] =?UTF-8?q?=E6=95=B4=E7=90=86JVM=E9=9D=A2=E8=AF=95?=
=?UTF-8?q?=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...21\351\235\242\350\257\225\351\242\230.md" | 123 ++++++++----------
1 file changed, 54 insertions(+), 69 deletions(-)
diff --git "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 28e75d2..3ddf03d 100644
--- "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -23,6 +23,7 @@
- [可作为GC Roots的对象有哪些?](#%E5%8F%AF%E4%BD%9C%E4%B8%BAgc-roots%E7%9A%84%E5%AF%B9%E8%B1%A1%E6%9C%89%E5%93%AA%E4%BA%9B)
- [什么情况下类会被卸载?](#%E4%BB%80%E4%B9%88%E6%83%85%E5%86%B5%E4%B8%8B%E7%B1%BB%E4%BC%9A%E8%A2%AB%E5%8D%B8%E8%BD%BD)
- [强引用、软引用、弱引用、虚引用是什么,有什么区别?](#%E5%BC%BA%E5%BC%95%E7%94%A8%E8%BD%AF%E5%BC%95%E7%94%A8%E5%BC%B1%E5%BC%95%E7%94%A8%E8%99%9A%E5%BC%95%E7%94%A8%E6%98%AF%E4%BB%80%E4%B9%88%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB)
+- [GC是什么?为什么要GC?](#gc%E6%98%AF%E4%BB%80%E4%B9%88%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81gc)
- [Minor GC 和 Full GC的区别?](#minor-gc-%E5%92%8C-full-gc%E7%9A%84%E5%8C%BA%E5%88%AB)
- [内存的分配策略?](#%E5%86%85%E5%AD%98%E7%9A%84%E5%88%86%E9%85%8D%E7%AD%96%E7%95%A5)
- [Full GC 的触发条件?](#full-gc-%E7%9A%84%E8%A7%A6%E5%8F%91%E6%9D%A1%E4%BB%B6)
@@ -30,8 +31,9 @@
- [有哪些垃圾回收器?](#%E6%9C%89%E5%93%AA%E4%BA%9B%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8)
- [常用的 JVM 调优的命令都有哪些?](#%E5%B8%B8%E7%94%A8%E7%9A%84-jvm-%E8%B0%83%E4%BC%98%E7%9A%84%E5%91%BD%E4%BB%A4%E9%83%BD%E6%9C%89%E5%93%AA%E4%BA%9B)
- [对象头了解吗?](#%E5%AF%B9%E8%B1%A1%E5%A4%B4%E4%BA%86%E8%A7%A3%E5%90%97)
+- [main方法执行过程](#main%E6%96%B9%E6%B3%95%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B)
+- [对象创建过程](#%E5%AF%B9%E8%B1%A1%E5%88%9B%E5%BB%BA%E8%BF%87%E7%A8%8B)
- [如何排查 OOM 的问题?](#%E5%A6%82%E4%BD%95%E6%8E%92%E6%9F%A5-oom-%E7%9A%84%E9%97%AE%E9%A2%98)
-- [GC是什么?为什么要GC?](#gc%E6%98%AF%E4%BB%80%E4%B9%88%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81gc)
@@ -115,19 +117,11 @@ JDK 1.8 的时候,`HotSpot`的永久代被彻底移除了,使用元空间替
运行时常量池是方法区的一部分,在类加载之后,会将编译器生成的各种字面量和符号引号放到运行时常量池。在运行期间动态生成的常量,如 String 类的 intern()方法,也会被放入运行时常量池。
-
-
-
-
-
-
-> 图片来源:https://blog.csdn.net/soonfly
-
### 直接内存
-直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。而且也可能导致 OutOfMemoryError 错误出现。
+直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。而且也可能导致 `OutOfMemoryError` 错误出现。
-NIO的Buffer提供了DirectBuffer,可以直接访问系统物理内存,避免堆内内存到堆外内存的数据拷贝操作,提高效率。DirectBuffer直接分配在物理内存中,并不占用堆空间,其可申请的最大内存受操作系统限制,不受最大堆内存的限制。
+NIO的Buffer提供了`DirectBuffer`,可以直接访问系统物理内存,避免堆内内存到堆外内存的数据拷贝操作,提高效率。`DirectBuffer`直接分配在物理内存中,并不占用堆空间,其可申请的最大内存受操作系统限制,不受最大堆内存的限制。
直接内存的读写操作比堆内存快,可以提升程序I/O操作的性能。通常在I/O通信过程中,会存在堆内内存到堆外内存的数据拷贝操作,对于需要频繁进行内存间数据拷贝且生命周期较短的暂存数据,都建议存储到直接内存。
@@ -357,6 +351,10 @@ WeakReference weakRef = new WeakReference(str);
**虚引用**:虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。**虚引用主要是为了能在对象被收集器回收时收到一个系统通知**。
+## GC是什么?为什么要GC?
+
+GC,`Gabage Collection`,即垃圾收集。内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序的不稳定甚至崩溃,Java 提供的 GC 功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的。
+
## Minor GC 和 Full GC的区别?
- **Minor GC**:回收新生代,因为新生代对象存活时间很短,因此 `Minor GC`会频繁执行,执行的速度一般也会比较快。
@@ -395,7 +393,7 @@ WeakReference weakRef = new WeakReference(str);
**老年代空间不足**
-老年代空间不足的常见场景为前文所讲的大对象直接进入老年代、长期存活的对象进入老年代等。为了避免以上原因引起的 Full GC,应当尽量不要创建过大的对象以及数组。除此之外,可以通过 -Xmn 参数调大新生代的大小,让对象尽量在新生代被回收掉,不进入老年代。还可以通过 -XX:MaxTenuringThreshold 调大对象进入老年代的年龄,让对象在新生代多存活一段时间。
+老年代空间不足的常见场景为前文所讲的大对象直接进入老年代、长期存活的对象进入老年代等。为了避免以上原因引起的 Full GC,应当尽量不要创建过大的对象以及数组。除此之外,可以通过 `-Xmn` 参数调大新生代的大小,让对象尽量在新生代被回收掉,不进入老年代。还可以通过 `-XX:MaxTenuringThreshold` 调大对象进入老年代的年龄,让对象在新生代多存活一段时间。
**空间分配担保失败**
@@ -403,7 +401,7 @@ WeakReference weakRef = new WeakReference(str);
**JDK 1.7 及以前的永久代空间不足**
-在 JDK 1.7 及以前,HotSpot 虚拟机中的方法区是用永久代实现的,永久代中存放的为一些 Class 的信息、常量、静态变量等数据。当系统中要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,在未配置为采用 CMS GC 的情况下也会执行 Full GC。如果经过 Full GC 仍然回收不了,那么虚拟机会抛出 java.lang.OutOfMemoryError。
+在 JDK 1.7 及以前,HotSpot 虚拟机中的方法区是用永久代实现的,永久代中存放的为一些 Class 的信息、常量、静态变量等数据。当系统中要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,在未配置为采用 CMS GC 的情况下也会执行 Full GC。如果经过 Full GC 仍然回收不了,那么虚拟机会抛出 `java.lang.OutOfMemoryError`。
@@ -690,49 +688,49 @@ Java 内存中的对象由以下三部分组成:**对象头**、**实例数据
**内存对齐的主要作用是:**
1. 平台原因:不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
-2. 性能原因:经过内存对齐后,CPU的内存访问速度大大提升。
-
-1. ## main方法执行过程
-
- 以下是示例代码:
-
- ```java
- public class Application {
- public static void main(String[] args) {
- Person p = new Person("大彬");
- p.getName();
- }
- }
-
- class Person {
- public String name;
-
- public Person(String name) {
- this.name = name;
- }
-
- public String getName() {
- return this.name;
- }
- }
- ```
-
- 执行`main`方法的过程如下:
-
- 1. 编译`Application.java`后得到 `Application.class` 后,执行这个`class`文件,系统会启动一个 `JVM` 进程,从类路径中找到一个名为 `Application.class` 的二进制文件,将 `Application` 类信息加载到运行时数据区的方法区内,这个过程叫做类的加载。
- 2. JVM 找到 `Application` 的主程序入口,执行`main`方法。
- 3. `main`方法的第一条语句为 `Person p = new Person("大彬") `,就是让 JVM 创建一个`Person`对象,但是这个时候方法区中是没有 `Person` 类的信息的,所以 JVM 马上加载 `Person` 类,把 `Person` 类的信息放到方法区中。
- 4. 加载完 `Person` 类后,JVM 在堆中分配内存给 `Person` 对象,然后调用构造函数初始化 `Person` 对象,这个 `Person` 对象持有**指向方法区中的 Person 类的类型信息**的引用。
- 5. 执行`p.getName()`时,JVM 根据 p 的引用找到 p 所指向的对象,然后根据此对象持有的引用定位到方法区中 `Person` 类的类型信息的方法表,获得 `getName()` 的字节码地址。
- 6. 执行`getName()`方法。
-
-1. ## 对象创建过程
-
- 1. **类加载检查**:当虚拟机遇到一条 `new` 指令时,首先检查是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那先执行类加载。
- 2. **分配内存**:在类加载检查通过后,接下来虚拟机将为对象实例分配内存。
- 3. **初始化**。分配到的内存空间都初始化为零值,通过这个操作保证了对象的字段可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。
- 4. **设置对象头**。`Hotspot` 虚拟机的对象头包括:存储对象自身的运行时数据(哈希码、分代年龄、锁标志等等)、类型指针和数据长度(数组对象才有),类型指针就是对象指向它的类信息的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
- 5. **按照`Java`代码进行初始化**。
+2. 性能原因:经过内存对齐后,CPU的内存访问速度大大提升。
+
+## main方法执行过程
+
+以下是示例代码:
+
+```java
+public class Application {
+ public static void main(String[] args) {
+ Person p = new Person("大彬");
+ p.getName();
+ }
+}
+
+class Person {
+ public String name;
+
+ public Person(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+}
+```
+
+执行`main`方法的过程如下:
+
+1. 编译`Application.java`后得到 `Application.class` 后,执行这个`class`文件,系统会启动一个 `JVM` 进程,从类路径中找到一个名为 `Application.class` 的二进制文件,将 `Application` 类信息加载到运行时数据区的方法区内,这个过程叫做类的加载。
+2. JVM 找到 `Application` 的主程序入口,执行`main`方法。
+3. `main`方法的第一条语句为 `Person p = new Person("大彬") `,就是让 JVM 创建一个`Person`对象,但是这个时候方法区中是没有 `Person` 类的信息的,所以 JVM 马上加载 `Person` 类,把 `Person` 类的信息放到方法区中。
+4. 加载完 `Person` 类后,JVM 在堆中分配内存给 `Person` 对象,然后调用构造函数初始化 `Person` 对象,这个 `Person` 对象持有**指向方法区中的 Person 类的类型信息**的引用。
+5. 执行`p.getName()`时,JVM 根据 p 的引用找到 p 所指向的对象,然后根据此对象持有的引用定位到方法区中 `Person` 类的类型信息的方法表,获得 `getName()` 的字节码地址。
+6. 执行`getName()`方法。
+
+## 对象创建过程
+
+1. **类加载检查**:当虚拟机遇到一条 `new` 指令时,首先检查是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那先执行类加载。
+2. **分配内存**:在类加载检查通过后,接下来虚拟机将为对象实例分配内存。
+3. **初始化**。分配到的内存空间都初始化为零值,通过这个操作保证了对象的字段可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。
+4. **设置对象头**。`Hotspot` 虚拟机的对象头包括:存储对象自身的运行时数据(哈希码、分代年龄、锁标志等等)、类型指针和数据长度(数组对象才有),类型指针就是对象指向它的类信息的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
+5. **按照`Java`代码进行初始化**。
## 如何排查 OOM 的问题?
@@ -742,22 +740,9 @@ Java 内存中的对象由以下三部分组成:**对象头**、**实例数据
- jstat 查看监控 JVM 的内存和 GC 情况,评估问题大概出在什么区域;
- 使用 MAT 工具载入 dump 文件,分析大对象的占用情况 。
-## GC是什么?为什么要GC?
-
-GC 是垃圾收集的意思(Gabage Collection)。内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序的不稳定甚至崩溃,Java 提供的 GC 功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的。
-
**参考资料**
- 周志明. 深入理解 Java 虚拟机 [M]. 机械工业出版社
-
-
-> 本文已经收录到github仓库,此仓库用于分享Java相关知识总结,包括Java基础、MySQL、Spring Boot、MyBatis、Redis、RabbitMQ、计算机网络、数据结构与算法等等,欢迎大家提pr和star!
->
-> github地址:https://github.com/Tyson0314/Java-learning
->
-> 如果github访问不了,可以访问gitee仓库。
->
-> gitee地址:https://gitee.com/tysondai/Java-learning
From 456157e0964f53ce5147ea3c69e29a445a689a7c Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 27 Feb 2022 18:07:38 +0800
Subject: [PATCH 015/112] =?UTF-8?q?=E6=95=B4=E7=90=86Spring=E9=9D=A2?=
=?UTF-8?q?=E8=AF=95=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...ng\351\235\242\350\257\225\351\242\230.md" | 148 ++++++------------
1 file changed, 46 insertions(+), 102 deletions(-)
diff --git "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md" "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
index 56c51c8..5ede718 100644
--- "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
@@ -41,7 +41,7 @@
## Spring 用到了哪些设计模式?
-1、简单工厂模式:`BeanFactory`就是简单工厂模式的体现,根据传入一个唯一标识来获得 Bean 对象。
+1、**简单工厂模式**:`BeanFactory`就是简单工厂模式的体现,根据传入一个唯一标识来获得 Bean 对象。
```java
@Override
@@ -51,11 +51,11 @@ public Object getBean(String name) throws BeansException {
}
```
-2、工厂方法模式:`FactoryBean`就是典型的工厂方法模式。spring在使用`getBean()`调用获得该bean时,会自动调用该bean的`getObject()`方法。每个 Bean 都会对应一个 `FactoryBean`,如 `SqlSessionFactory` 对应 `SqlSessionFactoryBean`。
+2、**工厂方法模式**:`FactoryBean`就是典型的工厂方法模式。spring在使用`getBean()`调用获得该bean时,会自动调用该bean的`getObject()`方法。每个 Bean 都会对应一个 `FactoryBean`,如 `SqlSessionFactory` 对应 `SqlSessionFactoryBean`。
-3、单例模式:一个类仅有一个实例,提供一个访问它的全局访问点。Spring 创建 Bean 实例默认是单例的。
+3、**单例模式**:一个类仅有一个实例,提供一个访问它的全局访问点。Spring 创建 Bean 实例默认是单例的。
-4、适配器模式:SpringMVC中的适配器`HandlerAdatper`。由于应用会有多个Controller实现,如果需要直接调用Controller方法,那么需要先判断是由哪一个Controller处理请求,然后调用相应的方法。当增加新的 Controller,需要修改原来的逻辑,违反了开闭原则(对修改关闭,对扩展开放)。
+4、**适配器模式**:SpringMVC中的适配器`HandlerAdatper`。由于应用会有多个Controller实现,如果需要直接调用Controller方法,那么需要先判断是由哪一个Controller处理请求,然后调用相应的方法。当增加新的 Controller,需要修改原来的逻辑,违反了开闭原则(对修改关闭,对扩展开放)。
为此,Spring提供了一个适配器接口,每一种 Controller 对应一种 `HandlerAdapter` 实现类,当请求过来,SpringMVC会调用`getHandler()`获取相应的Controller,然后获取该Controller对应的 `HandlerAdapter`,最后调用`HandlerAdapter`的`handle()`方法处理请求,实际上调用的是Controller的`handleRequest()`。每次添加新的 Controller 时,只需要增加一个适配器类就可以,无需修改原有的逻辑。
@@ -88,15 +88,15 @@ public class HttpRequestHandlerAdapter implements HandlerAdapter {
}
```
-5、代理模式:spring 的 aop 使用了动态代理,有两种方式`JdkDynamicAopProxy`和`Cglib2AopProxy`。
+5、**代理模式**:spring 的 aop 使用了动态代理,有两种方式`JdkDynamicAopProxy`和`Cglib2AopProxy`。
-6、观察者模式:spring 中 observer 模式常用的地方是 listener 的实现,如`ApplicationListener`。
+6、**观察者模式**:spring 中 observer 模式常用的地方是 listener 的实现,如`ApplicationListener`。
-7、模板模式: Spring 中 `jdbcTemplate`、`hibernateTemplate` 等,就使用到了模板模式。
+7、**模板模式**: Spring 中 `jdbcTemplate`、`hibernateTemplate` 等,就使用到了模板模式。
## 什么是AOP?
-面向切面编程,作为面向对象的一种补充,将公共逻辑(事务管理、日志、缓存等)封装成切面,跟业务代码进行分离,可以减少系统的重复代码和降低模块之间的耦合度。切面就是那些与业务无关,但所有业务模块都会调用的公共逻辑。
+**面向切面**编程,作为面向对象的一种补充,将公共逻辑(事务管理、日志、缓存等)封装成切面,跟业务代码进行分离,可以减少系统的重复代码和降低模块之间的耦合度。切面就是那些与业务无关,但所有业务模块都会调用的公共逻辑。
## AOP有哪些实现方式?
@@ -120,11 +120,11 @@ Spring AOP中的动态代理主要有两种方式:JDK动态代理和CGLIB动
如果目标类实现了接口,Spring AOP会选择使用JDK动态代理目标类。代理类根据目标类实现的接口动态生成,不需要自己编写,生成的动态代理类和目标类都实现相同的接口。JDK动态代理的核心是`InvocationHandler`接口和`Proxy`类。
-缺点:目标类必须有实现的接口。如果某个类没有实现接口,那么这个类就不能用JDK动态代理。
+缺点:**目标类必须有实现的接口**。如果某个类没有实现接口,那么这个类就不能用JDK动态代理。
**CGLIB来动态代理**
-通过继承实现。如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library)可以在运行时动态生成类的字节码,动态创建目标类的子类对象,在子类对象中增强目标类。
+**通过继承实现**。如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library)可以在运行时动态生成类的字节码,动态创建目标类的子类对象,在子类对象中增强目标类。
CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为`final`,那么它是无法使用CGLIB做动态代理的。
@@ -138,24 +138,24 @@ CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记
**两者的区别**:
-1. jdk动态代理使用jdk中的类Proxy来创建代理对象,它使用反射技术来实现,不需要导入其他依赖。cglib需要引入相关依赖:asm.jar,它使用字节码增强技术来实现。
+1. jdk动态代理使用jdk中的类Proxy来创建代理对象,它使用反射技术来实现,不需要导入其他依赖。cglib需要引入相关依赖:`asm.jar`,它使用字节码增强技术来实现。
2. 当目标类实现了接口的时候Spring Aop默认使用jdk动态代理方式来增强方法,没有实现接口的时候使用cglib动态代理方式增强方法。
## Spring AOP相关术语
-(1)切面(Aspect):切面是通知和切点的结合。通知和切点共同定义了切面的全部内容。
+(1)**切面**(Aspect):切面是通知和切点的结合。通知和切点共同定义了切面的全部内容。
-(2)连接点(Join point):指方法,在Spring AOP中,一个连接点总是代表一个方法的执行。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
+(2)**连接点**(Join point):指方法,在Spring AOP中,一个连接点总是代表一个方法的执行。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
-(3)通知(Advice):在AOP术语中,切面的工作被称为通知。
+(3)**通知**(Advice):在AOP术语中,切面的工作被称为通知。
-(4)切入点(Pointcut):切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。
+(4)**切入点**(Pointcut):切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。
-(5)引入(Introduction):引入允许我们向现有类添加新方法或属性。
+(5)**引入**(Introduction):引入允许我们向现有类添加新方法或属性。
-(6)目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。它通常是一个代理对象。
+(6)**目标对象**(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。它通常是一个代理对象。
-(7)织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。在目标对象的生命周期里有以下时间点可以进行织入:
+(7)**织入**(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。在目标对象的生命周期里有以下时间点可以进行织入:
- 编译期:切面在目标类编译时被织入。AspectJ的织入编译器是以这种方式织入切面的。
- 类加载期:切面在目标类加载到JVM时被织入。需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入就支持以这种方式织入切面。
@@ -167,15 +167,15 @@ CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记
Spring切面可以应用5种类型的通知:
-1. 前置通知(Before):在目标方法被调用之前调用通知功能;
-2. 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
-3. 返回通知(After-returning ):在目标方法成功执行之后调用通知;
-4. 异常通知(After-throwing):在目标方法抛出异常后调用通知;
-5. 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的逻辑。
+1. **前置通知**(Before):在目标方法被调用之前调用通知功能;
+2. **后置通知**(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
+3. **返回通知**(After-returning ):在目标方法成功执行之后调用通知;
+4. **异常通知**(After-throwing):在目标方法抛出异常后调用通知;
+5. **环绕通知**(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的逻辑。
## 什么是IOC?
-IOC:控制反转,由Spring容器管理bean的整个生命周期。通过反射实现对其他对象的控制,包括初始化、创建、销毁等,解放手动创建对象的过程,同时降低类之间的耦合度。
+IOC:**控制反转**,由Spring容器管理bean的整个生命周期。通过反射实现对其他对象的控制,包括初始化、创建、销毁等,解放手动创建对象的过程,同时降低类之间的耦合度。
IOC的好处:降低了类之间的耦合,对象创建和初始化交给Spring容器管理,在需要的时候只需向容器进行申请。
@@ -191,8 +191,6 @@ IOC的好处:降低了类之间的耦合,对象创建和初始化交给Sprin
## IOC容器初始化过程?
-ioc 容器初始化过程:BeanDefinition 的资源定位、解析和注册。
-
1. 从XML中读取配置文件。
2. 将bean标签解析成 BeanDefinition,如解析 property 元素, 并注入到 BeanDefinition 实例中。
3. 将 BeanDefinition 注册到容器 BeanDefinitionMap 中。
@@ -212,7 +210,7 @@ finishBeanFactoryInitialization(beanFactory);
## Bean的生命周期
-
+
1.调用bean的构造方法创建Bean
@@ -240,14 +238,11 @@ public interface BeanPostProcessor {
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
-
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
-
}
-
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
@@ -255,27 +250,25 @@ public interface InitializingBean {
## BeanFactory和FactoryBean的区别?
-BeanFactory:管理Bean的容器,Spring中生成的Bean都是由这个接口的实现来管理的。
+**BeanFactory**:管理Bean的容器,Spring中生成的Bean都是由这个接口的实现来管理的。
-FactoryBean:通常是用来创建比较复杂的bean,一般的bean 直接用xml配置即可,但如果一个bean的创建过程中涉及到很多其他的bean 和复杂的逻辑,直接用xml配置比较麻烦,这时可以考虑用FactoryBean,可以隐藏实例化复杂Bean的细节。
+**FactoryBean**:通常是用来创建比较复杂的bean,一般的bean 直接用xml配置即可,但如果一个bean的创建过程中涉及到很多其他的bean 和复杂的逻辑,直接用xml配置比较麻烦,这时可以考虑用FactoryBean,可以隐藏实例化复杂Bean的细节。
当配置文件中bean标签的class属性配置的实现类是FactoryBean时,通过 getBean()方法返回的不是FactoryBean本身,而是调用FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。如果想得到FactoryBean必须使用 '&' + beanName 的方式获取。
-Mybatis 提供了 SqlSessionFactoryBean,可以简化 SqlSessionFactory 的配置:
+Mybatis 提供了 `SqlSessionFactoryBean`,可以简化 `SqlSessionFactory`的配置:
```java
public class SqlSessionFactoryBean implements FactoryBean, InitializingBean, ApplicationListener {
-
@Override
public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
"Property 'configuration' and 'configLocation' can not specified with together");
-
this.sqlSessionFactory = buildSqlSessionFactory();
}
-
+
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
//复杂逻辑
}
@@ -285,7 +278,6 @@ public class SqlSessionFactoryBean implements FactoryBean, In
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
-
return this.sqlSessionFactory;
}
}
@@ -320,9 +312,7 @@ Spring 将会在应用启动时创建 `SqlSessionFactory`,并使用 `sqlSessio
/*把用到的资源导入到当前容器中*/
@Import({Dog.class, Cat.class})
public class App {
-
public static void main(String[] args) throws Exception {
-
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
System.out.println(context.getBean(Dog.class));
System.out.println(context.getBean(Cat.class));
@@ -335,25 +325,25 @@ public class App {
## Bean的作用域
-1、singleton:单例,Spring中的bean默认都是单例的。
+1、**singleton**:单例,Spring中的bean默认都是单例的。
-2、prototype:每次请求都会创建一个新的bean实例。
+2、**prototype**:每次请求都会创建一个新的bean实例。
-3、request:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。
+3、**request**:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。
-4、session:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP session内有效。
+4、**session**:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP session内有效。
-5、global-session:全局session作用域。
+5、**global-session**:全局session作用域。
## Spring自动装配的方式有哪些?
-Spring的自动装配有三种模式:byType(根据类型),byName(根据名称)、constructor(根据构造函数)。
+Spring的自动装配有三种模式:**byType**(根据类型),**byName**(根据名称)、**constructor**(根据构造函数)。
**byType**
找到与依赖类型相同的bean注入到另外的bean中,这个过程需要借助setter注入来完成,因此必须存在set方法,否则注入失败。
-当xml文件中存在多个相同类型名称不同的实例Bean时,Spring容器依赖注入仍然会失败,因为存在多种适合的选项,Spring容器无法知道该注入那种,此时我们需要为Spring容器提供帮助,指定注入那个Bean实例。可以通过<bean>标签的autowire-candidate设置为false来过滤那些不需要注入的实例Bean
+当xml文件中存在多个相同类型名称不同的实例Bean时,Spring容器依赖注入仍然会失败,因为存在多种适合的选项,Spring容器无法知道该注入那种,此时我们需要为Spring容器提供帮助,指定注入那个Bean实例。可以通过`<bean>`标签的autowire-candidate设置为false来过滤那些不需要注入的实例Bean
```xml
@@ -379,18 +369,11 @@ Spring的自动装配有三种模式:byType(根据类型),byName(根据名
**constructor**
-存在单个实例则优先按类型进行参数匹配(无论名称是否匹配),当存在多个类型相同实例时,按名称优先匹配,如果没有找到对应名称,则注入失败,此时可以使用autowire-candidate=”false” 过滤来解决。
-
-```xml
-
-
-
-
+存在单个实例则优先按类型进行参数匹配(无论名称是否匹配),当存在多个类型相同实例时,按名称优先匹配,如果没有找到对应名称,则注入失败。
-
-```
+## @Autowired和@Resource的区别?
-@Autowired 可以传递了一个required=false的属性,false指明当userDao实例存在就注入不存就忽略,如果为true,就必须注入,若userDao实例不存在,就抛出异常。由于默认情况下@Autowired是按类型匹配的(byType),如果需要按名称(byName)匹配的话,可以使用@Qualifier注解与@Autowired结合。
+默认情况下@Autowired是按类型匹配的(byType)。如果需要按名称(byName)匹配的话,可以使用@Qualifier注解与@Autowired结合。@Autowired 可以传递一个`required=false`的属性,false指明当userDao实例存在就注入不存就忽略,如果为true,就必须注入,若userDao实例不存在,就抛出异常。
```java
public class UserServiceImpl implements UserService {
@@ -401,24 +384,14 @@ public class UserServiceImpl implements UserService {
}
```
-byName模式 xml 配置:
-
-```xml
-
-
-
-
-
-```
-
@Resource,默认按 byName模式自动注入。@Resource有两个中重要的属性:name和type。Spring容器对于@Resource注解的name属性解析为bean的名字,type属性则解析为bean的类型。因此使用name属性,则按byName模式的自动注入策略,如果使用type属性则按 byType模式自动注入策略。倘若既不指定name也不指定type属性,Spring容器将通过反射技术默认按byName模式注入。
```java
-@Resource(name=“userDao”)
+@Resource(name="userDao")
private UserDao userDao;//用于成员变量
//也可以用于set方法标注
-@Resource(name=“userDao”)
+@Resource(name="userDao")
public void setUserDao(UserDao userDao) {
this.userDao= userDao;
}
@@ -449,36 +422,9 @@ public class UserServiceImpl implements UserService {
}
```
-xml配置文件:
-
-```xml
-
-
-
-
-
-
-
-
-
-
-
- classpath:/conf/jdbc.properties
-
-
-
-```
-
-
-
-## @Autowired和@Resource的区别?
-
-@Autowired注解是按照类型(byType)装配依赖对象的,但是存在多个类型⼀致的bean,⽆法通过byType注⼊时,就会再使⽤byName来注⼊,如果还是⽆法判断注⼊哪个bean则会UnsatisfiedDependencyException。
-@Resource会⾸先按照byName来装配,如果找不到bean,会⾃动byType再找⼀次。
-
## @Qualifier 注解有什么作用
-当需要创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时,可以使用@Qualifier 注解和 @Autowired 通过指定应该装配哪个 bean 来消除歧义。
+当需要创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时,可以使用`@Qualifier` 注解和 `@Autowired` 通过指定应该装配哪个 bean 来消除歧义。
## @Bean和@Component有什么区别?
@@ -523,8 +469,8 @@ public class WebSocketConfig {
事务就是一系列的操作原子执行。Spring事务机制主要包括声明式事务和编程式事务。
-- 编程式事务:通过编程的方式管理事务,这种方式带来了很大的灵活性,但很难维护。
-- 声明式事务:将事务管理代码从业务方法中分离出来,通过aop进行封装。Spring声明式事务使得我们无需要去处理获得连接、关闭连接、事务提交和回滚等这些操作。使用 `@Transactional` 注解开启声明式事务。
+- **编程式事务**:通过编程的方式管理事务,这种方式带来了很大的灵活性,但很难维护。
+- **声明式事务**:将事务管理代码从业务方法中分离出来,通过aop进行封装。Spring声明式事务使得我们无需要去处理获得连接、关闭连接、事务提交和回滚等这些操作。使用 `@Transactional` 注解开启声明式事务。
`@Transactional`相关属性如下:
@@ -628,12 +574,10 @@ protected void addSingletonFactory(String beanName, ObjectFactory> singletonFa
当多个用户同时请求一个服务时,容器会给每一个请求分配一个线程,这时多个线程会并发执行该请求对应的业务逻辑,如果业务逻辑有对单例状态的修改(体现为此单例的成员属性),则必须考虑线程安全问题。
-若每个线程中对全局变量、静态变量只有读操作,而无写操作,那么不会有线程安全问题;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。
-
**无状态bean和有状态bean**
- 有实例变量的bean,可以保存数据,是非线程安全的。
-- 没有实例变量的对象。不能保存数据,是线程安全的。
+- 没有实例变量的bean,不能保存数据,是线程安全的。
-在Spring中无状态的Bean适合用单例模式,这样可以共享实例提高性能。有状态的Bean在多线程环境下不安全,一般用Prototype模式或者使用ThreadLocal解决线程安全问题。
+在Spring中无状态的Bean适合用单例模式,这样可以共享实例提高性能。有状态的Bean在多线程环境下不安全,一般用`Prototype`模式或者使用`ThreadLocal`解决线程安全问题。
From 472280dcafdf69bac915a6df9a30237235ff4cc0 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 27 Feb 2022 19:05:28 +0800
Subject: [PATCH 016/112] update readme
---
README.md | 57 +++++++++++--------
...21\351\235\242\350\257\225\351\242\230.md" | 2 -
2 files changed, 34 insertions(+), 25 deletions(-)
diff --git a/README.md b/README.md
index 6dc68d2..d3dff9a 100644
--- a/README.md
+++ b/README.md
@@ -19,63 +19,75 @@
个人公众号
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
👍**推荐 [200多本经典的计算机书籍](https://github.com/Tyson0314/java-books)**
+# 大厂面试系列
+
+1. [字节跳动一面面经](https://mp.weixin.qq.com/s/RH-SunzjqUTTx8HWaCmCcw)
+2. [别再问我Java List八股文了!](https://mp.weixin.qq.com/s/doyy_GYGWoH_YHgyMijStA)
+3. [腾讯面试,你真的懂HTTP吗?](https://mp.weixin.qq.com/s/kC7XRBfO7Z5hZcX6Dz2viw)
+4. [美团二面面经,最后竟然有惊喜?](https://mp.weixin.qq.com/s/3HvOtTU29HGALqmeeOZNWw)
+5. [Java多线程,被面试官难倒了!](https://mp.weixin.qq.com/s/tv8pOLaS6hpwgbKOB9w0Zw)
+6. [京东二面,Redis为什么这么快?](https://mp.weixin.qq.com/s/S3vN5T9HpziRd2s5ysLaSg)
+7. [MySQL索引,给我整不会了!](https://mp.weixin.qq.com/s/Q5CrDlNInpnckJaBQSrA7w)
+8. [别再问我Java虚拟机八股文了!](https://mp.weixin.qq.com/s/npo5-VqQt5sqZiSwPv6LVw)
+9. [计算机网络,问傻了!](https://mp.weixin.qq.com/s/WXcMLa_tdxpRLhO4U8LHIQ)
+10. [Spring这几道题你会吗?](https://mp.weixin.qq.com/s/DtgYRFfOQxQdtQosCU-6aw)
+
+# 精选文章
+
+1. [记一次OOM问题排查](https://mp.weixin.qq.com/s/40C1OdtfGJSrecBoL9Mjbg)
+2. [推荐一款画图神器](https://mp.weixin.qq.com/s/vDtWku41rpeux8ZhZyA81w)
+3. [有哪些值得推荐的Java书籍?](https://mp.weixin.qq.com/s/MUB07APbyDH2-iftGDdGlA)
+4. [这款MarkDown神器,要收费啦!](https://mp.weixin.qq.com/s/aCEi-4vb3kNPF0-E4rB5Kg)
+5. [在B站学Java](https://mp.weixin.qq.com/s/_xfT2Tr6rT8qSAFrtRQ3wQ)
+6. [你见过这样使用Github的吗?](https://mp.weixin.qq.com/s/U_AItugf8KaUPyIun_xagg)
+
# Java
## 基础
1. [**Java面试题精选**](Java/Java基础面试题.md) **精心整理的大厂Java面试题目,附有详细答案** (推荐 :+1:)
-2. [Java基础知识总结](Java/Java基础.md)
-3. [Java8 新特性总结](Java/Java8.md)
+2. [Java8 新特性总结](Java/Java8.md)
4. [Java常见关键字总结](Java/Java关键字.md)
## 集合
-[Java集合高频面试题](Java/Java集合面试题.md)(**牛客点赞200+!推荐** :+1:)
-
-[Java常见集合总结](Java/集合.md)
+1. [Java集合高频面试题](Java/Java集合面试题.md)(**牛客点赞200+!推荐** :+1:)
## 并发
1. [Java并发高频面试题(精华版)](Java/Java并发面试题.md) **精心整理的大厂Java并发编程面试题目,附有详细答案** (推荐 :+1:)
-2. [Java并发知识点总结](Java/并发.md)
-
## JVM
-[【大厂面试】——JVM高频面试题](Java/JVM高频面试题.md)(推荐 :+1:)
-
-[一万多字!Java虚拟机总结](Java/JVM.md)
+1. [【大厂面试】——JVM高频面试题](Java/JVM高频面试题.md)(推荐 :+1:)
# 数据库
## MySQL
1. [【大厂面试】—— MySQL高频面试题50道](数据库/MySQL高频面试题.md)(**知乎1k+收藏,推荐** :+1:)
-2. [MySQL基础知识总结](数据库/MySQL基础.md)
3. [MySQL执行计划](数据库/MySQL执行计划.md)(推荐 :+1:)
## Redis
1. [【大厂面试】——Redis30问](中间件/Redis面试题.md)(牛客高赞,推荐 :+1:)
-2. [Redis基础总结](中间件/Redis入门指南总结.md)
-3. [Redis分布式锁(推荐 :+1:)](中间件/Redis分布式锁.md)
+2. [Redis分布式锁(推荐 :+1:)](中间件/Redis分布式锁.md)
4. [缓存穿透、缓存雪崩、缓存击穿](中间件/缓存穿透、缓存雪崩、缓存击穿.md)
# 框架
@@ -83,7 +95,6 @@
## Spring
1. [Spring高频面试题30道](框架/Spring面试题.md)(推荐 :+1:)
-2. [Spring知识点总结](框架/Spring总结.md)
3. [Spring用到哪些设计模式?](框架/Spring用到哪些设计模式.md)
## Spring Boot
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index c9619ff..c403e91 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -116,8 +116,6 @@ select @@transaction_isolation;
set session transaction isolation level read uncommitted;
```
-
-
## 索引
### 什么是索引?
From 665c518390f875bf6002256243f1746ae477d2ac Mon Sep 17 00:00:00 2001
From: tyson
Date: Mon, 28 Feb 2022 12:11:14 +0800
Subject: [PATCH 017/112] =?UTF-8?q?add=20Springboot=E9=9D=A2=E8=AF=95?=
=?UTF-8?q?=E9=A2=98=E6=80=BB=E7=BB=93?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 2 +-
...25\351\242\230\346\200\273\347\273\223.md" | 44 ++++++++++++++++++-
2 files changed, 43 insertions(+), 3 deletions(-)
rename "\346\241\206\346\236\266/SpringBoot\350\207\252\345\212\250\351\205\215\347\275\256\345\216\237\347\220\206.md" => "\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" (84%)
diff --git a/README.md b/README.md
index d3dff9a..1f99440 100644
--- a/README.md
+++ b/README.md
@@ -101,7 +101,7 @@
[Spring Boot总结](框架/SpringBoot实战.md)
-[SpringBoot自动配置原理](框架/SpringBoot自动配置原理.md)
+[SpringBoot面试题总结](框架/SpringBoot面试题总结.md)
## Spring MVC
diff --git "a/\346\241\206\346\236\266/SpringBoot\350\207\252\345\212\250\351\205\215\347\275\256\345\216\237\347\220\206.md" "b/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
similarity index 84%
rename from "\346\241\206\346\236\266/SpringBoot\350\207\252\345\212\250\351\205\215\347\275\256\345\216\237\347\220\206.md"
rename to "\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
index 9400537..7c93750 100644
--- "a/\346\241\206\346\236\266/SpringBoot\350\207\252\345\212\250\351\205\215\347\275\256\345\216\237\347\220\206.md"
+++ "b/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
@@ -7,7 +7,7 @@
-# 自动配置原理
+## 自动配置原理
SpringBoot实现自动配置原理图解:
@@ -95,7 +95,7 @@ public class ServerProperties {
}
```
-# 实现自动配置
+## 实现自动配置
实现当某个类存在时,自动配置这个类的bean,并且可以在application.properties中配置bean的属性。
@@ -274,3 +274,43 @@ public class SpringbootDemoApplication {
hello.msg=大彬
```
+
+
+
+
+## @Value原理
+
+@Value的解析就是在bean初始化阶段。BeanPostProcessor定义了bean初始化前后用户可以对bean进行操作的接口方法,它的一个重要实现类AutowiredAnnotationBeanPostProcessor为bean中的@Autowired和@Value注解的注入功能提供支持。
+
+
+
+## 启动过程
+
+准备Environment——发布事件——创建上下文、bean——刷新上下文——结束。
+
+构造SpringApplication的时候会进行初始化的工作,初始化的时候会做以下几件事:
+判断运行环境类型,有三种运行环境:NONE 非 web 的运行环境、SERVLET 普通 web 的运行环境、REACTIVE 响应式 web 的运行环境
+加载 spring.factories 配置文件, 并设置 ApplicationContextInitializer
+加载配置文件, 设置 ApplicationListener
+
+
+SpringApplication构造完成之后调用run方法,启动SpringApplication,run方法执行的时候会做以下几件事:
+构造一个StopWatch,观察SpringApplication的执行
+找出SpringApplicationRunListener,用于监听SpringApplication run方法的执行。监听的过程中会封装SpringApplicationEvent事件,然后使用ApplicationEventMulticaster广播出去,应用程序监听器ApplicationListener会监听到这些事件
+发布starting事件
+加载配置资源到environment,包括命令行参数、application.yml等
+发布environmentPrepared事件
+创建并初始化ApplicationContext,设置environment,加载配置
+refresh ApplicationContext
+
+- 设置beanFactory
+- 调用BeanFactoryPostProcessors
+- 初始化消息源
+- 初始化事件广播器(initApplicationEventMulticaster)
+- 调用onRefresh()方法,默认是空实现
+- 注册监听器
+- 实例化non-lazy-init单例
+- 完成refresh
+- 发布ContextRefreshedEvent事件
+
+发布started事件,启动结束
From a0f984384379fe5df12d47ab86716a0cdf3cfdd9 Mon Sep 17 00:00:00 2001
From: tyson
Date: Wed, 2 Mar 2022 18:00:46 +0800
Subject: [PATCH 018/112] =?UTF-8?q?=E5=AD=98=E5=82=A8=E8=BF=87=E7=A8=8B?=
=?UTF-8?q?=E8=AF=AD=E5=8F=A5=E6=9F=A5=E7=9C=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...72\347\241\200\351\235\242\350\257\225\351\242\230.md" | 5 ++---
.../MySQL\345\237\272\347\241\200.md" | 8 +++++++-
2 files changed, 9 insertions(+), 4 deletions(-)
diff --git "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
index 28aacf0..2104011 100644
--- "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
@@ -82,7 +82,7 @@
- 面向对象是把构成问题事务分解成各个对象,分别设计这些对象,然后将他们组装成有完整功能的系统。面向过程只用函数实现,面向对象是用类实现各个功能模块。
-以五子棋为例(来源网络),面向过程的设计思路就是首先分析问题的步骤:
+以五子棋为例,面向过程的设计思路就是首先分析问题的步骤:
1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤2,9、输出最后结果。
把上面每个步骤用分别的函数来实现,问题就解决了。
@@ -139,8 +139,7 @@ JRE是运行基于Java语言编写的程序所不可缺少的运行环境。也
## 什么是值传递和引用传递?
- 值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量。
-
- 引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。所以对引用对象进行操作会同时改变原对象。
+- 引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。所以对引用对象进行操作会同时改变原对象。
## 自动装箱和拆箱
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\345\237\272\347\241\200.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\345\237\272\347\241\200.md"
index 151eaad..b569ffc 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\345\237\272\347\241\200.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\345\237\272\347\241\200.md"
@@ -1032,13 +1032,19 @@ END;
CALL ordertotal(20005, 1, @total);
SELECT @total;
```
-### 检查
+### 查看
创建存储过程的 CREATE 语句。
```mysql
SHOW CREATE PROCEDURE ordertotal;
```
获得包括何时、由谁创建等详细信息的存储过程列表,使用`SHOW PROCEDURE STATUS LIKE 'ordertotal';`
+查看存储过程状态:
+
+```mysql
+SHOW PROCEDURE status;
+```
+
## 游标
存储了游标之后,应用程序可以根据需要滚动或浏览其中的数据。MySQL游标只能用于存储过程(和函数)。
### 创建游标
From c575e476cfaa9be5c8934c6de80b27baeef88335 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 6 Mar 2022 12:06:00 +0800
Subject: [PATCH 019/112] =?UTF-8?q?=E5=9B=BE=E8=A7=A3=E7=B4=A2=E5=BC=95?=
=?UTF-8?q?=E4=B8=8B=E6=8E=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...21\351\235\242\350\257\225\351\242\230.md" | 12 +++++----
...00\351\235\242\350\257\225\351\242\230.md" | 25 ++++++++++---------
...10\351\235\242\350\257\225\351\242\230.md" | 9 ++-----
README.md | 1 +
"\345\267\245\345\205\267/progit2.md" | 2 ++
5 files changed, 25 insertions(+), 24 deletions(-)
diff --git "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 3ddf03d..a3e420b 100644
--- "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -353,7 +353,7 @@ WeakReference weakRef = new WeakReference(str);
## GC是什么?为什么要GC?
-GC,`Gabage Collection`,即垃圾收集。内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序的不稳定甚至崩溃,Java 提供的 GC 功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的。
+GC(`Garbage Collection`),垃圾回收,是Java与C++的主要区别之一。作为Java开发者,一般不需要专门编写内存回收和垃圾清理代码。这是因为在Java虚拟机中,存在自动内存管理和垃圾清理机制。对JVM中的内存进行标记,并确定哪些内存需要回收,根据一定的回收策略,自动的回收内存,保证JVM中的内存空间,防止出现内存泄露和溢出问题。
## Minor GC 和 Full GC的区别?
@@ -734,11 +734,13 @@ class Person {
## 如何排查 OOM 的问题?
-排查 OOM 的方法:
+> 线上JVM必须配置`-XX:+HeapDumpOnOutOfMemoryError` 和`-XX:HeapDumpPath=/tmp/heapdump.hprof`,当OOM发生时自动 dump 堆内存信息到指定目录
-- 增加JVM参数 `-XX:+HeapDumpOnOutOfMemoryError` 和`-XX:HeapDumpPath=/tmp/heapdump.hprof`,当 OOM 发生时自动 dump 堆内存信息到指定目录;
-- jstat 查看监控 JVM 的内存和 GC 情况,评估问题大概出在什么区域;
-- 使用 MAT 工具载入 dump 文件,分析大对象的占用情况 。
+排查 OOM 的方法如下:
+
+- 查看服务器运行日志日志,捕捉到内存溢出异常
+- jstat 查看监控JVM的内存和GC情况,评估问题大概出在什么区域
+- 使用MAT工具载入dump文件,分析大对象的占用情况
diff --git "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
index 2104011..f79a18b 100644
--- "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
@@ -118,7 +118,7 @@ JRE是运行基于Java语言编写的程序所不可缺少的运行环境。也
- 静态多态性:通过重载实现,相同的方法有不同的參数列表,可以根据参数的不同,做出不同的处理。
- 动态多态性:在子类中重写父类的方法。运行期间判断所引用对象的实际类型,根据其实际类型调用相应的方法。
-3、抽象。把客观事物用代码抽象出来。
+4、抽象。把客观事物用代码抽象出来。
## Java的基本数据类型有哪些?
@@ -301,11 +301,11 @@ String类是final的,它的所有成员变量也都是final的。为什么是f
字符串常量池(String Pool)保存着所有字符串字面量,这些字面量在编译时期就确定。字符串常量池位于堆内存中,专门用来存储字符串常量。在创建字符串时,JVM首先会检查字符串常量池,如果该字符串已经存在池中,则返回其引用,如果不存在,则创建此字符串并放入池中,并返回其引用。
-## object常用方法有哪些?
+## Object常用方法有哪些?
Java面试经常会出现的一道题目,Object的常用方法。下面给大家整理一下。
-object常用方法有:`toString()`、`equals()`、`hashCode()`、`clone()`等。
+Object常用方法有:`toString()`、`equals()`、`hashCode()`、`clone()`等。
**toString**
@@ -429,7 +429,7 @@ public class Cat {
**clone**
-java赋值是复制对象引用,如果我们想要得到一个对象的副本,使用赋值操作是无法达到目的的。Object对象有个clone()方法,实现了对
+Java赋值是复制对象引用,如果我们想要得到一个对象的副本,使用赋值操作是无法达到目的的。Object对象有个clone()方法,实现了对
象中各个属性的复制,但它的可见范围是protected的。
@@ -493,7 +493,7 @@ public class Person {
当前线程调用对象的wait()方法之后,当前线程会释放对象锁,进入等待状态。等待其他线程调用此对象的notify()/notifyAll()唤醒或者等待超时时间wait(long timeout)自动唤醒。线程需要获取obj对象锁之后才能调用 obj.wait()。
-**notity**
+**notify**
obj.notify()唤醒在此对象上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象上等待的所有线程。
@@ -1032,15 +1032,16 @@ if (obj instanceof String) {
}
```
+## 什么是反射?
+动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
+在运行状态中,对于任意一个类,能够知道这个类的所有属性和方法。对于任意一个对象,能够调用它的任意一个方法和属性。
+## 反射有哪些应用场景呢?
-> 本文已经收录到github仓库,此仓库用于分享Java相关知识总结,包括Java基础、MySQL、Spring Boot、MyBatis、Redis、RabbitMQ、计算机网络、数据结构与算法等等,欢迎大家提pr和star!
->
-> github地址:https://github.com/Tyson0314/Java-learning
->
-> 如果github访问不了,可以访问gitee仓库。
->
-> gitee地址:https://gitee.com/tysondai/Java-learning
+1. JDBC连接数据库时使用`Class.forName()`通过反射加载数据库的驱动程序
+2. Eclispe、IDEA等开发工具利用反射动态解析对象的类型与结构,动态提示对象的属性和方法
+3. Web服务器中利用反射调用了Sevlet的service方法
+4. Spring AOP的特性也是依赖反射实现的
diff --git "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
index 518c377..cee3b13 100644
--- "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
@@ -196,18 +196,13 @@ return h&(length-1); //第三步 取模运算
### 红黑树的特点?
- 每个节点或者是黑色,或者是红色。
-- 根节点是黑色。
-- 每个叶子节点(`NIL`)是黑色。
+- 根节点和叶子节点(`NIL`)是黑色的。
- 如果一个节点是红色的,则它的子节点必须是黑色的。
- 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
-### 为什么使用红黑树而不使用AVL树?
-
-ConcurrentHashMap 在put的时候会加锁,使用红黑树插入速度更快,可以减少等待锁释放的时间。红黑树是对AVL树的优化,只要求部分平衡,用非严格的平衡来换取增删节点时候旋转次数的降低,提高了插入和删除的性能。
-
### 在解决 hash 冲突的时候,为什么选择先用链表,再转红黑树?
-因为红黑树需要进行左旋,右旋,变色这些操作来保持平衡,而单链表不需要。当元素小于 8 个的时候,链表结构可以保证查询性能。当元素大于 8 个的时候, 红黑树搜索时间复杂度是 `O(logn)`,而链表是 `O(n)`,此时需要红黑树来加快查询速度,但是插入和删除节点的效率变慢了。如果一开始就用红黑树结构,元素太少,插入和删除节点的效率又比较慢,浪费性能。
+因为红黑树需要进行左旋,右旋,变色这些操作来保持平衡,而单链表不需要。所以,当元素个数小于8个的时候,采用链表结构可以保证查询性能。而当元素个数大于8个的时候, 红黑树搜索时间复杂度是 `O(logn)`,而链表是 `O(n)`,此时使用红黑树可以加快查询速度。
### HashMap 的长度为什么是 2 的幂次方?
diff --git a/README.md b/README.md
index 1f99440..d936ec7 100644
--- a/README.md
+++ b/README.md
@@ -82,6 +82,7 @@
## MySQL
1. [【大厂面试】—— MySQL高频面试题50道](数据库/MySQL高频面试题.md)(**知乎1k+收藏,推荐** :+1:)
+2. [图解索引下推](https://mp.weixin.qq.com/s/W1XQYmihtSdbLWQKeNwZvQ)(推荐 :+1:)
3. [MySQL执行计划](数据库/MySQL执行计划.md)(推荐 :+1:)
## Redis
diff --git "a/\345\267\245\345\205\267/progit2.md" "b/\345\267\245\345\205\267/progit2.md"
index 62497a7..6dde18b 100644
--- "a/\345\267\245\345\205\267/progit2.md"
+++ "b/\345\267\245\345\205\267/progit2.md"
@@ -452,6 +452,8 @@ git remote add pb https://github.com/paulboone/ticgit
git remote remove origin
```
+### 给origin设置多个远程仓库
+
如果想要给origin设置两个远程仓库地址(git add会报错),可以使用`git remote set-url --add origin url`来设置。
```bash
From c07d8623b63ab75ca9d4b2f3956d5742a5b79f58 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 6 Mar 2022 18:09:06 +0800
Subject: [PATCH 020/112] =?UTF-8?q?=E6=80=8E=E4=B9=88=E5=86=99=E7=AE=80?=
=?UTF-8?q?=E5=8E=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 42 +++++++++++++++++++-----------------------
1 file changed, 19 insertions(+), 23 deletions(-)
diff --git a/README.md b/README.md
index d936ec7..057934a 100644
--- a/README.md
+++ b/README.md
@@ -30,10 +30,16 @@
+# 简历很重要
+[简历投递之后总是石沉大海?](https://zhuanlan.zhihu.com/p/406982597)
+# 精选资源
-👍**推荐 [200多本经典的计算机书籍](https://github.com/Tyson0314/java-books)**
+- [200多本经典的计算机书籍](https://github.com/Tyson0314/java-books)
+- [谷歌师兄刷题笔记](https://t.1yb.co/A6id)(推荐 :+1:)
+- [BAT大佬总结的刷题手册](https://t.1yb.co/yMbo)(推荐 :+1:)
+- [Java项目推荐](https://www.zhihu.com/question/325011850/answer/2257046656)
# 大厂面试系列
@@ -47,15 +53,11 @@
8. [别再问我Java虚拟机八股文了!](https://mp.weixin.qq.com/s/npo5-VqQt5sqZiSwPv6LVw)
9. [计算机网络,问傻了!](https://mp.weixin.qq.com/s/WXcMLa_tdxpRLhO4U8LHIQ)
10. [Spring这几道题你会吗?](https://mp.weixin.qq.com/s/DtgYRFfOQxQdtQosCU-6aw)
-
-# 精选文章
-
-1. [记一次OOM问题排查](https://mp.weixin.qq.com/s/40C1OdtfGJSrecBoL9Mjbg)
-2. [推荐一款画图神器](https://mp.weixin.qq.com/s/vDtWku41rpeux8ZhZyA81w)
-3. [有哪些值得推荐的Java书籍?](https://mp.weixin.qq.com/s/MUB07APbyDH2-iftGDdGlA)
-4. [这款MarkDown神器,要收费啦!](https://mp.weixin.qq.com/s/aCEi-4vb3kNPF0-E4rB5Kg)
-5. [在B站学Java](https://mp.weixin.qq.com/s/_xfT2Tr6rT8qSAFrtRQ3wQ)
-6. [你见过这样使用Github的吗?](https://mp.weixin.qq.com/s/U_AItugf8KaUPyIun_xagg)
+10. [面向对象编程?](https://mp.weixin.qq.com/s/M8jDnLat61YAbM1-jIhJIA)
+10. [Java内功深厚?](https://mp.weixin.qq.com/s/v_kWSHX9GMS_aoqfwUMKsg)
+10. [面试初级Java开发,面试官问我MySQL架构?](https://mp.weixin.qq.com/s/JvDZCk4IecmaEYfFsRhpjQ)
+10. [手写红黑树?](https://mp.weixin.qq.com/s/yznh_IfMg4hWqU62U-t9GQ)
+10. [面完阿里,直接入职!](https://mp.weixin.qq.com/s/49QJ1FzaGTe-_54PT8_8jA)
# Java
@@ -63,7 +65,6 @@
1. [**Java面试题精选**](Java/Java基础面试题.md) **精心整理的大厂Java面试题目,附有详细答案** (推荐 :+1:)
2. [Java8 新特性总结](Java/Java8.md)
-4. [Java常见关键字总结](Java/Java关键字.md)
## 集合
@@ -135,10 +136,6 @@
# 数据结构与算法
-[谷歌师兄刷题笔记](https://t.1yb.co/A6id)(推荐 :+1:)
-
-[BAT大佬总结的刷题手册](https://t.1yb.co/yMbo)(推荐 :+1:)
-
[7种常见的排序算法Java代码实现](数据结构与算法/常见的排序算法Java代码实现.md)
[二叉树前序、中序、后序、层序遍历代码实现](数据结构与算法/二叉树前序、中序、后序、层序遍历代码实现.md)
@@ -155,15 +152,14 @@
[Git 超详细总结!](工具/progit2.md)(推荐 :+1:)
-[Docker命令大全!](工具/docker.md)
+# 其他精选文章
-[Maven实战总结](工具/Maven实战.md)
-
-[Github使用技巧](工具/Github指南.md)
-
-[Jenkins一键部署](工具/jenkins.md)
-
-[npm命令](工具/NPM.md)
+1. [记一次OOM问题排查](https://mp.weixin.qq.com/s/40C1OdtfGJSrecBoL9Mjbg)
+2. [推荐一款画图神器](https://mp.weixin.qq.com/s/vDtWku41rpeux8ZhZyA81w)
+3. [有哪些值得推荐的Java书籍?](https://mp.weixin.qq.com/s/MUB07APbyDH2-iftGDdGlA)
+4. [这款MarkDown神器,要收费啦!](https://mp.weixin.qq.com/s/aCEi-4vb3kNPF0-E4rB5Kg)
+5. [在B站学Java](https://mp.weixin.qq.com/s/_xfT2Tr6rT8qSAFrtRQ3wQ)
+6. [你见过这样使用Github的吗?](https://mp.weixin.qq.com/s/U_AItugf8KaUPyIun_xagg)
From da27883e5049eaa3bfc9cdff183235a8cc2b76fa Mon Sep 17 00:00:00 2001
From: tyson
Date: Fri, 11 Mar 2022 08:49:32 +0800
Subject: [PATCH 021/112] =?UTF-8?q?LeetCode=E5=88=B7=E9=A2=98=E6=8A=80?=
=?UTF-8?q?=E5=B7=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 3 +++
...\230\351\242\221\351\235\242\350\257\225\351\242\230.md" | 6 ++++--
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 057934a..5b7d654 100644
--- a/README.md
+++ b/README.md
@@ -58,6 +58,7 @@
10. [面试初级Java开发,面试官问我MySQL架构?](https://mp.weixin.qq.com/s/JvDZCk4IecmaEYfFsRhpjQ)
10. [手写红黑树?](https://mp.weixin.qq.com/s/yznh_IfMg4hWqU62U-t9GQ)
10. [面完阿里,直接入职!](https://mp.weixin.qq.com/s/49QJ1FzaGTe-_54PT8_8jA)
+10. [华为面经](https://mp.weixin.qq.com/s/KmjwoG7pNvAHiX1UNnef6g)
# Java
@@ -136,6 +137,8 @@
# 数据结构与算法
+[如何高效的刷LeetCode?](https://www.zhihu.com/question/280279208/answer/2377906738)
+
[7种常见的排序算法Java代码实现](数据结构与算法/常见的排序算法Java代码实现.md)
[二叉树前序、中序、后序、层序遍历代码实现](数据结构与算法/二叉树前序、中序、后序、层序遍历代码实现.md)
diff --git "a/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index fc7a5a6..a940a1f 100644
--- "a/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -164,11 +164,13 @@ Content-Length:112
## HTTP长连接和短连接?
-**HTTP1.0默认使用的是短连接**。浏览器和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。
+HTTP短连接:浏览器和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。**HTTP1.0默认使用的是短连接**。
+
+HTTP长连接:指的是**复用TCP连接**。多个HTTP请求可以复用同一个TCP连接,这就节省了TCP连接建立和断开的消耗。
**HTTP/1.1起,默认使用长连接**。要使用长连接,客户端和服务器的HTTP首部的Connection都要设置为keep-alive,才能支持长连接。
-HTTP长连接,指的是**复用TCP连接**。多个HTTP请求可以复用同一个TCP连接,这就节省了TCP连接建立和断开的消耗。
+
## HTTP1.1和 HTTP2.0的区别?
From 24b957b1ea052967458a33a55d6cf2b83dbc6a1a Mon Sep 17 00:00:00 2001
From: tyson
Date: Sat, 19 Mar 2022 14:09:45 +0800
Subject: [PATCH 022/112] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Java=E5=B9=B6?=
=?UTF-8?q?=E5=8F=91=E9=9D=A2=E8=AF=95=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...21\351\235\242\350\257\225\351\242\230.md" | 2 +-
Java/Java8.md | 2 +-
...00\351\235\242\350\257\225\351\242\230.md" | 20 +++++--------
...21\351\235\242\350\257\225\351\242\230.md" | 29 +++++++++++++++++--
...10\351\235\242\350\257\225\351\242\230.md" | 4 +--
...21\351\235\242\350\257\225\351\242\230.md" | 3 +-
6 files changed, 39 insertions(+), 21 deletions(-)
diff --git "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index a3e420b..7ccae91 100644
--- "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -393,7 +393,7 @@ GC(`Garbage Collection`),垃圾回收,是Java与C++的主要区别之一
**老年代空间不足**
-老年代空间不足的常见场景为前文所讲的大对象直接进入老年代、长期存活的对象进入老年代等。为了避免以上原因引起的 Full GC,应当尽量不要创建过大的对象以及数组。除此之外,可以通过 `-Xmn` 参数调大新生代的大小,让对象尽量在新生代被回收掉,不进入老年代。还可以通过 `-XX:MaxTenuringThreshold` 调大对象进入老年代的年龄,让对象在新生代多存活一段时间。
+老年代空间不足的常见场景为前文所讲的大对象直接进入老年代、长期存活的对象进入老年代等。为了避免以上原因引起的 Full GC,应当尽量不要创建过大的对象以及数组、注意编码规范避免内存泄露。除此之外,可以通过 `-Xmn` 参数调大新生代的大小,让对象尽量在新生代被回收掉,不进入老年代。还可以通过 `-XX:MaxTenuringThreshold` 调大对象进入老年代的年龄,让对象在新生代多存活一段时间。
**空间分配担保失败**
diff --git a/Java/Java8.md b/Java/Java8.md
index 63025da..efd2c16 100644
--- a/Java/Java8.md
+++ b/Java/Java8.md
@@ -33,7 +33,7 @@
## 函数式编程
-面向对象编程:面向对象的语言,一切皆对象,如果想要调用一个函数,函数必须属于一个类或对象,然后在使用类或对象进行调用。面向对象编程会多写很多可能是重复的代码行。
+面向对象编程:面向对象的语言,一切皆对象,如果想要调用一个函数,函数必须属于一个类或对象,然后在使用类或对象进行调用。面向对象编程可能需要多写很多重复的代码行。
```java
Runnable runnable = new Runnable() {
diff --git "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
index f79a18b..6b07b32 100644
--- "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
@@ -645,13 +645,11 @@ public class LifeCycle {
## 常见的关键字有哪些?
-### static
+**static**
static可以用来修饰类的成员方法、类的成员变量。
-**静态变量**
-
-static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
+static变量也称作**静态变量**,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
以下例子,age为非静态变量,则p1打印结果是:`Name:zhangsan, Age:10`;若age使用static修饰,则p1打印结果是:`Name:zhangsan, Age:12`,因为static变量在内存只有一个副本。
@@ -681,9 +679,7 @@ public class Person {
}
```
-**静态方法**
-
-static方法一般称作静态方法。静态方法不依赖于任何对象就可以进行访问,通过类名即可调用静态方法。
+static方法一般称作**静态方法**。静态方法不依赖于任何对象就可以进行访问,通过类名即可调用静态方法。
```java
public class Utils {
@@ -697,9 +693,7 @@ public class Utils {
}
```
-**静态代码块**
-
-静态代码块只会在类加载的时候执行一次。以下例子,startDate和endDate在类加载的时候进行赋值。
+**静态代码块**只会在类加载的时候执行一次。以下例子,startDate和endDate在类加载的时候进行赋值。
```java
class Person {
@@ -744,7 +738,7 @@ public class OuterClass {
}
```
-### final
+**final**
1. **基本数据**类型用final修饰,则不能修改,是常量;**对象引用**用final修饰,则引用只能指向该对象,不能指向别的对象,但是对象本身可以修改。
@@ -752,7 +746,7 @@ public class OuterClass {
3. final修饰的类不能被继承。
-### this
+**this**
`this.属性名称`指访问类中的成员变量,可以用来区分成员变量和局部变量。如下代码所示,`this.name`访问类Person当前实例的变量。
@@ -796,7 +790,7 @@ public class Person {
}
```
-### super
+**super**
super 关键字用于在子类中访问父类的变量和方法。
diff --git "a/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
index a9a186f..cfcb15d 100644
--- "a/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
@@ -397,9 +397,9 @@ class RunnableDemo implements Runnable {
### 什么是线程死锁?
-多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。
+线程死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象。若无外力作用,它们都将无法推进下去。
-如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。
+如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方持有的资源,所以这两个线程就会互相等待而进入死锁状态。

@@ -1121,3 +1121,28 @@ public final void lazySet(int i, int newValue)//最终 将index=i 位置的元
- AtomicStampedReference:带有版本号的引用类型原子类。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。
- AtomicMarkableReference :原子更新带有标记的引用类型。该类将 boolean 标记与引用关联起来
+## 为什么要使用Executor线程池框架呢?
+
+- 每次执行任务都通过new Thread()去创建线程,比较消耗性能,创建一个线程是比较耗时、耗资源的
+- 调用new Thread()创建的线程缺乏管理,可以无限制的创建,线程之间的相互竞争会导致过多占用系统资源而导致系统瘫痪
+- 直接使用new Thread()启动的线程不利于扩展,比如定时执行、定期执行、定时定期执行、线程中断等都不好实现
+
+## 如何停止一个正在运行的线程?
+
+1. 使用共享变量的方式。共享变量可以被多个执行相同任务的线程用来作为是否停止的信号,通知停止线程的执行。
+2. 使用interrupt方法终止线程。当一个线程被阻塞,处于不可运行状态时,即使主程序中将该线程的共享变量设置为true,但该线程此时根本无法检查循环标志,当然也就无法立即中断。这时候可以使用Thread提供的interrupt()方法,因为该方法虽然不会中断一个正在运行的线程,但是它可以使一个被阻塞的线程抛出一个中断异常,从而使线程提前结束阻塞状态。
+
+## 什么是Daemon线程?
+
+后台(daemon)线程,是指在程序运行的时候在后台提供一种通用服务的线程,并且这个线程并不属于程序中不可或缺的部分。因此,当所有的非后台线程结束时,程序也就终止了,同时会杀死进程中的所有后台线程。反过来说,只要有任何非后台线程还在运行,程序就不会终止。必须在线程启动之前调用setDaemon()方法,才能把它设置为后台线程。
+
+注意:后台进程在不执行finally子句的情况下就会终止其run()方法。
+
+比如:JVM的垃圾回收线程就是Daemon线程,Finalizer也是守护线程。
+
+## SynchronizedMap和ConcurrentHashMap有什么区别?
+
+SynchronizedMap一次锁住整张表来保证线程安全,所以每次只能有一个线程来访问map。
+
+JDK1.8 ConcurrentHashMap采用CAS和synchronized来保证并发安全。数据结构采用数组+链表/红黑二叉树。synchronized只锁定当前链表或红黑二叉树的首节点,支持并发访问、修改。
+另外ConcurrentHashMap使用了一种不同的迭代方式。当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException,取而代之的是在改变时new新的数据从而不影响原有的数据 ,iterator完成后再将头指针替换为新的数据 ,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变。
diff --git "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
index cee3b13..bfee0b6 100644
--- "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
@@ -180,7 +180,7 @@ return h&(length-1); //第三步 取模运算
1.7链表新节点采用的是头插法,这样在线程一扩容迁移元素时,会将元素顺序改变,导致两个线程中出现元素的相互指向而形成循环链表,1.8采用了尾插法,避免了这种情况的发生。
-原数组的元素在重新计算hash之后,因为数组容量n变为2倍,那么n-1的mask范围在高位多1bit。在元素拷贝过程不需要重新计算元素在数组中的位置,只需要看看原来的hash值新增的那个bit是1还是0,是0的话索引没变,是1的话索引变成“原索引+oldCap”(根据`e.hash & (oldCap - 1) == 0`判断) 。这样可以省去重新计算hash值的时间,而且由于新增的1bit是0还是1可以认为是随机的,因此resize的过程会均匀的把之前的冲突的节点分散到新的bucket。
+原数组的元素在重新计算hash之后,因为数组容量n变为2倍,那么n-1的mask范围在高位多1bit。在元素拷贝过程不需要重新计算元素在数组中的位置,只需要看看原来的hash值新增的那个bit是1还是0,是0的话索引没变,是1的话索引变成“原索引+oldCap”(根据`e.hash & oldCap == 0`判断) 。这样可以省去重新计算hash值的时间,而且由于新增的1bit是0还是1可以认为是随机的,因此resize的过程会均匀的把之前的冲突的节点分散到新的bucket。
### put方法流程?
@@ -202,7 +202,7 @@ return h&(length-1); //第三步 取模运算
### 在解决 hash 冲突的时候,为什么选择先用链表,再转红黑树?
-因为红黑树需要进行左旋,右旋,变色这些操作来保持平衡,而单链表不需要。所以,当元素个数小于8个的时候,采用链表结构可以保证查询性能。而当元素个数大于8个的时候, 红黑树搜索时间复杂度是 `O(logn)`,而链表是 `O(n)`,此时使用红黑树可以加快查询速度。
+因为红黑树需要进行左旋,右旋,变色这些操作来保持平衡,而单链表不需要。所以,当元素个数小于8个的时候,采用链表结构可以保证查询性能。而当元素个数大于8个的时候并且数组容量大于64,会采用红黑树结构。因为红黑树搜索时间复杂度是 `O(logn)`,而链表是 `O(n)`,在n比较大的时候,使用红黑树可以加快查询速度。
### HashMap 的长度为什么是 2 的幂次方?
diff --git "a/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index a940a1f..2fb8b79 100644
--- "a/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -40,7 +40,7 @@

-TCP/IP五层模型:应用层、传输层、网络层、数据链路层、物理层。
+五层模型:应用层、传输层、网络层、数据链路层、物理层。
- **应用层**:为应用程序提供交互服务。在互联网中的应用层协议很多,如域名系统DNS、HTTP协议、SMTP协议等。
- **传输层**:负责向两台主机进程之间的通信提供数据传输服务。传输层的协议主要有传输控制协议TCP和用户数据协议UDP。
@@ -159,7 +159,6 @@ Content-Length:112
- GET请求参数通过URL传递,POST的参数放在请求体中。
- GET产生一个TCP数据包;POST产生两个TCP数据包。对于GET方式的请求,浏览器会把请求头和请求体一并发送出去;而对于POST,浏览器先发送请求头,服务器响应100 continue,浏览器再发送请求体。
- GET请求会被浏览器主动缓存,而POST不会,除非手动设置。
-- GET请求只能进行url编码,而POST支持多种编码方式。
- GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
## HTTP长连接和短连接?
From 76efb6e79383466814af0e3fbf0b2e2b02d20349 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 20 Mar 2022 11:40:39 +0800
Subject: [PATCH 023/112] =?UTF-8?q?Redis=E9=9D=A2=E8=AF=95=E9=A2=98?=
=?UTF-8?q?=E8=A1=A5=E5=85=85?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 3 +-
...is\351\235\242\350\257\225\351\242\230.md" | 75 +++++++++++++++----
2 files changed, 62 insertions(+), 16 deletions(-)
diff --git a/README.md b/README.md
index 5b7d654..1dbfbfd 100644
--- a/README.md
+++ b/README.md
@@ -39,7 +39,8 @@
- [200多本经典的计算机书籍](https://github.com/Tyson0314/java-books)
- [谷歌师兄刷题笔记](https://t.1yb.co/A6id)(推荐 :+1:)
- [BAT大佬总结的刷题手册](https://t.1yb.co/yMbo)(推荐 :+1:)
-- [Java项目推荐](https://www.zhihu.com/question/325011850/answer/2257046656)
+- [Java优质项目推荐](https://www.zhihu.com/question/325011850/answer/2257046656)
+- [优质视频教程推荐](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247487149&idx=1&sn=aa883c9f020945d3f210550bd688c7d0&chksm=ce98f3ebf9ef7afdae0b37c4d0751806b0fbbf08df783fba536e5ec20ec6a6e1512198dc6206&token=104697471&lang=zh_CN#rd)(推荐 :+1:)
# 大厂面试系列
diff --git "a/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md" "b/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
index 0de6aaa..43fb271 100644
--- "a/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
+++ "b/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
@@ -73,8 +73,16 @@ Redis(`Remote Dictionary Server`)是一个使用 C 语言编写的,高性
- **高效的数据结构**:Redis 每种数据类型底层都做了优化,目的就是为了追求更快的速度。
+## 讲讲Redis的线程模型?
-## Redis为何选择单线程
+Redis基于Reactor模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以Redis才叫单线程模型。
+
+- 文件事件处理器使用I/O多路复用(multiplexing)程序来同时监听多个套接字, 并根据套接字目前执行的任务来为套接字关联不同的事件处理器。
+- 当被监听的套接字准备好执行连接accept、read、write、close等操作时, 与操作相对应的文件事件就会产生, 这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。
+
+虽然文件事件处理器以单线程方式运行, 但通过使用 I/O 多路复用程序来监听多个套接字, 文件事件处理器既实现了高性能的网络通信模型, 又可以很好地与 redis 服务器中其他同样以单线程方式运行的模块进行对接, 这保持了 Redis 内部单线程设计的简单性。
+
+## Redis为何选择单线程模型?
* 避免过多的**上下文切换开销**。程序始终运行在进程中单个线程内,没有多线程切换的场景。
* **避免同步机制的开销**:如果 Redis选择多线程模型,需要考虑数据同步的问题,则必然会引入某些同步机制,会导致在操作数据过程中带来更多的开销,增加程序复杂度的同时还会降低性能。
@@ -105,6 +113,18 @@ Redis支持多线程主要有两个原因:
5. Redis 的速度比 Memcached 快很多。
6. Redis 使用**单线程的多路 IO 复用模型**,Memcached使用多线程的非阻塞 IO 模型。
+## 为什么要用 Redis 而不用 map/guava 做缓存?
+
+使用自带的 map 或者 guava 实现的是**本地缓存**,最主要的特点是轻量以及快速,生命周期随着 jvm 的销毁而结束,并且在多实例的情况下,每个实例都需要各自保存一份缓存,缓存不具有一致性。
+
+使用 redis 或 memcached 之类的称为**分布式缓存**,在多实例的情况下,各实例共用一份缓存数据,缓存具有一致性。
+
+## Redis的内存用完了会怎样?
+
+如果达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回)。
+
+也可以配置内存淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容。
+
## Redis 数据类型有哪些?
**基本数据类型**:
@@ -209,6 +229,14 @@ QUEUED
使用`UNWATCH`可以取消`WATCH`命令对`key`的监控,所有监控锁将会被取消。
+## Redis事务支持隔离性吗?
+
+Redis 是单进程程序,并且它保证在执行事务时,不会对事务进行中断,事务可以运行直到执行完所有事务队列中的命令为止。因此,Redis 的事务是总是带有隔离性的。
+
+## Redis事务保证原子性吗,支持回滚吗?
+
+Redis单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。
+
## 持久化机制
持久化就是把**内存的数据写到磁盘中**,防止服务宕机导致内存数据丢失。
@@ -294,24 +322,15 @@ appendfsync no //由操作系统决定何时进行同步操作
当RDB与AOF两种方式都开启时,Redis会优先使用AOF恢复数据,因为AOF保存的文件比RDB文件更完整。
-## Redis常见的部署方式有哪些?
-
-Redis的几种常见使用方式包括:
+## Redis有哪些部署方案?
-* 单机版
-* Redis主从
-* Redis Sentinel(哨兵)
-* Redis Cluster
+**单机版**:单机部署,单机redis能够承载的 QPS 大概就在上万到几万不等。这种部署方式很少使用。存在的问题:1、内存容量有限 2、处理能力有限 3、无法高可用。
-**使用场景**:
+**主从模式**:一主多从,主负责写,并且将数据复制到其它的 slave 节点,从节点负责读。所有的读请求全部走从节点。这样也可以很轻松实现水平扩容,支撑读高并发。master 节点挂掉后,需要手动指定新的 master,可用性不高,基本不用。
-单机版:很少使用。存在的问题:1、内存容量有限 2、处理能力有限 3、无法高可用。
+**哨兵模式**:主从复制存在不能自动故障转移、达不到高可用的问题。哨兵模式解决了这些问题。通过哨兵机制可以自动切换主从节点。master 节点挂掉后,哨兵进程会主动选举新的 master,可用性高,但是每个节点存储的数据是一样的,浪费内存空间。数据量不是很多,集群规模不是很大,需要自动容错容灾的时候使用。
-主从模式:master 节点挂掉后,需要手动指定新的 master,可用性不高,基本不用。
-
-哨兵模式:master 节点挂掉后,哨兵进程会主动选举新的 master,可用性高,但是每个节点存储的数据是一样的,浪费内存空间。数据量不是很多,集群规模不是很大,需要自动容错容灾的时候使用。
-
-Redis cluster:主要是针对海量数据+高并发+高可用的场景,如果是海量数据,如果你的数据量很大,那么建议就用Redis cluster,所有主节点的容量总和就是Redis cluster可缓存的数据容量。
+**Redis cluster**:服务端分片技术,3.0版本开始正式提供。Redis Cluster并没有使用一致性hash,而是采用slot(槽)的概念,一共分成16384个槽。将请求发送到任意节点,接收到请求的节点会将查询请求发送到正确的节点上执行。主要是针对海量数据+高并发+高可用的场景,如果是海量数据,如果你的数据量很大,那么建议就用Redis cluster,所有主节点的容量总和就是Redis cluster可缓存的数据容量。
## 主从复制
@@ -478,6 +497,31 @@ public String get(String key) {
}
```
+## 缓存预热
+
+缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
+
+解决方案:
+
+1. 直接写个缓存刷新页面,上线时手工操作一下;
+2. 数据量不大,可以在项目启动的时候自动进行加载;
+3. 定时刷新缓存;
+
+## 缓存降级
+
+当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
+
+缓存降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
+
+在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:
+
+1. 一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
+2. 警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
+3. 错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
+4. 严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。
+
+服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。
+
## Redis 怎么实现消息队列?
使用一个列表,让生产者将任务使用LPUSH命令放进列表,消费者不断用RPOP从列表取出任务。
@@ -584,3 +628,4 @@ Redis 官方站提出了一种权威的基于 Redis 实现分布式锁的方式
1. 安全特性:互斥访问,即永远只有一个 client 能拿到锁
2. 避免死锁:最终 client 都可能拿到锁,不会出现死锁的情况,即使原本锁住某资源的 client 挂掉了
3. 容错性:只要大部分 Redis 节点存活就可以正常提供服务
+
From b9e246dc14f55d87c41ce9397d5a687918bea22b Mon Sep 17 00:00:00 2001
From: tyson
Date: Mon, 21 Mar 2022 12:05:13 +0800
Subject: [PATCH 024/112] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B6=88=E6=81=AF?=
=?UTF-8?q?=E9=98=9F=E5=88=97=E9=9D=A2=E8=AF=95=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...00\351\235\242\350\257\225\351\242\230.md" | 40 +++++++++----------
README.md | 1 +
.../RabbitMQ.md" | 8 ----
...27\351\235\242\350\257\225\351\242\230.md" | 36 +++++++++++++++++
4 files changed, 57 insertions(+), 28 deletions(-)
create mode 100644 "\344\270\255\351\227\264\344\273\266/\346\266\210\346\201\257\351\230\237\345\210\227\351\235\242\350\257\225\351\242\230.md"
diff --git "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
index 6b07b32..5e54239 100644
--- "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
@@ -567,7 +567,7 @@ equals与hashcode的关系:
hashcode方法主要是用来**提升对象比较的效率**,先进行hashcode()的比较,如果不相同,那就不必在进行equals的比较,这样就大大减少了equals比较的次数,当比较对象的数量很大的时候能提升效率。
-之所以重写equals()要重写hashcode(),是为了保证equals()方法返回true的情况下hashcode值也要一致,如果重写了equals()没有重写hashcode(),就会出现两个对象相等但hashcode()不相等的情况。这样,当用其中的一个对象作为键保存到hashMap、hashTable或hashSet中,再以另一个对象作为键值去查找他们的时候,则会查找不到。
+之所以重写`equals()`要重写`hashcode()`,是为了保证`equals()`方法返回true的情况下hashcode值也要一致,如果重写了`equals()`没有重写`hashcode()`,就会出现两个对象相等但`hashcode()`不相等的情况。这样,当用其中的一个对象作为键保存到hashMap、hashTable或hashSet中,再以另一个对象作为键值去查找他们的时候,则会查找不到。
## Java创建对象有几种方式?
@@ -641,7 +641,7 @@ public class LifeCycle {
- 对于基本数据类型,==比较的是他们的值。基本数据类型没有equal方法;
-- 对于复合数据类型,==比较的是它们的存放地址(是否是同一个对象)。equals()默认比较地址值,重写的话按照重写逻辑去比较。
+- 对于复合数据类型,==比较的是它们的存放地址(是否是同一个对象)。`equals()`默认比较地址值,重写的话按照重写逻辑去比较。
## 常见的关键字有哪些?
@@ -879,14 +879,14 @@ public class Student extends Person {
## 接口与抽象类区别?
-1、语法层面上
+1、**语法层面**上的区别
- 抽象类可以有方法实现,而接口的方法中只能是抽象方法;
- 抽象类中的成员变量可以是各种类型的,接口中的成员变量只能是public static final类型;
- 接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
-2、设计层面上的区别
+2、**设计层面**上的区别
- 抽象层次不同。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口只是对类行为进行抽象。继承抽象类是一种"是不是"的关系,而接口实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是具备不具备的关系,比如鸟是否能飞。
- 继承抽象类的是具有相似特点的类,而实现接口的却可以不同的类。
@@ -907,24 +907,24 @@ class BMWCar extends Car implements Alarm {
常见的RuntimeException:
-1. ClassCastException //类型转换异常
-2. IndexOutOfBoundsException //数组越界异常
-3. NullPointerException //空指针
-4. ArrayStoreException //数组存储异常
-5. NumberFormatException //数字格式化异常
-6. ArithmeticException //数学运算异常
+1. `ClassCastException` //类型转换异常
+2. `IndexOutOfBoundsException` //数组越界异常
+3. `NullPointerException` //空指针
+4. `ArrayStoreException` //数组存储异常
+5. `NumberFormatException` //数字格式化异常
+6. `ArithmeticException` //数学运算异常
unchecked Exception:
-1. NoSuchFieldException //反射异常,没有对应的字段
-2. ClassNotFoundException //类没有找到异常
-3. IllegalAccessException //安全权限异常,可能是反射时调用了private方法
+1. `NoSuchFieldException` //反射异常,没有对应的字段
+2. `ClassNotFoundException` //类没有找到异常
+3. `IllegalAccessException` //安全权限异常,可能是反射时调用了private方法
## Error和Exception的区别?
-Error:JVM 无法解决的严重问题,如栈溢出`StackOverflowError`、内存溢出`OOM`等。程序无法处理的错误。
+**Error**:JVM 无法解决的严重问题,如栈溢出`StackOverflowError`、内存溢出`OOM`等。程序无法处理的错误。
-Exception:其它因编程错误或偶然的外在因素导致的一般性问题。可以在代码中进行处理。如:空指针异常、数组下标越界等。
+**Exception**:其它因编程错误或偶然的外在因素导致的一般性问题。可以在代码中进行处理。如:空指针异常、数组下标越界等。
## 运行时异常和非运行时异常(checked)的区别?
@@ -941,11 +941,11 @@ Exception:其它因编程错误或偶然的外在因素导致的一般性问
## BIO/NIO/AIO区别的区别?
-同步阻塞IO : 用户进程发起一个IO操作以后,必须等待IO操作的真正完成后,才能继续运行。
+**同步阻塞IO** : 用户进程发起一个IO操作以后,必须等待IO操作的真正完成后,才能继续运行。
-同步非阻塞IO: 客户端与服务器通过Channel连接,采用多路复用器轮询注册的`Channel`。提高吞吐量和可靠性。用户进程发起一个IO操作以后,可做其它事情,但用户进程需要轮询IO操作是否完成,这样造成不必要的CPU资源浪费。
+**同步非阻塞IO**: 客户端与服务器通过Channel连接,采用多路复用器轮询注册的`Channel`。提高吞吐量和可靠性。用户进程发起一个IO操作以后,可做其它事情,但用户进程需要轮询IO操作是否完成,这样造成不必要的CPU资源浪费。
-异步非阻塞IO: 非阻塞异步通信模式,NIO的升级版,采用异步通道实现异步通信,其read和write方法均是异步方法。用户进程发起一个IO操作,然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知。类似Future模式。
+**异步非阻塞IO**: 非阻塞异步通信模式,NIO的升级版,采用异步通道实现异步通信,其read和write方法均是异步方法。用户进程发起一个IO操作,然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知。类似Future模式。
## 守护线程是什么?
@@ -1036,6 +1036,6 @@ if (obj instanceof String) {
1. JDBC连接数据库时使用`Class.forName()`通过反射加载数据库的驱动程序
2. Eclispe、IDEA等开发工具利用反射动态解析对象的类型与结构,动态提示对象的属性和方法
-3. Web服务器中利用反射调用了Sevlet的service方法
-4. Spring AOP的特性也是依赖反射实现的
+3. Web服务器中利用反射调用了Sevlet的`service`方法
+4. JDK动态代理底层依赖反射实现
diff --git a/README.md b/README.md
index 1dbfbfd..1ffd1af 100644
--- a/README.md
+++ b/README.md
@@ -128,6 +128,7 @@
## RabbitMQ
1. [RabbitMQ核心知识总结](中间件/RabbitMQ.md) (推荐 :+1:)
+1. [消息队列面试题](中间件/消息队列面试题.md)
2. [死信队列](中间件/死信队列.md)
# 计算机网络
diff --git "a/\344\270\255\351\227\264\344\273\266/RabbitMQ.md" "b/\344\270\255\351\227\264\344\273\266/RabbitMQ.md"
index 87b20bf..2042d70 100644
--- "a/\344\270\255\351\227\264\344\273\266/RabbitMQ.md"
+++ "b/\344\270\255\351\227\264\344\273\266/RabbitMQ.md"
@@ -433,11 +433,3 @@ msg.getMessageProperties().setExpiration("3000");
[线上rabbitmq问题](https://juejin.im/post/6844904088212094983#heading-0)
-
-> 最后给大家分享一个github仓库,上面放了**200多本经典的计算机书籍**,包括C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生等,可以star一下,下次找书直接在上面搜索,仓库持续更新中~
->
-> github地址:https://github.com/Tyson0314/java-books
->
-> 如果github访问不了,可以访问gitee仓库。
->
-> gitee地址:https://gitee.com/tysondai/java-books
\ No newline at end of file
diff --git "a/\344\270\255\351\227\264\344\273\266/\346\266\210\346\201\257\351\230\237\345\210\227\351\235\242\350\257\225\351\242\230.md" "b/\344\270\255\351\227\264\344\273\266/\346\266\210\346\201\257\351\230\237\345\210\227\351\235\242\350\257\225\351\242\230.md"
new file mode 100644
index 0000000..bf71231
--- /dev/null
+++ "b/\344\270\255\351\227\264\344\273\266/\346\266\210\346\201\257\351\230\237\345\210\227\351\235\242\350\257\225\351\242\230.md"
@@ -0,0 +1,36 @@
+## 为什么要使用消息队列?
+
+总结一下,主要三点原因:**解耦、异步、削峰**。
+
+1、解耦。比如,用户下单后,订单系统需要通知库存系统,假如库存系统无法访问,则订单减库存将失败,从而导致订单操作失败。订单系统与库存系统耦合,这个时候如果使用消息队列,可以返回给用户成功,先把消息持久化,等库存系统恢复后,就可以正常消费减去库存了。
+
+2、异步。将消息写入消息队列,非必要的业务逻辑以异步的方式运行,不影响主流程业务。
+
+3、削峰。消费端慢慢的按照数据库能处理的并发量,从消息队列中慢慢拉取消息。在生产中,这个短暂的高峰期积压是允许的。比如秒杀活动,一般会因为流量过大,从而导致流量暴增,应用挂掉。这个时候加上消息队列,服务器接收到用户的请求后,首先写入消息队列,如果消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面。
+
+## 使用了消息队列会有什么缺点
+
+- 系统可用性降低。引入消息队列之后,如果消息队列挂了,可能会影响到业务系统的可用性。
+- 系统复杂性增加。加入了消息队列,要多考虑很多方面的问题,比如:一致性问题、如何保证消息不被重复消费、如何保证消息可靠性传输等。
+
+## RabbitMQ消息怎么路由?
+
+消息路由必须有三部分:**交换器、路由、绑定**。生产者把消息发布到交换器上;绑定决定了消息如何从路由器路由到特定的队列;消息最终到达队列,并被消费者接收。
+
+1、消息发布到交换器时,消息将拥有一个路由键(routing key),在消息创建时设定。
+
+2、通过队列路由键,可以把队列绑定到交换器上。
+
+3、消息到达交换器后,RabbitMQ会将消息的路由键与队列的路由键进行匹配(针对不同的交换器有不同的路由规则)。如果能够匹配到队列,则消息会投递到相应队列中。
+
+## RabbitMQ如何保证消息的顺序性?
+
+当生产者只有1个,消费者有多个,每个consumer依次从mq中拿数据取数据消费,这种时候,即使mq中的消息不乱序,但由于消费者处理消息的速度不同,最终产生结果的顺序会不同(比如消费者逻辑是插入数据到数据库)
+
+解决办法: 保证顺序的消息都放到1个queue里,同时只被1个消费者消费。
+
+## 如何避免消息重复消费?
+
+在消息生产时,MQ内部针对每条生产者发送的消息生成一个唯一id,作为去重和幂等的依据(消息投递失败并重传),避免重复的消息进入队列。
+
+在消息消费时,要求消息体中也要有一全局唯一id作为去重和幂等的依据,避免同一条消息被重复消费。
\ No newline at end of file
From dc17e5157e9de057dd4f34062cb395b7475daf9a Mon Sep 17 00:00:00 2001
From: tyson
Date: Sat, 26 Mar 2022 10:51:22 +0800
Subject: [PATCH 025/112] =?UTF-8?q?Java=E5=9F=BA=E7=A1=80=E9=9D=A2?=
=?UTF-8?q?=E8=AF=95=E9=A2=98=E8=A1=A5=E5=85=85?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...00\351\235\242\350\257\225\351\242\230.md" | 23 +++++++++++++++++++
...10\351\235\242\350\257\225\351\242\230.md" | 6 +++++
2 files changed, 29 insertions(+)
diff --git "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
index 5e54239..7d2ab7c 100644
--- "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
@@ -136,11 +136,23 @@ JRE是运行基于Java语言编写的程序所不可缺少的运行环境。也
| 二进制位数 | 1 | 8 | 16 | 16 | 32 | 64 | 32 | 64 |
| 包装类 | Boolean | Byte | Character | Short | Integer | Long | Float | Double |
+## 为什么不能用浮点型表示金额?
+
+由于计算机中保存的小数其实是十进制的小数的近似值,并不是准确值,所以,千万不要在代码中使用浮点数来表示金额等重要的指标。
+
+建议使用BigDecimal或者Long来表示金额。
+
## 什么是值传递和引用传递?
- 值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量。
- 引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。所以对引用对象进行操作会同时改变原对象。
+## 了解Java的包装类型吗?为什么需要包装类?
+
+Java 是一种面向对象语言,很多地方都需要使用对象而不是基本数据类型。比如,在集合类中,我们是无法将 int 、double 等类型放进去的。因为集合的容器要求元素是 Object 类型。
+
+为了让基本类型也具有对象的特征,就出现了包装类型。相当于将基本类型包装起来,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。
+
## 自动装箱和拆箱
Java中基础数据类型与它们对应的包装类见下表:
@@ -1026,6 +1038,12 @@ if (obj instanceof String) {
}
```
+## transient关键字的作用?
+
+Java语言的关键字,变量修饰符,如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。
+
+也就是说被transient修饰的成员变量,在序列化的时候其值会被忽略,在被反序列化后, transient 变量的值被设为初始值, 如 int 型的是 0,对象型的是 null。
+
## 什么是反射?
动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
@@ -1039,3 +1057,8 @@ if (obj instanceof String) {
3. Web服务器中利用反射调用了Sevlet的`service`方法
4. JDK动态代理底层依赖反射实现
+## 讲讲什么是泛型?
+
+Java泛型是JDK 5中引⼊的⼀个新特性, 允许在定义类和接口的时候使⽤类型参数。声明的类型参数在使⽤时⽤具体的类型来替换。
+
+泛型最⼤的好处是可以提⾼代码的复⽤性。以List接口为例,我们可以将String、 Integer等类型放⼊List中, 如不⽤泛型, 存放String类型要写⼀个List接口, 存放Integer要写另外⼀个List接口, 泛型可以很好的解决这个问题。
diff --git "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
index bfee0b6..174a642 100644
--- "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
@@ -174,6 +174,12 @@ return h&(length-1); //第三步 取模运算
在JDK1.8的实现中,优化了高位运算的算法,通过`hashCode()`的高16位异或低16位实现的:这么做可以在数组比较小的时候,也能保证考虑到高低位都参与到Hash的计算中,可以减少冲突,同时不会有太大的开销。
+## 为什么建议设置HashMap的容量?
+
+HashMap有扩容机制,就是当达到扩容条件时会进行扩容。扩容条件就是当HashMap中的元素个数超过临界值时就会自动扩容(threshold = loadFactor * capacity)。
+
+如果我们没有设置初始容量大小,随着元素的不断增加,HashMap会发生多次扩容。而HashMap每次扩容都需要重建hash表,非常影响性能。所以建议开发者在创建HashMap的时候指定初始化容量。
+
### 扩容过程?
1.8扩容机制:当元素个数大于`threshold`时,会进行扩容,使用2倍容量的数组代替原有数组。采用尾插入的方式将原数组元素拷贝到新数组。1.8扩容之后链表元素相对位置没有变化,而1.7扩容之后链表元素会倒置。
From 09ae10db27d8352d7a60fbada8f2f797594cfe90 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sat, 26 Mar 2022 14:33:16 +0800
Subject: [PATCH 026/112] =?UTF-8?q?=E6=9B=B4=E6=96=B0Java=E5=9F=BA?=
=?UTF-8?q?=E7=A1=80=E9=9D=A2=E8=AF=95=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...00\351\235\242\350\257\225\351\242\230.md" | 25 +++++++------------
README.md | 18 ++++++-------
2 files changed, 18 insertions(+), 25 deletions(-)
diff --git "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
index 7d2ab7c..8e229cd 100644
--- "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
@@ -190,26 +190,19 @@ int y = x; // 拆箱 调⽤了 X.intValue()
下面看一道常见的面试题:
```java
-public void testAutoBox() {
- int a = 100;
- Integer b = 100;
- System.out.println(a == b);
-
- Integer c = 100;
- Integer d = 100;
- System.out.println(c == d);
-
- Integer e = 200;
- Integer f = 200;
- System.out.println(e == f);
-}
+Integer a = 100;
+Integer b = 100;
+System.out.println(a == b);
+
+Integer c = 200;
+Integer d = 200;
+System.out.println(c == d);
```
输出:
```java
true
-true
false
```
@@ -223,7 +216,7 @@ public static Integer valueOf(int i) {
}
```
-`Integer e = 200;` 会调用 调⽤`Integer.valueOf(200)`。而从Integer的valueOf()源码可以看到,这里的实现并不是简单的new Integer,而是用IntegerCache做一个cache。
+`Integer c = 200;` 会调用 调⽤`Integer.valueOf(200)`。而从Integer的valueOf()源码可以看到,这里的实现并不是简单的new Integer,而是用IntegerCache做一个cache。
```java
private static class IntegerCache {
@@ -252,7 +245,7 @@ private static class IntegerCache {
}
```
-这是IntegerCache静态代码块中的一段,默认Integer cache 的下限是-128,上限默认127。当赋值100给Integer时,刚好在这个范围内,所以从cache中取对应的Integer并返回,所以二次返回的是同一个对象,所以==比较是相等的,当赋值200给Integer时,不在cache 的范围内,所以会new Integer并返回,当然==比较的结果是不相等的。
+这是IntegerCache静态代码块中的一段,默认Integer cache 的下限是-128,上限默认127。当赋值100给Integer时,刚好在这个范围内,所以从cache中取对应的Integer并返回,所以a和b返回的是同一个对象,所以==比较是相等的,当赋值200给Integer时,不在cache 的范围内,所以会new Integer并返回,当然==比较的结果是不相等的。
## String 为什么不可变?
diff --git a/README.md b/README.md
index 1ffd1af..c0d36c4 100644
--- a/README.md
+++ b/README.md
@@ -16,17 +16,17 @@
如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的**公众号『 程序员大彬 』**,后台回复『 PDF 』可以**下载最新版本的大厂高频面试题目PDF版本**。
-
+
个人公众号
-
-
-
-
-
-
+
+
+
+
+
+
@@ -176,7 +176,7 @@
如果想进**技术、面试交流群**,可以扫描下方二维码加我微信,**备注加群**,我拉你进群,群里有BAT大佬,互相学习~
-

+
@@ -187,7 +187,7 @@
| 微信 | 支付宝 |
| ----------------------------------------------------------- | ------------------------------------------------------------ |
-|  |  |
+|  |  |
每笔赞赏我会在下面记录下来,感谢你们,我会更加努力,砥砺前行~
From 9234ac1f36f8d3920b537a1fc7b41034319f201f Mon Sep 17 00:00:00 2001
From: tyson
Date: Sat, 26 Mar 2022 14:34:34 +0800
Subject: [PATCH 027/112] =?UTF-8?q?add=20=E6=93=8D=E4=BD=9C=E7=B3=BB?=
=?UTF-8?q?=E7=BB=9F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...37\351\235\242\350\257\225\351\242\230.md" | 41 +++++++++++++++++++
1 file changed, 41 insertions(+)
create mode 100644 "\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md"
diff --git "a/\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md" "b/\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md"
new file mode 100644
index 0000000..aab0c1a
--- /dev/null
+++ "b/\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md"
@@ -0,0 +1,41 @@
+## 操作系统里的内存碎片怎么理解?
+
+内存碎片通常分为内部碎片和外部碎片:
+
+1. 内部碎片是由于采用固定大小的内存分区,当一个进程不能完全使用分给它的固定内存区域时就会产生内部碎片。通常内部碎片难以完全避免
+2. 外部碎片是由于某些未分配的连续内存区域太小,以至于不能满足任意进程的内存分配请求,从而不能被进程利用的内存区域。
+
+## 有什么解决办法?
+
+现在普遍采取的内存分配方式是段页式内存分配。将内存分为不同的段,再将每一段分成固定大小的页。通过页表机制,使段内的页可以不必连续处于同一内存区域。
+
+## 操作系统的四个特性?
+
+并发:同一段时间内多个程序执行(与并行区分,并行指的是同一时刻有多个事件,多处理器系统可以使程序并行执行)
+
+共享:系统中的资源可以被内存中多个并发执行的进线程共同使用
+
+虚拟:通过分时复用(如分时系统)以及空分复用(如虚拟内存)技术把一个物理实体虚拟为多个
+
+异步:系统进程用一种走走停停的方式执行,(并不是一下子走完),进程什么时候以怎样的速度向前推进是不可预知的
+
+## 多线程相较单线程的好处
+
+1、并发提升程序执行效率
+
+2、提升CPU利用率,访存的时候可以切换线程来执行
+
+3、更快的响应速度,可以有专门的线程来监听用户请求和专门的线程来处理请求。比如监听线程和工作线程是两个线程,这样监听就负责监听,工作的就负责工作,监听到用户请求马上把请求转到工作线程去处理,监听线程继续监听
+
+## 什么是协程?
+
+协程是一种用户态的轻量级线程。
+
+协程不是由操作系统内核管理,而是完全由用户程序所控制,这样带来的好处就是性能得到了很大的提升,不会像线程切换那样消耗资源。
+
+协程可以理解为可以暂停执行的函数。它拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
+
+## 线程和协程有什么区别呢?
+
+1、线程是抢占式,而协程是非抢占式的,所以需要用户自己释放使用权来切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力。
+2、线程是协程的资源。协程通过 可以关联任意线程或线程池的执行器(Interceptor)来间接使用线程的资源的。
\ No newline at end of file
From e44546f63d22dd1f953d561405f69b77348c0d3c Mon Sep 17 00:00:00 2001
From: tyson
Date: Sat, 26 Mar 2022 14:46:53 +0800
Subject: [PATCH 028/112] update img path
---
.gitignore | 15 +++++++
Java/JVM.md | 32 +++++++-------
...21\351\235\242\350\257\225\351\242\230.md" | 14 +++---
"Java/Java\345\237\272\347\241\200.md" | 10 ++---
...21\351\235\242\350\257\225\351\242\230.md" | 12 ++---
...10\351\235\242\350\257\225\351\242\230.md" | 8 ++--
"Java/\345\271\266\345\217\221.md" | 22 +++++-----
"Java/\351\233\206\345\220\210.md" | 8 ++--
.../RabbitMQ.md" | 10 ++---
...07\345\215\227\346\200\273\347\273\223.md" | 16 +++----
...is\351\235\242\350\257\225\351\242\230.md" | 10 ++---
.../GitHub\346\214\207\345\215\227.md" | 18 ++++----
"\345\267\245\345\205\267/progit2.md" | 10 ++---
...47\350\241\214\350\256\241\345\210\222.md" | 44 +++++++++----------
.../MySQL\350\277\233\351\230\266.md" | 24 +++++-----
...21\351\235\242\350\257\225\351\242\230.md" | 24 +++++-----
...43\347\240\201\345\256\236\347\216\260.md" | 2 +-
...25\351\242\230\346\200\273\347\273\223.md" | 2 +-
...ng\351\235\242\350\257\225\351\242\230.md" | 2 +-
.../\347\275\221\347\273\234.md" | 2 +-
...21\351\235\242\350\257\225\351\242\230.md" | 34 +++++++-------
21 files changed, 167 insertions(+), 152 deletions(-)
create mode 100644 .gitignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..af0556b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+.DS_Store
+node_modules/
+/dist/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.py
diff --git a/Java/JVM.md b/Java/JVM.md
index 74430f8..05f5634 100644
--- a/Java/JVM.md
+++ b/Java/JVM.md
@@ -90,7 +90,7 @@ Java 内存模型(JMM)是基于共享内存的多线程通信机制。
JVM内存结构 = 类加载器 + 执行引擎 + 运行时数据区域 。
-
+
> 图片来源:深入理解Java虚拟机-周志明
@@ -172,11 +172,11 @@ JDK 1.8 的时候,HotSpot 的永久代被彻底移除了,使用元空间替
运行时常量池是方法区的一部分,在类加载之后,会将编译器生成的各种字面量和符号引号放到运行时常量池。在运行期间动态生成的常量,如 String 类的 intern()方法,也会被放入运行时常量池。
-
+
-
+
-
+
> 图片来源:https://blog.csdn.net/soonfly
@@ -194,11 +194,11 @@ Java 程序通过栈上的 reference 数据来操作堆上的具体对象。对
- 如果使用句柄的话,那么 Java 堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。使用句柄来访问的最大好处是 reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而 reference 本身不需要修改。
-
+
- 直接指针。reference 中存储的直接就是对象的地址。对象包含到对象类型数据的指针,通过这个指针可以访问对象类型数据。使用直接指针访问方式最大的好处就是访问对象速度快,它节省了一次指针定位的时间开销,虚拟机hotspot主要是使用直接指针来访问对象。
-
+
@@ -245,7 +245,7 @@ ClassFile {
加载、验证、准备、解析、初始化、使用和卸载。
-
+
## 类加载的过程
@@ -281,7 +281,7 @@ ClassFile {
一个类加载器收到一个类的加载请求时,它首先不会自己尝试去加载它,而是把这个请求委派给父类加载器去完成,这样层层委派,因此所有的加载请求最终都会传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
-
+
双亲委派模型的好处:可以防止内存中出现多份同样的字节码。如果没有双亲委派模型而是由各个类加载器自行加载的话,如果用户编写了一个java.lang.Object的同名类并放在ClassPath中,多个类加载器都去加载这个类到内存中,系统中将会出现多个不同的Object类,那么类之间的比较结果及类的唯一性将无法保证。
@@ -339,7 +339,7 @@ public abstract class ClassLoader {
堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断那些对象已经死亡(即不能再被任何途径使用的对象)。
-
+
### 引用计数法
@@ -365,7 +365,7 @@ public class ReferenceCountingGc {
通过GC Root对象为起点,从这些节点向下搜索,搜索所走过的路径叫引用链,当一个对象到GC Root没有任何的引用链相连时,说明这个对象是不可用的。
-
+
#### 可作为GC Roots的对象
@@ -469,13 +469,13 @@ public class ReferenceCountingGc {
标记清除算法就是分为“标记”和“清除”两个阶段。标记出所有需要回收的对象,标记结束后统一回收所有被标记的对象。这种垃圾回收算法效率较低,并且会产生大量不连续的空间碎片。
-
+
### 复制清除算法
半区复制,用于新生代垃圾回收。将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。
-
+
特点:实现简单,运行高效,但可用内存缩小为了原来的一半,浪费空间。
@@ -534,7 +534,7 @@ java -XX:+PrintCommandLineFlags -version
单线程收集器,使用一条垃圾收集线程去完成垃圾收集工作,在进行垃圾收集工作的时候必须暂停其他所有的工作线程( "Stop The World" ),直到它收集结束。
-
+
特点:简单高效;内存消耗最小;没有线程交互的开销,单线程收集效率高;需暂停所有的工作线程,用户体验不好。
@@ -542,7 +542,7 @@ java -XX:+PrintCommandLineFlags -version
Serial 收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全一样。
-
+
除了 Serial 收集器外,只有它能与 CMS 收集器配合工作。
@@ -582,7 +582,7 @@ Concurrent Mark Sweep 并发标记清除,目的是获取最短应用停顿时
- 重新标记: 在并发标记期间对象的引用关系可能会变化,需要重新进行标记。此阶段也会stw,停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短。
- 并发清除:清除死亡对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。
-
+
由于在整个过程中耗时最长的并发标记和并发清除阶段中,垃圾收集器线程都可以与用户线程一起工作,所以从总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。
@@ -607,7 +607,7 @@ G1垃圾收集器的目标是用在多核、大内存的机器上,在不同应
G1将整个堆分成相同大小的分区(Region),有四种不同类型的分区:Eden、Survivor、Old和Humongous(大对象)。分区的大小取值范围为1M到32M,都是2的幂次方。Region大小可以通过`-XX:G1HeapRegionSize`参数指定。Humongous区域用于存储大对象。G1认为只要大小超过了一个Region容量一半的对象即可判定为大对象。
-
+
G1 收集器对各个Region回收所获得的空间大小和回收所需时间的经验值进行排序,得到一个优先级列表,每次根据用户设置的最大的回收停顿时间(使用参数-XX:MaxGCPauseMillis指定,默认值是200毫秒),优先处理回收价值最大的 Region。
diff --git "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 7ccae91..e4bddb5 100644
--- "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -49,7 +49,7 @@
JVM内存结构分为5大区域,**程序计数器**、**虚拟机栈**、**本地方法栈**、**堆**、**方法区**。
-
+
### 程序计数器
@@ -185,7 +185,7 @@ ClassFile {
类的加载指的是将类的`class`文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个此类的对象,通过这个对象可以访问到方法区对应的类信息。
-
+
**加载**
@@ -213,7 +213,7 @@ ClassFile {
一个类加载器收到一个类的加载请求时,它首先不会自己尝试去加载它,而是把这个请求**委派**给父类加载器去完成,这样层层委派,因此所有的加载请求最终都会传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
-
+
双亲委派模型的具体实现代码在 `java.lang.ClassLoader`中,此类的 `loadClass()` 方法运行过程如下:先检查类是否已经加载过,如果没有则让父类加载器去加载。当父类加载器加载失败时抛出 `ClassNotFoundException`,此时尝试自己去加载。源码如下:
@@ -312,7 +312,7 @@ public class ReferenceCount {
通过`GC Root`对象为起点,从这些节点向下搜索,搜索所走过的路径叫引用链,当一个对象到`GC Root`没有任何的引用链相连时,说明这个对象是不可用的。
-
+
## 可作为GC Roots的对象有哪些?
@@ -413,7 +413,7 @@ GC(`Garbage Collection`),垃圾回收,是Java与C++的主要区别之一
首先利用可达性去遍历内存,把存活对象和垃圾对象进行标记。标记结束后统一将所有标记的对象回收掉。这种垃圾回收算法效率较低,并且会**产生大量不连续的空间碎片**。
-
+
**复制清除算法**
@@ -425,7 +425,7 @@ GC(`Garbage Collection`),垃圾回收,是Java与C++的主要区别之一
根据老年代的特点提出的一种标记算法,标记过程仍然与`标记-清除`算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。
-
+
**分类收集算法**
@@ -508,7 +508,7 @@ G1垃圾收集器的目标是在不同应用场景中**追求高吞吐量和低
G1将整个堆分成相同大小的分区(`Region`),有四种不同类型的分区:`Eden、Survivor、Old和Humongous`。分区的大小取值范围为 1M 到 32M,都是2的幂次方。分区大小可以通过`-XX:G1HeapRegionSize`参数指定。`Humongous`区域用于存储大对象。G1规定只要大小超过了一个分区容量一半的对象就认为是大对象。
-
+
G1 收集器对各个分区回收所获得的空间大小和回收所需时间的经验值进行排序,得到一个优先级列表,每次根据用户设置的最大回收停顿时间,优先回收价值最大的分区。
diff --git "a/Java/Java\345\237\272\347\241\200.md" "b/Java/Java\345\237\272\347\241\200.md"
index 0f3e5c8..72cff97 100644
--- "a/Java/Java\345\237\272\347\241\200.md"
+++ "b/Java/Java\345\237\272\347\241\200.md"
@@ -976,7 +976,7 @@ Class 类提供了一些方法,可以获取成员变量、成员方法、接
Field提供了类和接口中字段的信息,通过Field类可以动态访问这些字段。下图是Field类提供的一些方法。
-
+
## Method类
@@ -1101,7 +1101,7 @@ public class GenericMethod {
Throwable类是Error和Exception的父类,只有继承于Throwable的类或者其子类才能被抛出。Throwable分为两类:
-
+
- Error:JVM 无法解决的严重问题,如栈溢出(StackOverflowError)、内存溢出(OOM)等。程序无法处理的错误。
@@ -1198,7 +1198,7 @@ public class ExceptionTest {
Java IO流的核心就是对文件的操作,对于字节 、字符类型的输入和输出流。IO流主要分为两大类,字节流和字符流。字节流可以处理任何类型的数据,如图片,视频等,字符流只能处理字符类型的数据。
-
+
> 图片参考:[Java io学习整理](https://zhuanlan.zhihu.com/p/25418336)
@@ -1251,13 +1251,13 @@ OutputStreamWriter:字符到字节的转换,可对读取到的字符数据
同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。
-
+
## NIO
NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了 NIO 框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。
-
+
NIO与IO区别:
diff --git "a/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
index cfcb15d..a65d22c 100644
--- "a/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
@@ -64,7 +64,7 @@
### 线程池执行原理?
-
+
1. 当线程池里存活的线程数小于核心线程数`corePoolSize`时,这时对于一个新提交的任务,线程池会创建一个线程去处理任务。当线程池里面存活的线程数小于等于核心线程数`corePoolSize`时,线程池里面的线程会一直存活着,就算空闲时间超过了`keepAliveTime`,线程也不会被销毁,而是一直阻塞在那里一直等待任务队列的任务来执行。
2. 当线程池里面存活的线程数已经等于corePoolSize了,这是对于一个新提交的任务,会被放进任务队列workQueue排队等待执行。
@@ -199,7 +199,7 @@ public static ExecutorService newCachedThreadPool() {
3. 修改 `ScheduledFutureTask` 的 time 变量为下次将要被执行的时间;
4. 把这个修改 time 之后的 `ScheduledFutureTask` 放回 `DelayQueue` 中(`DelayQueue.add()`)。
-
+
适用场景:周期性执行任务的场景,需要限制线程数量的场景。
@@ -225,7 +225,7 @@ public static ExecutorService newCachedThreadPool() {
**终止(TERMINATED)**:表示该线程已经执行完毕。
-
+
> 图片来源:Java并发编程的艺术
@@ -401,7 +401,7 @@ class RunnableDemo implements Runnable {
如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方持有的资源,所以这两个线程就会互相等待而进入死锁状态。
-
+
下面通过例子说明线程死锁,代码来自并发编程之美。
@@ -686,7 +686,7 @@ class SeasonThreadTask implements Runnable{
每个线程都有一个`ThreadLocalMap`(`ThreadLocal`内部类),Map中元素的键为`ThreadLocal`,而值对应线程的变量副本。
-
+
调用`threadLocal.set()`-->调用`getMap(Thread)`-->返回当前线程的`ThreadLocalMap`-->`map.set(this, value)`,this是`threadLocal`本身。源码如下:
@@ -783,7 +783,7 @@ private volatile int state;//共享变量,使用volatile修饰保证线程可
同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态(独占或共享 )构造成为一个节点(Node)并将其加入同步队列并进行自旋,当同步状态释放时,会把首节点中的后继节点对应的线程唤醒,使其再次尝试获取同步状态。
-
+
## ReentrantLock 是如何实现可重入性的?
diff --git "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
index 174a642..bfec451 100644
--- "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
@@ -54,9 +54,9 @@ Java集合类主要由两个接口**Collection**和**Map**派生出来的,Coll
Java集合框架图如下:
-
+
-
+
List代表了有序可重复集合,可直接根据元素的索引来访问;Set代表无序不可重复集合,只能根据元素本身来访问;Queue是队列集合。Map代表的是存储key-value对的集合,可根据元素的key来访问value。
@@ -197,7 +197,7 @@ HashMap有扩容机制,就是当达到扩容条件时会进行扩容。扩容
5. 链表的数量大于阈值8,就要转换成红黑树的结构
6. 添加成功后会检查是否需要扩容
-
+
### 红黑树的特点?
@@ -271,7 +271,7 @@ public class TreeMap
TreeMap 的继承结构:
-
+
**TreeMap的特点:**
diff --git "a/Java/\345\271\266\345\217\221.md" "b/Java/\345\271\266\345\217\221.md"
index c9734f5..1f35396 100644
--- "a/Java/\345\271\266\345\217\221.md"
+++ "b/Java/\345\271\266\345\217\221.md"
@@ -87,7 +87,7 @@ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveT
创建新的线程需要获取全局锁,通过这种设计可以尽量避免获取全局锁,当 ThreadPoolExecutor 完成预热之后(当前运行的线程数大于等于 corePoolSize),提交的大部分任务都会被放到 BlockingQueue。
-
+
ThreadPoolExecutor 的通用构造函数:
@@ -326,7 +326,7 @@ public static ExecutorService newCachedThreadPool() {
3. 修改 `ScheduledFutureTask` 的 time 变量为下次将要被执行的时间;
4. 把这个修改 time 之后的 `ScheduledFutureTask` 放回 `DelayQueue` 中(`DelayQueue.add()`)。
-
+
适用场景:周期性执行任务的场景,需要限制线程数量的场景。
@@ -340,7 +340,7 @@ FixedThreadPool和SingleThreadPool。允许请求队列长度为 Integer.MAX_VAL
CachedThreadPool。创建的线程池允许的最大线程数是Integer.MAX_VALUE,当添加任务的速度大于线程池处理任务的速度,可能会创建大量的线程,消耗资源,甚至导致OOM。
-
+
正确示例(阿里巴巴编码规范):
@@ -369,7 +369,7 @@ Java内存模型:线程之间的共享变量存储在主内存里,每个线
1. 线程a把本地内存a中的更新过的共享变量刷新到主内存中去。
2. 线程b到主内存中去读取线程a刷新过的共享变量,然后复制一份到本地内存b中去。
-
+
本地内存是JMM的一个抽象概念,并不真实存在,它包括缓存、写缓冲区、寄存器以及其他硬件和编译器优化。
@@ -392,7 +392,7 @@ Java内存模型:线程之间的共享变量存储在主内存里,每个线
终止(TERMINATED):表示该线程已经执行完毕。
-
+
> 图片来源:Java并发编程的艺术
@@ -680,7 +680,7 @@ public class SynchronizedDemo {
Synchronized底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。
-
+
**方法的同步**不是通过添加monitorenter和monitorexit指令来完成,而是在其常量池中添加了ACC_SYNCHRONIZED标识符。JVM就是根据该标识符来实现方法的同步的:当线程调用方法时,会先检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,说明此方法是同步方法,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他线程无法再获得同一个monitor对象。
@@ -692,7 +692,7 @@ public class SynchronizedMethod {
}
```
-
+
#### 锁的状态
@@ -1077,7 +1077,7 @@ private volatile int state;//共享变量,使用volatile修饰保证线程可
同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态(独占或共享 )构造成为一个节点(Node)并将其加入同步队列并进行自旋,当同步状态释放时,会把首节中的后继节点对应的线程唤醒,使其再次尝试获取同步状态。
-
+
@@ -1112,19 +1112,19 @@ public void conditionSignal() throws InterruptedException {
每个Condition对象都包含着一个等待队列,如果一个线程成功获取了锁之后调用了Condition.await()方法,那么该线程将会释放同步状态、唤醒同步队列中的后继节点,然后构造成节点加入等待队列。只有当线程再次获取Condition相关联的锁之后,才能从await()方法返回。
-
+
> 图片来源:Java并发编程的艺术
在Object的监视器模型上,一个对象拥有一个同步队列和等待队列。Lock通过AQS实现,AQS可以有多个Condition,所以Lock拥有一个同步队列和多个等待队列。
-
+
> 图片来源:Java并发编程的艺术
线程获取了锁之后,调用Condition的signal()方法,会将等待队列的队首节点移到同步队列中,然后该节点的线程会尝试去获取同步状态。成功获取同步状态之后,线程将await()方法返回。
-
+
> 图片来源:Java并发编程的艺术
diff --git "a/Java/\351\233\206\345\220\210.md" "b/Java/\351\233\206\345\220\210.md"
index c7283d5..83fecae 100644
--- "a/Java/\351\233\206\345\220\210.md"
+++ "b/Java/\351\233\206\345\220\210.md"
@@ -51,7 +51,7 @@
以 Map 结尾的类都实现了 Map 接口,其他所有的类都实现了 Collection 接口。
-
+
### HashMap
@@ -88,7 +88,7 @@ put方法流程:
5. 链表的数量大于阈值8,就要转换成红黑树的结构
6. 添加成功后会检查是否需要扩容
-
+
> 参考链接:
>
@@ -121,7 +121,7 @@ HashMap是无序的,迭代HashMap所得到元素的顺序并不是它们最初
LinkedHashMap继承于HashMap,是HashMap和LinkedList的融合体,具备两者的特性。每次put操作都会将entry插入到双向链表的尾部。
-
+
### TreeMap
@@ -136,7 +136,7 @@ public class TreeMap
TreeMap 的继承结构:
-
+
**TreeMap的特点:**
diff --git "a/\344\270\255\351\227\264\344\273\266/RabbitMQ.md" "b/\344\270\255\351\227\264\344\273\266/RabbitMQ.md"
index 2042d70..e5ba58b 100644
--- "a/\344\270\255\351\227\264\344\273\266/RabbitMQ.md"
+++ "b/\344\270\255\351\227\264\344\273\266/RabbitMQ.md"
@@ -39,13 +39,13 @@
文章目录:
-
+
# 简介
RabbitMQ是一个由erlang开发的消息队列。消息队列用于应用间的异步协作。
-
+
## 基本概念
@@ -94,19 +94,19 @@ Exchange规则。
direct交换机会将消息路由到binding key 和 routing key完全匹配的队列中。它是完全匹配、单播的模式。
-
+
## fanout
所有发到 fanout 类型交换机的消息都会路由到所有与该交换机绑定的队列上去。fanout 类型转发消息是最快的。
-
+
## topic
topic交换机使用routing key和binding key进行模糊匹配,匹配成功则将消息发送到相应的队列。routing key和binding key都是句点号“. ”分隔的字符串,binding key中可以存在两种特殊字符“*”与“#”,用于做模糊匹配,其中“\*”用于匹配一个单词,“#”用于匹配多个单词。
-
+
## headers
diff --git "a/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md" "b/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
index f329ea8..8d4e0d7 100644
--- "a/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
+++ "b/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
@@ -71,7 +71,7 @@
目录结构如下:
-
+
## 简介
@@ -503,7 +503,7 @@ ziplist是 Redis 为了节约内存而开发的, 由一系列特殊编码的
- 最底层的链表包含所有的元素
- 跳跃表的查找次数近似于层数,时间复杂度为O(logn),插入、删除也为 O(logn)
-
+
#### 对象
@@ -545,7 +545,7 @@ hash类型内部编码有两种:
Redis3.2版本提供了quicklist内部编码,简单地说它是以一个ziplist为节点的linkedlist,它结合了ziplist和linkedlist两者的优势,为列表类型提供了一种更为优秀的内部编码实现。
-
+
使用场景:
@@ -630,7 +630,7 @@ GET #返回文章ID。
3. EXEC命令进行提交事务
-
+
DISCARD:放弃事务,即该事务内的所有命令都将取消
@@ -653,7 +653,7 @@ QUEUED
事务里的命令执行时会读取最新的值:
-
+
### WATCH命令
@@ -800,7 +800,7 @@ SLAVEOF NO ONE //停止接收其他数据库的同步并转化为主数据库。
5. 同步数据集。第一次同步的时候,从数据库启动后会向主数据库发送SYNC命令。主数据库接收到命令后开始在后台保存快照(RDB持久化过程),并将保存快照过程接收到的命令缓存起来。当快照完成后,Redis会将快照文件和缓存的命令发送到从数据库。从数据库接收到后,会载入快照文件并执行缓存的命令。以上过程称为复制初始化。
6. 复制初始化完成后,主数据库每次收到写命令就会将命令同步给从数据库,从而实现主从数据库数据的一致性。
-
+
Redis在2.8及以上版本使用psync命令完成主从数据同步,同步过程分为:全量复制和部分复制。
@@ -932,7 +932,7 @@ redis 127.0.0.1:6379> EVALSHA "232fd51614574cf0867b83d384a5e898cfd24e5a" 0
使用evalsha执行Lua脚本过程如下:
-
+
### lua脚本作用
@@ -1075,7 +1075,7 @@ public void write(String key,Object data){
解决方法:
-
+
> 图片来源:https://tech.it168.com
diff --git "a/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md" "b/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
index 43fb271..c7fe523 100644
--- "a/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
+++ "b/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
@@ -178,7 +178,7 @@ scan的缺点:在scan的过程中如果有键的变化(增加、删除、修
3. EXEC命令进行提交事务
-
+
一个事务范围内某个命令出错不会影响其他命令的执行,不保证原子性:
@@ -249,7 +249,7 @@ Redis支持两种方式的持久化,一种是`RDB`的方式,一种是`AOF`
`bgsave`是主流的触发 RDB 持久化的方式,执行过程如下:
-
+
- 执行`BGSAVE`命令
- Redis 父进程判断当前**是否存在正在执行的子进程**,如果存在,`BGSAVE`命令直接返回。
@@ -294,7 +294,7 @@ appendfsync no //由操作系统决定何时进行同步操作
接下来看一下 AOF 持久化执行流程:
-
+
1. 所有的写入命令会追加到 AOP 缓冲区中。
2. AOF 缓冲区根据对应的策略向硬盘同步。
@@ -359,7 +359,7 @@ SLAVEOF NO ONE //停止接收其他数据库的同步并转化为主数据库。
客户端连接Redis的时候,先连接哨兵,哨兵会告诉客户端Redis主节点的地址,然后客户端连接上Redis并进行后续的操作。当主节点宕机的时候,哨兵监测到主节点宕机,会重新推选出某个表现良好的从节点成为新的主节点,然后通过发布订阅模式通知其他的从服务器,让它们切换主机。
-
+
**工作原理**
@@ -378,7 +378,7 @@ Redis cluster集群节点最小配置6个节点以上(3主3从),其中主
Redis cluster采用**虚拟槽分区**,所有的键根据哈希函数映射到0~16383个整数槽内,每个节点负责维护一部分槽以及槽所映射的键值数据。
-
+
**哈希槽是如何映射到 Redis 实例上的?**
diff --git "a/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md" "b/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md"
index cca4277..a125d4b 100644
--- "a/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md"
+++ "b/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md"
@@ -22,15 +22,15 @@ Git又是什么呢?简单的说,**Git 是一个管理你的代码历史记
- 创建一个新的仓库,用来存放项目。
- 
+ 
- 或者你在GitHub上看到别人有一个超级无敌有趣的项目,可以直接fork过来,可以理解成复制过来,变成你自己的。之后你想怎么改就怎么改!
- 
+ 
- 然后你可以通过Git命令行`git clone xxx`把项目clone到本地,在本地进行创作。
- 
+ 
- 最后,在本地创作完成,可以使用`git commit -m xxx`提交到本地库,然后使用`git push`把修改推送到GitHub仓库。之后就可以在GitHub上面看到你修改的内容啦~
@@ -42,23 +42,23 @@ Git又是什么呢?简单的说,**Git 是一个管理你的代码历史记
- 评价GitHub项目的两个重要的参数:star和fork。
-
+
比较优秀和热门的项目,star数目和fork数目都会比较多。我们可以根据这两个参数筛选出比较好的项目。使用`关键字 stars:>=xxx forks:>=xxx` 可以筛选出star和fork数目大于xxx的相关项目。
-
+
- 使用 `awesome 关键字`,可以筛选出比较高质量的学习、书籍、工具类或者插件类的集合。
-
+
- 在特定位置搜索关键词。有些关键词出现在项目的不同位置,比如项目名称、项目描述和README等。使用`关键词 in name/description/Readme`,可以搜索到相关的内容。比如使用`spring in name`,可以搜索到在项目名中包含spring的项目。
-
+
- 指定条件搜索关键词。如`tool language:java`搜索到的是包含关键字tool的Java项目,`tool followers:>1000`可以搜索到包含关键字tool,且follower数量大于1000的项目。
-
+
@@ -72,7 +72,7 @@ Git又是什么呢?简单的说,**Git 是一个管理你的代码历史记
- 文档神器。可以为自己的项目建立wiki,可以用markdown语法写wiki;
-
+
- 使用GitHub pages建立个人静态网站,搞一个有自己域名的独立博客,想想都觉得开心。使用GitHub pages的好处是搭建简单而且免费,支持静态脚本,并且可以绑定自己的域名。具体可以参考:[GitHub Pages 建立个人网站详细教程 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/58229299)
diff --git "a/\345\267\245\345\205\267/progit2.md" "b/\345\267\245\345\205\267/progit2.md"
index 6dde18b..68b8c2b 100644
--- "a/\345\267\245\345\205\267/progit2.md"
+++ "b/\345\267\245\345\205\267/progit2.md"
@@ -85,7 +85,7 @@ Git工作流程如下:
Git 的工作流程图如下:
-
+
> 图片来源:https://blog.csdn.net/ThinkWon/article/details/94346816
@@ -103,11 +103,11 @@ Git 的三种状态:已修改(modified)、已暂存(staged)和已提
基本的 Git 工作流程:在工作目录修改文件;暂存文件,将文件快照放到暂存区域;提交更新到本地库。暂存区保存了下次将要提交的文件列表信息,一般在 Git 仓库目录中。
-
+
> 图片来源:`https://img2018.cnblogs.com/blog/1252910/201907/1252910-20190726163829113-2056815874.png`
-
+
## 配置
@@ -156,7 +156,7 @@ git clone https://github.com/...
查看文件状态:`git status`
-
+
> 图片来源:`https://img2018.cnblogs.com/blog/1252910/201907/1252910-20190726163854195-886320537.png`
@@ -262,7 +262,7 @@ git commit -m "add readme.md"
单独执行`git commit`,不带上-m参数,会进入 vim 编辑器界面:
-
+
此时应该这么操作:
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md"
index 2378225..e9f255a 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md"
@@ -91,7 +91,7 @@ where blog_id = (
三个表依次嵌套,发现最里层的子查询 `id`最大,最先执行。
-
+
## select_type
@@ -107,13 +107,13 @@ where blog_id = (
查询的表名,并不一定是真实存在的表,有别名显示别名,也可能为临时表。当from子句中有子查询时,table列是 ``的格式,表示当前查询依赖 id为N的查询,会先执行 id为N的查询。
-
+
## partitions
查询时匹配到的分区信息,对于非分区表值为`NULL`,当查询的是分区表时,`partitions`显示分区表命中的分区情况。
-
+
## type
@@ -123,25 +123,25 @@ where blog_id = (
当表仅有一行记录时(系统表),数据量很少,往往不需要进行磁盘IO,速度非常快。比如,Mysql系统表proxies_priv在Mysql服务启动时候已经加载在内存中,对这个表进行查询不需要进行磁盘 IO。
-
+
### const
单表操作的时候,查询使用了主键或者唯一索引。
-
+
### eq_ref
**多表关联**查询的时候,主键和唯一索引作为关联条件。如下图的sql,对于user表(外循环)的每一行,user_role表(内循环)只有一行满足join条件,只要查找到这行记录,就会跳出内循环,继续外循环的下一轮查询。
-
+
### ref
查找条件列使用了索引而且不为主键和唯一索引。虽然使用了索引,但该索引列的值并不唯一,这样即使使用索引查找到了第一条数据,仍然不能停止,要在目标值附近进行小范围扫描。但它的好处是不需要扫全表,因为索引是有序的,即便有重复值,也是在一个非常小的范围内做扫描。
-
+
### ref_or_null
@@ -151,13 +151,13 @@ where blog_id = (
使用了索引合并优化方法,查询使用了两个以上的索引。新建comment表,id为主键,value_id为非唯一索引,执行`explain select content from comment where value_id = 1181000 and id > 1000;`,执行结果显示查询同时使用了id和value_id索引,type列的值为index_merge。
-
+
### range
有范围的索引扫描,相对于index的全索引扫描,它有范围限制,因此要优于index。像between、and、'>'、'<'、in和or都是范围索引扫描。
-
+
### index
@@ -165,17 +165,17 @@ index包括select索引列,order by主键两种情况。
1. order by主键。这种情况会按照索引顺序全表扫描数据,拿到的数据是按照主键排好序的,不需要额外进行排序。
- 
+ 
2. select索引列。type为index,而且extra字段为using index,也称这种情况为索引覆盖。所需要取的数据都在索引列,无需回表查询。
- 
+ 
### all
全表扫描,查询没有用到索引,性能最差。
-
+
## possible_keys
@@ -249,13 +249,13 @@ CREATE TABLE `t_orderdetail` (
查询的列未被索引覆盖,where筛选条件非索引的前导列。对存储引擎返回的结果进行过滤(Post-filter,后过滤),一般发生在MySQL服务器,而不是存储引擎层。
-
+
### using index
查询的列被索引覆盖,并且where筛选条件符合最左前缀原则,通过**索引查找**就能直接找到符合条件的数据,不需要回表查询数据。
-
+
### Using where&Using index
@@ -265,17 +265,17 @@ CREATE TABLE `t_orderdetail` (
- where筛选条件不符合最左前缀原则
- 
+ 
- where筛选条件是索引列前导列的一个范围
- 
+ 
### null
查询的列未被索引覆盖,并且where筛选条件是索引的前导列,也就是用到了索引,但是部分字段未被索引覆盖,必须回表查询这些字段,Extra中为NULL。
-
+
### using index condition
@@ -283,11 +283,11 @@ CREATE TABLE `t_orderdetail` (
不使用ICP的情况(`set optimizer_switch='index_condition_pushdown=off'`),如下图,在步骤4中,没有使用where条件过滤索引:
-
+
使用ICP的情况(`set optimizer_switch='index_condition_pushdown=on'`):
-
+
下面的例子使用了ICP:
@@ -296,11 +296,11 @@ explain select user_id, order_id, order_status
from t_order where user_id > 1 and user_id < 5\G;
```
-
+
关掉ICP之后(`set optimizer_switch='index_condition_pushdown=off'`),可以看到extra列为using where,不会使用索引下推。
-
+
### using temporary
@@ -314,7 +314,7 @@ from t_order where user_id > 1 and user_id < 5\G;
- select 查询字段不全是索引字段
- select 查询字段都是索引字段,但是 order by 字段和索引字段的顺序不一致
-
+
### using join buffer
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md"
index 4ff8a64..7caa4e5 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md"
@@ -138,7 +138,7 @@ B+ 树是基于B 树和叶子节点顺序访问指针进行实现,它具有B
在 B+ 树中,节点中的 key 从左到右递增排列,如果某个指针的左右相邻 key 分别是 keyi 和 keyi+1,则该指针指向节点的所有 key 大于等于 keyi 且小于等于 keyi+1。
-
+
进行查找操作时,首先在根节点进行二分查找,找到key所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出 key 所对应的数据项。
@@ -170,15 +170,15 @@ Index_comment:
如下图,col1 是主键,col2和col3是普通字段。
-
+
下图是主键索引对应的 B+树结构,每个节点对应磁盘的一页。
-
+
对col3 建立一个单列索引,对应的B+树结构:
-
+
### 索引分类
1. 主键索引:名为primary的唯一非空索引,不允许有空值。
@@ -208,7 +208,7 @@ Index_comment:
对(a, b) 建立索引,a 在索引树中是全局有序的,而 b 是全局无序,局部有序(当a相等时,会对b进行比较排序)。直接执行`b = 2`这种查询条件没有办法利用索引。
-
+
从局部来看,当a的值确定的时候,b是有序的。例如a = 1时,b值为1,2是有序的状态。当a=2时候,b的值为1,4也是有序状态。 因此,你执行`a = 1 and b = 2`是a,b字段能用到索引的。而你执行`a > 1 and b = 2`时,a字段能用到索引,b字段用不到索引。因为a的值此时是一个范围,不是固定的,在这个范围内b值不是有序的,因此b字段用不上索引。
@@ -218,7 +218,7 @@ InnoDB使用表的主键构造主键索引树,同时叶子节点中存放的
聚集索引的叶子节点就是整张表的行记录。InnoDB 主键使用的是聚簇索引。聚集索引要比非聚集索引查询效率高很多。
-
+
对于InnoDB来说,聚集索引一般是表中的主键索引,如果表中没有显示指定主键,则会选择表中的第一个不允许为NULL的唯一索引。如果没有主键也没有合适的唯一索引,那么innodb内部会生成一个隐藏的主键作为聚集索引,这个隐藏的主键长度为6个字节,它的值会随着数据的插入自增。
@@ -244,7 +244,7 @@ explain select user_id from user_like where blog_id = 1;
Extra中为`Using where; Using index`, 查询的列被索引覆盖,where筛选条件不符合最左前缀原则,无法通过索引查找找到符合条件的数据,但可以通过**索引扫描**找到符合条件的数据,也不需要回表查询数据。
-
+
### 索引失效
@@ -322,7 +322,7 @@ MEMORY引擎默认使用哈希索引,将键的哈希值和指向数据行的
myisam引擎主键索引和其他索引区别不大,叶子节点都包含索引值和行指针。
innodb引擎二级索引叶子存储的是索引值和主键值(不是行指针),这样可以减少行移动和数据页分裂时二级索引的维护工作。
- 
+ 
@@ -351,7 +351,7 @@ mvcc实现依赖于版本链,版本链是通过表的三个隐藏字段实现
3. 修改当前行的值,生成一个新版本,更新事务id,使回滚指针指向旧版本的记录,这样就形成一条版本链;
4. 记录redo log;
-
+
### read view
@@ -388,14 +388,14 @@ repeatable read:在一个事务范围内,第一次select时更新这个read_
事务a和事务b同时开启事务,事务a插入数据然后提交,事务b执行全表的update,然后执行查询,查到了事务A中添加的数据。
-
+
MySQL如何实现避免幻读:
- 在快照读情况下,MySQL通过mvcc来避免幻读。
- 在当前读情况下,MySQL通过next-key来避免幻读(加行锁和间隙锁来实现的)。
-
+
next-key包括两部分:行锁和间隙锁。行锁是加在索引上的锁,间隙锁是加在索引之间的。
@@ -531,7 +531,7 @@ MySQL主要分为 Server 层和存储引擎层:
- **Server 层**:主要包括连接器、查询缓存、分析器、优化器、执行器等,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图,函数等,还有一个通用的日志模块 binglog 日志模块。
- **存储引擎**: 主要负责数据的存储和读取。server 层通过api与存储引擎进行通信。
-
+
### Server 层基本组件
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index c403e91..8d7fee4 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -163,7 +163,7 @@ B+ 树是基于B 树和叶子节点顺序访问指针进行实现,它具有B
在 B+ 树中,节点中的 `key` 从左到右递增排列,如果某个指针的左右相邻 `key` 分别是 keyi 和 keyi+1,则该指针指向节点的所有 `key` 大于等于 keyi 且小于等于 keyi+1。
-
+
进行查找操作时,首先在根节点进行二分查找,找到`key`所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出`key`所对应的数据项。
@@ -233,7 +233,7 @@ ADD CONSTRAINT constraint_name UNIQUE KEY(column_1,column_2,...);
如下图,对(a, b) 建立索引,a 在索引树中是全局有序的,而 b 是全局无序,局部有序(当a相等时,会根据b进行排序)。直接执行`b = 2`这种查询条件无法使用索引。
-
+
当a的值确定的时候,b是有序的。例如`a = 1`时,b值为1,2是有序的状态。当`a = 2`时候,b的值为1,4也是有序状态。 当执行`a = 1 and b = 2`时a和b字段能用到索引。而执行`a > 1 and b = 2`时,a字段能用到索引,b字段用不到索引。因为a的值此时是一个范围,不是固定的,在这个范围内b值不是有序的,因此b字段无法使用索引。
@@ -267,7 +267,7 @@ explain select user_id from user_like where blog_id = 1;
`explain`结果的`Extra`列为`Using where; Using index`, 查询的列被索引覆盖,where筛选条件不符合最左前缀原则,无法通过索引查找找到符合条件的数据,但可以通过**索引扫描**找到符合条件的数据,也不需要回表查询数据。
-
+
### 索引的设计原则?
@@ -373,7 +373,7 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
每条表记录大概是这样的:
-
+
使用事务更新行记录的时候,就会生成版本链,执行过程如下:
@@ -385,15 +385,15 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
1、初始数据如下,其中`DB_ROW_ID`和`DB_ROLL_PTR`为空。
-
+
2、事务A对该行数据做了修改,将`age`修改为12,效果如下:
-
+
3、之后事务B也对该行记录做了修改,将`age`修改为8,效果如下:
-
+
4、此时undo log有两行记录,并且通过回滚指针连在一起。
@@ -413,7 +413,7 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
**前提**:`DATA_TRX_ID` 表示每个数据行的最新的事务ID;`up_limit_id`表示当前快照中的最先开始的事务;`low_limit_id`表示当前快照中的最慢开始的事务,即最后一个事务。
-
+
- 如果`DATA_TRX_ID` < `up_limit_id`:说明在创建`read view`时,修改该数据行的事务已提交,该版本的记录可被当前事务读取到。
- 如果`DATA_TRX_ID` >= `low_limit_id`:说明当前版本的记录的事务是在创建`read view`之后生成的,该版本的数据行不可以被当前事务访问。此时需要通过版本链找到上一个版本,然后重新判断该版本的记录对当前事务的可见性。
@@ -438,7 +438,7 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
1、首先,user表只有两条记录,具体如下:
-
+
2、事务a和事务b同时开启事务`start transaction`;
@@ -456,7 +456,7 @@ update user set user_name = 'a';
5、事务b然后执行查询,查到了事务a中插入的数据。(下图左边是事务b,右边是事务a。事务开始之前只有两条记录,事务a插入一条数据之后,事务b查询出来是三条数据)
-
+
以上就是当前读出现的幻读现象。
@@ -545,7 +545,7 @@ MySQL主要分为 Server 层和存储引擎层:
垂直划分数据库是根据业务进行划分,例如购物场景,可以将库中涉及商品、订单、用户的表分别划分出成一个库,通过降低单库的大小来提高性能。同样的,分表的情况就是将一个大表根据业务功能拆分成一个个子表,例如商品基本信息和商品描述,商品基本信息一般会展示在商品列表,商品描述在商品详情页,可以将商品基本信息和商品描述拆分成两张表。
-
+
**优点**:行记录变小,数据页可以存放更多记录,在查询时减少I/O次数。
@@ -559,7 +559,7 @@ MySQL主要分为 Server 层和存储引擎层:
水平划分是根据一定规则,例如时间或id序列值等进行数据的拆分。比如根据年份来拆分不同的数据库。每个数据库结构一致,但是数据得以拆分,从而提升性能。
-
+
**优点**:单库(表)的数据量得以减少,提高性能;切分出的表结构相同,程序改动较少。
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
index 8d8d1be..d8eef1a 100644
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
+++ "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
@@ -14,7 +14,7 @@
常见的排序算法主要有:冒泡排序、插入排序、选择排序、快速排序、归并排序、堆排序、基数排序。各种排序算法的时间空间复杂度、稳定性见下图。
-
+
# 冒泡排序
diff --git "a/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" "b/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
index 7c93750..9df8bdd 100644
--- "a/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
+++ "b/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
@@ -11,7 +11,7 @@
SpringBoot实现自动配置原理图解:
-
+
在 application.properties 中设置属性 debug=true,可以在控制台查看已启用和未启用的自动配置。
diff --git "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md" "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
index 5ede718..9f7b047 100644
--- "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
@@ -210,7 +210,7 @@ finishBeanFactoryInitialization(beanFactory);
## Bean的生命周期
-
+
1.调用bean的构造方法创建Bean
diff --git "a/\347\275\221\347\273\234/\347\275\221\347\273\234.md" "b/\347\275\221\347\273\234/\347\275\221\347\273\234.md"
index 0320b99..60a94c1 100644
--- "a/\347\275\221\347\273\234/\347\275\221\347\273\234.md"
+++ "b/\347\275\221\347\273\234/\347\275\221\347\273\234.md"
@@ -333,7 +333,7 @@ HTTP长连接,指的是复用TCP连接。多个HTTP请求可以复用同一个
CDN用户访问流程:
-
+
1.用户向浏览器输入www.web.com这个域名,浏览器第一次发现本地没有dns缓存,则向网站的DNS服务器请求;
diff --git "a/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 2fb8b79..48969f2 100644
--- "a/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -38,7 +38,7 @@
计算机网络体系大致分为三种,OSI七层模型、TCP/IP四层模型和五层模型。一般面试的时候考察比较多的是五层模型。
-
+
五层模型:应用层、传输层、网络层、数据链路层、物理层。
@@ -52,7 +52,7 @@
假设发送端为客户端,接收端为服务端。开始时客户端和服务端的状态都是`CLOSED`。
-
+
1. 第一次握手:客户端向服务端发起建立连接请求,客户端会随机生成一个起始序列号x,客户端向服务端发送的字段中包含标志位`SYN=1`,序列号`seq=x`。第一次握手前客户端的状态为`CLOSE`,第一次握手后客户端的状态为`SYN-SENT`。此时服务端的状态为`LISTEN`。
2. 第二次握手:服务端在收到客户端发来的报文后,会随机生成一个服务端的起始序列号y,然后给客户端回复一段报文,其中包括标志位`SYN=1`,`ACK=1`,序列号`seq=y`,确认号`ack=x+1`。第二次握手前服务端的状态为`LISTEN`,第二次握手后服务端的状态为`SYN-RCVD`,此时客户端的状态为`SYN-SENT`。(其中`SYN=1`表示要和客户端建立一个连接,`ACK=1`表示确认序号有效)
@@ -69,7 +69,7 @@
## 四次挥手
-
+
1. A的应用进程先向其TCP发出连接释放报文段(`FIN=1,seq=u`),并停止再发送数据,主动关闭TCP连接,进入`FIN-WAIT-1`(终止等待1)状态,等待B的确认。
2. B收到连接释放报文段后即发出确认报文段(`ACK=1,ack=u+1,seq=v`),B进入`CLOSE-WAIT`(关闭等待)状态,此时的TCP处于半关闭状态,A到B的连接释放。
@@ -152,7 +152,7 @@ Content-Length:112
## HTTP状态码有哪些?
-
+
## POST和GET的区别?
@@ -194,7 +194,7 @@ HTTP2.0相比HTTP1.1支持的特性:
服务端可以向证书颁发机构CA申请证书,以避免中间人攻击(防止证书被篡改)。证书包含三部分内容:**证书内容、证书签名算法和签名**,签名是为了验证身份。
-
+
服务端把证书传输给浏览器,浏览器从证书里取公钥。证书可以证明该公钥对应本网站。
@@ -216,29 +216,29 @@ HTTP2.0相比HTTP1.1支持的特性:
1. **协商加密算法** 。在`Client Hello`里面客户端会告知服务端自己当前的一些信息,包括客户端要使用的TLS版本,支持的加密算法,要访问的域名,给服务端生成的一个随机数(Nonce)等。需要提前告知服务器想要访问的域名以便服务器发送相应的域名的证书过来。
- 
+ 
2. 服务端响应`Server Hello`,告诉客户端服务端**选中的加密算法**。
- 
+ 
3. 接着服务端给客户端发来了2个证书。第二个证书是第一个证书的签发机构(CA)的证书。
- 
+ 
4. 客户端使用证书的认证机构CA公开发布的RSA公钥**对该证书进行验证**,下图表明证书认证成功。
- 
+ 
5. 验证通过之后,浏览器和服务器通过**密钥交换算法**产生共享的**对称密钥**。
- 
+ 
- 
+ 
6. 开始传输数据,使用同一个对称密钥来加解密。
- 
+ 
## DNS 的解析过程?
@@ -257,7 +257,7 @@ HTTP2.0相比HTTP1.1支持的特性:
4. 服务器**响应请求**,返回响应数据。
5. 浏览器**解析响应内容,进行渲染**,呈现给用户。
-
+
## 什么是cookie和session?
@@ -265,7 +265,7 @@ HTTP2.0相比HTTP1.1支持的特性:
由于HTTP协议是无状态的协议,需要用某种机制来识具体的用户身份,用来跟踪用户的整个会话。常用的会话跟踪技术是cookie与session。
**cookie**就是由服务器发给客户端的特殊信息,而这些信息以文本文件的方式存放在客户端,然后客户端每次向服务器发送请求的时候都会带上这些特殊的信息。说得更具体一些:当用户使用浏览器访问一个支持cookie的网站的时候,用户会提供包括用户名在内的个人信息并且提交至服务器;接着,服务器在向客户端回传相应的超文本的同时也会发回这些个人信息,当然这些信息并不是存放在HTTP响应体中的,而是存放于HTTP响应头;当客户端浏览器接收到来自服务器的响应之后,浏览器会将这些信息存放在一个统一的位置。 自此,客户端再向服务器发送请求的时候,都会把相应的cookie存放在HTTP请求头再次发回至服务器。服务器在接收到来自客户端浏览器的请求之后,就能够通过分析存放于请求头的cookie得到客户端特有的信息,从而动态生成与该客户端相对应的内容。网站的登录界面中“请记住我”这样的选项,就是通过cookie实现的。
-
+
**cookie工作流程**:
@@ -274,7 +274,7 @@ HTTP2.0相比HTTP1.1支持的特性:
3. 下次访问时,浏览器将自动携带cookie数据发送给服务器。
**session原理**:首先浏览器请求服务器访问web站点时,服务器首先会检查这个客户端请求是否已经包含了一个session标识、称为SESSIONID,如果已经包含了一个sessionid则说明以前已经为此客户端创建过session,服务器就按照sessionid把这个session检索出来使用,如果客户端请求不包含session id,则服务器为此客户端创建一个session,并且生成一个与此session相关联的独一无二的sessionid存放到cookie中,这个sessionid将在本次响应中返回到客户端保存,这样在交互的过程中,浏览器端每次请求时,都会带着这个sessionid,服务器根据这个sessionid就可以找得到对应的session。以此来达到共享数据的目的。 这里需要注意的是,session不会随着浏览器的关闭而死亡,而是等待超时时间。
-
+
## Cookie和Session的区别?
@@ -293,7 +293,7 @@ HTTP2.0相比HTTP1.1支持的特性:
TCP 利用滑动窗口实现流量控制。流量控制是为了控制发送方发送速率,保证接收方来得及接收。 TCP会话的双方都各自维护一个发送窗口和一个接收窗口。接收窗口大小取决于应用、系统、硬件的限制。发送窗口则取决于对端通告的接收窗口。接收方发送的确认报文中的window字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将接收方的确认报文window字段设置为 0,则发送方不能发送数据。
-
+
TCP头包含window字段,16bit位,它代表的是窗口的字节容量,最大为65535。这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。接收窗口的大小是约等于发送窗口的大小。
@@ -302,7 +302,7 @@ TCP头包含window字段,16bit位,它代表的是窗口的字节容量,最
防止过多的数据注入到网络中。 几种拥塞控制方法:慢开始( slow-start )、拥塞避免( congestion avoidance )、快重传( fast retransmit )和快恢复( fast recovery )。
-
+
### 慢开始
From 75801c53fe4162b5b615edb91101666607eea014 Mon Sep 17 00:00:00 2001
From: tyson
Date: Tue, 10 May 2022 21:02:30 +0800
Subject: [PATCH 029/112] update
---
...00\351\235\242\350\257\225\351\242\230.md" | 118 +++++++++++++++++-
...10\351\235\242\350\257\225\351\242\230.md" | 14 ++-
...is\351\235\242\350\257\225\351\242\230.md" | 45 ++-----
...27\351\235\242\350\257\225\351\242\230.md" | 4 +-
...76\350\256\241\346\250\241\345\274\217.md" | 9 ++
...37\351\235\242\350\257\225\351\242\230.md" | 17 ++-
...21\351\235\242\350\257\225\351\242\230.md" | 14 ++-
...43\347\240\201\345\256\236\347\216\260.md" | 15 ++-
...60\346\215\256\347\273\223\346\236\204.md" | 38 +-----
.../SpringBoot\345\256\236\346\210\230.md" | 8 +-
...25\351\242\230\346\200\273\347\273\223.md" | 68 ++++------
...15\345\212\241\345\256\236\346\210\230.md" | 4 +-
...ng\351\235\242\350\257\225\351\242\230.md" | 45 +++++--
13 files changed, 248 insertions(+), 151 deletions(-)
diff --git "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
index 8e229cd..5fa2390 100644
--- "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
@@ -95,15 +95,53 @@
黑白双方负责接受用户的输入,并告知棋盘系统棋子布局发生变化,棋盘系统接收到了棋子的变化的信息就负责在屏幕上面显示出这种变化,同时利用规则系统来对棋局进行判定。
-## JKD和JRE的区别?
+## JKD/JRE/JVM三者的关系
-JDK和JRE是Java开发和运行工具,其中JDK包含了JRE,而JRE是可以独立安装的。
+### JVM
-**JDK**:Java Development Kit,JAVA语言的软件工具开发包,是整个JAVA开发的核心,它包含了JAVA的运行(JVM+JAVA类库)环境和JAVA工具。
+**JVM** :英文名称(Java Virtual Machine),就是我们耳熟能详的 Java 虚拟机。Java 能够跨平台运行的核心在于 JVM 。
-**JRE**:Java Runtime Environment,Java运行环境,包含JVM标准实现及Java核心类库。JRE是Java运行环境,并不是一个开发环境,所以没有包含任何开发工具(如编译器和调试器)。
+
-JRE是运行基于Java语言编写的程序所不可缺少的运行环境。也是通过它,Java的开发者才得以将自己开发的程序发布到用户手中,让用户使用。
+所有的java程序会首先被编译为.class的类文件,这种类文件可以在虚拟机上执行。也就是说class文件并不直接与机器的操作系统交互,而是经过虚拟机间接与操作系统交互,由虚拟机将程序解释给本地系统执行。
+
+针对不同的系统有不同的 jvm 实现,有 Linux 版本的 jvm 实现,也有Windows 版本的 jvm 实现,但是同一段代码在编译后的字节码是一样的。这就是Java能够跨平台,实现一次编写,多处运行的原因所在。
+
+### JRE
+
+英文名称(Java Runtime Environment),就是Java 运行时环境。我们编写的Java程序必须要在JRE才能运行。它主要包含两个部分,JVM 和 Java 核心类库。
+
+
+
+JRE是Java的运行环境,并不是一个开发环境,所以没有包含任何开发工具,如编译器和调试器等。
+
+如果你只是想运行Java程序,而不是开发Java程序的话,那么你只需要安装JRE即可。
+
+### JDK
+
+英文名称(Java Development Kit),就是 Java 开发工具包
+
+学过Java的同学,都应该安装过JDK。当我们安装完JDK之后,目录结构是这样的
+
+
+
+可以看到,JDK目录下有个JRE,也就是JDK中已经集成了 JRE,不用单独安装JRE。
+
+另外,JDK中还有一些好用的工具,如jinfo,jps,jstack等。
+
+
+
+
+
+### 总结
+
+最后,总结一下JDK/JRE/JVM,他们三者的关系
+
+JRE = JVM + Java 核心类库
+
+JDK = JRE + Java工具 + 编译器 + 调试器
+
+
## 面向对象有哪些特性?
@@ -944,6 +982,76 @@ unchecked Exception:
- **throws**:用在方法签名中,用于声明该方法可能抛出的异常。子类方法抛出的异常范围更加小,或者根本不抛异常。
+## 通过故事讲清楚NIO
+
+下面通过一个例子来讲解下。
+
+假设某银行只有10个职员。该银行的业务流程分为以下4个步骤:
+
+1) 顾客填申请表(5分钟);
+
+2) 职员审核(1分钟);
+
+3) 职员叫保安去金库取钱(3分钟);
+
+4) 职员打印票据,并将钱和票据返回给顾客(1分钟)。
+
+下面我们看看银行不同的工作方式对其工作效率到底有何影响。
+
+首先是BIO方式。
+
+每来一个顾客,马上由一位职员来接待处理,并且这个职员需要负责以上4个完整流程。当超过10个顾客时,剩余的顾客需要排队等候。
+
+一个职员处理一个顾客需要10分钟(5+1+3+1)时间。一个小时(60分钟)能处理6个顾客,一共10个职员,那就是只能处理60个顾客。
+
+可以看到银行职员的工作状态并不饱和,比如在第1步,其实是处于等待中。
+
+这种工作其实就是BIO,每次来一个请求(顾客),就分配到线程池中由一个线程(职员)处理,如果超出了线程池的最大上限(10个),就扔到队列等待 。
+
+那么如何提高银行的吞吐量呢?
+
+思路就是:**分而治之**,将任务拆分开来,由专门的人负责专门的任务。
+
+具体来讲,银行专门指派一名职员A,A的工作就是每当有顾客到银行,他就递上表格让顾客填写。每当有顾客填好表后,A就将其随机指派给剩余的9名职员完成后续步骤。
+
+这种方式下,假设顾客非常多,职员A的工作处于饱和中,他不断的将填好表的顾客带到柜台处理。
+
+柜台一个职员5分钟能处理完一个顾客,一个小时9名职员能处理:9*(60/5)=108。
+
+可见工作方式的转变能带来效率的极大提升。
+
+这种工作方式其实就NIO的思路。
+
+下图是非常经典的NIO说明图,`mainReactor`线程负责监听server socket,接收新连接,并将建立的socket分派给`subReactor`
+
+`subReactor`可以是一个线程,也可以是线程池,负责多路分离已连接的socket,读写网络数据。这里的读写网络数据可类比顾客填表这一耗时动作,对具体的业务处理功能,其扔给worker线程池完成
+
+可以看到典型NIO有三类线程,分别是`mainReactor`线程、`subReactor`线程、`work`线程。
+
+不同的线程干专业的事情,最终每个线程都没空着,系统的吞吐量自然就上去了。
+
+
+
+
+
+**那这个流程还有没有什么可以提高的地方呢?**
+
+可以看到,在这个业务流程里边第3个步骤,职员叫保安去金库取钱(3分钟)。这3分钟柜台职员是在等待中度过的,可以把这3分钟利用起来。
+
+还是分而治之的思路,指派1个职员B来专门负责第3步骤。
+
+每当柜台员工完成第2步时,就通知职员B来负责与保安沟通取钱。这时候柜台员工可以继续处理下一个顾客。
+
+当职员B拿到钱之后,通知顾客钱已经到柜台了,让顾客重新排队处理,当柜台职员再次服务该顾客时,发现该顾客前3步已经完成,直接执行第4步即可。
+
+在当今web服务中,经常需要通过RPC或者Http等方式调用第三方服务,这里对应的就是第3步,如果这步耗时较长,通过异步方式将能极大降低资源使用率。
+
+NIO+异步的方式能让少量的线程做大量的事情。这适用于很多应用场景,比如代理服务、api服务、长连接服务等等。这些应用如果用同步方式将耗费大量机器资源。
+
+不过虽然NIO+异步能提高系统吞吐量,但其并不能让一个请求的等待时间下降,相反可能会增加等待时间。
+
+最后,NIO基本思想总结起来就是:**分而治之,将任务拆分开来,由专门的人负责专门的任务**
+
## BIO/NIO/AIO区别的区别?
**同步阻塞IO** : 用户进程发起一个IO操作以后,必须等待IO操作的真正完成后,才能继续运行。
diff --git "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
index bfec451..14acae6 100644
--- "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
@@ -141,7 +141,7 @@ while(itr.hasNext()) {
## Arraylist 和 Vector 的区别
-1. ArrayList在内存不够时默认是扩展50% + 1个,Vector是默认扩展1倍。
+1. ArrayList在内存不够时扩容为原来的1.5倍,Vector是扩容为原来的2倍。
2. Vector属于线程安全级别的,但是大多数情况下不使用Vector,因为操作Vector效率比较低。
## Arraylist 与 LinkedList的区别
@@ -422,7 +422,7 @@ put 操作流程:
### CopyOnWrite
-写时复制。当我们往容器添加元素时,不直接往容器添加,而是先将当前容器进行复制,复制出一个新的容器,然后往新的容器添加元素,添加完元素之后,再将原容器的引用指向新容器。这样做的好处就是可以对`CopyOnWrite`容器进行并发的读而不需要加锁,因为当前容器不会被修改。
+Copy-On-Write,写时复制。当我们往容器添加元素时,不直接往容器添加,而是先将当前容器进行复制,复制出一个新的容器,然后往新的容器添加元素,添加完元素之后,再将原容器的引用指向新容器。这样做的好处就是可以对`CopyOnWrite`容器进行并发的读而不需要加锁,因为当前容器不会被修改。
```java
public boolean add(E e) {
@@ -443,13 +443,19 @@ put 操作流程:
从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是`CopyOnWriteArrayList`和`CopyOnWriteArraySet`。
-`CopyOnWriteArrayList`中add方法添加的时候是需要加锁的,保证同步,避免了多线程写的时候复制出多个副本。读的时候不需要加锁,如果读的时候有其他线程正在向`CopyOnWriteArrayList`添加数据,还是可以读到旧的数据。
-
**缺点:**
- 内存占用问题。由于CopyOnWrite的写时复制机制,在进行写操作的时候,内存里会同时驻扎两个对象的内存。
- CopyOnWrite容器不能保证数据的实时一致性,可能读取到旧数据。
+### CopyOnWriteArrayList
+
+CopyOnWriteArrayList相当于线程安全的ArrayList,CopyOnWriteArrayList使用了一种叫写时复制的方法,当有新元素add到CopyOnWriteArrayList时,先从原有的数组中拷贝一份出来,然后在新的数组做写操作,写完之后,再将原来的数组引用指向到新数组。
+
+`CopyOnWriteArrayList`中add方法添加的时候是需要加锁的,保证同步,避免了多线程写的时候复制出多个副本。读的时候不需要加锁,如果读的时候有其他线程正在向`CopyOnWriteArrayList`添加数据,还是可以读到旧的数据。
+
+CopyOnWrite并发容器用于读多写少的并发场景。
+
### ConcurrentLinkedQueue
非阻塞队列。高效的并发队列,使用链表实现。可以看做一个线程安全的 `LinkedList`,通过 CAS 操作实现。
diff --git "a/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md" "b/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
index c7fe523..d5704d2 100644
--- "a/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
+++ "b/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
@@ -35,14 +35,6 @@
-> 首先给大家分享一个github仓库,上面放了**200多本经典的计算机书籍**,包括C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生等,可以star一下,下次找书直接在上面搜索,仓库持续更新中~
->
-> github地址:https://github.com/Tyson0314/java-books
->
-> 如果github访问不了,可以访问gitee仓库。
->
-> gitee地址:https://gitee.com/tysondai/java-books
-
## Redis是什么?
Redis(`Remote Dictionary Server`)是一个使用 C 语言编写的,高性能非关系型的键值对数据库。与传统数据库不同的是,Redis 的数据是存在内存中的,所以读写速度非常快,被广泛应用于缓存方向。Redis可以将数据写入磁盘中,保证了数据的安全不丢失,而且Redis的操作是原子性的。
@@ -52,11 +44,11 @@ Redis(`Remote Dictionary Server`)是一个使用 C 语言编写的,高性
**优点**:
1. **基于内存操作**,内存读写速度快。
-2. Redis是**单线程**的,避免线程切换开销及多线程的竞争问题。单线程是指网络请求使用一个线程来处理,即一个线程处理所有网络请求,Redis 运行时不止有一个线程,比如数据持久化的过程会另起线程。
-3. **支持多种数据类型**,包括String、Hash、List、Set、ZSet等。
-4. **支持持久化**。Redis支持RDB和AOF两种持久化机制,持久化功能可以有效地避免数据丢失问题。
-5. **支持事务**。Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。
+2. **支持多种数据类型**,包括String、Hash、List、Set、ZSet等。
+3. **支持持久化**。Redis支持RDB和AOF两种持久化机制,持久化功能可以有效地避免数据丢失问题。
+4. **支持事务**。Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。
6. **支持主从复制**。主节点会自动将数据同步到从节点,可以进行读写分离。
+6. Redis命令的处理是**单线程**的。Redis6.0引入了多线程,需要注意的是,**多线程用于处理网络数据的读写和协议解析**,Redis命令执行还是单线程的。
**缺点**:
@@ -67,10 +59,7 @@ Redis(`Remote Dictionary Server`)是一个使用 C 语言编写的,高性
## Redis为什么这么快?
- **基于内存**:Redis是使用内存存储,没有磁盘IO上的开销。数据存在内存中,读写速度快。
-- **单线程实现**( Redis 6.0以前):Redis使用单个线程处理请求,避免了多个线程之间线程切换和锁资源争用的开销。
-
- **IO多路复用模型**:Redis 采用 IO 多路复用技术。Redis 使用单线程来轮询描述符,将数据库的操作都转换成了事件,不在网络I/O上浪费过多的时间。
-
- **高效的数据结构**:Redis 每种数据类型底层都做了优化,目的就是为了追求更快的速度。
## 讲讲Redis的线程模型?
@@ -82,20 +71,6 @@ Redis基于Reactor模式开发了网络事件处理器,这个处理器被称
虽然文件事件处理器以单线程方式运行, 但通过使用 I/O 多路复用程序来监听多个套接字, 文件事件处理器既实现了高性能的网络通信模型, 又可以很好地与 redis 服务器中其他同样以单线程方式运行的模块进行对接, 这保持了 Redis 内部单线程设计的简单性。
-## Redis为何选择单线程模型?
-
-* 避免过多的**上下文切换开销**。程序始终运行在进程中单个线程内,没有多线程切换的场景。
-* **避免同步机制的开销**:如果 Redis选择多线程模型,需要考虑数据同步的问题,则必然会引入某些同步机制,会导致在操作数据过程中带来更多的开销,增加程序复杂度的同时还会降低性能。
-* **实现简单,方便维护**:如果 Redis使用多线程模式,那么所有的底层数据结构的设计都必须考虑线程安全问题,那么 Redis 的实现将会变得更加复杂。
-
-## Redis6.0为何引入多线程?
-
-Redis支持多线程主要有两个原因:
-
-* 可以充分利用服务器 CPU 资源,单线程模型的主线程只能利用一个cpu;
-
-* 多线程任务可以分摊 Redis 同步 IO 读写的负荷。
-
## Redis应用场景有哪些?
1. **缓存热点数据**,缓解数据库的压力。
@@ -106,12 +81,12 @@ Redis支持多线程主要有两个原因:
## Memcached和Redis的区别?
-1. Redis 只使用**单核**,而 Memcached 可以使用多核。
-2. MemCached 数据结构单一,仅用来缓存数据,而 **Redis 支持多种数据类型**。
-3. MemCached 不支持数据持久化,重启后数据会消失。**Redis 支持数据持久化**。
-4. **Redis 提供主从同步机制和 cluster 集群部署能力**,能够提供高可用服务。Memcached 没有提供原生的集群模式,需要依靠客户端实现往集群中分片写入数据。
-5. Redis 的速度比 Memcached 快很多。
-6. Redis 使用**单线程的多路 IO 复用模型**,Memcached使用多线程的非阻塞 IO 模型。
+1. MemCached 数据结构单一,仅用来缓存数据,而 **Redis 支持多种数据类型**。
+2. MemCached 不支持数据持久化,重启后数据会消失。**Redis 支持数据持久化**。
+3. **Redis 提供主从同步机制和 cluster 集群部署能力**,能够提供高可用服务。Memcached 没有提供原生的集群模式,需要依靠客户端实现往集群中分片写入数据。
+4. Redis 的速度比 Memcached 快很多。
+5. Redis 使用**单线程的多路 IO 复用模型**,Memcached使用多线程的非阻塞 IO 模型。(Redis6.0引入了多线程IO,**用来处理网络数据的读写和协议解析**,但是命令的执行仍然是单线程)
+6. value 值大小不同:Redis 最大可以达到 512M;memcache 只有 1mb。
## 为什么要用 Redis 而不用 map/guava 做缓存?
diff --git "a/\344\270\255\351\227\264\344\273\266/\346\266\210\346\201\257\351\230\237\345\210\227\351\235\242\350\257\225\351\242\230.md" "b/\344\270\255\351\227\264\344\273\266/\346\266\210\346\201\257\351\230\237\345\210\227\351\235\242\350\257\225\351\242\230.md"
index bf71231..adbb65e 100644
--- "a/\344\270\255\351\227\264\344\273\266/\346\266\210\346\201\257\351\230\237\345\210\227\351\235\242\350\257\225\351\242\230.md"
+++ "b/\344\270\255\351\227\264\344\273\266/\346\266\210\346\201\257\351\230\237\345\210\227\351\235\242\350\257\225\351\242\230.md"
@@ -25,9 +25,7 @@
## RabbitMQ如何保证消息的顺序性?
-当生产者只有1个,消费者有多个,每个consumer依次从mq中拿数据取数据消费,这种时候,即使mq中的消息不乱序,但由于消费者处理消息的速度不同,最终产生结果的顺序会不同(比如消费者逻辑是插入数据到数据库)
-
-解决办法: 保证顺序的消息都放到1个queue里,同时只被1个消费者消费。
+单线程消费保证消息的顺序性;对消息进行编号,消费者根据编号处理消息。
## 如何避免消息重复消费?
diff --git "a/\345\205\266\344\273\226/\350\256\276\350\256\241\346\250\241\345\274\217.md" "b/\345\205\266\344\273\226/\350\256\276\350\256\241\346\250\241\345\274\217.md"
index e8f37eb..3808e3a 100644
--- "a/\345\205\266\344\273\226/\350\256\276\350\256\241\346\250\241\345\274\217.md"
+++ "b/\345\205\266\344\273\226/\350\256\276\350\256\241\346\250\241\345\274\217.md"
@@ -22,6 +22,15 @@
+## 设计模式的六大原则
+
+- 开闭原则:对扩展开放,对修改关闭,多使用抽象类和接口。
+- 里氏替换原则:基类可以被子类替换,使用抽象类继承,不使用具体类继承。
+- 依赖倒转原则:要依赖于抽象,不要依赖于具体,针对接口编程,不针对实现编程。
+- 接口隔离原则:使用多个隔离的接口,比使用单个接口好,建立最小的接口。
+- 迪米特法则:一个软件实体应当尽可能少地与其他实体发生相互作用,通过中间类建立联系。
+- 合成复用原则:尽量使用合成/聚合,而不是使用继承。
+
## 单例模式
需要对实例字段使用线程安全的延迟初始化,使用双重检查锁定的方案;需要对静态字段使用线程安全的延迟初始化,使用静态内部类的方案。
diff --git "a/\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md" "b/\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md"
index aab0c1a..c068c14 100644
--- "a/\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md"
@@ -38,4 +38,19 @@
## 线程和协程有什么区别呢?
1、线程是抢占式,而协程是非抢占式的,所以需要用户自己释放使用权来切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力。
-2、线程是协程的资源。协程通过 可以关联任意线程或线程池的执行器(Interceptor)来间接使用线程的资源的。
\ No newline at end of file
+2、线程是协程的资源。协程通过 可以关联任意线程或线程池的执行器(Interceptor)来间接使用线程的资源的。
+
+## 进程通信
+
+进程间通信方式有以下几种:
+
+1、**管道通信**
+
+ 匿名管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
+ 有名管道是半双工的通信方式,数据只能单向流动。
+
+2、**消息队列**
+
+3、**共享内存**。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
+
+4、**信号量**。信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
\ No newline at end of file
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 8d7fee4..72a67a0 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -70,7 +70,7 @@
举个例子。假定选课关系表为`student_course`(student_no, student_name, age, course_name, grade, credit),主键为(student_no, course_name)。其中学分完全依赖于课程名称,姓名年龄完全依赖学号,不符合第二范式,会导致数据冗余(学生选n门课,姓名年龄有n条记录)、插入异常(插入一门新课,因为没有学号,无法保存新课记录)等问题。
-可以拆分成三个表:学生:`student`(stuent_no, student_name, 年龄);课程:`course`(course_name, credit);选课关系:`student_course_relation`(student_no, course_name, grade)。
+应该拆分成三个表:学生:`student`(stuent_no, student_name, 年龄);课程:`course`(course_name, credit);选课关系:`student_course_relation`(student_no, course_name, grade)。
**第三范式3NF**
@@ -302,6 +302,10 @@ explain select user_id from user_like where blog_id = 1;
ALTER TABLE table_name ADD KEY(column_name(prefix_length));
```
+### 索引下推
+
+参考我的另一篇文章:[图解索引下推!](http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247486661&idx=1&sn=4771369fd5c624c96647696ae76eca4a&chksm=ce98f183f9ef789562ac15c02b0d0dfdf3d2f178ea26667f5aded5d24c1004f049b951e85b33&scene=126&&sessionid=1650167859#rd)
+
## 常见的存储引擎有哪些?
MySQL中常用的四种存储引擎分别是: **MyISAM**、**InnoDB**、**MEMORY**、**ARCHIVE**。MySQL 5.5版本后默认的存储引擎为`InnoDB`。
@@ -571,13 +575,15 @@ MySQL主要分为 Server 层和存储引擎层:
## 什么是分区表?
-分区表是一个独立的逻辑表,但是底层由多个物理子表组成。
+分区是把一张表的数据分成N多个区块。分区表是一个独立的逻辑表,但是底层由多个物理子表组成。
当查询条件的数据分布在某一个分区的时候,查询引擎只会去某一个分区查询,而不是遍历整个表。在管理层面,如果需要删除某一个分区的数据,只需要删除对应的分区即可。
+分区一般都是放在单机里的,用的比较多的是时间范围分区,方便归档。只不过分库分表需要代码实现,分区则是mysql内部实现。分库分表和分区并不冲突,可以结合使用。
+
## 分区表类型
-**按照范围分区。**
+**range分区**,按照范围分区。比如按照时间范围分区
```java
CREATE TABLE test_range_partition(
@@ -614,7 +620,7 @@ CREATE TABLE test_range_partition(
**list分区**
-对于`List`分区,分区字段必须是已知的,如果插入的字段不在分区时枚举值中,将无法插入。
+list分区和range分区相似,主要区别在于list是枚举值列表的集合,range是连续的区间值的集合。对于list分区,分区字段必须是已知的,如果插入的字段不在分区时的枚举值中,将无法插入。
```java
create table test_list_partiotion
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
index d8eef1a..9ad4fb0 100644
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
+++ "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
@@ -1,6 +1,5 @@
-**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [冒泡排序](#%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F)
- [插入排序](#%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F)
@@ -16,7 +15,7 @@

-# 冒泡排序
+## 冒泡排序
```java
public void bubbleSort(int[] arr) {
@@ -41,7 +40,7 @@
}
```
-# 插入排序
+## 插入排序
```java
public void insertSort(int[] arr) {
@@ -59,7 +58,7 @@
}
```
-# 选择排序
+## 选择排序
```java
public void selectionSort(int[] arr) {
@@ -78,12 +77,12 @@
}
```
-# 基数排序
+## 基数排序
在基数排序中,因为没有比较操作,所以在时间复杂上,最好的情况与最坏的情况在时间上是一致的,均为 O(d * (n + r))。d 为位数,r 为基数,n 为原数组个数。

-# 快速排序
+## 快速排序
```java
public void quickSort(int[] arr) {
@@ -126,7 +125,7 @@
}
```
-# 归并排序
+## 归并排序

@@ -179,7 +178,7 @@ public class MergeSort {
}
```
-# 堆排序
+## 堆排序
堆是具有下列性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\225\260\346\215\256\347\273\223\346\236\204.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\225\260\346\215\256\347\273\223\346\236\204.md"
index 19ab566..6f5ed74 100644
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\225\260\346\215\256\347\273\223\346\236\204.md"
+++ "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\225\260\346\215\256\347\273\223\346\236\204.md"
@@ -11,7 +11,7 @@
-# 各种数据结构应用场景
+## 各种数据结构应用场景
- 栈:逆序输出;语法检查,符号成对判断;方法调用
- 二叉树:表达式树
@@ -22,7 +22,7 @@
- # AVL树
+ ## AVL树
平衡二叉搜索树,它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1。
@@ -38,7 +38,7 @@
-# B树
+## B树
也称B-树,属于多叉树又名平衡多路查找树。
@@ -66,7 +66,7 @@ B-和B+树的区别
-# 红黑树
+## 红黑树
红黑树是对AVL树的优化,只要求部分平衡,用非严格的平衡来换取增删节点时候旋转次数的降低,提高了插入和删除的性能。查找性能并没有提高,查找的时间复杂度是O(logn)。红黑树通过左旋、右旋和变色维持平衡。
@@ -85,7 +85,7 @@ B-和B+树的区别
-# 图
+## 图
图由顶点集(vertex set)和边集(edge set)所组成。

@@ -180,31 +180,3 @@ public interface Graph
-# 二分查找
-
-```java
- public int binarySearch(int[] arr, int target) {
- if (arr == null || arr.length <= 1) {
- return -1;
- }
-
- int left = 0;
- int right = arr.length - 1;
-
- while (left <= right) {
- int mid = (left + right) >>> 1;
- if (arr[mid] > target) {
- right = mid - 1;
- } else if (arr[mid] < target) {
- left = mid + 1;
- } else {
- return mid;
- }
- }
-
- return -1;
- }
-```
-
-
-
diff --git "a/\346\241\206\346\236\266/SpringBoot\345\256\236\346\210\230.md" "b/\346\241\206\346\236\266/SpringBoot\345\256\236\346\210\230.md"
index 72e6551..2c1a423 100644
--- "a/\346\241\206\346\236\266/SpringBoot\345\256\236\346\210\230.md"
+++ "b/\346\241\206\346\236\266/SpringBoot\345\256\236\346\210\230.md"
@@ -38,10 +38,10 @@
## Spring Boot基础
理念:习惯优于配置,内置习惯性配置,无需手动进行配置。使用Spring boot可以很快创建一个独立运行、准生产级别的基于Spring框架的项目,不需要或者只需很少的Spring配置。
### 特点
-- 内置servlet容器,不需要在服务器部署 tomcat。只需要将项目打成 jar 包,使用 java -jar xxx.jar一键式启动项目;
-- SpringBoot提供了starter,把常用库聚合在一起,简化复杂的环境配置,快速搭建spring应用环境。
-
-
+- 内置servlet容器,不需要在服务器部署 tomcat。只需要将项目打成 jar 包,使用 java -jar xxx.jar一键式启动项目
+- SpringBoot提供了starter,把常用库聚合在一起,简化复杂的环境配置,快速搭建spring应用环境
+- 可以快速创建独立运行的spring项目,集成主流框架
+- 准生产环境的运行应用监控
## Spring Boot核心
diff --git "a/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" "b/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
index 9df8bdd..e9ddb23 100644
--- "a/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
+++ "b/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
@@ -1,16 +1,33 @@
-
-
-**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
+## Springboot的优点
-- [自动配置原理](#%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AE%E5%8E%9F%E7%90%86)
-- [实现自动配置](#%E5%AE%9E%E7%8E%B0%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AE)
+- 内置servlet容器,不需要在服务器部署 tomcat。只需要将项目打成 jar 包,使用 java -jar xxx.jar一键式启动项目
+- SpringBoot提供了starter,把常用库聚合在一起,简化复杂的环境配置,快速搭建spring应用环境
+- 可以快速创建独立运行的spring项目,集成主流框架
+- 准生产环境的运行应用监控
-
+## SpringBoot 中的 starter 到底是什么 ?
+
+starter提供了一个自动化配置类,一般命名为 XXXAutoConfiguration ,在这个配置类中通过条件注解来决定一个配置是否生效(条件注解就是 Spring 中原本就有的),然后它还会提供一系列的默认配置,也允许开发者根据实际情况自定义相关配置,然后通过类型安全的属性注入将这些配置属性注入进来,新注入的属性会代替掉默认属性。正因为如此,很多第三方框架,我们只需要引入依赖就可以直接使用了。
+
+## 运行 SpringBoot 有哪几种方式?
+
+1. 打包用命令或者者放到容器中运行
+2. 用 Maven/Gradle 插件运行
+3. 直接执行 main 方法运行
+
+## SpringBoot 常用的 Starter 有哪些?
+
+1. spring-boot-starter-web :提供 Spring MVC + 内嵌的 Tomcat 。
+2. spring-boot-starter-data-jpa :提供 Spring JPA + Hibernate 。
+3. spring-boot-starter-data-Redis :提供 Redis 。
+4. mybatis-spring-boot-starter :提供 MyBatis 。
## 自动配置原理
SpringBoot实现自动配置原理图解:
+> 公众号【程序员大彬】,回复【自动配置】下载高清图片
+

在 application.properties 中设置属性 debug=true,可以在控制台查看已启用和未启用的自动配置。
@@ -274,43 +291,6 @@ public class SpringbootDemoApplication {
hello.msg=大彬
```
-
-
-
-
## @Value原理
-@Value的解析就是在bean初始化阶段。BeanPostProcessor定义了bean初始化前后用户可以对bean进行操作的接口方法,它的一个重要实现类AutowiredAnnotationBeanPostProcessor为bean中的@Autowired和@Value注解的注入功能提供支持。
-
-
-
-## 启动过程
-
-准备Environment——发布事件——创建上下文、bean——刷新上下文——结束。
-
-构造SpringApplication的时候会进行初始化的工作,初始化的时候会做以下几件事:
-判断运行环境类型,有三种运行环境:NONE 非 web 的运行环境、SERVLET 普通 web 的运行环境、REACTIVE 响应式 web 的运行环境
-加载 spring.factories 配置文件, 并设置 ApplicationContextInitializer
-加载配置文件, 设置 ApplicationListener
-
-
-SpringApplication构造完成之后调用run方法,启动SpringApplication,run方法执行的时候会做以下几件事:
-构造一个StopWatch,观察SpringApplication的执行
-找出SpringApplicationRunListener,用于监听SpringApplication run方法的执行。监听的过程中会封装SpringApplicationEvent事件,然后使用ApplicationEventMulticaster广播出去,应用程序监听器ApplicationListener会监听到这些事件
-发布starting事件
-加载配置资源到environment,包括命令行参数、application.yml等
-发布environmentPrepared事件
-创建并初始化ApplicationContext,设置environment,加载配置
-refresh ApplicationContext
-
-- 设置beanFactory
-- 调用BeanFactoryPostProcessors
-- 初始化消息源
-- 初始化事件广播器(initApplicationEventMulticaster)
-- 调用onRefresh()方法,默认是空实现
-- 注册监听器
-- 实例化non-lazy-init单例
-- 完成refresh
-- 发布ContextRefreshedEvent事件
-
-发布started事件,启动结束
+@Value的解析就是在bean初始化阶段。BeanPostProcessor定义了bean初始化前后用户可以对bean进行操作的接口方法,它的一个重要实现类`AutowiredAnnotationBeanPostProcessor`为bean中的@Autowired和@Value注解的注入功能提供支持。
diff --git "a/\346\241\206\346\236\266/SpringCloud\345\276\256\346\234\215\345\212\241\345\256\236\346\210\230.md" "b/\346\241\206\346\236\266/SpringCloud\345\276\256\346\234\215\345\212\241\345\256\236\346\210\230.md"
index ce0d58b..ed70b24 100644
--- "a/\346\241\206\346\236\266/SpringCloud\345\276\256\346\234\215\345\212\241\345\256\236\346\210\230.md"
+++ "b/\346\241\206\346\236\266/SpringCloud\345\276\256\346\234\215\345\212\241\345\256\236\346\210\230.md"
@@ -58,7 +58,7 @@
## 基础知识
-微服务是将一个原本独立的系统拆分成多个小型服务,这些小型服务都在各自独立的进程中运行,服务之间通过基于HTTP的RESTful API进行通信协作。
+微服务是将一个原本独立的系统拆分成多个小型服务,这些小型服务都在各自独立的进程中运行。
[从单体应用到微服务](https://www.zhihu.com/question/65502802/answer/802678798)
@@ -354,7 +354,7 @@ com.tyson.helloservice.HelloController : /hello, host:DESKTOP-8F30VS1, service
## Spring Cloud Hystrix
-在微服务架构中,服务与服务之间通过远程调用的方式进行通信,一旦某个被调用的服务发生了故障,其依赖服务也会发生故障,此时就会发生故障的蔓延,最终导致系统瘫痪。Hystrix实现了断路器模式,当某个服务发生故障时,通过断路器的监控,给调用方返回一个错误响应,而不是长时间的等待,这样就不会使得调用方由于长时间得不到响应而占用线程,从而防止故障的蔓延。Hystrix具备服务降级、服务熔断、线程隔离、请求缓存、请求合并及服务监控等强大功能。
+在微服务架构中,服务与服务之间通过远程调用的方式进行通信,一旦某个被调用的服务发生了故障,其依赖服务也会发生故障,此时就会发生故障的蔓延,最终导致系统瘫痪。Hystrix实现了断路器模式,当某个服务发生故障时,通过断路器的监控,给调用方返回一个错误响应,而不是长时间的等待,这样就不会使得调用方由于**长时间得不到响应而占用线程**,从而防止故障的蔓延。Hystrix具备服务降级、服务熔断、线程隔离、请求缓存、请求合并及服务监控等强大功能。
### 代码实例
diff --git "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md" "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
index 9f7b047..a85f845 100644
--- "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
@@ -32,11 +32,11 @@
## Spring的优点
-- **轻量**,基本版本大约2MB。
- 通过控制反转和依赖注入实现**松耦合**。
- 支持**面向切面**的编程,并且把应用业务逻辑和系统服务分开。
- 通过切面和模板减少样板式代码。
-- 方便集成各种优秀框架。内部提供了对各种优秀框架的直接支持(如:Hibernate、MyBatis等)。
+- 声明事物的支持。可以从单调繁冗的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。
+- 方便集成各种优秀框架。内部提供了对各种优秀框架的直接支持(如:Hessian、Quartz、MyBatis等)。
- 方便程序的测试。Spring支持Junit4,添加注解便可以测试Spring程序。
## Spring 用到了哪些设计模式?
@@ -177,13 +177,15 @@ Spring切面可以应用5种类型的通知:
IOC:**控制反转**,由Spring容器管理bean的整个生命周期。通过反射实现对其他对象的控制,包括初始化、创建、销毁等,解放手动创建对象的过程,同时降低类之间的耦合度。
-IOC的好处:降低了类之间的耦合,对象创建和初始化交给Spring容器管理,在需要的时候只需向容器进行申请。
+## IOC的好处?
-## IOC的优点是什么?
+ioc的思想最核心的地方在于,资源不由使用资源者管理,而由不使用资源的第三方管理,这可以带来很多好处。第一,资源集中管理,实现资源的可配置和易管理。第二,降低了使用资源双方的依赖程度,也就是我们说的耦合度。
-- IOC 和依赖注入降低了应用的代码量。
-- 松耦合。
-- 支持加载服务时的饿汉式初始化和懒加载。
+也就是说,甲方要达成某种目的不需要直接依赖乙方,它只需要达到的目的告诉第三方机构就可以了,比如甲方需要一双袜子,而乙方它卖一双袜子,它要把袜子卖出去,并不需要自己去直接找到一个卖家来完成袜子的卖出。它也只需要找第三方,告诉别人我要卖一双袜子。这下好了,甲乙双方进行交易活动,都不需要自己直接去找卖家,相当于程序内部开放接口,卖家由第三方作为参数传入。甲乙互相不依赖,而且只有在进行交易活动的时候,甲才和乙产生联系。反之亦然。这样做什么好处么呢,甲乙可以在对方不真实存在的情况下独立存在,而且保证不交易时候无联系,想交易的时候可以很容易的产生联系。甲乙交易活动不需要双方见面,避免了双方的互不信任造成交易失败的问题。因为交易由第三方来负责联系,而且甲乙都认为第三方可靠。那么交易就能很可靠很灵活的产生和进行了。
+
+这就是ioc的核心思想。生活中这种例子比比皆是,支付宝在整个淘宝体系里就是庞大的ioc容器,交易双方之外的第三方,提供可靠性可依赖可灵活变更交易方的资源管理中心。另外人事代理也是,雇佣机构和个人之外的第三方。
+
+参考链接:https://www.zhihu.com/question/23277575/answer/24259844
## 什么是依赖注入?
@@ -570,7 +572,7 @@ protected void addSingletonFactory(String beanName, ObjectFactory> singletonFa
-## Spring 的单例 Bean 是否有线程安全问题?
+## Spring 的单例 Bean 是否有并发安全问题?
当多个用户同时请求一个服务时,容器会给每一个请求分配一个线程,这时多个线程会并发执行该请求对应的业务逻辑,如果业务逻辑有对单例状态的修改(体现为此单例的成员属性),则必须考虑线程安全问题。
@@ -581,3 +583,30 @@ protected void addSingletonFactory(String beanName, ObjectFactory> singletonFa
在Spring中无状态的Bean适合用单例模式,这样可以共享实例提高性能。有状态的Bean在多线程环境下不安全,一般用`Prototype`模式或者使用`ThreadLocal`解决线程安全问题。
+## Spring Bean如何保证并发安全?
+
+Spring的Bean默认都是单例的,某些情况下,单例是并发不安全的。
+
+以 `Controller` 举例,假如我们在 `Controller` 中定义了成员变量。当多个请求来临,进入的都是同一个单例的 `Controller` 对象,并对此成员变量的值进行修改操作,因此会互相影响,会有并发安全的问题。
+
+应该怎么解决呢?
+
+为了让多个HTTP请求之间不互相影响,可以采取以下措施:
+
+**1、单例变原型**
+
+对 web 项目,可以 `Controller` 类上加注解 @`Scope("prototype")` 或 `@Scope("request")`,对非 web 项目,在 `Component` 类上添加注解 `@Scope("prototype")` 。
+
+这种方式实现起来非常简单,但是很大程度上增大了 Bean 创建实例化销毁的服务器资源开销。
+
+**2、尽量避免使用成员变量**
+
+在业务允许的条件下,可以将成员变量替换为方法中的局部变量。这种方式个人认为是最恰当的。
+
+**3、使用并发安全的类**
+
+如果非要在单例Bean中使用成员变量,可以考虑使用并发安全的容器,如 `ConcurrentHashMap`、`ConcurrentHashSet` 等等,将我们的成员变量包装到这些并发安全的容器中进行管理即可。
+
+**4、分布式或微服务的并发安全**
+
+如果还要进一步考虑到微服务或分布式服务的影响,方式3便不合适了。这种情况下可以借助于可以共享某些信息的分布式缓存中间件,如Redis等。这样即可保证同一种服务的不同服务实例都拥有同一份共享信息了。
From 14844a8d7c1441c5184ef02cab7ce54f41a2d4be Mon Sep 17 00:00:00 2001
From: tyson
Date: Wed, 18 May 2022 21:15:06 +0800
Subject: [PATCH 030/112] update img url
---
Java/JVM.md | 32 ++++----
...21\351\235\242\350\257\225\351\242\230.md" | 14 ++--
"Java/Java\345\237\272\347\241\200.md" | 10 +--
...00\351\235\242\350\257\225\351\242\230.md" | 6 +-
...21\351\235\242\350\257\225\351\242\230.md" | 12 +--
...10\351\235\242\350\257\225\351\242\230.md" | 8 +-
"Java/\345\271\266\345\217\221.md" | 22 +++---
"Java/\351\233\206\345\220\210.md" | 8 +-
.../RabbitMQ.md" | 10 +--
...07\345\215\227\346\200\273\347\273\223.md" | 16 ++--
...is\351\235\242\350\257\225\351\242\230.md" | 10 +--
"\345\205\266\344\273\226/note.md" | 73 -------------------
.../GitHub\346\214\207\345\215\227.md" | 18 ++---
"\345\267\245\345\205\267/progit2.md" | 10 +--
...47\350\241\214\350\256\241\345\210\222.md" | 44 +++++------
.../MySQL\350\277\233\351\230\266.md" | 24 +++---
...21\351\235\242\350\257\225\351\242\230.md" | 46 ++++++++----
...43\347\240\201\345\256\236\347\216\260.md" | 2 +-
...25\351\242\230\346\200\273\347\273\223.md" | 2 +-
...ng\351\235\242\350\257\225\351\242\230.md" | 8 +-
.../\347\275\221\347\273\234.md" | 2 +-
...21\351\235\242\350\257\225\351\242\230.md" | 34 ++++-----
22 files changed, 179 insertions(+), 232 deletions(-)
diff --git a/Java/JVM.md b/Java/JVM.md
index 05f5634..74430f8 100644
--- a/Java/JVM.md
+++ b/Java/JVM.md
@@ -90,7 +90,7 @@ Java 内存模型(JMM)是基于共享内存的多线程通信机制。
JVM内存结构 = 类加载器 + 执行引擎 + 运行时数据区域 。
-
+
> 图片来源:深入理解Java虚拟机-周志明
@@ -172,11 +172,11 @@ JDK 1.8 的时候,HotSpot 的永久代被彻底移除了,使用元空间替
运行时常量池是方法区的一部分,在类加载之后,会将编译器生成的各种字面量和符号引号放到运行时常量池。在运行期间动态生成的常量,如 String 类的 intern()方法,也会被放入运行时常量池。
-
+
-
+
-
+
> 图片来源:https://blog.csdn.net/soonfly
@@ -194,11 +194,11 @@ Java 程序通过栈上的 reference 数据来操作堆上的具体对象。对
- 如果使用句柄的话,那么 Java 堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。使用句柄来访问的最大好处是 reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而 reference 本身不需要修改。
-
+
- 直接指针。reference 中存储的直接就是对象的地址。对象包含到对象类型数据的指针,通过这个指针可以访问对象类型数据。使用直接指针访问方式最大的好处就是访问对象速度快,它节省了一次指针定位的时间开销,虚拟机hotspot主要是使用直接指针来访问对象。
-
+
@@ -245,7 +245,7 @@ ClassFile {
加载、验证、准备、解析、初始化、使用和卸载。
-
+
## 类加载的过程
@@ -281,7 +281,7 @@ ClassFile {
一个类加载器收到一个类的加载请求时,它首先不会自己尝试去加载它,而是把这个请求委派给父类加载器去完成,这样层层委派,因此所有的加载请求最终都会传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
-
+
双亲委派模型的好处:可以防止内存中出现多份同样的字节码。如果没有双亲委派模型而是由各个类加载器自行加载的话,如果用户编写了一个java.lang.Object的同名类并放在ClassPath中,多个类加载器都去加载这个类到内存中,系统中将会出现多个不同的Object类,那么类之间的比较结果及类的唯一性将无法保证。
@@ -339,7 +339,7 @@ public abstract class ClassLoader {
堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断那些对象已经死亡(即不能再被任何途径使用的对象)。
-
+
### 引用计数法
@@ -365,7 +365,7 @@ public class ReferenceCountingGc {
通过GC Root对象为起点,从这些节点向下搜索,搜索所走过的路径叫引用链,当一个对象到GC Root没有任何的引用链相连时,说明这个对象是不可用的。
-
+
#### 可作为GC Roots的对象
@@ -469,13 +469,13 @@ public class ReferenceCountingGc {
标记清除算法就是分为“标记”和“清除”两个阶段。标记出所有需要回收的对象,标记结束后统一回收所有被标记的对象。这种垃圾回收算法效率较低,并且会产生大量不连续的空间碎片。
-
+
### 复制清除算法
半区复制,用于新生代垃圾回收。将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。
-
+
特点:实现简单,运行高效,但可用内存缩小为了原来的一半,浪费空间。
@@ -534,7 +534,7 @@ java -XX:+PrintCommandLineFlags -version
单线程收集器,使用一条垃圾收集线程去完成垃圾收集工作,在进行垃圾收集工作的时候必须暂停其他所有的工作线程( "Stop The World" ),直到它收集结束。
-
+
特点:简单高效;内存消耗最小;没有线程交互的开销,单线程收集效率高;需暂停所有的工作线程,用户体验不好。
@@ -542,7 +542,7 @@ java -XX:+PrintCommandLineFlags -version
Serial 收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全一样。
-
+
除了 Serial 收集器外,只有它能与 CMS 收集器配合工作。
@@ -582,7 +582,7 @@ Concurrent Mark Sweep 并发标记清除,目的是获取最短应用停顿时
- 重新标记: 在并发标记期间对象的引用关系可能会变化,需要重新进行标记。此阶段也会stw,停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短。
- 并发清除:清除死亡对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。
-
+
由于在整个过程中耗时最长的并发标记和并发清除阶段中,垃圾收集器线程都可以与用户线程一起工作,所以从总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。
@@ -607,7 +607,7 @@ G1垃圾收集器的目标是用在多核、大内存的机器上,在不同应
G1将整个堆分成相同大小的分区(Region),有四种不同类型的分区:Eden、Survivor、Old和Humongous(大对象)。分区的大小取值范围为1M到32M,都是2的幂次方。Region大小可以通过`-XX:G1HeapRegionSize`参数指定。Humongous区域用于存储大对象。G1认为只要大小超过了一个Region容量一半的对象即可判定为大对象。
-
+
G1 收集器对各个Region回收所获得的空间大小和回收所需时间的经验值进行排序,得到一个优先级列表,每次根据用户设置的最大的回收停顿时间(使用参数-XX:MaxGCPauseMillis指定,默认值是200毫秒),优先处理回收价值最大的 Region。
diff --git "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index e4bddb5..7ccae91 100644
--- "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -49,7 +49,7 @@
JVM内存结构分为5大区域,**程序计数器**、**虚拟机栈**、**本地方法栈**、**堆**、**方法区**。
-
+
### 程序计数器
@@ -185,7 +185,7 @@ ClassFile {
类的加载指的是将类的`class`文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个此类的对象,通过这个对象可以访问到方法区对应的类信息。
-
+
**加载**
@@ -213,7 +213,7 @@ ClassFile {
一个类加载器收到一个类的加载请求时,它首先不会自己尝试去加载它,而是把这个请求**委派**给父类加载器去完成,这样层层委派,因此所有的加载请求最终都会传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
-
+
双亲委派模型的具体实现代码在 `java.lang.ClassLoader`中,此类的 `loadClass()` 方法运行过程如下:先检查类是否已经加载过,如果没有则让父类加载器去加载。当父类加载器加载失败时抛出 `ClassNotFoundException`,此时尝试自己去加载。源码如下:
@@ -312,7 +312,7 @@ public class ReferenceCount {
通过`GC Root`对象为起点,从这些节点向下搜索,搜索所走过的路径叫引用链,当一个对象到`GC Root`没有任何的引用链相连时,说明这个对象是不可用的。
-
+
## 可作为GC Roots的对象有哪些?
@@ -413,7 +413,7 @@ GC(`Garbage Collection`),垃圾回收,是Java与C++的主要区别之一
首先利用可达性去遍历内存,把存活对象和垃圾对象进行标记。标记结束后统一将所有标记的对象回收掉。这种垃圾回收算法效率较低,并且会**产生大量不连续的空间碎片**。
-
+
**复制清除算法**
@@ -425,7 +425,7 @@ GC(`Garbage Collection`),垃圾回收,是Java与C++的主要区别之一
根据老年代的特点提出的一种标记算法,标记过程仍然与`标记-清除`算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。
-
+
**分类收集算法**
@@ -508,7 +508,7 @@ G1垃圾收集器的目标是在不同应用场景中**追求高吞吐量和低
G1将整个堆分成相同大小的分区(`Region`),有四种不同类型的分区:`Eden、Survivor、Old和Humongous`。分区的大小取值范围为 1M 到 32M,都是2的幂次方。分区大小可以通过`-XX:G1HeapRegionSize`参数指定。`Humongous`区域用于存储大对象。G1规定只要大小超过了一个分区容量一半的对象就认为是大对象。
-
+
G1 收集器对各个分区回收所获得的空间大小和回收所需时间的经验值进行排序,得到一个优先级列表,每次根据用户设置的最大回收停顿时间,优先回收价值最大的分区。
diff --git "a/Java/Java\345\237\272\347\241\200.md" "b/Java/Java\345\237\272\347\241\200.md"
index 72cff97..0f3e5c8 100644
--- "a/Java/Java\345\237\272\347\241\200.md"
+++ "b/Java/Java\345\237\272\347\241\200.md"
@@ -976,7 +976,7 @@ Class 类提供了一些方法,可以获取成员变量、成员方法、接
Field提供了类和接口中字段的信息,通过Field类可以动态访问这些字段。下图是Field类提供的一些方法。
-
+
## Method类
@@ -1101,7 +1101,7 @@ public class GenericMethod {
Throwable类是Error和Exception的父类,只有继承于Throwable的类或者其子类才能被抛出。Throwable分为两类:
-
+
- Error:JVM 无法解决的严重问题,如栈溢出(StackOverflowError)、内存溢出(OOM)等。程序无法处理的错误。
@@ -1198,7 +1198,7 @@ public class ExceptionTest {
Java IO流的核心就是对文件的操作,对于字节 、字符类型的输入和输出流。IO流主要分为两大类,字节流和字符流。字节流可以处理任何类型的数据,如图片,视频等,字符流只能处理字符类型的数据。
-
+
> 图片参考:[Java io学习整理](https://zhuanlan.zhihu.com/p/25418336)
@@ -1251,13 +1251,13 @@ OutputStreamWriter:字符到字节的转换,可对读取到的字符数据
同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。
-
+
## NIO
NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了 NIO 框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。
-
+
NIO与IO区别:
diff --git "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
index 5fa2390..095093c 100644
--- "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
@@ -101,7 +101,7 @@
**JVM** :英文名称(Java Virtual Machine),就是我们耳熟能详的 Java 虚拟机。Java 能够跨平台运行的核心在于 JVM 。
-
+
所有的java程序会首先被编译为.class的类文件,这种类文件可以在虚拟机上执行。也就是说class文件并不直接与机器的操作系统交互,而是经过虚拟机间接与操作系统交互,由虚拟机将程序解释给本地系统执行。
@@ -111,7 +111,7 @@
英文名称(Java Runtime Environment),就是Java 运行时环境。我们编写的Java程序必须要在JRE才能运行。它主要包含两个部分,JVM 和 Java 核心类库。
-
+
JRE是Java的运行环境,并不是一个开发环境,所以没有包含任何开发工具,如编译器和调试器等。
@@ -141,7 +141,7 @@ JRE = JVM + Java 核心类库
JDK = JRE + Java工具 + 编译器 + 调试器
-
+
## 面向对象有哪些特性?
diff --git "a/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
index a65d22c..cfcb15d 100644
--- "a/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
@@ -64,7 +64,7 @@
### 线程池执行原理?
-
+
1. 当线程池里存活的线程数小于核心线程数`corePoolSize`时,这时对于一个新提交的任务,线程池会创建一个线程去处理任务。当线程池里面存活的线程数小于等于核心线程数`corePoolSize`时,线程池里面的线程会一直存活着,就算空闲时间超过了`keepAliveTime`,线程也不会被销毁,而是一直阻塞在那里一直等待任务队列的任务来执行。
2. 当线程池里面存活的线程数已经等于corePoolSize了,这是对于一个新提交的任务,会被放进任务队列workQueue排队等待执行。
@@ -199,7 +199,7 @@ public static ExecutorService newCachedThreadPool() {
3. 修改 `ScheduledFutureTask` 的 time 变量为下次将要被执行的时间;
4. 把这个修改 time 之后的 `ScheduledFutureTask` 放回 `DelayQueue` 中(`DelayQueue.add()`)。
-
+
适用场景:周期性执行任务的场景,需要限制线程数量的场景。
@@ -225,7 +225,7 @@ public static ExecutorService newCachedThreadPool() {
**终止(TERMINATED)**:表示该线程已经执行完毕。
-
+
> 图片来源:Java并发编程的艺术
@@ -401,7 +401,7 @@ class RunnableDemo implements Runnable {
如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方持有的资源,所以这两个线程就会互相等待而进入死锁状态。
-
+
下面通过例子说明线程死锁,代码来自并发编程之美。
@@ -686,7 +686,7 @@ class SeasonThreadTask implements Runnable{
每个线程都有一个`ThreadLocalMap`(`ThreadLocal`内部类),Map中元素的键为`ThreadLocal`,而值对应线程的变量副本。
-
+
调用`threadLocal.set()`-->调用`getMap(Thread)`-->返回当前线程的`ThreadLocalMap`-->`map.set(this, value)`,this是`threadLocal`本身。源码如下:
@@ -783,7 +783,7 @@ private volatile int state;//共享变量,使用volatile修饰保证线程可
同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态(独占或共享 )构造成为一个节点(Node)并将其加入同步队列并进行自旋,当同步状态释放时,会把首节点中的后继节点对应的线程唤醒,使其再次尝试获取同步状态。
-
+
## ReentrantLock 是如何实现可重入性的?
diff --git "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
index 14acae6..4a011e4 100644
--- "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
@@ -54,9 +54,9 @@ Java集合类主要由两个接口**Collection**和**Map**派生出来的,Coll
Java集合框架图如下:
-
+
-
+
List代表了有序可重复集合,可直接根据元素的索引来访问;Set代表无序不可重复集合,只能根据元素本身来访问;Queue是队列集合。Map代表的是存储key-value对的集合,可根据元素的key来访问value。
@@ -197,7 +197,7 @@ HashMap有扩容机制,就是当达到扩容条件时会进行扩容。扩容
5. 链表的数量大于阈值8,就要转换成红黑树的结构
6. 添加成功后会检查是否需要扩容
-
+
### 红黑树的特点?
@@ -271,7 +271,7 @@ public class TreeMap
TreeMap 的继承结构:
-
+
**TreeMap的特点:**
diff --git "a/Java/\345\271\266\345\217\221.md" "b/Java/\345\271\266\345\217\221.md"
index 1f35396..c9734f5 100644
--- "a/Java/\345\271\266\345\217\221.md"
+++ "b/Java/\345\271\266\345\217\221.md"
@@ -87,7 +87,7 @@ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveT
创建新的线程需要获取全局锁,通过这种设计可以尽量避免获取全局锁,当 ThreadPoolExecutor 完成预热之后(当前运行的线程数大于等于 corePoolSize),提交的大部分任务都会被放到 BlockingQueue。
-
+
ThreadPoolExecutor 的通用构造函数:
@@ -326,7 +326,7 @@ public static ExecutorService newCachedThreadPool() {
3. 修改 `ScheduledFutureTask` 的 time 变量为下次将要被执行的时间;
4. 把这个修改 time 之后的 `ScheduledFutureTask` 放回 `DelayQueue` 中(`DelayQueue.add()`)。
-
+
适用场景:周期性执行任务的场景,需要限制线程数量的场景。
@@ -340,7 +340,7 @@ FixedThreadPool和SingleThreadPool。允许请求队列长度为 Integer.MAX_VAL
CachedThreadPool。创建的线程池允许的最大线程数是Integer.MAX_VALUE,当添加任务的速度大于线程池处理任务的速度,可能会创建大量的线程,消耗资源,甚至导致OOM。
-
+
正确示例(阿里巴巴编码规范):
@@ -369,7 +369,7 @@ Java内存模型:线程之间的共享变量存储在主内存里,每个线
1. 线程a把本地内存a中的更新过的共享变量刷新到主内存中去。
2. 线程b到主内存中去读取线程a刷新过的共享变量,然后复制一份到本地内存b中去。
-
+
本地内存是JMM的一个抽象概念,并不真实存在,它包括缓存、写缓冲区、寄存器以及其他硬件和编译器优化。
@@ -392,7 +392,7 @@ Java内存模型:线程之间的共享变量存储在主内存里,每个线
终止(TERMINATED):表示该线程已经执行完毕。
-
+
> 图片来源:Java并发编程的艺术
@@ -680,7 +680,7 @@ public class SynchronizedDemo {
Synchronized底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。
-
+
**方法的同步**不是通过添加monitorenter和monitorexit指令来完成,而是在其常量池中添加了ACC_SYNCHRONIZED标识符。JVM就是根据该标识符来实现方法的同步的:当线程调用方法时,会先检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,说明此方法是同步方法,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他线程无法再获得同一个monitor对象。
@@ -692,7 +692,7 @@ public class SynchronizedMethod {
}
```
-
+
#### 锁的状态
@@ -1077,7 +1077,7 @@ private volatile int state;//共享变量,使用volatile修饰保证线程可
同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态(独占或共享 )构造成为一个节点(Node)并将其加入同步队列并进行自旋,当同步状态释放时,会把首节中的后继节点对应的线程唤醒,使其再次尝试获取同步状态。
-
+
@@ -1112,19 +1112,19 @@ public void conditionSignal() throws InterruptedException {
每个Condition对象都包含着一个等待队列,如果一个线程成功获取了锁之后调用了Condition.await()方法,那么该线程将会释放同步状态、唤醒同步队列中的后继节点,然后构造成节点加入等待队列。只有当线程再次获取Condition相关联的锁之后,才能从await()方法返回。
-
+
> 图片来源:Java并发编程的艺术
在Object的监视器模型上,一个对象拥有一个同步队列和等待队列。Lock通过AQS实现,AQS可以有多个Condition,所以Lock拥有一个同步队列和多个等待队列。
-
+
> 图片来源:Java并发编程的艺术
线程获取了锁之后,调用Condition的signal()方法,会将等待队列的队首节点移到同步队列中,然后该节点的线程会尝试去获取同步状态。成功获取同步状态之后,线程将await()方法返回。
-
+
> 图片来源:Java并发编程的艺术
diff --git "a/Java/\351\233\206\345\220\210.md" "b/Java/\351\233\206\345\220\210.md"
index 83fecae..c7283d5 100644
--- "a/Java/\351\233\206\345\220\210.md"
+++ "b/Java/\351\233\206\345\220\210.md"
@@ -51,7 +51,7 @@
以 Map 结尾的类都实现了 Map 接口,其他所有的类都实现了 Collection 接口。
-
+
### HashMap
@@ -88,7 +88,7 @@ put方法流程:
5. 链表的数量大于阈值8,就要转换成红黑树的结构
6. 添加成功后会检查是否需要扩容
-
+
> 参考链接:
>
@@ -121,7 +121,7 @@ HashMap是无序的,迭代HashMap所得到元素的顺序并不是它们最初
LinkedHashMap继承于HashMap,是HashMap和LinkedList的融合体,具备两者的特性。每次put操作都会将entry插入到双向链表的尾部。
-
+
### TreeMap
@@ -136,7 +136,7 @@ public class TreeMap
TreeMap 的继承结构:
-
+
**TreeMap的特点:**
diff --git "a/\344\270\255\351\227\264\344\273\266/RabbitMQ.md" "b/\344\270\255\351\227\264\344\273\266/RabbitMQ.md"
index e5ba58b..2042d70 100644
--- "a/\344\270\255\351\227\264\344\273\266/RabbitMQ.md"
+++ "b/\344\270\255\351\227\264\344\273\266/RabbitMQ.md"
@@ -39,13 +39,13 @@
文章目录:
-
+
# 简介
RabbitMQ是一个由erlang开发的消息队列。消息队列用于应用间的异步协作。
-
+
## 基本概念
@@ -94,19 +94,19 @@ Exchange规则。
direct交换机会将消息路由到binding key 和 routing key完全匹配的队列中。它是完全匹配、单播的模式。
-
+
## fanout
所有发到 fanout 类型交换机的消息都会路由到所有与该交换机绑定的队列上去。fanout 类型转发消息是最快的。
-
+
## topic
topic交换机使用routing key和binding key进行模糊匹配,匹配成功则将消息发送到相应的队列。routing key和binding key都是句点号“. ”分隔的字符串,binding key中可以存在两种特殊字符“*”与“#”,用于做模糊匹配,其中“\*”用于匹配一个单词,“#”用于匹配多个单词。
-
+
## headers
diff --git "a/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md" "b/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
index 8d4e0d7..f329ea8 100644
--- "a/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
+++ "b/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
@@ -71,7 +71,7 @@
目录结构如下:
-
+
## 简介
@@ -503,7 +503,7 @@ ziplist是 Redis 为了节约内存而开发的, 由一系列特殊编码的
- 最底层的链表包含所有的元素
- 跳跃表的查找次数近似于层数,时间复杂度为O(logn),插入、删除也为 O(logn)
-
+
#### 对象
@@ -545,7 +545,7 @@ hash类型内部编码有两种:
Redis3.2版本提供了quicklist内部编码,简单地说它是以一个ziplist为节点的linkedlist,它结合了ziplist和linkedlist两者的优势,为列表类型提供了一种更为优秀的内部编码实现。
-
+
使用场景:
@@ -630,7 +630,7 @@ GET #返回文章ID。
3. EXEC命令进行提交事务
-
+
DISCARD:放弃事务,即该事务内的所有命令都将取消
@@ -653,7 +653,7 @@ QUEUED
事务里的命令执行时会读取最新的值:
-
+
### WATCH命令
@@ -800,7 +800,7 @@ SLAVEOF NO ONE //停止接收其他数据库的同步并转化为主数据库。
5. 同步数据集。第一次同步的时候,从数据库启动后会向主数据库发送SYNC命令。主数据库接收到命令后开始在后台保存快照(RDB持久化过程),并将保存快照过程接收到的命令缓存起来。当快照完成后,Redis会将快照文件和缓存的命令发送到从数据库。从数据库接收到后,会载入快照文件并执行缓存的命令。以上过程称为复制初始化。
6. 复制初始化完成后,主数据库每次收到写命令就会将命令同步给从数据库,从而实现主从数据库数据的一致性。
-
+
Redis在2.8及以上版本使用psync命令完成主从数据同步,同步过程分为:全量复制和部分复制。
@@ -932,7 +932,7 @@ redis 127.0.0.1:6379> EVALSHA "232fd51614574cf0867b83d384a5e898cfd24e5a" 0
使用evalsha执行Lua脚本过程如下:
-
+
### lua脚本作用
@@ -1075,7 +1075,7 @@ public void write(String key,Object data){
解决方法:
-
+
> 图片来源:https://tech.it168.com
diff --git "a/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md" "b/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
index d5704d2..8a0e0d6 100644
--- "a/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
+++ "b/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
@@ -153,7 +153,7 @@ scan的缺点:在scan的过程中如果有键的变化(增加、删除、修
3. EXEC命令进行提交事务
-
+
一个事务范围内某个命令出错不会影响其他命令的执行,不保证原子性:
@@ -224,7 +224,7 @@ Redis支持两种方式的持久化,一种是`RDB`的方式,一种是`AOF`
`bgsave`是主流的触发 RDB 持久化的方式,执行过程如下:
-
+
- 执行`BGSAVE`命令
- Redis 父进程判断当前**是否存在正在执行的子进程**,如果存在,`BGSAVE`命令直接返回。
@@ -269,7 +269,7 @@ appendfsync no //由操作系统决定何时进行同步操作
接下来看一下 AOF 持久化执行流程:
-
+
1. 所有的写入命令会追加到 AOP 缓冲区中。
2. AOF 缓冲区根据对应的策略向硬盘同步。
@@ -334,7 +334,7 @@ SLAVEOF NO ONE //停止接收其他数据库的同步并转化为主数据库。
客户端连接Redis的时候,先连接哨兵,哨兵会告诉客户端Redis主节点的地址,然后客户端连接上Redis并进行后续的操作。当主节点宕机的时候,哨兵监测到主节点宕机,会重新推选出某个表现良好的从节点成为新的主节点,然后通过发布订阅模式通知其他的从服务器,让它们切换主机。
-
+
**工作原理**
@@ -353,7 +353,7 @@ Redis cluster集群节点最小配置6个节点以上(3主3从),其中主
Redis cluster采用**虚拟槽分区**,所有的键根据哈希函数映射到0~16383个整数槽内,每个节点负责维护一部分槽以及槽所映射的键值数据。
-
+
**哈希槽是如何映射到 Redis 实例上的?**
diff --git "a/\345\205\266\344\273\226/note.md" "b/\345\205\266\344\273\226/note.md"
index dd2906f..dad41bf 100644
--- "a/\345\205\266\344\273\226/note.md"
+++ "b/\345\205\266\344\273\226/note.md"
@@ -146,15 +146,6 @@ HMACSHA256(
-## 秒杀系统
-
-rabbitmq是为了削峰,如果是有1000件商品参与秒杀,每个商品有10件,那么系统的最大并发就是1万,db扛不住这么大的并发的,如果商品数量更大,这个并发量会更大。
-通过Redis预减库存减少到DB的请求,通过消息队列异步写库缓解数据库的压力。用消息队列来缓冲瞬时流量,把同步的直接调用转换成异步操作。
-
-把判断库存扣减库存的操作封装成lua脚本,实现原子性操作,避免超卖。
-
-
-
## shiro
@@ -271,31 +262,6 @@ select、poll和epoll都是IO多路复用的机制。
-## 超卖问题
-
-先到数据库查询库存,在减库存。不是原子操作,会有超卖问题。
-
-通过加排他锁解决该问题。
-
-- 开始事务。
-- 查询库存,并显式的设置排他锁:SELECT * FROM table_name WHERE … FOR UPDATE
-- 生成订单。
-- 去库存,update会隐式的设置排他锁:UPDATE products SET count=count-1 WHERE id=1
-- commit,释放锁。
-
-也可以通过乐观锁实现。使用版本号实现乐观锁。
-
-假设此时version = 100, num = 1; 100个线程进入到了这里,同时他们select出来版本号都是version = 100。
-
-然后直接update的时候,只有其中一个先update了,同时更新了版本号。
-
-那么其他99个在更新的时候,会发觉version并不等于上次select的version,就说明version被其他线程修改过了,则放弃此次update,重试直到成功。
-
-```mysql
-select version from goods WHERE id= 1001
-update goods set num = num - 1, version = version + 1 WHERE id= 1001 AND num > 0 AND version = @version(上面查到的version);
-```
-
## TPS和QPS
@@ -318,42 +284,3 @@ Tps即每秒处理事务数,包括了三个过程:
-## 微服务
-
-单体应用:将所有功能都打包成在一个独立单元的应用程序。
-
-微服务将单一应用程序划分成一组小的服务,服务之间相互协调、互相配合。每个服务运行在其独立的进程中,服务和服务之间采用轻量级的通信机制相互沟通。每个服务可以被独立的部署到生产环境。
-
-
-
-## 进程通信
-
-进程间通信方式:
-1、管道通信
- 匿名管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
- 有名管道是半双工的通信方式,数据只能单向流动。
-2、消息队列
-3、共享内存。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
-4、信号量。信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
-
-
-
-## 分布式事务
-
-分布式事务是指事务的参与者,支持事务的服务器,资源服务器以及事务管理器分别位于分布式系统的不同节点之上。通常一个分布式事务中会涉及对多个数据源或业务系统的操作。分布式事务也可以被定义为一种嵌套型的事务,同时也就具有了ACID事务的特性。
-CAP理论
-Consistency(一致性):数据一致更新,所有数据变动都是同步的(强一致性)。
-Availability(可用性):好的响应性能
-Partition tolerance(分区容错性) :可靠性
-
-定理:任何分布式系统只可同时满足二点,没法三者兼顾。
-
-CA系统(放弃P):指将所有数据(或者仅仅是那些与事务相关的数据)都放在一个分布式节点上,就不会存在网络分区。所以强一致性以及可用性得到满足。
-CP系统(放弃A):如果要求数据在各个服务器上是强一致的,然而网络分区会导致同步时间无限延长,那么如此一来可用性就得不到保障了。坚持事务ACID(原子性、一致性、隔离性和持久性)的传统数据库以及对结果一致性非常敏感的应用通常会做出这样的选择。
-AP系统(放弃C):这里所说的放弃一致性,并不是完全放弃数据一致性,而是放弃数据的强一致性,而保留数据的最终一致性。如果即要求系统高可用又要求分区容错,那么就要放弃一致性了。因为一旦发生网络分区,节点之间将无法通信,为了满足高可用,每个节点只能用本地数据提供服务,这样就会导致数据不一致。一些遵守BASE原则数据库,(如:Cassandra、CouchDB等)往往会放宽对一致性的要求(满足最终一致性即可),一次来获取基本的可用性。
-
-BASE理论
-Basically Available基本可用:指分布式系统在出现不可预知的故障的时候,允许损失部分可用性——但不是系统不可用。
-响应时间上的损失:假如正常一个在线搜索0.5秒之内返回,但由于故障(机房断电或网络不通),查询结果的响应时间增加到1—2秒。功能上的损失:如果流量激增或者一个请求需要多个服务间配合,而此时有的服务发生了故障,这时需要进行服务降级,进而保护系统稳定性。
-Soft state软状态:允许系统在不同节点的数据副本之间进行数据同步的过程存在延迟。Eventually consistent最终一致:最终数据是一致的就可以了,而不是时时高一致。
-BASE思想主要强调基本的可用性,如果你需要High 可用性,也就是纯粹的高性能,那么就要以一致性或容错性为牺牲。
\ No newline at end of file
diff --git "a/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md" "b/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md"
index a125d4b..cca4277 100644
--- "a/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md"
+++ "b/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md"
@@ -22,15 +22,15 @@ Git又是什么呢?简单的说,**Git 是一个管理你的代码历史记
- 创建一个新的仓库,用来存放项目。
- 
+ 
- 或者你在GitHub上看到别人有一个超级无敌有趣的项目,可以直接fork过来,可以理解成复制过来,变成你自己的。之后你想怎么改就怎么改!
- 
+ 
- 然后你可以通过Git命令行`git clone xxx`把项目clone到本地,在本地进行创作。
- 
+ 
- 最后,在本地创作完成,可以使用`git commit -m xxx`提交到本地库,然后使用`git push`把修改推送到GitHub仓库。之后就可以在GitHub上面看到你修改的内容啦~
@@ -42,23 +42,23 @@ Git又是什么呢?简单的说,**Git 是一个管理你的代码历史记
- 评价GitHub项目的两个重要的参数:star和fork。
-
+
比较优秀和热门的项目,star数目和fork数目都会比较多。我们可以根据这两个参数筛选出比较好的项目。使用`关键字 stars:>=xxx forks:>=xxx` 可以筛选出star和fork数目大于xxx的相关项目。
-
+
- 使用 `awesome 关键字`,可以筛选出比较高质量的学习、书籍、工具类或者插件类的集合。
-
+
- 在特定位置搜索关键词。有些关键词出现在项目的不同位置,比如项目名称、项目描述和README等。使用`关键词 in name/description/Readme`,可以搜索到相关的内容。比如使用`spring in name`,可以搜索到在项目名中包含spring的项目。
-
+
- 指定条件搜索关键词。如`tool language:java`搜索到的是包含关键字tool的Java项目,`tool followers:>1000`可以搜索到包含关键字tool,且follower数量大于1000的项目。
-
+
@@ -72,7 +72,7 @@ Git又是什么呢?简单的说,**Git 是一个管理你的代码历史记
- 文档神器。可以为自己的项目建立wiki,可以用markdown语法写wiki;
-
+
- 使用GitHub pages建立个人静态网站,搞一个有自己域名的独立博客,想想都觉得开心。使用GitHub pages的好处是搭建简单而且免费,支持静态脚本,并且可以绑定自己的域名。具体可以参考:[GitHub Pages 建立个人网站详细教程 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/58229299)
diff --git "a/\345\267\245\345\205\267/progit2.md" "b/\345\267\245\345\205\267/progit2.md"
index 68b8c2b..6dde18b 100644
--- "a/\345\267\245\345\205\267/progit2.md"
+++ "b/\345\267\245\345\205\267/progit2.md"
@@ -85,7 +85,7 @@ Git工作流程如下:
Git 的工作流程图如下:
-
+
> 图片来源:https://blog.csdn.net/ThinkWon/article/details/94346816
@@ -103,11 +103,11 @@ Git 的三种状态:已修改(modified)、已暂存(staged)和已提
基本的 Git 工作流程:在工作目录修改文件;暂存文件,将文件快照放到暂存区域;提交更新到本地库。暂存区保存了下次将要提交的文件列表信息,一般在 Git 仓库目录中。
-
+
> 图片来源:`https://img2018.cnblogs.com/blog/1252910/201907/1252910-20190726163829113-2056815874.png`
-
+
## 配置
@@ -156,7 +156,7 @@ git clone https://github.com/...
查看文件状态:`git status`
-
+
> 图片来源:`https://img2018.cnblogs.com/blog/1252910/201907/1252910-20190726163854195-886320537.png`
@@ -262,7 +262,7 @@ git commit -m "add readme.md"
单独执行`git commit`,不带上-m参数,会进入 vim 编辑器界面:
-
+
此时应该这么操作:
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md"
index e9f255a..2378225 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md"
@@ -91,7 +91,7 @@ where blog_id = (
三个表依次嵌套,发现最里层的子查询 `id`最大,最先执行。
-
+
## select_type
@@ -107,13 +107,13 @@ where blog_id = (
查询的表名,并不一定是真实存在的表,有别名显示别名,也可能为临时表。当from子句中有子查询时,table列是 ``的格式,表示当前查询依赖 id为N的查询,会先执行 id为N的查询。
-
+
## partitions
查询时匹配到的分区信息,对于非分区表值为`NULL`,当查询的是分区表时,`partitions`显示分区表命中的分区情况。
-
+
## type
@@ -123,25 +123,25 @@ where blog_id = (
当表仅有一行记录时(系统表),数据量很少,往往不需要进行磁盘IO,速度非常快。比如,Mysql系统表proxies_priv在Mysql服务启动时候已经加载在内存中,对这个表进行查询不需要进行磁盘 IO。
-
+
### const
单表操作的时候,查询使用了主键或者唯一索引。
-
+
### eq_ref
**多表关联**查询的时候,主键和唯一索引作为关联条件。如下图的sql,对于user表(外循环)的每一行,user_role表(内循环)只有一行满足join条件,只要查找到这行记录,就会跳出内循环,继续外循环的下一轮查询。
-
+
### ref
查找条件列使用了索引而且不为主键和唯一索引。虽然使用了索引,但该索引列的值并不唯一,这样即使使用索引查找到了第一条数据,仍然不能停止,要在目标值附近进行小范围扫描。但它的好处是不需要扫全表,因为索引是有序的,即便有重复值,也是在一个非常小的范围内做扫描。
-
+
### ref_or_null
@@ -151,13 +151,13 @@ where blog_id = (
使用了索引合并优化方法,查询使用了两个以上的索引。新建comment表,id为主键,value_id为非唯一索引,执行`explain select content from comment where value_id = 1181000 and id > 1000;`,执行结果显示查询同时使用了id和value_id索引,type列的值为index_merge。
-
+
### range
有范围的索引扫描,相对于index的全索引扫描,它有范围限制,因此要优于index。像between、and、'>'、'<'、in和or都是范围索引扫描。
-
+
### index
@@ -165,17 +165,17 @@ index包括select索引列,order by主键两种情况。
1. order by主键。这种情况会按照索引顺序全表扫描数据,拿到的数据是按照主键排好序的,不需要额外进行排序。
- 
+ 
2. select索引列。type为index,而且extra字段为using index,也称这种情况为索引覆盖。所需要取的数据都在索引列,无需回表查询。
- 
+ 
### all
全表扫描,查询没有用到索引,性能最差。
-
+
## possible_keys
@@ -249,13 +249,13 @@ CREATE TABLE `t_orderdetail` (
查询的列未被索引覆盖,where筛选条件非索引的前导列。对存储引擎返回的结果进行过滤(Post-filter,后过滤),一般发生在MySQL服务器,而不是存储引擎层。
-
+
### using index
查询的列被索引覆盖,并且where筛选条件符合最左前缀原则,通过**索引查找**就能直接找到符合条件的数据,不需要回表查询数据。
-
+
### Using where&Using index
@@ -265,17 +265,17 @@ CREATE TABLE `t_orderdetail` (
- where筛选条件不符合最左前缀原则
- 
+ 
- where筛选条件是索引列前导列的一个范围
- 
+ 
### null
查询的列未被索引覆盖,并且where筛选条件是索引的前导列,也就是用到了索引,但是部分字段未被索引覆盖,必须回表查询这些字段,Extra中为NULL。
-
+
### using index condition
@@ -283,11 +283,11 @@ CREATE TABLE `t_orderdetail` (
不使用ICP的情况(`set optimizer_switch='index_condition_pushdown=off'`),如下图,在步骤4中,没有使用where条件过滤索引:
-
+
使用ICP的情况(`set optimizer_switch='index_condition_pushdown=on'`):
-
+
下面的例子使用了ICP:
@@ -296,11 +296,11 @@ explain select user_id, order_id, order_status
from t_order where user_id > 1 and user_id < 5\G;
```
-
+
关掉ICP之后(`set optimizer_switch='index_condition_pushdown=off'`),可以看到extra列为using where,不会使用索引下推。
-
+
### using temporary
@@ -314,7 +314,7 @@ from t_order where user_id > 1 and user_id < 5\G;
- select 查询字段不全是索引字段
- select 查询字段都是索引字段,但是 order by 字段和索引字段的顺序不一致
-
+
### using join buffer
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md"
index 7caa4e5..4ff8a64 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md"
@@ -138,7 +138,7 @@ B+ 树是基于B 树和叶子节点顺序访问指针进行实现,它具有B
在 B+ 树中,节点中的 key 从左到右递增排列,如果某个指针的左右相邻 key 分别是 keyi 和 keyi+1,则该指针指向节点的所有 key 大于等于 keyi 且小于等于 keyi+1。
-
+
进行查找操作时,首先在根节点进行二分查找,找到key所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出 key 所对应的数据项。
@@ -170,15 +170,15 @@ Index_comment:
如下图,col1 是主键,col2和col3是普通字段。
-
+
下图是主键索引对应的 B+树结构,每个节点对应磁盘的一页。
-
+
对col3 建立一个单列索引,对应的B+树结构:
-
+
### 索引分类
1. 主键索引:名为primary的唯一非空索引,不允许有空值。
@@ -208,7 +208,7 @@ Index_comment:
对(a, b) 建立索引,a 在索引树中是全局有序的,而 b 是全局无序,局部有序(当a相等时,会对b进行比较排序)。直接执行`b = 2`这种查询条件没有办法利用索引。
-
+
从局部来看,当a的值确定的时候,b是有序的。例如a = 1时,b值为1,2是有序的状态。当a=2时候,b的值为1,4也是有序状态。 因此,你执行`a = 1 and b = 2`是a,b字段能用到索引的。而你执行`a > 1 and b = 2`时,a字段能用到索引,b字段用不到索引。因为a的值此时是一个范围,不是固定的,在这个范围内b值不是有序的,因此b字段用不上索引。
@@ -218,7 +218,7 @@ InnoDB使用表的主键构造主键索引树,同时叶子节点中存放的
聚集索引的叶子节点就是整张表的行记录。InnoDB 主键使用的是聚簇索引。聚集索引要比非聚集索引查询效率高很多。
-
+
对于InnoDB来说,聚集索引一般是表中的主键索引,如果表中没有显示指定主键,则会选择表中的第一个不允许为NULL的唯一索引。如果没有主键也没有合适的唯一索引,那么innodb内部会生成一个隐藏的主键作为聚集索引,这个隐藏的主键长度为6个字节,它的值会随着数据的插入自增。
@@ -244,7 +244,7 @@ explain select user_id from user_like where blog_id = 1;
Extra中为`Using where; Using index`, 查询的列被索引覆盖,where筛选条件不符合最左前缀原则,无法通过索引查找找到符合条件的数据,但可以通过**索引扫描**找到符合条件的数据,也不需要回表查询数据。
-
+
### 索引失效
@@ -322,7 +322,7 @@ MEMORY引擎默认使用哈希索引,将键的哈希值和指向数据行的
myisam引擎主键索引和其他索引区别不大,叶子节点都包含索引值和行指针。
innodb引擎二级索引叶子存储的是索引值和主键值(不是行指针),这样可以减少行移动和数据页分裂时二级索引的维护工作。
- 
+ 
@@ -351,7 +351,7 @@ mvcc实现依赖于版本链,版本链是通过表的三个隐藏字段实现
3. 修改当前行的值,生成一个新版本,更新事务id,使回滚指针指向旧版本的记录,这样就形成一条版本链;
4. 记录redo log;
-
+
### read view
@@ -388,14 +388,14 @@ repeatable read:在一个事务范围内,第一次select时更新这个read_
事务a和事务b同时开启事务,事务a插入数据然后提交,事务b执行全表的update,然后执行查询,查到了事务A中添加的数据。
-
+
MySQL如何实现避免幻读:
- 在快照读情况下,MySQL通过mvcc来避免幻读。
- 在当前读情况下,MySQL通过next-key来避免幻读(加行锁和间隙锁来实现的)。
-
+
next-key包括两部分:行锁和间隙锁。行锁是加在索引上的锁,间隙锁是加在索引之间的。
@@ -531,7 +531,7 @@ MySQL主要分为 Server 层和存储引擎层:
- **Server 层**:主要包括连接器、查询缓存、分析器、优化器、执行器等,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图,函数等,还有一个通用的日志模块 binglog 日志模块。
- **存储引擎**: 主要负责数据的存储和读取。server 层通过api与存储引擎进行通信。
-
+
### Server 层基本组件
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 72a67a0..61ed2f8 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -116,6 +116,22 @@ select @@transaction_isolation;
set session transaction isolation level read uncommitted;
```
+## 生产环境数据库一般用的什么隔离级别呢?
+
+**生产环境大多使用RC**。为什么不是RR呢?
+
+> 可重复读(Repeatable Read),简称为RR
+> 读已提交(Read Commited),简称为RC
+
+缘由一:在RR隔离级别下,存在间隙锁,导致出现死锁的几率比RC大的多!
+缘由二:在RR隔离级别下,条件列未命中索引会锁表!而在RC隔离级别下,只锁行!
+
+也就是说,RC的并发性高于RR。
+
+并且大部分场景下,不可重复读问题是可以接受的。毕竟数据都已经提交了,读出来本身就没有太大问题!
+
+[互联网项目中mysql应该选什么事务隔离级别](https://zhuanlan.zhihu.com/p/59061106)
+
## 索引
### 什么是索引?
@@ -148,10 +164,10 @@ set session transaction isolation level read uncommitted;
### 什么情况下不建索引?
1. `where`条件中用不到的字段不适合建立索引
-2. 表记录较少
-3. 需要经常增删改
+2. 表记录较少。比如只有几百条数据,没必要加索引。
+3. 需要经常增删改。需要评估是否适合加索引
4. **参与列计算**的列不适合建索引
-5. **区分度不高**的字段不适合建立索引,如性别等
+5. **区分度不高**的字段不适合建立索引,如性别,只有男/女/未知三个值。加了索引,查询效率也不会提高。
### 索引的数据结构
@@ -163,7 +179,7 @@ B+ 树是基于B 树和叶子节点顺序访问指针进行实现,它具有B
在 B+ 树中,节点中的 `key` 从左到右递增排列,如果某个指针的左右相邻 `key` 分别是 keyi 和 keyi+1,则该指针指向节点的所有 `key` 大于等于 keyi 且小于等于 keyi+1。
-
+
进行查找操作时,首先在根节点进行二分查找,找到`key`所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出`key`所对应的数据项。
@@ -233,7 +249,7 @@ ADD CONSTRAINT constraint_name UNIQUE KEY(column_1,column_2,...);
如下图,对(a, b) 建立索引,a 在索引树中是全局有序的,而 b 是全局无序,局部有序(当a相等时,会根据b进行排序)。直接执行`b = 2`这种查询条件无法使用索引。
-
+
当a的值确定的时候,b是有序的。例如`a = 1`时,b值为1,2是有序的状态。当`a = 2`时候,b的值为1,4也是有序状态。 当执行`a = 1 and b = 2`时a和b字段能用到索引。而执行`a > 1 and b = 2`时,a字段能用到索引,b字段用不到索引。因为a的值此时是一个范围,不是固定的,在这个范围内b值不是有序的,因此b字段无法使用索引。
@@ -267,7 +283,7 @@ explain select user_id from user_like where blog_id = 1;
`explain`结果的`Extra`列为`Using where; Using index`, 查询的列被索引覆盖,where筛选条件不符合最左前缀原则,无法通过索引查找找到符合条件的数据,但可以通过**索引扫描**找到符合条件的数据,也不需要回表查询数据。
-
+
### 索引的设计原则?
@@ -377,7 +393,7 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
每条表记录大概是这样的:
-
+
使用事务更新行记录的时候,就会生成版本链,执行过程如下:
@@ -389,15 +405,15 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
1、初始数据如下,其中`DB_ROW_ID`和`DB_ROLL_PTR`为空。
-
+
2、事务A对该行数据做了修改,将`age`修改为12,效果如下:
-
+
3、之后事务B也对该行记录做了修改,将`age`修改为8,效果如下:
-
+
4、此时undo log有两行记录,并且通过回滚指针连在一起。
@@ -417,7 +433,7 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
**前提**:`DATA_TRX_ID` 表示每个数据行的最新的事务ID;`up_limit_id`表示当前快照中的最先开始的事务;`low_limit_id`表示当前快照中的最慢开始的事务,即最后一个事务。
-
+
- 如果`DATA_TRX_ID` < `up_limit_id`:说明在创建`read view`时,修改该数据行的事务已提交,该版本的记录可被当前事务读取到。
- 如果`DATA_TRX_ID` >= `low_limit_id`:说明当前版本的记录的事务是在创建`read view`之后生成的,该版本的数据行不可以被当前事务访问。此时需要通过版本链找到上一个版本,然后重新判断该版本的记录对当前事务的可见性。
@@ -442,7 +458,7 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
1、首先,user表只有两条记录,具体如下:
-
+
2、事务a和事务b同时开启事务`start transaction`;
@@ -460,7 +476,7 @@ update user set user_name = 'a';
5、事务b然后执行查询,查到了事务a中插入的数据。(下图左边是事务b,右边是事务a。事务开始之前只有两条记录,事务a插入一条数据之后,事务b查询出来是三条数据)
-
+
以上就是当前读出现的幻读现象。
@@ -549,7 +565,7 @@ MySQL主要分为 Server 层和存储引擎层:
垂直划分数据库是根据业务进行划分,例如购物场景,可以将库中涉及商品、订单、用户的表分别划分出成一个库,通过降低单库的大小来提高性能。同样的,分表的情况就是将一个大表根据业务功能拆分成一个个子表,例如商品基本信息和商品描述,商品基本信息一般会展示在商品列表,商品描述在商品详情页,可以将商品基本信息和商品描述拆分成两张表。
-
+
**优点**:行记录变小,数据页可以存放更多记录,在查询时减少I/O次数。
@@ -563,7 +579,7 @@ MySQL主要分为 Server 层和存储引擎层:
水平划分是根据一定规则,例如时间或id序列值等进行数据的拆分。比如根据年份来拆分不同的数据库。每个数据库结构一致,但是数据得以拆分,从而提升性能。
-
+
**优点**:单库(表)的数据量得以减少,提高性能;切分出的表结构相同,程序改动较少。
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
index 9ad4fb0..9a99fb1 100644
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
+++ "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
@@ -13,7 +13,7 @@
常见的排序算法主要有:冒泡排序、插入排序、选择排序、快速排序、归并排序、堆排序、基数排序。各种排序算法的时间空间复杂度、稳定性见下图。
-
+
## 冒泡排序
diff --git "a/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" "b/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
index e9ddb23..dd8b7fb 100644
--- "a/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
+++ "b/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
@@ -28,7 +28,7 @@ SpringBoot实现自动配置原理图解:
> 公众号【程序员大彬】,回复【自动配置】下载高清图片
-
+
在 application.properties 中设置属性 debug=true,可以在控制台查看已启用和未启用的自动配置。
diff --git "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md" "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
index a85f845..ae4a031 100644
--- "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
@@ -112,6 +112,10 @@ AOP有两种实现方式:静态代理和动态代理。
动态代理:代理类在程序运行时创建,AOP框架不会去修改字节码,而是在内存中临时生成一个代理对象,在运行期间对业务方法进行增强,不会生成新类。
+## Spring AOP的实现原理
+
+`Spring`的`AOP`实现原理其实很简单,就是通过**动态代理**实现的。如果我们为`Spring`的某个`bean`配置了切面,那么`Spring`在创建这个`bean`的时候,实际上创建的是这个`bean`的一个代理对象,我们后续对`bean`中方法的调用,实际上调用的是代理类重写的代理方法。而`Spring`的`AOP`使用了两种动态代理,分别是**JDK的动态代理**,以及**CGLib的动态代理**。
+
## JDK动态代理和CGLIB动态代理的区别?
Spring AOP中的动态代理主要有两种方式:JDK动态代理和CGLIB动态代理。
@@ -122,7 +126,7 @@ Spring AOP中的动态代理主要有两种方式:JDK动态代理和CGLIB动
缺点:**目标类必须有实现的接口**。如果某个类没有实现接口,那么这个类就不能用JDK动态代理。
-**CGLIB来动态代理**
+**CGLIB动态代理**
**通过继承实现**。如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library)可以在运行时动态生成类的字节码,动态创建目标类的子类对象,在子类对象中增强目标类。
@@ -212,7 +216,7 @@ finishBeanFactoryInitialization(beanFactory);
## Bean的生命周期
-
+
1.调用bean的构造方法创建Bean
diff --git "a/\347\275\221\347\273\234/\347\275\221\347\273\234.md" "b/\347\275\221\347\273\234/\347\275\221\347\273\234.md"
index 60a94c1..0320b99 100644
--- "a/\347\275\221\347\273\234/\347\275\221\347\273\234.md"
+++ "b/\347\275\221\347\273\234/\347\275\221\347\273\234.md"
@@ -333,7 +333,7 @@ HTTP长连接,指的是复用TCP连接。多个HTTP请求可以复用同一个
CDN用户访问流程:
-
+
1.用户向浏览器输入www.web.com这个域名,浏览器第一次发现本地没有dns缓存,则向网站的DNS服务器请求;
diff --git "a/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 48969f2..2fb8b79 100644
--- "a/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -38,7 +38,7 @@
计算机网络体系大致分为三种,OSI七层模型、TCP/IP四层模型和五层模型。一般面试的时候考察比较多的是五层模型。
-
+
五层模型:应用层、传输层、网络层、数据链路层、物理层。
@@ -52,7 +52,7 @@
假设发送端为客户端,接收端为服务端。开始时客户端和服务端的状态都是`CLOSED`。
-
+
1. 第一次握手:客户端向服务端发起建立连接请求,客户端会随机生成一个起始序列号x,客户端向服务端发送的字段中包含标志位`SYN=1`,序列号`seq=x`。第一次握手前客户端的状态为`CLOSE`,第一次握手后客户端的状态为`SYN-SENT`。此时服务端的状态为`LISTEN`。
2. 第二次握手:服务端在收到客户端发来的报文后,会随机生成一个服务端的起始序列号y,然后给客户端回复一段报文,其中包括标志位`SYN=1`,`ACK=1`,序列号`seq=y`,确认号`ack=x+1`。第二次握手前服务端的状态为`LISTEN`,第二次握手后服务端的状态为`SYN-RCVD`,此时客户端的状态为`SYN-SENT`。(其中`SYN=1`表示要和客户端建立一个连接,`ACK=1`表示确认序号有效)
@@ -69,7 +69,7 @@
## 四次挥手
-
+
1. A的应用进程先向其TCP发出连接释放报文段(`FIN=1,seq=u`),并停止再发送数据,主动关闭TCP连接,进入`FIN-WAIT-1`(终止等待1)状态,等待B的确认。
2. B收到连接释放报文段后即发出确认报文段(`ACK=1,ack=u+1,seq=v`),B进入`CLOSE-WAIT`(关闭等待)状态,此时的TCP处于半关闭状态,A到B的连接释放。
@@ -152,7 +152,7 @@ Content-Length:112
## HTTP状态码有哪些?
-
+
## POST和GET的区别?
@@ -194,7 +194,7 @@ HTTP2.0相比HTTP1.1支持的特性:
服务端可以向证书颁发机构CA申请证书,以避免中间人攻击(防止证书被篡改)。证书包含三部分内容:**证书内容、证书签名算法和签名**,签名是为了验证身份。
-
+
服务端把证书传输给浏览器,浏览器从证书里取公钥。证书可以证明该公钥对应本网站。
@@ -216,29 +216,29 @@ HTTP2.0相比HTTP1.1支持的特性:
1. **协商加密算法** 。在`Client Hello`里面客户端会告知服务端自己当前的一些信息,包括客户端要使用的TLS版本,支持的加密算法,要访问的域名,给服务端生成的一个随机数(Nonce)等。需要提前告知服务器想要访问的域名以便服务器发送相应的域名的证书过来。
- 
+ 
2. 服务端响应`Server Hello`,告诉客户端服务端**选中的加密算法**。
- 
+ 
3. 接着服务端给客户端发来了2个证书。第二个证书是第一个证书的签发机构(CA)的证书。
- 
+ 
4. 客户端使用证书的认证机构CA公开发布的RSA公钥**对该证书进行验证**,下图表明证书认证成功。
- 
+ 
5. 验证通过之后,浏览器和服务器通过**密钥交换算法**产生共享的**对称密钥**。
- 
+ 
- 
+ 
6. 开始传输数据,使用同一个对称密钥来加解密。
- 
+ 
## DNS 的解析过程?
@@ -257,7 +257,7 @@ HTTP2.0相比HTTP1.1支持的特性:
4. 服务器**响应请求**,返回响应数据。
5. 浏览器**解析响应内容,进行渲染**,呈现给用户。
-
+
## 什么是cookie和session?
@@ -265,7 +265,7 @@ HTTP2.0相比HTTP1.1支持的特性:
由于HTTP协议是无状态的协议,需要用某种机制来识具体的用户身份,用来跟踪用户的整个会话。常用的会话跟踪技术是cookie与session。
**cookie**就是由服务器发给客户端的特殊信息,而这些信息以文本文件的方式存放在客户端,然后客户端每次向服务器发送请求的时候都会带上这些特殊的信息。说得更具体一些:当用户使用浏览器访问一个支持cookie的网站的时候,用户会提供包括用户名在内的个人信息并且提交至服务器;接着,服务器在向客户端回传相应的超文本的同时也会发回这些个人信息,当然这些信息并不是存放在HTTP响应体中的,而是存放于HTTP响应头;当客户端浏览器接收到来自服务器的响应之后,浏览器会将这些信息存放在一个统一的位置。 自此,客户端再向服务器发送请求的时候,都会把相应的cookie存放在HTTP请求头再次发回至服务器。服务器在接收到来自客户端浏览器的请求之后,就能够通过分析存放于请求头的cookie得到客户端特有的信息,从而动态生成与该客户端相对应的内容。网站的登录界面中“请记住我”这样的选项,就是通过cookie实现的。
-
+
**cookie工作流程**:
@@ -274,7 +274,7 @@ HTTP2.0相比HTTP1.1支持的特性:
3. 下次访问时,浏览器将自动携带cookie数据发送给服务器。
**session原理**:首先浏览器请求服务器访问web站点时,服务器首先会检查这个客户端请求是否已经包含了一个session标识、称为SESSIONID,如果已经包含了一个sessionid则说明以前已经为此客户端创建过session,服务器就按照sessionid把这个session检索出来使用,如果客户端请求不包含session id,则服务器为此客户端创建一个session,并且生成一个与此session相关联的独一无二的sessionid存放到cookie中,这个sessionid将在本次响应中返回到客户端保存,这样在交互的过程中,浏览器端每次请求时,都会带着这个sessionid,服务器根据这个sessionid就可以找得到对应的session。以此来达到共享数据的目的。 这里需要注意的是,session不会随着浏览器的关闭而死亡,而是等待超时时间。
-
+
## Cookie和Session的区别?
@@ -293,7 +293,7 @@ HTTP2.0相比HTTP1.1支持的特性:
TCP 利用滑动窗口实现流量控制。流量控制是为了控制发送方发送速率,保证接收方来得及接收。 TCP会话的双方都各自维护一个发送窗口和一个接收窗口。接收窗口大小取决于应用、系统、硬件的限制。发送窗口则取决于对端通告的接收窗口。接收方发送的确认报文中的window字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将接收方的确认报文window字段设置为 0,则发送方不能发送数据。
-
+
TCP头包含window字段,16bit位,它代表的是窗口的字节容量,最大为65535。这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。接收窗口的大小是约等于发送窗口的大小。
@@ -302,7 +302,7 @@ TCP头包含window字段,16bit位,它代表的是窗口的字节容量,最
防止过多的数据注入到网络中。 几种拥塞控制方法:慢开始( slow-start )、拥塞避免( congestion avoidance )、快重传( fast retransmit )和快恢复( fast recovery )。
-
+
### 慢开始
From 415bd82b7b98c8e75e60a4f70c32d4a081a84493 Mon Sep 17 00:00:00 2001
From: tyson
Date: Wed, 18 May 2022 21:20:58 +0800
Subject: [PATCH 031/112] update img url
---
Java/JVM.md | 32 +++++++-------
...21\351\235\242\350\257\225\351\242\230.md" | 14 +++---
"Java/Java\345\237\272\347\241\200.md" | 10 ++---
...00\351\235\242\350\257\225\351\242\230.md" | 8 ++--
...21\351\235\242\350\257\225\351\242\230.md" | 12 ++---
...10\351\235\242\350\257\225\351\242\230.md" | 8 ++--
"Java/\345\271\266\345\217\221.md" | 22 +++++-----
"Java/\351\233\206\345\220\210.md" | 8 ++--
.../RabbitMQ.md" | 10 ++---
...07\345\215\227\346\200\273\347\273\223.md" | 16 +++----
...is\351\235\242\350\257\225\351\242\230.md" | 10 ++---
.../GitHub\346\214\207\345\215\227.md" | 18 ++++----
"\345\267\245\345\205\267/progit2.md" | 10 ++---
...47\350\241\214\350\256\241\345\210\222.md" | 44 +++++++++----------
.../MySQL\350\277\233\351\230\266.md" | 24 +++++-----
...21\351\235\242\350\257\225\351\242\230.md" | 24 +++++-----
...43\347\240\201\345\256\236\347\216\260.md" | 2 +-
...25\351\242\230\346\200\273\347\273\223.md" | 2 +-
...ng\351\235\242\350\257\225\351\242\230.md" | 2 +-
.../\347\275\221\347\273\234.md" | 2 +-
...21\351\235\242\350\257\225\351\242\230.md" | 34 +++++++-------
21 files changed, 156 insertions(+), 156 deletions(-)
diff --git a/Java/JVM.md b/Java/JVM.md
index 74430f8..05f5634 100644
--- a/Java/JVM.md
+++ b/Java/JVM.md
@@ -90,7 +90,7 @@ Java 内存模型(JMM)是基于共享内存的多线程通信机制。
JVM内存结构 = 类加载器 + 执行引擎 + 运行时数据区域 。
-
+
> 图片来源:深入理解Java虚拟机-周志明
@@ -172,11 +172,11 @@ JDK 1.8 的时候,HotSpot 的永久代被彻底移除了,使用元空间替
运行时常量池是方法区的一部分,在类加载之后,会将编译器生成的各种字面量和符号引号放到运行时常量池。在运行期间动态生成的常量,如 String 类的 intern()方法,也会被放入运行时常量池。
-
+
-
+
-
+
> 图片来源:https://blog.csdn.net/soonfly
@@ -194,11 +194,11 @@ Java 程序通过栈上的 reference 数据来操作堆上的具体对象。对
- 如果使用句柄的话,那么 Java 堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。使用句柄来访问的最大好处是 reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而 reference 本身不需要修改。
-
+
- 直接指针。reference 中存储的直接就是对象的地址。对象包含到对象类型数据的指针,通过这个指针可以访问对象类型数据。使用直接指针访问方式最大的好处就是访问对象速度快,它节省了一次指针定位的时间开销,虚拟机hotspot主要是使用直接指针来访问对象。
-
+
@@ -245,7 +245,7 @@ ClassFile {
加载、验证、准备、解析、初始化、使用和卸载。
-
+
## 类加载的过程
@@ -281,7 +281,7 @@ ClassFile {
一个类加载器收到一个类的加载请求时,它首先不会自己尝试去加载它,而是把这个请求委派给父类加载器去完成,这样层层委派,因此所有的加载请求最终都会传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
-
+
双亲委派模型的好处:可以防止内存中出现多份同样的字节码。如果没有双亲委派模型而是由各个类加载器自行加载的话,如果用户编写了一个java.lang.Object的同名类并放在ClassPath中,多个类加载器都去加载这个类到内存中,系统中将会出现多个不同的Object类,那么类之间的比较结果及类的唯一性将无法保证。
@@ -339,7 +339,7 @@ public abstract class ClassLoader {
堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断那些对象已经死亡(即不能再被任何途径使用的对象)。
-
+
### 引用计数法
@@ -365,7 +365,7 @@ public class ReferenceCountingGc {
通过GC Root对象为起点,从这些节点向下搜索,搜索所走过的路径叫引用链,当一个对象到GC Root没有任何的引用链相连时,说明这个对象是不可用的。
-
+
#### 可作为GC Roots的对象
@@ -469,13 +469,13 @@ public class ReferenceCountingGc {
标记清除算法就是分为“标记”和“清除”两个阶段。标记出所有需要回收的对象,标记结束后统一回收所有被标记的对象。这种垃圾回收算法效率较低,并且会产生大量不连续的空间碎片。
-
+
### 复制清除算法
半区复制,用于新生代垃圾回收。将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。
-
+
特点:实现简单,运行高效,但可用内存缩小为了原来的一半,浪费空间。
@@ -534,7 +534,7 @@ java -XX:+PrintCommandLineFlags -version
单线程收集器,使用一条垃圾收集线程去完成垃圾收集工作,在进行垃圾收集工作的时候必须暂停其他所有的工作线程( "Stop The World" ),直到它收集结束。
-
+
特点:简单高效;内存消耗最小;没有线程交互的开销,单线程收集效率高;需暂停所有的工作线程,用户体验不好。
@@ -542,7 +542,7 @@ java -XX:+PrintCommandLineFlags -version
Serial 收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全一样。
-
+
除了 Serial 收集器外,只有它能与 CMS 收集器配合工作。
@@ -582,7 +582,7 @@ Concurrent Mark Sweep 并发标记清除,目的是获取最短应用停顿时
- 重新标记: 在并发标记期间对象的引用关系可能会变化,需要重新进行标记。此阶段也会stw,停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短。
- 并发清除:清除死亡对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。
-
+
由于在整个过程中耗时最长的并发标记和并发清除阶段中,垃圾收集器线程都可以与用户线程一起工作,所以从总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。
@@ -607,7 +607,7 @@ G1垃圾收集器的目标是用在多核、大内存的机器上,在不同应
G1将整个堆分成相同大小的分区(Region),有四种不同类型的分区:Eden、Survivor、Old和Humongous(大对象)。分区的大小取值范围为1M到32M,都是2的幂次方。Region大小可以通过`-XX:G1HeapRegionSize`参数指定。Humongous区域用于存储大对象。G1认为只要大小超过了一个Region容量一半的对象即可判定为大对象。
-
+
G1 收集器对各个Region回收所获得的空间大小和回收所需时间的经验值进行排序,得到一个优先级列表,每次根据用户设置的最大的回收停顿时间(使用参数-XX:MaxGCPauseMillis指定,默认值是200毫秒),优先处理回收价值最大的 Region。
diff --git "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 7ccae91..e4bddb5 100644
--- "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -49,7 +49,7 @@
JVM内存结构分为5大区域,**程序计数器**、**虚拟机栈**、**本地方法栈**、**堆**、**方法区**。
-
+
### 程序计数器
@@ -185,7 +185,7 @@ ClassFile {
类的加载指的是将类的`class`文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个此类的对象,通过这个对象可以访问到方法区对应的类信息。
-
+
**加载**
@@ -213,7 +213,7 @@ ClassFile {
一个类加载器收到一个类的加载请求时,它首先不会自己尝试去加载它,而是把这个请求**委派**给父类加载器去完成,这样层层委派,因此所有的加载请求最终都会传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
-
+
双亲委派模型的具体实现代码在 `java.lang.ClassLoader`中,此类的 `loadClass()` 方法运行过程如下:先检查类是否已经加载过,如果没有则让父类加载器去加载。当父类加载器加载失败时抛出 `ClassNotFoundException`,此时尝试自己去加载。源码如下:
@@ -312,7 +312,7 @@ public class ReferenceCount {
通过`GC Root`对象为起点,从这些节点向下搜索,搜索所走过的路径叫引用链,当一个对象到`GC Root`没有任何的引用链相连时,说明这个对象是不可用的。
-
+
## 可作为GC Roots的对象有哪些?
@@ -413,7 +413,7 @@ GC(`Garbage Collection`),垃圾回收,是Java与C++的主要区别之一
首先利用可达性去遍历内存,把存活对象和垃圾对象进行标记。标记结束后统一将所有标记的对象回收掉。这种垃圾回收算法效率较低,并且会**产生大量不连续的空间碎片**。
-
+
**复制清除算法**
@@ -425,7 +425,7 @@ GC(`Garbage Collection`),垃圾回收,是Java与C++的主要区别之一
根据老年代的特点提出的一种标记算法,标记过程仍然与`标记-清除`算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。
-
+
**分类收集算法**
@@ -508,7 +508,7 @@ G1垃圾收集器的目标是在不同应用场景中**追求高吞吐量和低
G1将整个堆分成相同大小的分区(`Region`),有四种不同类型的分区:`Eden、Survivor、Old和Humongous`。分区的大小取值范围为 1M 到 32M,都是2的幂次方。分区大小可以通过`-XX:G1HeapRegionSize`参数指定。`Humongous`区域用于存储大对象。G1规定只要大小超过了一个分区容量一半的对象就认为是大对象。
-
+
G1 收集器对各个分区回收所获得的空间大小和回收所需时间的经验值进行排序,得到一个优先级列表,每次根据用户设置的最大回收停顿时间,优先回收价值最大的分区。
diff --git "a/Java/Java\345\237\272\347\241\200.md" "b/Java/Java\345\237\272\347\241\200.md"
index 0f3e5c8..72cff97 100644
--- "a/Java/Java\345\237\272\347\241\200.md"
+++ "b/Java/Java\345\237\272\347\241\200.md"
@@ -976,7 +976,7 @@ Class 类提供了一些方法,可以获取成员变量、成员方法、接
Field提供了类和接口中字段的信息,通过Field类可以动态访问这些字段。下图是Field类提供的一些方法。
-
+
## Method类
@@ -1101,7 +1101,7 @@ public class GenericMethod {
Throwable类是Error和Exception的父类,只有继承于Throwable的类或者其子类才能被抛出。Throwable分为两类:
-
+
- Error:JVM 无法解决的严重问题,如栈溢出(StackOverflowError)、内存溢出(OOM)等。程序无法处理的错误。
@@ -1198,7 +1198,7 @@ public class ExceptionTest {
Java IO流的核心就是对文件的操作,对于字节 、字符类型的输入和输出流。IO流主要分为两大类,字节流和字符流。字节流可以处理任何类型的数据,如图片,视频等,字符流只能处理字符类型的数据。
-
+
> 图片参考:[Java io学习整理](https://zhuanlan.zhihu.com/p/25418336)
@@ -1251,13 +1251,13 @@ OutputStreamWriter:字符到字节的转换,可对读取到的字符数据
同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。
-
+
## NIO
NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了 NIO 框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。
-
+
NIO与IO区别:
diff --git "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
index 095093c..758e026 100644
--- "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
@@ -101,7 +101,7 @@
**JVM** :英文名称(Java Virtual Machine),就是我们耳熟能详的 Java 虚拟机。Java 能够跨平台运行的核心在于 JVM 。
-
+
所有的java程序会首先被编译为.class的类文件,这种类文件可以在虚拟机上执行。也就是说class文件并不直接与机器的操作系统交互,而是经过虚拟机间接与操作系统交互,由虚拟机将程序解释给本地系统执行。
@@ -111,7 +111,7 @@
英文名称(Java Runtime Environment),就是Java 运行时环境。我们编写的Java程序必须要在JRE才能运行。它主要包含两个部分,JVM 和 Java 核心类库。
-
+
JRE是Java的运行环境,并不是一个开发环境,所以没有包含任何开发工具,如编译器和调试器等。
@@ -141,7 +141,7 @@ JRE = JVM + Java 核心类库
JDK = JRE + Java工具 + 编译器 + 调试器
-
+
## 面向对象有哪些特性?
@@ -1030,7 +1030,7 @@ unchecked Exception:
不同的线程干专业的事情,最终每个线程都没空着,系统的吞吐量自然就上去了。
-
+
diff --git "a/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
index cfcb15d..a65d22c 100644
--- "a/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
@@ -64,7 +64,7 @@
### 线程池执行原理?
-
+
1. 当线程池里存活的线程数小于核心线程数`corePoolSize`时,这时对于一个新提交的任务,线程池会创建一个线程去处理任务。当线程池里面存活的线程数小于等于核心线程数`corePoolSize`时,线程池里面的线程会一直存活着,就算空闲时间超过了`keepAliveTime`,线程也不会被销毁,而是一直阻塞在那里一直等待任务队列的任务来执行。
2. 当线程池里面存活的线程数已经等于corePoolSize了,这是对于一个新提交的任务,会被放进任务队列workQueue排队等待执行。
@@ -199,7 +199,7 @@ public static ExecutorService newCachedThreadPool() {
3. 修改 `ScheduledFutureTask` 的 time 变量为下次将要被执行的时间;
4. 把这个修改 time 之后的 `ScheduledFutureTask` 放回 `DelayQueue` 中(`DelayQueue.add()`)。
-
+
适用场景:周期性执行任务的场景,需要限制线程数量的场景。
@@ -225,7 +225,7 @@ public static ExecutorService newCachedThreadPool() {
**终止(TERMINATED)**:表示该线程已经执行完毕。
-
+
> 图片来源:Java并发编程的艺术
@@ -401,7 +401,7 @@ class RunnableDemo implements Runnable {
如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方持有的资源,所以这两个线程就会互相等待而进入死锁状态。
-
+
下面通过例子说明线程死锁,代码来自并发编程之美。
@@ -686,7 +686,7 @@ class SeasonThreadTask implements Runnable{
每个线程都有一个`ThreadLocalMap`(`ThreadLocal`内部类),Map中元素的键为`ThreadLocal`,而值对应线程的变量副本。
-
+
调用`threadLocal.set()`-->调用`getMap(Thread)`-->返回当前线程的`ThreadLocalMap`-->`map.set(this, value)`,this是`threadLocal`本身。源码如下:
@@ -783,7 +783,7 @@ private volatile int state;//共享变量,使用volatile修饰保证线程可
同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态(独占或共享 )构造成为一个节点(Node)并将其加入同步队列并进行自旋,当同步状态释放时,会把首节点中的后继节点对应的线程唤醒,使其再次尝试获取同步状态。
-
+
## ReentrantLock 是如何实现可重入性的?
diff --git "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
index 4a011e4..14acae6 100644
--- "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
@@ -54,9 +54,9 @@ Java集合类主要由两个接口**Collection**和**Map**派生出来的,Coll
Java集合框架图如下:
-
+
-
+
List代表了有序可重复集合,可直接根据元素的索引来访问;Set代表无序不可重复集合,只能根据元素本身来访问;Queue是队列集合。Map代表的是存储key-value对的集合,可根据元素的key来访问value。
@@ -197,7 +197,7 @@ HashMap有扩容机制,就是当达到扩容条件时会进行扩容。扩容
5. 链表的数量大于阈值8,就要转换成红黑树的结构
6. 添加成功后会检查是否需要扩容
-
+
### 红黑树的特点?
@@ -271,7 +271,7 @@ public class TreeMap
TreeMap 的继承结构:
-
+
**TreeMap的特点:**
diff --git "a/Java/\345\271\266\345\217\221.md" "b/Java/\345\271\266\345\217\221.md"
index c9734f5..1f35396 100644
--- "a/Java/\345\271\266\345\217\221.md"
+++ "b/Java/\345\271\266\345\217\221.md"
@@ -87,7 +87,7 @@ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveT
创建新的线程需要获取全局锁,通过这种设计可以尽量避免获取全局锁,当 ThreadPoolExecutor 完成预热之后(当前运行的线程数大于等于 corePoolSize),提交的大部分任务都会被放到 BlockingQueue。
-
+
ThreadPoolExecutor 的通用构造函数:
@@ -326,7 +326,7 @@ public static ExecutorService newCachedThreadPool() {
3. 修改 `ScheduledFutureTask` 的 time 变量为下次将要被执行的时间;
4. 把这个修改 time 之后的 `ScheduledFutureTask` 放回 `DelayQueue` 中(`DelayQueue.add()`)。
-
+
适用场景:周期性执行任务的场景,需要限制线程数量的场景。
@@ -340,7 +340,7 @@ FixedThreadPool和SingleThreadPool。允许请求队列长度为 Integer.MAX_VAL
CachedThreadPool。创建的线程池允许的最大线程数是Integer.MAX_VALUE,当添加任务的速度大于线程池处理任务的速度,可能会创建大量的线程,消耗资源,甚至导致OOM。
-
+
正确示例(阿里巴巴编码规范):
@@ -369,7 +369,7 @@ Java内存模型:线程之间的共享变量存储在主内存里,每个线
1. 线程a把本地内存a中的更新过的共享变量刷新到主内存中去。
2. 线程b到主内存中去读取线程a刷新过的共享变量,然后复制一份到本地内存b中去。
-
+
本地内存是JMM的一个抽象概念,并不真实存在,它包括缓存、写缓冲区、寄存器以及其他硬件和编译器优化。
@@ -392,7 +392,7 @@ Java内存模型:线程之间的共享变量存储在主内存里,每个线
终止(TERMINATED):表示该线程已经执行完毕。
-
+
> 图片来源:Java并发编程的艺术
@@ -680,7 +680,7 @@ public class SynchronizedDemo {
Synchronized底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。
-
+
**方法的同步**不是通过添加monitorenter和monitorexit指令来完成,而是在其常量池中添加了ACC_SYNCHRONIZED标识符。JVM就是根据该标识符来实现方法的同步的:当线程调用方法时,会先检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,说明此方法是同步方法,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他线程无法再获得同一个monitor对象。
@@ -692,7 +692,7 @@ public class SynchronizedMethod {
}
```
-
+
#### 锁的状态
@@ -1077,7 +1077,7 @@ private volatile int state;//共享变量,使用volatile修饰保证线程可
同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态(独占或共享 )构造成为一个节点(Node)并将其加入同步队列并进行自旋,当同步状态释放时,会把首节中的后继节点对应的线程唤醒,使其再次尝试获取同步状态。
-
+
@@ -1112,19 +1112,19 @@ public void conditionSignal() throws InterruptedException {
每个Condition对象都包含着一个等待队列,如果一个线程成功获取了锁之后调用了Condition.await()方法,那么该线程将会释放同步状态、唤醒同步队列中的后继节点,然后构造成节点加入等待队列。只有当线程再次获取Condition相关联的锁之后,才能从await()方法返回。
-
+
> 图片来源:Java并发编程的艺术
在Object的监视器模型上,一个对象拥有一个同步队列和等待队列。Lock通过AQS实现,AQS可以有多个Condition,所以Lock拥有一个同步队列和多个等待队列。
-
+
> 图片来源:Java并发编程的艺术
线程获取了锁之后,调用Condition的signal()方法,会将等待队列的队首节点移到同步队列中,然后该节点的线程会尝试去获取同步状态。成功获取同步状态之后,线程将await()方法返回。
-
+
> 图片来源:Java并发编程的艺术
diff --git "a/Java/\351\233\206\345\220\210.md" "b/Java/\351\233\206\345\220\210.md"
index c7283d5..83fecae 100644
--- "a/Java/\351\233\206\345\220\210.md"
+++ "b/Java/\351\233\206\345\220\210.md"
@@ -51,7 +51,7 @@
以 Map 结尾的类都实现了 Map 接口,其他所有的类都实现了 Collection 接口。
-
+
### HashMap
@@ -88,7 +88,7 @@ put方法流程:
5. 链表的数量大于阈值8,就要转换成红黑树的结构
6. 添加成功后会检查是否需要扩容
-
+
> 参考链接:
>
@@ -121,7 +121,7 @@ HashMap是无序的,迭代HashMap所得到元素的顺序并不是它们最初
LinkedHashMap继承于HashMap,是HashMap和LinkedList的融合体,具备两者的特性。每次put操作都会将entry插入到双向链表的尾部。
-
+
### TreeMap
@@ -136,7 +136,7 @@ public class TreeMap
TreeMap 的继承结构:
-
+
**TreeMap的特点:**
diff --git "a/\344\270\255\351\227\264\344\273\266/RabbitMQ.md" "b/\344\270\255\351\227\264\344\273\266/RabbitMQ.md"
index 2042d70..e5ba58b 100644
--- "a/\344\270\255\351\227\264\344\273\266/RabbitMQ.md"
+++ "b/\344\270\255\351\227\264\344\273\266/RabbitMQ.md"
@@ -39,13 +39,13 @@
文章目录:
-
+
# 简介
RabbitMQ是一个由erlang开发的消息队列。消息队列用于应用间的异步协作。
-
+
## 基本概念
@@ -94,19 +94,19 @@ Exchange规则。
direct交换机会将消息路由到binding key 和 routing key完全匹配的队列中。它是完全匹配、单播的模式。
-
+
## fanout
所有发到 fanout 类型交换机的消息都会路由到所有与该交换机绑定的队列上去。fanout 类型转发消息是最快的。
-
+
## topic
topic交换机使用routing key和binding key进行模糊匹配,匹配成功则将消息发送到相应的队列。routing key和binding key都是句点号“. ”分隔的字符串,binding key中可以存在两种特殊字符“*”与“#”,用于做模糊匹配,其中“\*”用于匹配一个单词,“#”用于匹配多个单词。
-
+
## headers
diff --git "a/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md" "b/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
index f329ea8..8d4e0d7 100644
--- "a/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
+++ "b/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
@@ -71,7 +71,7 @@
目录结构如下:
-
+
## 简介
@@ -503,7 +503,7 @@ ziplist是 Redis 为了节约内存而开发的, 由一系列特殊编码的
- 最底层的链表包含所有的元素
- 跳跃表的查找次数近似于层数,时间复杂度为O(logn),插入、删除也为 O(logn)
-
+
#### 对象
@@ -545,7 +545,7 @@ hash类型内部编码有两种:
Redis3.2版本提供了quicklist内部编码,简单地说它是以一个ziplist为节点的linkedlist,它结合了ziplist和linkedlist两者的优势,为列表类型提供了一种更为优秀的内部编码实现。
-
+
使用场景:
@@ -630,7 +630,7 @@ GET #返回文章ID。
3. EXEC命令进行提交事务
-
+
DISCARD:放弃事务,即该事务内的所有命令都将取消
@@ -653,7 +653,7 @@ QUEUED
事务里的命令执行时会读取最新的值:
-
+
### WATCH命令
@@ -800,7 +800,7 @@ SLAVEOF NO ONE //停止接收其他数据库的同步并转化为主数据库。
5. 同步数据集。第一次同步的时候,从数据库启动后会向主数据库发送SYNC命令。主数据库接收到命令后开始在后台保存快照(RDB持久化过程),并将保存快照过程接收到的命令缓存起来。当快照完成后,Redis会将快照文件和缓存的命令发送到从数据库。从数据库接收到后,会载入快照文件并执行缓存的命令。以上过程称为复制初始化。
6. 复制初始化完成后,主数据库每次收到写命令就会将命令同步给从数据库,从而实现主从数据库数据的一致性。
-
+
Redis在2.8及以上版本使用psync命令完成主从数据同步,同步过程分为:全量复制和部分复制。
@@ -932,7 +932,7 @@ redis 127.0.0.1:6379> EVALSHA "232fd51614574cf0867b83d384a5e898cfd24e5a" 0
使用evalsha执行Lua脚本过程如下:
-
+
### lua脚本作用
@@ -1075,7 +1075,7 @@ public void write(String key,Object data){
解决方法:
-
+
> 图片来源:https://tech.it168.com
diff --git "a/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md" "b/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
index 8a0e0d6..d5704d2 100644
--- "a/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
+++ "b/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
@@ -153,7 +153,7 @@ scan的缺点:在scan的过程中如果有键的变化(增加、删除、修
3. EXEC命令进行提交事务
-
+
一个事务范围内某个命令出错不会影响其他命令的执行,不保证原子性:
@@ -224,7 +224,7 @@ Redis支持两种方式的持久化,一种是`RDB`的方式,一种是`AOF`
`bgsave`是主流的触发 RDB 持久化的方式,执行过程如下:
-
+
- 执行`BGSAVE`命令
- Redis 父进程判断当前**是否存在正在执行的子进程**,如果存在,`BGSAVE`命令直接返回。
@@ -269,7 +269,7 @@ appendfsync no //由操作系统决定何时进行同步操作
接下来看一下 AOF 持久化执行流程:
-
+
1. 所有的写入命令会追加到 AOP 缓冲区中。
2. AOF 缓冲区根据对应的策略向硬盘同步。
@@ -334,7 +334,7 @@ SLAVEOF NO ONE //停止接收其他数据库的同步并转化为主数据库。
客户端连接Redis的时候,先连接哨兵,哨兵会告诉客户端Redis主节点的地址,然后客户端连接上Redis并进行后续的操作。当主节点宕机的时候,哨兵监测到主节点宕机,会重新推选出某个表现良好的从节点成为新的主节点,然后通过发布订阅模式通知其他的从服务器,让它们切换主机。
-
+
**工作原理**
@@ -353,7 +353,7 @@ Redis cluster集群节点最小配置6个节点以上(3主3从),其中主
Redis cluster采用**虚拟槽分区**,所有的键根据哈希函数映射到0~16383个整数槽内,每个节点负责维护一部分槽以及槽所映射的键值数据。
-
+
**哈希槽是如何映射到 Redis 实例上的?**
diff --git "a/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md" "b/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md"
index cca4277..a125d4b 100644
--- "a/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md"
+++ "b/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md"
@@ -22,15 +22,15 @@ Git又是什么呢?简单的说,**Git 是一个管理你的代码历史记
- 创建一个新的仓库,用来存放项目。
- 
+ 
- 或者你在GitHub上看到别人有一个超级无敌有趣的项目,可以直接fork过来,可以理解成复制过来,变成你自己的。之后你想怎么改就怎么改!
- 
+ 
- 然后你可以通过Git命令行`git clone xxx`把项目clone到本地,在本地进行创作。
- 
+ 
- 最后,在本地创作完成,可以使用`git commit -m xxx`提交到本地库,然后使用`git push`把修改推送到GitHub仓库。之后就可以在GitHub上面看到你修改的内容啦~
@@ -42,23 +42,23 @@ Git又是什么呢?简单的说,**Git 是一个管理你的代码历史记
- 评价GitHub项目的两个重要的参数:star和fork。
-
+
比较优秀和热门的项目,star数目和fork数目都会比较多。我们可以根据这两个参数筛选出比较好的项目。使用`关键字 stars:>=xxx forks:>=xxx` 可以筛选出star和fork数目大于xxx的相关项目。
-
+
- 使用 `awesome 关键字`,可以筛选出比较高质量的学习、书籍、工具类或者插件类的集合。
-
+
- 在特定位置搜索关键词。有些关键词出现在项目的不同位置,比如项目名称、项目描述和README等。使用`关键词 in name/description/Readme`,可以搜索到相关的内容。比如使用`spring in name`,可以搜索到在项目名中包含spring的项目。
-
+
- 指定条件搜索关键词。如`tool language:java`搜索到的是包含关键字tool的Java项目,`tool followers:>1000`可以搜索到包含关键字tool,且follower数量大于1000的项目。
-
+
@@ -72,7 +72,7 @@ Git又是什么呢?简单的说,**Git 是一个管理你的代码历史记
- 文档神器。可以为自己的项目建立wiki,可以用markdown语法写wiki;
-
+
- 使用GitHub pages建立个人静态网站,搞一个有自己域名的独立博客,想想都觉得开心。使用GitHub pages的好处是搭建简单而且免费,支持静态脚本,并且可以绑定自己的域名。具体可以参考:[GitHub Pages 建立个人网站详细教程 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/58229299)
diff --git "a/\345\267\245\345\205\267/progit2.md" "b/\345\267\245\345\205\267/progit2.md"
index 6dde18b..68b8c2b 100644
--- "a/\345\267\245\345\205\267/progit2.md"
+++ "b/\345\267\245\345\205\267/progit2.md"
@@ -85,7 +85,7 @@ Git工作流程如下:
Git 的工作流程图如下:
-
+
> 图片来源:https://blog.csdn.net/ThinkWon/article/details/94346816
@@ -103,11 +103,11 @@ Git 的三种状态:已修改(modified)、已暂存(staged)和已提
基本的 Git 工作流程:在工作目录修改文件;暂存文件,将文件快照放到暂存区域;提交更新到本地库。暂存区保存了下次将要提交的文件列表信息,一般在 Git 仓库目录中。
-
+
> 图片来源:`https://img2018.cnblogs.com/blog/1252910/201907/1252910-20190726163829113-2056815874.png`
-
+
## 配置
@@ -156,7 +156,7 @@ git clone https://github.com/...
查看文件状态:`git status`
-
+
> 图片来源:`https://img2018.cnblogs.com/blog/1252910/201907/1252910-20190726163854195-886320537.png`
@@ -262,7 +262,7 @@ git commit -m "add readme.md"
单独执行`git commit`,不带上-m参数,会进入 vim 编辑器界面:
-
+
此时应该这么操作:
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md"
index 2378225..e9f255a 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md"
@@ -91,7 +91,7 @@ where blog_id = (
三个表依次嵌套,发现最里层的子查询 `id`最大,最先执行。
-
+
## select_type
@@ -107,13 +107,13 @@ where blog_id = (
查询的表名,并不一定是真实存在的表,有别名显示别名,也可能为临时表。当from子句中有子查询时,table列是 ``的格式,表示当前查询依赖 id为N的查询,会先执行 id为N的查询。
-
+
## partitions
查询时匹配到的分区信息,对于非分区表值为`NULL`,当查询的是分区表时,`partitions`显示分区表命中的分区情况。
-
+
## type
@@ -123,25 +123,25 @@ where blog_id = (
当表仅有一行记录时(系统表),数据量很少,往往不需要进行磁盘IO,速度非常快。比如,Mysql系统表proxies_priv在Mysql服务启动时候已经加载在内存中,对这个表进行查询不需要进行磁盘 IO。
-
+
### const
单表操作的时候,查询使用了主键或者唯一索引。
-
+
### eq_ref
**多表关联**查询的时候,主键和唯一索引作为关联条件。如下图的sql,对于user表(外循环)的每一行,user_role表(内循环)只有一行满足join条件,只要查找到这行记录,就会跳出内循环,继续外循环的下一轮查询。
-
+
### ref
查找条件列使用了索引而且不为主键和唯一索引。虽然使用了索引,但该索引列的值并不唯一,这样即使使用索引查找到了第一条数据,仍然不能停止,要在目标值附近进行小范围扫描。但它的好处是不需要扫全表,因为索引是有序的,即便有重复值,也是在一个非常小的范围内做扫描。
-
+
### ref_or_null
@@ -151,13 +151,13 @@ where blog_id = (
使用了索引合并优化方法,查询使用了两个以上的索引。新建comment表,id为主键,value_id为非唯一索引,执行`explain select content from comment where value_id = 1181000 and id > 1000;`,执行结果显示查询同时使用了id和value_id索引,type列的值为index_merge。
-
+
### range
有范围的索引扫描,相对于index的全索引扫描,它有范围限制,因此要优于index。像between、and、'>'、'<'、in和or都是范围索引扫描。
-
+
### index
@@ -165,17 +165,17 @@ index包括select索引列,order by主键两种情况。
1. order by主键。这种情况会按照索引顺序全表扫描数据,拿到的数据是按照主键排好序的,不需要额外进行排序。
- 
+ 
2. select索引列。type为index,而且extra字段为using index,也称这种情况为索引覆盖。所需要取的数据都在索引列,无需回表查询。
- 
+ 
### all
全表扫描,查询没有用到索引,性能最差。
-
+
## possible_keys
@@ -249,13 +249,13 @@ CREATE TABLE `t_orderdetail` (
查询的列未被索引覆盖,where筛选条件非索引的前导列。对存储引擎返回的结果进行过滤(Post-filter,后过滤),一般发生在MySQL服务器,而不是存储引擎层。
-
+
### using index
查询的列被索引覆盖,并且where筛选条件符合最左前缀原则,通过**索引查找**就能直接找到符合条件的数据,不需要回表查询数据。
-
+
### Using where&Using index
@@ -265,17 +265,17 @@ CREATE TABLE `t_orderdetail` (
- where筛选条件不符合最左前缀原则
- 
+ 
- where筛选条件是索引列前导列的一个范围
- 
+ 
### null
查询的列未被索引覆盖,并且where筛选条件是索引的前导列,也就是用到了索引,但是部分字段未被索引覆盖,必须回表查询这些字段,Extra中为NULL。
-
+
### using index condition
@@ -283,11 +283,11 @@ CREATE TABLE `t_orderdetail` (
不使用ICP的情况(`set optimizer_switch='index_condition_pushdown=off'`),如下图,在步骤4中,没有使用where条件过滤索引:
-
+
使用ICP的情况(`set optimizer_switch='index_condition_pushdown=on'`):
-
+
下面的例子使用了ICP:
@@ -296,11 +296,11 @@ explain select user_id, order_id, order_status
from t_order where user_id > 1 and user_id < 5\G;
```
-
+
关掉ICP之后(`set optimizer_switch='index_condition_pushdown=off'`),可以看到extra列为using where,不会使用索引下推。
-
+
### using temporary
@@ -314,7 +314,7 @@ from t_order where user_id > 1 and user_id < 5\G;
- select 查询字段不全是索引字段
- select 查询字段都是索引字段,但是 order by 字段和索引字段的顺序不一致
-
+
### using join buffer
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md"
index 4ff8a64..7caa4e5 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md"
@@ -138,7 +138,7 @@ B+ 树是基于B 树和叶子节点顺序访问指针进行实现,它具有B
在 B+ 树中,节点中的 key 从左到右递增排列,如果某个指针的左右相邻 key 分别是 keyi 和 keyi+1,则该指针指向节点的所有 key 大于等于 keyi 且小于等于 keyi+1。
-
+
进行查找操作时,首先在根节点进行二分查找,找到key所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出 key 所对应的数据项。
@@ -170,15 +170,15 @@ Index_comment:
如下图,col1 是主键,col2和col3是普通字段。
-
+
下图是主键索引对应的 B+树结构,每个节点对应磁盘的一页。
-
+
对col3 建立一个单列索引,对应的B+树结构:
-
+
### 索引分类
1. 主键索引:名为primary的唯一非空索引,不允许有空值。
@@ -208,7 +208,7 @@ Index_comment:
对(a, b) 建立索引,a 在索引树中是全局有序的,而 b 是全局无序,局部有序(当a相等时,会对b进行比较排序)。直接执行`b = 2`这种查询条件没有办法利用索引。
-
+
从局部来看,当a的值确定的时候,b是有序的。例如a = 1时,b值为1,2是有序的状态。当a=2时候,b的值为1,4也是有序状态。 因此,你执行`a = 1 and b = 2`是a,b字段能用到索引的。而你执行`a > 1 and b = 2`时,a字段能用到索引,b字段用不到索引。因为a的值此时是一个范围,不是固定的,在这个范围内b值不是有序的,因此b字段用不上索引。
@@ -218,7 +218,7 @@ InnoDB使用表的主键构造主键索引树,同时叶子节点中存放的
聚集索引的叶子节点就是整张表的行记录。InnoDB 主键使用的是聚簇索引。聚集索引要比非聚集索引查询效率高很多。
-
+
对于InnoDB来说,聚集索引一般是表中的主键索引,如果表中没有显示指定主键,则会选择表中的第一个不允许为NULL的唯一索引。如果没有主键也没有合适的唯一索引,那么innodb内部会生成一个隐藏的主键作为聚集索引,这个隐藏的主键长度为6个字节,它的值会随着数据的插入自增。
@@ -244,7 +244,7 @@ explain select user_id from user_like where blog_id = 1;
Extra中为`Using where; Using index`, 查询的列被索引覆盖,where筛选条件不符合最左前缀原则,无法通过索引查找找到符合条件的数据,但可以通过**索引扫描**找到符合条件的数据,也不需要回表查询数据。
-
+
### 索引失效
@@ -322,7 +322,7 @@ MEMORY引擎默认使用哈希索引,将键的哈希值和指向数据行的
myisam引擎主键索引和其他索引区别不大,叶子节点都包含索引值和行指针。
innodb引擎二级索引叶子存储的是索引值和主键值(不是行指针),这样可以减少行移动和数据页分裂时二级索引的维护工作。
- 
+ 
@@ -351,7 +351,7 @@ mvcc实现依赖于版本链,版本链是通过表的三个隐藏字段实现
3. 修改当前行的值,生成一个新版本,更新事务id,使回滚指针指向旧版本的记录,这样就形成一条版本链;
4. 记录redo log;
-
+
### read view
@@ -388,14 +388,14 @@ repeatable read:在一个事务范围内,第一次select时更新这个read_
事务a和事务b同时开启事务,事务a插入数据然后提交,事务b执行全表的update,然后执行查询,查到了事务A中添加的数据。
-
+
MySQL如何实现避免幻读:
- 在快照读情况下,MySQL通过mvcc来避免幻读。
- 在当前读情况下,MySQL通过next-key来避免幻读(加行锁和间隙锁来实现的)。
-
+
next-key包括两部分:行锁和间隙锁。行锁是加在索引上的锁,间隙锁是加在索引之间的。
@@ -531,7 +531,7 @@ MySQL主要分为 Server 层和存储引擎层:
- **Server 层**:主要包括连接器、查询缓存、分析器、优化器、执行器等,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图,函数等,还有一个通用的日志模块 binglog 日志模块。
- **存储引擎**: 主要负责数据的存储和读取。server 层通过api与存储引擎进行通信。
-
+
### Server 层基本组件
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 61ed2f8..2a8d716 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -179,7 +179,7 @@ B+ 树是基于B 树和叶子节点顺序访问指针进行实现,它具有B
在 B+ 树中,节点中的 `key` 从左到右递增排列,如果某个指针的左右相邻 `key` 分别是 keyi 和 keyi+1,则该指针指向节点的所有 `key` 大于等于 keyi 且小于等于 keyi+1。
-
+
进行查找操作时,首先在根节点进行二分查找,找到`key`所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出`key`所对应的数据项。
@@ -249,7 +249,7 @@ ADD CONSTRAINT constraint_name UNIQUE KEY(column_1,column_2,...);
如下图,对(a, b) 建立索引,a 在索引树中是全局有序的,而 b 是全局无序,局部有序(当a相等时,会根据b进行排序)。直接执行`b = 2`这种查询条件无法使用索引。
-
+
当a的值确定的时候,b是有序的。例如`a = 1`时,b值为1,2是有序的状态。当`a = 2`时候,b的值为1,4也是有序状态。 当执行`a = 1 and b = 2`时a和b字段能用到索引。而执行`a > 1 and b = 2`时,a字段能用到索引,b字段用不到索引。因为a的值此时是一个范围,不是固定的,在这个范围内b值不是有序的,因此b字段无法使用索引。
@@ -283,7 +283,7 @@ explain select user_id from user_like where blog_id = 1;
`explain`结果的`Extra`列为`Using where; Using index`, 查询的列被索引覆盖,where筛选条件不符合最左前缀原则,无法通过索引查找找到符合条件的数据,但可以通过**索引扫描**找到符合条件的数据,也不需要回表查询数据。
-
+
### 索引的设计原则?
@@ -393,7 +393,7 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
每条表记录大概是这样的:
-
+
使用事务更新行记录的时候,就会生成版本链,执行过程如下:
@@ -405,15 +405,15 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
1、初始数据如下,其中`DB_ROW_ID`和`DB_ROLL_PTR`为空。
-
+
2、事务A对该行数据做了修改,将`age`修改为12,效果如下:
-
+
3、之后事务B也对该行记录做了修改,将`age`修改为8,效果如下:
-
+
4、此时undo log有两行记录,并且通过回滚指针连在一起。
@@ -433,7 +433,7 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
**前提**:`DATA_TRX_ID` 表示每个数据行的最新的事务ID;`up_limit_id`表示当前快照中的最先开始的事务;`low_limit_id`表示当前快照中的最慢开始的事务,即最后一个事务。
-
+
- 如果`DATA_TRX_ID` < `up_limit_id`:说明在创建`read view`时,修改该数据行的事务已提交,该版本的记录可被当前事务读取到。
- 如果`DATA_TRX_ID` >= `low_limit_id`:说明当前版本的记录的事务是在创建`read view`之后生成的,该版本的数据行不可以被当前事务访问。此时需要通过版本链找到上一个版本,然后重新判断该版本的记录对当前事务的可见性。
@@ -458,7 +458,7 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
1、首先,user表只有两条记录,具体如下:
-
+
2、事务a和事务b同时开启事务`start transaction`;
@@ -476,7 +476,7 @@ update user set user_name = 'a';
5、事务b然后执行查询,查到了事务a中插入的数据。(下图左边是事务b,右边是事务a。事务开始之前只有两条记录,事务a插入一条数据之后,事务b查询出来是三条数据)
-
+
以上就是当前读出现的幻读现象。
@@ -565,7 +565,7 @@ MySQL主要分为 Server 层和存储引擎层:
垂直划分数据库是根据业务进行划分,例如购物场景,可以将库中涉及商品、订单、用户的表分别划分出成一个库,通过降低单库的大小来提高性能。同样的,分表的情况就是将一个大表根据业务功能拆分成一个个子表,例如商品基本信息和商品描述,商品基本信息一般会展示在商品列表,商品描述在商品详情页,可以将商品基本信息和商品描述拆分成两张表。
-
+
**优点**:行记录变小,数据页可以存放更多记录,在查询时减少I/O次数。
@@ -579,7 +579,7 @@ MySQL主要分为 Server 层和存储引擎层:
水平划分是根据一定规则,例如时间或id序列值等进行数据的拆分。比如根据年份来拆分不同的数据库。每个数据库结构一致,但是数据得以拆分,从而提升性能。
-
+
**优点**:单库(表)的数据量得以减少,提高性能;切分出的表结构相同,程序改动较少。
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
index 9a99fb1..9ad4fb0 100644
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
+++ "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
@@ -13,7 +13,7 @@
常见的排序算法主要有:冒泡排序、插入排序、选择排序、快速排序、归并排序、堆排序、基数排序。各种排序算法的时间空间复杂度、稳定性见下图。
-
+
## 冒泡排序
diff --git "a/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" "b/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
index dd8b7fb..e9ddb23 100644
--- "a/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
+++ "b/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
@@ -28,7 +28,7 @@ SpringBoot实现自动配置原理图解:
> 公众号【程序员大彬】,回复【自动配置】下载高清图片
-
+
在 application.properties 中设置属性 debug=true,可以在控制台查看已启用和未启用的自动配置。
diff --git "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md" "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
index ae4a031..641525e 100644
--- "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
@@ -216,7 +216,7 @@ finishBeanFactoryInitialization(beanFactory);
## Bean的生命周期
-
+
1.调用bean的构造方法创建Bean
diff --git "a/\347\275\221\347\273\234/\347\275\221\347\273\234.md" "b/\347\275\221\347\273\234/\347\275\221\347\273\234.md"
index 0320b99..60a94c1 100644
--- "a/\347\275\221\347\273\234/\347\275\221\347\273\234.md"
+++ "b/\347\275\221\347\273\234/\347\275\221\347\273\234.md"
@@ -333,7 +333,7 @@ HTTP长连接,指的是复用TCP连接。多个HTTP请求可以复用同一个
CDN用户访问流程:
-
+
1.用户向浏览器输入www.web.com这个域名,浏览器第一次发现本地没有dns缓存,则向网站的DNS服务器请求;
diff --git "a/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 2fb8b79..48969f2 100644
--- "a/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -38,7 +38,7 @@
计算机网络体系大致分为三种,OSI七层模型、TCP/IP四层模型和五层模型。一般面试的时候考察比较多的是五层模型。
-
+
五层模型:应用层、传输层、网络层、数据链路层、物理层。
@@ -52,7 +52,7 @@
假设发送端为客户端,接收端为服务端。开始时客户端和服务端的状态都是`CLOSED`。
-
+
1. 第一次握手:客户端向服务端发起建立连接请求,客户端会随机生成一个起始序列号x,客户端向服务端发送的字段中包含标志位`SYN=1`,序列号`seq=x`。第一次握手前客户端的状态为`CLOSE`,第一次握手后客户端的状态为`SYN-SENT`。此时服务端的状态为`LISTEN`。
2. 第二次握手:服务端在收到客户端发来的报文后,会随机生成一个服务端的起始序列号y,然后给客户端回复一段报文,其中包括标志位`SYN=1`,`ACK=1`,序列号`seq=y`,确认号`ack=x+1`。第二次握手前服务端的状态为`LISTEN`,第二次握手后服务端的状态为`SYN-RCVD`,此时客户端的状态为`SYN-SENT`。(其中`SYN=1`表示要和客户端建立一个连接,`ACK=1`表示确认序号有效)
@@ -69,7 +69,7 @@
## 四次挥手
-
+
1. A的应用进程先向其TCP发出连接释放报文段(`FIN=1,seq=u`),并停止再发送数据,主动关闭TCP连接,进入`FIN-WAIT-1`(终止等待1)状态,等待B的确认。
2. B收到连接释放报文段后即发出确认报文段(`ACK=1,ack=u+1,seq=v`),B进入`CLOSE-WAIT`(关闭等待)状态,此时的TCP处于半关闭状态,A到B的连接释放。
@@ -152,7 +152,7 @@ Content-Length:112
## HTTP状态码有哪些?
-
+
## POST和GET的区别?
@@ -194,7 +194,7 @@ HTTP2.0相比HTTP1.1支持的特性:
服务端可以向证书颁发机构CA申请证书,以避免中间人攻击(防止证书被篡改)。证书包含三部分内容:**证书内容、证书签名算法和签名**,签名是为了验证身份。
-
+
服务端把证书传输给浏览器,浏览器从证书里取公钥。证书可以证明该公钥对应本网站。
@@ -216,29 +216,29 @@ HTTP2.0相比HTTP1.1支持的特性:
1. **协商加密算法** 。在`Client Hello`里面客户端会告知服务端自己当前的一些信息,包括客户端要使用的TLS版本,支持的加密算法,要访问的域名,给服务端生成的一个随机数(Nonce)等。需要提前告知服务器想要访问的域名以便服务器发送相应的域名的证书过来。
- 
+ 
2. 服务端响应`Server Hello`,告诉客户端服务端**选中的加密算法**。
- 
+ 
3. 接着服务端给客户端发来了2个证书。第二个证书是第一个证书的签发机构(CA)的证书。
- 
+ 
4. 客户端使用证书的认证机构CA公开发布的RSA公钥**对该证书进行验证**,下图表明证书认证成功。
- 
+ 
5. 验证通过之后,浏览器和服务器通过**密钥交换算法**产生共享的**对称密钥**。
- 
+ 
- 
+ 
6. 开始传输数据,使用同一个对称密钥来加解密。
- 
+ 
## DNS 的解析过程?
@@ -257,7 +257,7 @@ HTTP2.0相比HTTP1.1支持的特性:
4. 服务器**响应请求**,返回响应数据。
5. 浏览器**解析响应内容,进行渲染**,呈现给用户。
-
+
## 什么是cookie和session?
@@ -265,7 +265,7 @@ HTTP2.0相比HTTP1.1支持的特性:
由于HTTP协议是无状态的协议,需要用某种机制来识具体的用户身份,用来跟踪用户的整个会话。常用的会话跟踪技术是cookie与session。
**cookie**就是由服务器发给客户端的特殊信息,而这些信息以文本文件的方式存放在客户端,然后客户端每次向服务器发送请求的时候都会带上这些特殊的信息。说得更具体一些:当用户使用浏览器访问一个支持cookie的网站的时候,用户会提供包括用户名在内的个人信息并且提交至服务器;接着,服务器在向客户端回传相应的超文本的同时也会发回这些个人信息,当然这些信息并不是存放在HTTP响应体中的,而是存放于HTTP响应头;当客户端浏览器接收到来自服务器的响应之后,浏览器会将这些信息存放在一个统一的位置。 自此,客户端再向服务器发送请求的时候,都会把相应的cookie存放在HTTP请求头再次发回至服务器。服务器在接收到来自客户端浏览器的请求之后,就能够通过分析存放于请求头的cookie得到客户端特有的信息,从而动态生成与该客户端相对应的内容。网站的登录界面中“请记住我”这样的选项,就是通过cookie实现的。
-
+
**cookie工作流程**:
@@ -274,7 +274,7 @@ HTTP2.0相比HTTP1.1支持的特性:
3. 下次访问时,浏览器将自动携带cookie数据发送给服务器。
**session原理**:首先浏览器请求服务器访问web站点时,服务器首先会检查这个客户端请求是否已经包含了一个session标识、称为SESSIONID,如果已经包含了一个sessionid则说明以前已经为此客户端创建过session,服务器就按照sessionid把这个session检索出来使用,如果客户端请求不包含session id,则服务器为此客户端创建一个session,并且生成一个与此session相关联的独一无二的sessionid存放到cookie中,这个sessionid将在本次响应中返回到客户端保存,这样在交互的过程中,浏览器端每次请求时,都会带着这个sessionid,服务器根据这个sessionid就可以找得到对应的session。以此来达到共享数据的目的。 这里需要注意的是,session不会随着浏览器的关闭而死亡,而是等待超时时间。
-
+
## Cookie和Session的区别?
@@ -293,7 +293,7 @@ HTTP2.0相比HTTP1.1支持的特性:
TCP 利用滑动窗口实现流量控制。流量控制是为了控制发送方发送速率,保证接收方来得及接收。 TCP会话的双方都各自维护一个发送窗口和一个接收窗口。接收窗口大小取决于应用、系统、硬件的限制。发送窗口则取决于对端通告的接收窗口。接收方发送的确认报文中的window字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将接收方的确认报文window字段设置为 0,则发送方不能发送数据。
-
+
TCP头包含window字段,16bit位,它代表的是窗口的字节容量,最大为65535。这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。接收窗口的大小是约等于发送窗口的大小。
@@ -302,7 +302,7 @@ TCP头包含window字段,16bit位,它代表的是窗口的字节容量,最
防止过多的数据注入到网络中。 几种拥塞控制方法:慢开始( slow-start )、拥塞避免( congestion avoidance )、快重传( fast retransmit )和快恢复( fast recovery )。
-
+
### 慢开始
From bc29969701ab5354886331a1c405cd0975e96be8 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 22 May 2022 16:57:41 +0800
Subject: [PATCH 032/112] update
---
Java/JVM.md | 32 +-
...21\351\235\242\350\257\225\351\242\230.md" | 14 +-
"Java/Java\345\237\272\347\241\200.md" | 10 +-
...00\351\235\242\350\257\225\351\242\230.md" | 8 +-
...21\351\235\242\350\257\225\351\242\230.md" | 12 +-
...10\351\235\242\350\257\225\351\242\230.md" | 8 +-
"Java/\345\271\266\345\217\221.md" | 22 +-
"Java/\351\233\206\345\220\210.md" | 8 +-
README.md | 10 +-
.../RabbitMQ.md" | 13 +-
...07\345\215\227\346\200\273\347\273\223.md" | 16 +-
...06\345\270\203\345\274\217\351\224\201.md" | 159 --
...is\351\235\242\350\257\225\351\242\230.md" | 10 +-
.../elasticsearch.md" | 2438 -----------------
...27\351\235\242\350\257\225\351\242\230.md" | 46 +-
.../GitHub\346\214\207\345\215\227.md" | 18 +-
"\345\267\245\345\205\267/progit2.md" | 10 +-
...47\350\241\214\350\256\241\345\210\222.md" | 44 +-
.../MySQL\350\277\233\351\230\266.md" | 24 +-
...21\351\235\242\350\257\225\351\242\230.md" | 126 +-
...43\347\240\201\345\256\236\347\216\260.md" | 2 +-
...is\351\235\242\350\257\225\351\242\230.md" | 117 +
...25\351\242\230\346\200\273\347\273\223.md" | 2 +-
...15\345\212\241\345\256\236\346\210\230.md" | 10 +
...VC\351\235\242\350\257\225\351\242\230.md" | 88 +
...ng\351\235\242\350\257\225\351\242\230.md" | 2 +-
.../\347\275\221\347\273\234.md" | 2 +-
...21\351\235\242\350\257\225\351\242\230.md" | 34 +-
28 files changed, 519 insertions(+), 2766 deletions(-)
delete mode 100644 "\344\270\255\351\227\264\344\273\266/Redis\345\210\206\345\270\203\345\274\217\351\224\201.md"
delete mode 100644 "\344\270\255\351\227\264\344\273\266/elasticsearch.md"
create mode 100644 "\346\241\206\346\236\266/Mybatis\351\235\242\350\257\225\351\242\230.md"
create mode 100644 "\346\241\206\346\236\266/SpringMVC\351\235\242\350\257\225\351\242\230.md"
diff --git a/Java/JVM.md b/Java/JVM.md
index 05f5634..74430f8 100644
--- a/Java/JVM.md
+++ b/Java/JVM.md
@@ -90,7 +90,7 @@ Java 内存模型(JMM)是基于共享内存的多线程通信机制。
JVM内存结构 = 类加载器 + 执行引擎 + 运行时数据区域 。
-
+
> 图片来源:深入理解Java虚拟机-周志明
@@ -172,11 +172,11 @@ JDK 1.8 的时候,HotSpot 的永久代被彻底移除了,使用元空间替
运行时常量池是方法区的一部分,在类加载之后,会将编译器生成的各种字面量和符号引号放到运行时常量池。在运行期间动态生成的常量,如 String 类的 intern()方法,也会被放入运行时常量池。
-
+
-
+
-
+
> 图片来源:https://blog.csdn.net/soonfly
@@ -194,11 +194,11 @@ Java 程序通过栈上的 reference 数据来操作堆上的具体对象。对
- 如果使用句柄的话,那么 Java 堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。使用句柄来访问的最大好处是 reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而 reference 本身不需要修改。
-
+
- 直接指针。reference 中存储的直接就是对象的地址。对象包含到对象类型数据的指针,通过这个指针可以访问对象类型数据。使用直接指针访问方式最大的好处就是访问对象速度快,它节省了一次指针定位的时间开销,虚拟机hotspot主要是使用直接指针来访问对象。
-
+
@@ -245,7 +245,7 @@ ClassFile {
加载、验证、准备、解析、初始化、使用和卸载。
-
+
## 类加载的过程
@@ -281,7 +281,7 @@ ClassFile {
一个类加载器收到一个类的加载请求时,它首先不会自己尝试去加载它,而是把这个请求委派给父类加载器去完成,这样层层委派,因此所有的加载请求最终都会传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
-
+
双亲委派模型的好处:可以防止内存中出现多份同样的字节码。如果没有双亲委派模型而是由各个类加载器自行加载的话,如果用户编写了一个java.lang.Object的同名类并放在ClassPath中,多个类加载器都去加载这个类到内存中,系统中将会出现多个不同的Object类,那么类之间的比较结果及类的唯一性将无法保证。
@@ -339,7 +339,7 @@ public abstract class ClassLoader {
堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断那些对象已经死亡(即不能再被任何途径使用的对象)。
-
+
### 引用计数法
@@ -365,7 +365,7 @@ public class ReferenceCountingGc {
通过GC Root对象为起点,从这些节点向下搜索,搜索所走过的路径叫引用链,当一个对象到GC Root没有任何的引用链相连时,说明这个对象是不可用的。
-
+
#### 可作为GC Roots的对象
@@ -469,13 +469,13 @@ public class ReferenceCountingGc {
标记清除算法就是分为“标记”和“清除”两个阶段。标记出所有需要回收的对象,标记结束后统一回收所有被标记的对象。这种垃圾回收算法效率较低,并且会产生大量不连续的空间碎片。
-
+
### 复制清除算法
半区复制,用于新生代垃圾回收。将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。
-
+
特点:实现简单,运行高效,但可用内存缩小为了原来的一半,浪费空间。
@@ -534,7 +534,7 @@ java -XX:+PrintCommandLineFlags -version
单线程收集器,使用一条垃圾收集线程去完成垃圾收集工作,在进行垃圾收集工作的时候必须暂停其他所有的工作线程( "Stop The World" ),直到它收集结束。
-
+
特点:简单高效;内存消耗最小;没有线程交互的开销,单线程收集效率高;需暂停所有的工作线程,用户体验不好。
@@ -542,7 +542,7 @@ java -XX:+PrintCommandLineFlags -version
Serial 收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全一样。
-
+
除了 Serial 收集器外,只有它能与 CMS 收集器配合工作。
@@ -582,7 +582,7 @@ Concurrent Mark Sweep 并发标记清除,目的是获取最短应用停顿时
- 重新标记: 在并发标记期间对象的引用关系可能会变化,需要重新进行标记。此阶段也会stw,停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短。
- 并发清除:清除死亡对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。
-
+
由于在整个过程中耗时最长的并发标记和并发清除阶段中,垃圾收集器线程都可以与用户线程一起工作,所以从总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。
@@ -607,7 +607,7 @@ G1垃圾收集器的目标是用在多核、大内存的机器上,在不同应
G1将整个堆分成相同大小的分区(Region),有四种不同类型的分区:Eden、Survivor、Old和Humongous(大对象)。分区的大小取值范围为1M到32M,都是2的幂次方。Region大小可以通过`-XX:G1HeapRegionSize`参数指定。Humongous区域用于存储大对象。G1认为只要大小超过了一个Region容量一半的对象即可判定为大对象。
-
+
G1 收集器对各个Region回收所获得的空间大小和回收所需时间的经验值进行排序,得到一个优先级列表,每次根据用户设置的最大的回收停顿时间(使用参数-XX:MaxGCPauseMillis指定,默认值是200毫秒),优先处理回收价值最大的 Region。
diff --git "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index e4bddb5..7ccae91 100644
--- "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -49,7 +49,7 @@
JVM内存结构分为5大区域,**程序计数器**、**虚拟机栈**、**本地方法栈**、**堆**、**方法区**。
-
+
### 程序计数器
@@ -185,7 +185,7 @@ ClassFile {
类的加载指的是将类的`class`文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个此类的对象,通过这个对象可以访问到方法区对应的类信息。
-
+
**加载**
@@ -213,7 +213,7 @@ ClassFile {
一个类加载器收到一个类的加载请求时,它首先不会自己尝试去加载它,而是把这个请求**委派**给父类加载器去完成,这样层层委派,因此所有的加载请求最终都会传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
-
+
双亲委派模型的具体实现代码在 `java.lang.ClassLoader`中,此类的 `loadClass()` 方法运行过程如下:先检查类是否已经加载过,如果没有则让父类加载器去加载。当父类加载器加载失败时抛出 `ClassNotFoundException`,此时尝试自己去加载。源码如下:
@@ -312,7 +312,7 @@ public class ReferenceCount {
通过`GC Root`对象为起点,从这些节点向下搜索,搜索所走过的路径叫引用链,当一个对象到`GC Root`没有任何的引用链相连时,说明这个对象是不可用的。
-
+
## 可作为GC Roots的对象有哪些?
@@ -413,7 +413,7 @@ GC(`Garbage Collection`),垃圾回收,是Java与C++的主要区别之一
首先利用可达性去遍历内存,把存活对象和垃圾对象进行标记。标记结束后统一将所有标记的对象回收掉。这种垃圾回收算法效率较低,并且会**产生大量不连续的空间碎片**。
-
+
**复制清除算法**
@@ -425,7 +425,7 @@ GC(`Garbage Collection`),垃圾回收,是Java与C++的主要区别之一
根据老年代的特点提出的一种标记算法,标记过程仍然与`标记-清除`算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。
-
+
**分类收集算法**
@@ -508,7 +508,7 @@ G1垃圾收集器的目标是在不同应用场景中**追求高吞吐量和低
G1将整个堆分成相同大小的分区(`Region`),有四种不同类型的分区:`Eden、Survivor、Old和Humongous`。分区的大小取值范围为 1M 到 32M,都是2的幂次方。分区大小可以通过`-XX:G1HeapRegionSize`参数指定。`Humongous`区域用于存储大对象。G1规定只要大小超过了一个分区容量一半的对象就认为是大对象。
-
+
G1 收集器对各个分区回收所获得的空间大小和回收所需时间的经验值进行排序,得到一个优先级列表,每次根据用户设置的最大回收停顿时间,优先回收价值最大的分区。
diff --git "a/Java/Java\345\237\272\347\241\200.md" "b/Java/Java\345\237\272\347\241\200.md"
index 72cff97..0f3e5c8 100644
--- "a/Java/Java\345\237\272\347\241\200.md"
+++ "b/Java/Java\345\237\272\347\241\200.md"
@@ -976,7 +976,7 @@ Class 类提供了一些方法,可以获取成员变量、成员方法、接
Field提供了类和接口中字段的信息,通过Field类可以动态访问这些字段。下图是Field类提供的一些方法。
-
+
## Method类
@@ -1101,7 +1101,7 @@ public class GenericMethod {
Throwable类是Error和Exception的父类,只有继承于Throwable的类或者其子类才能被抛出。Throwable分为两类:
-
+
- Error:JVM 无法解决的严重问题,如栈溢出(StackOverflowError)、内存溢出(OOM)等。程序无法处理的错误。
@@ -1198,7 +1198,7 @@ public class ExceptionTest {
Java IO流的核心就是对文件的操作,对于字节 、字符类型的输入和输出流。IO流主要分为两大类,字节流和字符流。字节流可以处理任何类型的数据,如图片,视频等,字符流只能处理字符类型的数据。
-
+
> 图片参考:[Java io学习整理](https://zhuanlan.zhihu.com/p/25418336)
@@ -1251,13 +1251,13 @@ OutputStreamWriter:字符到字节的转换,可对读取到的字符数据
同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。
-
+
## NIO
NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了 NIO 框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。
-
+
NIO与IO区别:
diff --git "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
index 758e026..095093c 100644
--- "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
@@ -101,7 +101,7 @@
**JVM** :英文名称(Java Virtual Machine),就是我们耳熟能详的 Java 虚拟机。Java 能够跨平台运行的核心在于 JVM 。
-
+
所有的java程序会首先被编译为.class的类文件,这种类文件可以在虚拟机上执行。也就是说class文件并不直接与机器的操作系统交互,而是经过虚拟机间接与操作系统交互,由虚拟机将程序解释给本地系统执行。
@@ -111,7 +111,7 @@
英文名称(Java Runtime Environment),就是Java 运行时环境。我们编写的Java程序必须要在JRE才能运行。它主要包含两个部分,JVM 和 Java 核心类库。
-
+
JRE是Java的运行环境,并不是一个开发环境,所以没有包含任何开发工具,如编译器和调试器等。
@@ -141,7 +141,7 @@ JRE = JVM + Java 核心类库
JDK = JRE + Java工具 + 编译器 + 调试器
-
+
## 面向对象有哪些特性?
@@ -1030,7 +1030,7 @@ unchecked Exception:
不同的线程干专业的事情,最终每个线程都没空着,系统的吞吐量自然就上去了。
-
+
diff --git "a/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
index a65d22c..cfcb15d 100644
--- "a/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
@@ -64,7 +64,7 @@
### 线程池执行原理?
-
+
1. 当线程池里存活的线程数小于核心线程数`corePoolSize`时,这时对于一个新提交的任务,线程池会创建一个线程去处理任务。当线程池里面存活的线程数小于等于核心线程数`corePoolSize`时,线程池里面的线程会一直存活着,就算空闲时间超过了`keepAliveTime`,线程也不会被销毁,而是一直阻塞在那里一直等待任务队列的任务来执行。
2. 当线程池里面存活的线程数已经等于corePoolSize了,这是对于一个新提交的任务,会被放进任务队列workQueue排队等待执行。
@@ -199,7 +199,7 @@ public static ExecutorService newCachedThreadPool() {
3. 修改 `ScheduledFutureTask` 的 time 变量为下次将要被执行的时间;
4. 把这个修改 time 之后的 `ScheduledFutureTask` 放回 `DelayQueue` 中(`DelayQueue.add()`)。
-
+
适用场景:周期性执行任务的场景,需要限制线程数量的场景。
@@ -225,7 +225,7 @@ public static ExecutorService newCachedThreadPool() {
**终止(TERMINATED)**:表示该线程已经执行完毕。
-
+
> 图片来源:Java并发编程的艺术
@@ -401,7 +401,7 @@ class RunnableDemo implements Runnable {
如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方持有的资源,所以这两个线程就会互相等待而进入死锁状态。
-
+
下面通过例子说明线程死锁,代码来自并发编程之美。
@@ -686,7 +686,7 @@ class SeasonThreadTask implements Runnable{
每个线程都有一个`ThreadLocalMap`(`ThreadLocal`内部类),Map中元素的键为`ThreadLocal`,而值对应线程的变量副本。
-
+
调用`threadLocal.set()`-->调用`getMap(Thread)`-->返回当前线程的`ThreadLocalMap`-->`map.set(this, value)`,this是`threadLocal`本身。源码如下:
@@ -783,7 +783,7 @@ private volatile int state;//共享变量,使用volatile修饰保证线程可
同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态(独占或共享 )构造成为一个节点(Node)并将其加入同步队列并进行自旋,当同步状态释放时,会把首节点中的后继节点对应的线程唤醒,使其再次尝试获取同步状态。
-
+
## ReentrantLock 是如何实现可重入性的?
diff --git "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
index 14acae6..4a011e4 100644
--- "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
@@ -54,9 +54,9 @@ Java集合类主要由两个接口**Collection**和**Map**派生出来的,Coll
Java集合框架图如下:
-
+
-
+
List代表了有序可重复集合,可直接根据元素的索引来访问;Set代表无序不可重复集合,只能根据元素本身来访问;Queue是队列集合。Map代表的是存储key-value对的集合,可根据元素的key来访问value。
@@ -197,7 +197,7 @@ HashMap有扩容机制,就是当达到扩容条件时会进行扩容。扩容
5. 链表的数量大于阈值8,就要转换成红黑树的结构
6. 添加成功后会检查是否需要扩容
-
+
### 红黑树的特点?
@@ -271,7 +271,7 @@ public class TreeMap
TreeMap 的继承结构:
-
+
**TreeMap的特点:**
diff --git "a/Java/\345\271\266\345\217\221.md" "b/Java/\345\271\266\345\217\221.md"
index 1f35396..c9734f5 100644
--- "a/Java/\345\271\266\345\217\221.md"
+++ "b/Java/\345\271\266\345\217\221.md"
@@ -87,7 +87,7 @@ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveT
创建新的线程需要获取全局锁,通过这种设计可以尽量避免获取全局锁,当 ThreadPoolExecutor 完成预热之后(当前运行的线程数大于等于 corePoolSize),提交的大部分任务都会被放到 BlockingQueue。
-
+
ThreadPoolExecutor 的通用构造函数:
@@ -326,7 +326,7 @@ public static ExecutorService newCachedThreadPool() {
3. 修改 `ScheduledFutureTask` 的 time 变量为下次将要被执行的时间;
4. 把这个修改 time 之后的 `ScheduledFutureTask` 放回 `DelayQueue` 中(`DelayQueue.add()`)。
-
+
适用场景:周期性执行任务的场景,需要限制线程数量的场景。
@@ -340,7 +340,7 @@ FixedThreadPool和SingleThreadPool。允许请求队列长度为 Integer.MAX_VAL
CachedThreadPool。创建的线程池允许的最大线程数是Integer.MAX_VALUE,当添加任务的速度大于线程池处理任务的速度,可能会创建大量的线程,消耗资源,甚至导致OOM。
-
+
正确示例(阿里巴巴编码规范):
@@ -369,7 +369,7 @@ Java内存模型:线程之间的共享变量存储在主内存里,每个线
1. 线程a把本地内存a中的更新过的共享变量刷新到主内存中去。
2. 线程b到主内存中去读取线程a刷新过的共享变量,然后复制一份到本地内存b中去。
-
+
本地内存是JMM的一个抽象概念,并不真实存在,它包括缓存、写缓冲区、寄存器以及其他硬件和编译器优化。
@@ -392,7 +392,7 @@ Java内存模型:线程之间的共享变量存储在主内存里,每个线
终止(TERMINATED):表示该线程已经执行完毕。
-
+
> 图片来源:Java并发编程的艺术
@@ -680,7 +680,7 @@ public class SynchronizedDemo {
Synchronized底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。
-
+
**方法的同步**不是通过添加monitorenter和monitorexit指令来完成,而是在其常量池中添加了ACC_SYNCHRONIZED标识符。JVM就是根据该标识符来实现方法的同步的:当线程调用方法时,会先检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,说明此方法是同步方法,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他线程无法再获得同一个monitor对象。
@@ -692,7 +692,7 @@ public class SynchronizedMethod {
}
```
-
+
#### 锁的状态
@@ -1077,7 +1077,7 @@ private volatile int state;//共享变量,使用volatile修饰保证线程可
同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态(独占或共享 )构造成为一个节点(Node)并将其加入同步队列并进行自旋,当同步状态释放时,会把首节中的后继节点对应的线程唤醒,使其再次尝试获取同步状态。
-
+
@@ -1112,19 +1112,19 @@ public void conditionSignal() throws InterruptedException {
每个Condition对象都包含着一个等待队列,如果一个线程成功获取了锁之后调用了Condition.await()方法,那么该线程将会释放同步状态、唤醒同步队列中的后继节点,然后构造成节点加入等待队列。只有当线程再次获取Condition相关联的锁之后,才能从await()方法返回。
-
+
> 图片来源:Java并发编程的艺术
在Object的监视器模型上,一个对象拥有一个同步队列和等待队列。Lock通过AQS实现,AQS可以有多个Condition,所以Lock拥有一个同步队列和多个等待队列。
-
+
> 图片来源:Java并发编程的艺术
线程获取了锁之后,调用Condition的signal()方法,会将等待队列的队首节点移到同步队列中,然后该节点的线程会尝试去获取同步状态。成功获取同步状态之后,线程将await()方法返回。
-
+
> 图片来源:Java并发编程的艺术
diff --git "a/Java/\351\233\206\345\220\210.md" "b/Java/\351\233\206\345\220\210.md"
index 83fecae..c7283d5 100644
--- "a/Java/\351\233\206\345\220\210.md"
+++ "b/Java/\351\233\206\345\220\210.md"
@@ -51,7 +51,7 @@
以 Map 结尾的类都实现了 Map 接口,其他所有的类都实现了 Collection 接口。
-
+
### HashMap
@@ -88,7 +88,7 @@ put方法流程:
5. 链表的数量大于阈值8,就要转换成红黑树的结构
6. 添加成功后会检查是否需要扩容
-
+
> 参考链接:
>
@@ -121,7 +121,7 @@ HashMap是无序的,迭代HashMap所得到元素的顺序并不是它们最初
LinkedHashMap继承于HashMap,是HashMap和LinkedList的融合体,具备两者的特性。每次put操作都会将entry插入到双向链表的尾部。
-
+
### TreeMap
@@ -136,7 +136,7 @@ public class TreeMap
TreeMap 的继承结构:
-
+
**TreeMap的特点:**
diff --git a/README.md b/README.md
index c0d36c4..79ae7ad 100644
--- a/README.md
+++ b/README.md
@@ -109,11 +109,15 @@
## Spring MVC
-[Spring MVC知识点总结](框架/SpringMVC.md)
+[Spring MVC入门知识点](框架/SpringMVC.md)
+
+[Spring MVC面试题总结](框架/SpringMVC面试题.md)
## Mybatis
-[Mybatis知识点总结](框架/深入浅出Mybatis技术原理与实战.md)
+[Mybatis入门知识点](框架/深入浅出Mybatis技术原理与实战.md)
+
+[Mybatis面试题总结](框架/Mybatis面试题.md)
## SpringCloud
@@ -127,8 +131,8 @@
## RabbitMQ
-1. [RabbitMQ核心知识总结](中间件/RabbitMQ.md) (推荐 :+1:)
1. [消息队列面试题](中间件/消息队列面试题.md)
+1. [RabbitMQ核心知识总结](中间件/RabbitMQ.md) (推荐 :+1:)
2. [死信队列](中间件/死信队列.md)
# 计算机网络
diff --git "a/\344\270\255\351\227\264\344\273\266/RabbitMQ.md" "b/\344\270\255\351\227\264\344\273\266/RabbitMQ.md"
index e5ba58b..e57f2b1 100644
--- "a/\344\270\255\351\227\264\344\273\266/RabbitMQ.md"
+++ "b/\344\270\255\351\227\264\344\273\266/RabbitMQ.md"
@@ -39,13 +39,13 @@
文章目录:
-
+
# 简介
RabbitMQ是一个由erlang开发的消息队列。消息队列用于应用间的异步协作。
-
+
## 基本概念
@@ -94,26 +94,24 @@ Exchange规则。
direct交换机会将消息路由到binding key 和 routing key完全匹配的队列中。它是完全匹配、单播的模式。
-
+
## fanout
所有发到 fanout 类型交换机的消息都会路由到所有与该交换机绑定的队列上去。fanout 类型转发消息是最快的。
-
+
## topic
topic交换机使用routing key和binding key进行模糊匹配,匹配成功则将消息发送到相应的队列。routing key和binding key都是句点号“. ”分隔的字符串,binding key中可以存在两种特殊字符“*”与“#”,用于做模糊匹配,其中“\*”用于匹配一个单词,“#”用于匹配多个单词。
-
+
## headers
headers交换机是根据发送的消息内容中的headers属性进行路由的。在绑定Queue与Exchange时指定一组键值对;当消息发送到Exchange时,RabbitMQ会取到该消息的headers(也是一个键值对的形式),对比其中的键值对是否完全匹配Queue与Exchange绑定时指定的键值对;如果完全匹配则消息会路由到该Queue,否则不会路由到该Queue。
-
-
# 消息丢失
消息丢失场景:生产者生产消息到RabbitMQ Server消息丢失、RabbitMQ Server存储的消息丢失和RabbitMQ Server到消费者消息丢失。
@@ -432,4 +430,3 @@ msg.getMessageProperties().setExpiration("3000");
[线上rabbitmq问题](https://juejin.im/post/6844904088212094983#heading-0)
-
diff --git "a/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md" "b/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
index 8d4e0d7..f329ea8 100644
--- "a/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
+++ "b/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
@@ -71,7 +71,7 @@
目录结构如下:
-
+
## 简介
@@ -503,7 +503,7 @@ ziplist是 Redis 为了节约内存而开发的, 由一系列特殊编码的
- 最底层的链表包含所有的元素
- 跳跃表的查找次数近似于层数,时间复杂度为O(logn),插入、删除也为 O(logn)
-
+
#### 对象
@@ -545,7 +545,7 @@ hash类型内部编码有两种:
Redis3.2版本提供了quicklist内部编码,简单地说它是以一个ziplist为节点的linkedlist,它结合了ziplist和linkedlist两者的优势,为列表类型提供了一种更为优秀的内部编码实现。
-
+
使用场景:
@@ -630,7 +630,7 @@ GET #返回文章ID。
3. EXEC命令进行提交事务
-
+
DISCARD:放弃事务,即该事务内的所有命令都将取消
@@ -653,7 +653,7 @@ QUEUED
事务里的命令执行时会读取最新的值:
-
+
### WATCH命令
@@ -800,7 +800,7 @@ SLAVEOF NO ONE //停止接收其他数据库的同步并转化为主数据库。
5. 同步数据集。第一次同步的时候,从数据库启动后会向主数据库发送SYNC命令。主数据库接收到命令后开始在后台保存快照(RDB持久化过程),并将保存快照过程接收到的命令缓存起来。当快照完成后,Redis会将快照文件和缓存的命令发送到从数据库。从数据库接收到后,会载入快照文件并执行缓存的命令。以上过程称为复制初始化。
6. 复制初始化完成后,主数据库每次收到写命令就会将命令同步给从数据库,从而实现主从数据库数据的一致性。
-
+
Redis在2.8及以上版本使用psync命令完成主从数据同步,同步过程分为:全量复制和部分复制。
@@ -932,7 +932,7 @@ redis 127.0.0.1:6379> EVALSHA "232fd51614574cf0867b83d384a5e898cfd24e5a" 0
使用evalsha执行Lua脚本过程如下:
-
+
### lua脚本作用
@@ -1075,7 +1075,7 @@ public void write(String key,Object data){
解决方法:
-
+
> 图片来源:https://tech.it168.com
diff --git "a/\344\270\255\351\227\264\344\273\266/Redis\345\210\206\345\270\203\345\274\217\351\224\201.md" "b/\344\270\255\351\227\264\344\273\266/Redis\345\210\206\345\270\203\345\274\217\351\224\201.md"
deleted file mode 100644
index d7536ac..0000000
--- "a/\344\270\255\351\227\264\344\273\266/Redis\345\210\206\345\270\203\345\274\217\351\224\201.md"
+++ /dev/null
@@ -1,159 +0,0 @@
-
-
-
-
-- [Redis分布式锁](#redis%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81)
- - [Jedis实现分布式锁](#jedis%E5%AE%9E%E7%8E%B0%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81)
- - [加锁](#%E5%8A%A0%E9%94%81)
- - [解锁](#%E8%A7%A3%E9%94%81)
-
-
-
-在单机环境下,当存在多个线程可以同时改变某个变量(可变共享变量)时,就会出现线程安全问题。这个问题可以通过 JAVA 提供的 volatile、ReentrantLock、synchronized 以及 concurrent 并发包下一些线程安全的类等来避免。
-
-而在多机部署环境,需要在多进程下保证线程的安全性,Java提供的这些API仅能保证在单个JVM进程内对多线程访问共享资源的线程安全,已经不满足需求了。这时候就需要使用分布式锁来保证线程安全。通过分布式锁,可以保证在分布式部署的应用集群中,同一个方法在同一时间只能被一台机器上的一个线程执行。
-
-分布式锁需要满足四个条件:
-
-1. 互斥性。在任意时刻,只有一个客户端能持有锁。
-2. 不会死锁。即使有客户端在持有锁的期间崩溃而没有主动解锁,也要保证后续其他客户端能加锁。
-3. 加锁和解锁必须是同一个客户端。客户端a不能将客户端b的锁解开,即不能误解锁。
-4. 容错性。只要大多数Redis节点正常运行,客户端就能够获取和释放锁。
-
-# Redis分布式锁
-
-常见的实现分布式锁的方式有:数据库、Redis、Zookeeper。下面主要介绍使用Redis实现分布式锁。
-
-Redis 2.6.12 之前的版本中采用 setnx + expire 方式实现分布式锁,在 Redis 2.6.12 版本后 setnx 增加了过期时间参数:
-
-```java
-SET lockKey value NX PX expire-time
-```
-
-所以在Redis 2.6.12 版本后,只需要使用setnx就可以实现分布式锁了。
-
-加锁逻辑:
-
-1. setnx争抢key的锁,如果已有key存在,则不作操作,过段时间继续重试,保证只有一个客户端能持有锁。
-2. value设置为 requestId(可以使用机器ip拼接当前线程名称),表示这把锁是哪个请求加的,在解锁的时候需要判断当前请求是否持有锁,防止误解锁。比如客户端A加锁,在执行解锁之前,锁过期了,此时客户端B尝试加锁成功,然后客户端A再执行del()方法,则将客户端B的锁给解除了。
-3. 再用expire给锁加一个过期时间,防止异常导致锁没有释放。
-
-解锁逻辑:
-
-首先获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁。使用lua脚本实现原子操作,保证线程安全。
-
-下面我们通过Jedis(基于java语言的redis客户端)来演示分布式锁的实现。
-
-## Jedis实现分布式锁
-
-引入Jedis jar包,在pom.xml文件增加代码:
-
-```xml
-
- redis.clients
- jedis
- 2.9.0
-
-```
-
-### 加锁
-
-调用jedis的set()实现加锁,加锁代码如下:
-
-```java
-/**
- * @description:
- * @author: 程序员大彬
- * @time: 2021-08-01 17:13
- */
-public class RedisTest {
-
- private static final String LOCK_SUCCESS = "OK";
- private static final String SET_IF_NOT_EXIST = "NX";
- private static final String SET_EXPIRE_TIME = "PX";
-
- @Autowired
- private JedisPool jedisPool;
-
- public boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) {
- Jedis jedis = jedisPool.getResource();
- String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_EXPIRE_TIME, expireTime);
-
- if (LOCK_SUCCESS.equals(result)) {
- return true;
- }
- return false;
- }
-}
-```
-
-各参数说明:
-
-- lockKey:使用key来当锁,需要保证key是唯一的。可以使用系统号拼接自定义的key。
-- requestId:表示这把锁是哪个请求加的,可以使用机器ip拼接当前线程名称。在解锁的时候需要判断当前请求是否持有锁,防止误解锁。比如客户端A加锁,在执行解锁之前,锁过期了,此时客户端B尝试加锁成功,然后客户端A再执行del()方法,则将客户端B的锁给解除了。
-- NX:意思是SET IF NOT EXIST,保证如果已有key存在,则不作操作,过段时间继续重试。NX参数保证只有一个客户端能持有锁。
-- PX:给key加一个过期的设置,具体时间由expireTime决定。
-- expireTime:设置key的过期时间,防止异常导致锁没有释放。
-
-### 解锁
-
-首先需要获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁。这里使用lua脚本实现原子操作,保证线程安全。
-
-使用eval命令执行Lua脚本的时候,不会有其他脚本或 Redis 命令被执行,实现组合命令的原子操作。lua脚本如下:
-
-```
-//KEYS[1]是lockKey,ARGV[1]是requestId
-String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
-Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
-```
-
-Jedis的eval()方法源码如下:
-
-```java
-public Object eval(String script, List keys, List args) {
- return this.eval(script, keys.size(), getParams(keys, args));
-}
-```
-
-lua脚本的意思是:调用get获取锁(KEYS[1])对应的value值,检查是否与requestId(ARGV[1])相等,如果相等则调用del删除锁。否则返回0。
-
-完整的解锁代码如下:
-
-```java
-/**
- * @description:
- * @author: 程序员大彬
- * @time: 2021-08-01 17:13
- */
-public class RedisTest {
- private static final Long RELEASE_SUCCESS = 1L;
-
- @Autowired
- private JedisPool jedisPool;
-
- public boolean releaseDistributedLock(String lockKey, String requestId) {
- Jedis jedis = jedisPool.getResource();
- ////KEYS[1]是lockKey,ARGV[1]是requestId
- String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
- Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
-
- if (RELEASE_SUCCESS.equals(result)) {
- return true;
- }
- return false;
- }
-}
-```
-
-
-
-以上是使用Redis实现分布式锁的全部内容,希望对你有帮助。
-
-> 本文已经收录到github/gitee仓库,欢迎大家围观、star
->
-> github仓库: https://github.com/Tyson0314/Java-learning
->
-> 如果github访问不了,可以访问gitee仓库。
->
-> gitee仓库:https://gitee.com/tysondai/Java-learning
-
diff --git "a/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md" "b/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
index d5704d2..8a0e0d6 100644
--- "a/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
+++ "b/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
@@ -153,7 +153,7 @@ scan的缺点:在scan的过程中如果有键的变化(增加、删除、修
3. EXEC命令进行提交事务
-
+
一个事务范围内某个命令出错不会影响其他命令的执行,不保证原子性:
@@ -224,7 +224,7 @@ Redis支持两种方式的持久化,一种是`RDB`的方式,一种是`AOF`
`bgsave`是主流的触发 RDB 持久化的方式,执行过程如下:
-
+
- 执行`BGSAVE`命令
- Redis 父进程判断当前**是否存在正在执行的子进程**,如果存在,`BGSAVE`命令直接返回。
@@ -269,7 +269,7 @@ appendfsync no //由操作系统决定何时进行同步操作
接下来看一下 AOF 持久化执行流程:
-
+
1. 所有的写入命令会追加到 AOP 缓冲区中。
2. AOF 缓冲区根据对应的策略向硬盘同步。
@@ -334,7 +334,7 @@ SLAVEOF NO ONE //停止接收其他数据库的同步并转化为主数据库。
客户端连接Redis的时候,先连接哨兵,哨兵会告诉客户端Redis主节点的地址,然后客户端连接上Redis并进行后续的操作。当主节点宕机的时候,哨兵监测到主节点宕机,会重新推选出某个表现良好的从节点成为新的主节点,然后通过发布订阅模式通知其他的从服务器,让它们切换主机。
-
+
**工作原理**
@@ -353,7 +353,7 @@ Redis cluster集群节点最小配置6个节点以上(3主3从),其中主
Redis cluster采用**虚拟槽分区**,所有的键根据哈希函数映射到0~16383个整数槽内,每个节点负责维护一部分槽以及槽所映射的键值数据。
-
+
**哈希槽是如何映射到 Redis 实例上的?**
diff --git "a/\344\270\255\351\227\264\344\273\266/elasticsearch.md" "b/\344\270\255\351\227\264\344\273\266/elasticsearch.md"
deleted file mode 100644
index 7e2b094..0000000
--- "a/\344\270\255\351\227\264\344\273\266/elasticsearch.md"
+++ /dev/null
@@ -1,2438 +0,0 @@
-
-
-
-
-- [基础](#%E5%9F%BA%E7%A1%80)
- - [概念](#%E6%A6%82%E5%BF%B5)
- - [启动和关闭](#%E5%90%AF%E5%8A%A8%E5%92%8C%E5%85%B3%E9%97%AD)
- - [配置文件](#%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6)
- - [PUT](#put)
- - [GET](#get)
- - [全文搜索](#%E5%85%A8%E6%96%87%E6%90%9C%E7%B4%A2)
- - [高亮搜索](#%E9%AB%98%E4%BA%AE%E6%90%9C%E7%B4%A2)
- - [分析](#%E5%88%86%E6%9E%90)
- - [分布式特性](#%E5%88%86%E5%B8%83%E5%BC%8F%E7%89%B9%E6%80%A7)
-- [集群原理](#%E9%9B%86%E7%BE%A4%E5%8E%9F%E7%90%86)
- - [术语](#%E6%9C%AF%E8%AF%AD)
- - [节点](#%E8%8A%82%E7%82%B9)
- - [分片](#%E5%88%86%E7%89%87)
- - [集群健康](#%E9%9B%86%E7%BE%A4%E5%81%A5%E5%BA%B7)
- - [索引](#%E7%B4%A2%E5%BC%95)
- - [故障转移](#%E6%95%85%E9%9A%9C%E8%BD%AC%E7%A7%BB)
- - [单机多节点](#%E5%8D%95%E6%9C%BA%E5%A4%9A%E8%8A%82%E7%82%B9)
-- [数据输入与输出](#%E6%95%B0%E6%8D%AE%E8%BE%93%E5%85%A5%E4%B8%8E%E8%BE%93%E5%87%BA)
- - [文档元数据](#%E6%96%87%E6%A1%A3%E5%85%83%E6%95%B0%E6%8D%AE)
- - [创建新文档](#%E5%88%9B%E5%BB%BA%E6%96%B0%E6%96%87%E6%A1%A3)
- - [取回文档](#%E5%8F%96%E5%9B%9E%E6%96%87%E6%A1%A3)
- - [取回多个文档](#%E5%8F%96%E5%9B%9E%E5%A4%9A%E4%B8%AA%E6%96%87%E6%A1%A3)
- - [删除文档](#%E5%88%A0%E9%99%A4%E6%96%87%E6%A1%A3)
- - [检查文档是否存在](#%E6%A3%80%E6%9F%A5%E6%96%87%E6%A1%A3%E6%98%AF%E5%90%A6%E5%AD%98%E5%9C%A8)
- - [更新文档](#%E6%9B%B4%E6%96%B0%E6%96%87%E6%A1%A3)
- - [更新和冲突](#%E6%9B%B4%E6%96%B0%E5%92%8C%E5%86%B2%E7%AA%81)
- - [批量操作](#%E6%89%B9%E9%87%8F%E6%93%8D%E4%BD%9C)
-- [分布式文档存储](#%E5%88%86%E5%B8%83%E5%BC%8F%E6%96%87%E6%A1%A3%E5%AD%98%E5%82%A8)
- - [路由文档到分片](#%E8%B7%AF%E7%94%B1%E6%96%87%E6%A1%A3%E5%88%B0%E5%88%86%E7%89%87)
-- [映射和分析](#%E6%98%A0%E5%B0%84%E5%92%8C%E5%88%86%E6%9E%90)
- - [核心简单域类型](#%E6%A0%B8%E5%BF%83%E7%AE%80%E5%8D%95%E5%9F%9F%E7%B1%BB%E5%9E%8B)
- - [查看映射](#%E6%9F%A5%E7%9C%8B%E6%98%A0%E5%B0%84)
- - [自定义映射器](#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%98%A0%E5%B0%84%E5%99%A8)
- - [index](#index)
- - [analyzer](#analyzer)
- - [更新映射](#%E6%9B%B4%E6%96%B0%E6%98%A0%E5%B0%84)
- - [测试映射](#%E6%B5%8B%E8%AF%95%E6%98%A0%E5%B0%84)
- - [分析器](#%E5%88%86%E6%9E%90%E5%99%A8)
- - [测试分析器](#%E6%B5%8B%E8%AF%95%E5%88%86%E6%9E%90%E5%99%A8)
- - [复杂核心域类型](#%E5%A4%8D%E6%9D%82%E6%A0%B8%E5%BF%83%E5%9F%9F%E7%B1%BB%E5%9E%8B)
- - [多值域](#%E5%A4%9A%E5%80%BC%E5%9F%9F)
- - [内部对象](#%E5%86%85%E9%83%A8%E5%AF%B9%E8%B1%A1)
-- [搜索](#%E6%90%9C%E7%B4%A2)
- - [空搜索](#%E7%A9%BA%E6%90%9C%E7%B4%A2)
- - [多索引多类型](#%E5%A4%9A%E7%B4%A2%E5%BC%95%E5%A4%9A%E7%B1%BB%E5%9E%8B)
- - [轻量搜索](#%E8%BD%BB%E9%87%8F%E6%90%9C%E7%B4%A2)
-- [请求体查询](#%E8%AF%B7%E6%B1%82%E4%BD%93%E6%9F%A5%E8%AF%A2)
- - [空查询](#%E7%A9%BA%E6%9F%A5%E8%AF%A2)
- - [提升权重](#%E6%8F%90%E5%8D%87%E6%9D%83%E9%87%8D)
- - [explain](#explain)
- - [查询和过滤器的区别](#%E6%9F%A5%E8%AF%A2%E5%92%8C%E8%BF%87%E6%BB%A4%E5%99%A8%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [排序与相关性](#%E6%8E%92%E5%BA%8F%E4%B8%8E%E7%9B%B8%E5%85%B3%E6%80%A7)
- - [按照字段的值排序](#%E6%8C%89%E7%85%A7%E5%AD%97%E6%AE%B5%E7%9A%84%E5%80%BC%E6%8E%92%E5%BA%8F)
- - [多级排序](#%E5%A4%9A%E7%BA%A7%E6%8E%92%E5%BA%8F)
- - [多值字段的排序](#%E5%A4%9A%E5%80%BC%E5%AD%97%E6%AE%B5%E7%9A%84%E6%8E%92%E5%BA%8F)
- - [字符串排序](#%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%8E%92%E5%BA%8F)
-- [索引管理](#%E7%B4%A2%E5%BC%95%E7%AE%A1%E7%90%86)
- - [创建索引](#%E5%88%9B%E5%BB%BA%E7%B4%A2%E5%BC%95)
- - [删除索引](#%E5%88%A0%E9%99%A4%E7%B4%A2%E5%BC%95)
- - [索引设置](#%E7%B4%A2%E5%BC%95%E8%AE%BE%E7%BD%AE)
- - [配置分析器](#%E9%85%8D%E7%BD%AE%E5%88%86%E6%9E%90%E5%99%A8)
- - [自定义分析器](#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%88%86%E6%9E%90%E5%99%A8)
- - [类型和映射](#%E7%B1%BB%E5%9E%8B%E5%92%8C%E6%98%A0%E5%B0%84)
- - [根对象](#%E6%A0%B9%E5%AF%B9%E8%B1%A1)
-- [分片内部原理](#%E5%88%86%E7%89%87%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86)
- - [倒排索引](#%E5%80%92%E6%8E%92%E7%B4%A2%E5%BC%95)
-- [深入搜索](#%E6%B7%B1%E5%85%A5%E6%90%9C%E7%B4%A2)
- - [精确值查找](#%E7%B2%BE%E7%A1%AE%E5%80%BC%E6%9F%A5%E6%89%BE)
- - [term 查询文本](#term-%E6%9F%A5%E8%AF%A2%E6%96%87%E6%9C%AC)
- - [terms 查询](#terms-%E6%9F%A5%E8%AF%A2)
- - [全文搜索](#%E5%85%A8%E6%96%87%E6%90%9C%E7%B4%A2-1)
- - [match 查询](#match-%E6%9F%A5%E8%AF%A2)
- - [range 查询](#range-%E6%9F%A5%E8%AF%A2)
- - [数字范围](#%E6%95%B0%E5%AD%97%E8%8C%83%E5%9B%B4)
- - [日期范围](#%E6%97%A5%E6%9C%9F%E8%8C%83%E5%9B%B4)
- - [分页](#%E5%88%86%E9%A1%B5)
- - [exists 查询](#exists-%E6%9F%A5%E8%AF%A2)
- - [bool 组合查询](#bool-%E7%BB%84%E5%90%88%E6%9F%A5%E8%AF%A2)
- - [如何使用 bool 查询](#%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8-bool-%E6%9F%A5%E8%AF%A2)
- - [constant_score 查询](#constant_score-%E6%9F%A5%E8%AF%A2)
- - [多字段搜索](#%E5%A4%9A%E5%AD%97%E6%AE%B5%E6%90%9C%E7%B4%A2)
- - [多字符串查询](#%E5%A4%9A%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%9F%A5%E8%AF%A2)
- - [multi_match 查询](#multi_match-%E6%9F%A5%E8%AF%A2)
- - [多字段映射](#%E5%A4%9A%E5%AD%97%E6%AE%B5%E6%98%A0%E5%B0%84)
- - [copy_to 定制组合 field](#copy_to-%E5%AE%9A%E5%88%B6%E7%BB%84%E5%90%88-field)
-- [springboot 集成 es](#springboot-%E9%9B%86%E6%88%90-es)
-- [mall](#mall)
-
-
-
-[Elasticsearch 权威指南](https://www.elastic.co/guide/cn/elasticsearch/guide/current/_talking_to_elasticsearch.html)
-
-## 基础
-
-Elasticsearch 是一个开源的搜索引擎,建立在全文搜索引擎库 lucene 基础之上。Elasticsearch 也是使用 Java 编写的,它的内部使用 Lucene 做索引与搜索,但是它的目的是使全文检索变得简单, 通过隐藏 Lucene 的复杂性,取而代之的提供一套简单一致的 RESTful API。
-
-ES与mysql的对应关系:
-
-- index –> DB
-- type –> Table
-- Document –> row
-
-### 概念
-
-[参考--阮一峰es教程](http://www.ruanyifeng.com/blog/2017/08/elasticsearch.html)
-
-1. node 和 cluster
-
- Elastic 本质上是一个分布式数据库,允许多台服务器协同工作,每台服务器可以运行多个 Elastic 实例。单个 Elastic 实例称为一个节点(node)。一组节点构成一个集群(cluster)。
-
-2. Index
-
- 数据库的同义词。每个 Index (即数据库)的名字必须是小写。查看当前节点的所有index:`GET _cat/indices`
-
-3. Type
-
- 类似于表结构。不同的 Type 应该有相似的结构(schema),举例来说,`id`字段不能在这个组是字符串,在另一个组是数值。这是与关系型数据库的表的区别。性质完全不同的数据(比如`products`和`logs`)应该存成两个 Index,而不是一个 Index 里面的两个 Type。
-
-4. Document
-
- 单条的记录称为 Document。Document 可以分组,比如`weather`这个 Index 里面,可以按城市分组(北京和上海),这种分组就是 Type。同一个 Index 里面的 Document,不要求有相同的结构(scheme),但是最好保持相同,这样有利于提高搜索效率。
-
-
-### 启动和关闭
-
-不用安装,解压即可。
-
-启动:`bin\elasticsearch.bat`
-
-关闭:ctrl+c/`curl -XPOST http://localhost:9200/_cluster/nodes/_shutdown `关掉整个集群
-
-启动 head 插件:到 head 安装目录下运行`grunt server`
-
-### 配置文件
-
-安装目录config下的 elasticsearch.yml 可以配置集群的信息,如cluster.name 和 node.name。
-
-```yaml
-# ---------------------------------- MyConfig ----------------------------------
-#cluster.name: xxx
-node.name: node-2
-#指明该节点可以是主节点,也可以是数据节点
-node.master: true
-node.data: true
-network.host: 127.0.0.1
-http.port: 9200
-transport.tcp.port: 9300
-```
-
-http.port 是elasticsearch对外提供服务的http端口配置。
-
-transport.tcp.port 指定了elasticsearch集群内数据通讯使用的端口,默认情况下为9300。
-
-
-### PUT
-
-```json
-PUT /company/employee/1
-{
- "first_name" : "Tyson",
- "last_name" : "dai",
- "age" : 23,
- "interests" : ["sport", "music"],
- "hire_date" : "2014-01-01"
-}
-```
-
-company:索引名称,employee:类型名称,1是 ID。
-
-### GET
-
-```json
-GET /company/employee/1
-```
-
-搜索所有雇员:
-
-```json
-GET /company/employee/_search
-```
-
-搜索结果如下,雇员数据放在 hits 数组中:
-
-```json
-{
- "took": 413,
- "timed_out": false,
- "_shards": {
- "total": 5,
- "successful": 5,
- "skipped": 0,
- "failed": 0
- },
- "hits": {
- "total": 2,
- "max_score": 1,
- "hits": [
- {
- "_index": "company",
- "_type": "employee",
- "_id": "2",
- "_score": 1,
- "_source": {
- "first_name": "Lily",
- "last_name": "dai",
- "age": 23,
- "interests": [
- "sport",
- "music"
- ]
- }
- },
- {
- "_index": "company",
- "_type": "employee",
- "_id": "1",
- "_score": 1,
- "_source": {
- "first_name": "Tyson",
- "last_name": "dai",
- "age": 23,
- "interests": [
- "sport",
- "music"
- ]
- }
- }
- ]
- }
-}
-```
-
-按特定条件搜索:
-
-```json
-GET /company/employee/_search?q=first_name:Tyson
-```
-
-使用查询表达式搜索:
-
-```json
-GET /company/employee/_search
-{
- "query": {
- "match": {
- "first_name": "Tyson"
- }
- }
-}
-```
-
-复杂查询:
-
-姓氏为 Dai的雇员,但这次我们只需要年龄大于 20 的。
-
-```json
-GET /company/employee/_search
-{
- "query": {
- "bool" : {
- "must" : {
- "match" : {
- "last_name" : "dai"
- }
- },
- "filter": {
- "range": {
- "age": {"gt" : 20}
- }
- }
- }
- }
-}
-```
-
-### 全文搜索
-
-传统数据库确实很难搞定的任务。
-
-```json
-GET /company/employee/_search
-{
- "query": {
- "match": {
- "about": "rock climbing"
- }
- }
-}
-```
-
-返回`"about": "rock climbing"`和`"about": "rock albums"`两条记录,默认按照每个文档跟查询的匹配程度排序。
-
-### 高亮搜索
-
-```json
-GET /company/employee/_search
-{
- "query": {
- "match_phrase": {
- "about": "climbing"
- }
- },
- "highlight": {
- "fields": {
- "about": {}
- }
- }
-}
-```
-
-返回结果多 `highlight` 的部分。这个部分包含了 `about` 属性匹配的文本片段,并以 HTML 标签 `` 封装。
-
-```json
-{
- "took": 43,
- "timed_out": false,
- "_shards": {
- "total": 5,
- "successful": 5,
- "skipped": 0,
- "failed": 0
- },
- "hits": {
- "total": 1,
- "max_score": 0.2876821,
- "hits": [
- {
- "_index": "company",
- "_type": "employee",
- "_id": "3",
- "_score": 0.2876821,
- "_source": {
- "first_name": "Tyson",
- "last_name": "dai",
- "age": 23,
- "interests": [
- "sport",
- "music"
- ],
- "about": "rock climbing"
- },
- "highlight": {
- "about": [
- "rock climbing"
- ]
- }
- }
- ]
- }
-}
-```
-
-### 分析
-
-Elasticsearch 有一个功能叫聚合(aggregations),允许我们基于数据生成一些精细的分析结果。聚合与 SQL 中的 `GROUP BY` 类似但更强大。
-
-```json
-GET /company/employee/_search
-{
- "aggs": {
- "all_interests": {
- "terms": { "field": "interests" }
- }
- }
-}
-```
-
-直接执行上面的代码会报错,原因是5.x后对排序,聚合这些操作用单独的数据结构(fielddata)缓存到内存里了,需要单独开启。
-
-```json
-PUT company/_mapping/employee/
-{
- "properties": {
- "interests": {
- "type": "text",
- "fielddata": true
- }
- }
-}
-```
-
-搜索结果:
-
-```json
-{
- ...
- "hits": { ... },
- "aggregations": {
- "all_interests": {
- "doc_count_error_upper_bound": 0,
- "sum_other_doc_count": 0,
- "buckets": [
- {
- "key": "music",
- "doc_count": 4
- },
- {
- "key": "sport",
- "doc_count": 4
- }
- ]
- }
- }
-}
-```
-
-如果想知道叫 Tyson的雇员中最受欢迎的兴趣爱好,可以直接添加适当的查询来组合查询:
-
-```json
-GET company/employee/_search
-{
- "query": {
- "match": {
- "first_name": "Tyson"
- }
- },
- "aggs": {
- "all_interests": {
- "terms": {
- "field": "interests"
- }
- }
- }
-}
-```
-
-搜索结果:
-
-```json
-{
- "took": 33,
- "timed_out": false,
- "_shards": {
- "total": 5,
- "successful": 5,
- "skipped": 0,
- "failed": 0
- },
- "hits": {
- "total": 1,
- "max_score": 0.2876821,
- "hits": [
- {
- "_index": "company",
- "_type": "employee",
- "_id": "3",
- "_score": 0.2876821,
- "_source": {
- "first_name": "Tyson",
- "last_name": "dai",
- "age": 23,
- "interests": [
- "sport",
- "music"
- ],
- "about": "rock climbing"
- }
- }
- ]
- },
- "aggregations": {
- "all_interests": {
- "doc_count_error_upper_bound": 0,
- "sum_other_doc_count": 0,
- "buckets": [
- {
- "key": "music",
- "doc_count": 1
- },
- {
- "key": "sport",
- "doc_count": 1
- }
- ]
- }
- }
-}
-```
-
-聚合还支持分级汇总 。比如,查询特定兴趣爱好员工的平均年龄:
-
-```java
-GET company/employee/_search
-{
- "aggs": {
- "all_interests": {
- "terms": {
- "field": "interests"
- },
- "aggs": {
- "avg_age": {
- "avg": {
- "field": "age"
- }
- }
- }
- }
- }
-}
-```
-
-搜索结果:
-
-```json
-"aggregations": {
- "all_interests": {
- "doc_count_error_upper_bound": 0,
- "sum_other_doc_count": 0,
- "buckets": [
- {
- "key": "music",
- "doc_count": 4,
- "avg_age": {
- "value": 18.5
- }
- },
- {
- "key": "sport",
- "doc_count": 4,
- "avg_age": {
- "value": 18.5
- }
- }
- ]
- }
- }
-```
-
-### 分布式特性
-
-Elasticsearch 可以横向扩展至数百(甚至数千)的服务器节点,同时可以处理PB级数据。
-
-## 集群原理
-
-ElasticSearch 的主旨是随时可用和按需扩容。扩容可以通过购买性能更强大( *垂直扩容* ) 或者数量更多的服务器( *水平扩容* )来实现。
-
-### 术语
-
-#### 节点
-
-一个运行中的 Elasticsearch 实例称为节点,而集群是由一个或者多个拥有相同 `cluster.name` 配置的节点组成, 它们共同承担数据和负载的压力。当有节点加入集群中或者从集群中移除节点时,集群将会重新平均分布所有的数据。
-
-当一个节点被选举成为主节点时, 它将负责管理集群范围内的所有变更,例如增加、删除索引,或者增加、删除节点等。 而主节点并不需要涉及到文档级别的变更和搜索等操作,所以当集群只拥有一个主节点的情况下,即使流量的增加它也不会成为瓶颈。 任何节点都可以成为主节点。
-
-我们可以将请求发送到集群中的任何节点,包括主节点。 每个节点都知道任意文档所处的位置,并且能够将我们的请求直接转发到存储我们所需文档的节点。 无论我们将请求发送到哪个节点,它都能负责从各个包含我们所需文档的节点收集回数据,并将最终结果返回給客户端。 Elasticsearch 对这一切的管理都是透明的。
-
-获取节点信息:`http://localhost:9200/_cluster/state/nodes?pretty`,pretty 用于换行。
-
-#### 分片
-
-每个节点可以分配一个或多个分片。分片是数据的容器,文档保存在分片内,分片又被分配到集群内的各个节点里。 当你的集群规模扩大或者缩小时, Elasticsearch 会自动的在各节点中迁移分片,使得数据仍然均匀分布在集群里。
-
-一个分片可以是 *主* 分片或者 *副本* 分片。 索引内任意一个文档都归属于一个主分片,所以主分片的数目决定着索引能够保存的最大数据量。
-
-一个副本分片只是一个主分片的拷贝。 副本分片作为硬件故障时保护数据不丢失的冗余备份,并为搜索和返回文档等读操作提供服务。
-
-### 集群健康
-
-`GET /_cluster/health`
-
-返回内容:
-
-```json
-{
- "cluster_name": "elasticsearch",
- "status": "yellow",
- "timed_out": false,
- "number_of_nodes": 1,
- "number_of_data_nodes": 1,
- "active_primary_shards": 6,
- "active_shards": 6,
- "relocating_shards": 0,
- "initializing_shards": 0,
- "unassigned_shards": 5,
- "delayed_unassigned_shards": 0,
- "number_of_pending_tasks": 0,
- "number_of_in_flight_fetch": 0,
- "task_max_waiting_in_queue_millis": 0,
- "active_shards_percent_as_number": 54.54545454545454
-}
-```
-
-status 字段表示当前集群总体是否正常,它有三个值:
-
-1. green,所有的主分片和副本分片都正常运行。
-
-2. yellow,所有的主分片都正常运行,但不是所有的副本分片都正常运行。
-3. red,有主分片不能正常运行。
-
-### 索引
-
-往 elasticsearch 添加数据时需要用到索引,索引是指向一个或多个分片的逻辑命名空间。一个分片是一个 Lucene 的实例,以及它本身就是一个完整的搜索引擎。
-
-当我们只开启一个节点时,索引在默认情况下会被分配5个主分片,每个主分片拥有一个副本分片。主分片会被分配到这个节点上,而副本分片不会被分配到任何节点。
-
-```json
-{
- "cluster_name": "elasticsearch",
- "status": "yellow",
- ...
- "unassigned_shards": 5,//5个副本分片都是 unassigned,都没有被分配到任何节点
- ...
-}
-```
-
-当前我们的集群是正常运行的,但是在硬件故障时有丢失数据的风险。
-
-### 故障转移
-
-可以在同一个目录下开启另一个节点,副本分片会被分配到这个节点上,此时计算有硬件故障也不会丢失数据。
-
-### 单机多节点
-
-node1 的 elasticsearch.yml 做如下配置:
-
-```yaml
-# ---------------------------------- MyConfig ----------------------------------
-#cluster.name: xxx
-node.name: node-1
-#指明该节点可以是主节点,也可以是数据节点
-#node.master: true
-#node.data: true
-network.host: 127.0.0.1
-http.port: 9200
-transport.tcp.port: 9300
-discovery.zen.ping.unicast.hosts: ["127.0.0.1:9300", "127.0.0.1:9301", "127.0.0.1:9302"]
-
-# 解决elasticsearch-head 集群健康值: 未连接问题
-http.cors.enabled: true
-http.cors.allow-origin: "*"
-```
-
-node2 的 elasticsearch.yml 做如下配置:
-
-```yaml
-# ---------------------------------- MyConfig ----------------------------------
-#cluster.name: xxx
-node.name: node-2
-#指明该节点可以是主节点,也可以是数据节点
-#node.master: true
-#node.data: true
-network.host: 127.0.0.1
-http.port: 9201
-transport.tcp.port: 9301
-discovery.zen.ping.unicast.hosts: ["127.0.0.1:9300", "127.0.0.1:9301", "127.0.0.1:9302"]
-
-# 解决elasticsearch-head 集群健康值: 未连接问题
-http.cors.enabled: true
-http.cors.allow-origin: "*"
-```
-
-同理 node3 也做好配置。然后重启 elasticsearch 节点,就可以实现单机多节点集群搭建。
-
-
-
-## 数据输入与输出
-
-### 文档元数据
-
-一个文档不仅包含数据,也包含元数据。三个必须的元数据元素如下:\_index、\_type和_id。
-
-索引名字必须小写,不能以下划线开头,不能包含逗号。
-
-\_type在索引中对数据进行逻辑分区,如产品下面还可以分为很多子类。一个 `_type` 命名可以是大写或者小写,但是不能以下划线或者句号开头,不应该包含逗号, 并且长度限制为256个字符。
-
-### 创建新文档
-
-使用 PUT 请求,需要定义 _id:
-
-```json
-PUT /{index}/{type}/{id}
-{
- "field": "value",
- ...
-}
-```
-
-id 为2的文档存在时会报错: document already exists
-
-```json
-PUT company/employee/2/_create
-{
- "first_name" : "Tyson",
- "last_name" : "dai",
- "age" : 23,
- "interests" : ["sport", "music"]
-}
-```
-
-不存在时才创建:
-
-```json
-PUT company/employee/2?op_type=create
-{
- "first_name" : "Tyson",
- "last_name" : "dai",
- "age" : 23,
- "interests" : ["sport", "music"]
-}
-```
-
-使用 `POST` 可以让 Elasticsearch 自动生成唯一 的_id。
-
-```json
-POST company/employee
-{
- "first_name" : "Tyson",
- "last_name" : "dai",
- "age" : 23,
- "interests" : ["sport", "music"]
-}
-```
-
-### 取回文档
-
-返回文档的一部分:
-
-```json
-GET /company/employee/1?_source=first_name,interests
-```
-
-只得到_source 字段(即id为1的整个文档):
-
-```json
-GET /company/employee/1/_source
-```
-
-
-### 取回多个文档
-
-mget api 要求传入一个 docs 数组作为参数,可以通过 _source 指定返回字段。
-
-```json
-GET /_mget
-{
- "docs": [
- {
- "_index": "company",
- "_type": "employee",
- "_id": 2
- },
- {
- "_index": "class",
- "_type": "student",
- "_id": 1,
- "_source": "about"
- }
- ]
-}
-```
-
-如果获取的文档 _index 和 _type 相同,可以在 url 指定默认的 _index 和 _type,传入一个 ids 数组。
-
-```json
-GET /company/employee/_mget
-{
- "ids": ["1", "2"]
-}
-```
-
-通过单独请求可以覆盖默认的 _index 和 _type。
-
-```json
-GET /company/employee/_mget
-{
- "docs" : [
- { "_id" : 2 },
- { "_index" : "class", "_type" : "student", "_id" : 1 }
- ]
-}
-```
-
-### 删除文档
-
-`DELETE company/employee/1`,删除文档,版本号会增加。
-
-```json
-{
- "_index": "company",
- "_type": "employee",
- "_id": "1",
- "_version": 2,
- "result": "deleted",
- "_shards": {
- "total": 2,
- "successful": 1,
- "failed": 0
- },
- "_seq_no": 1,
- "_primary_term": 4
-}
-```
-
-### 检查文档是否存在
-
-`HEAD /company/employee/1`
-
-返回结果:`200 - OK`
-
-### 更新文档
-
-文档是不可变的,不能被修改,只能被替换。 `update` API 必须遵循同样的规则。 从外部来看,我们在一个文档的某个位置进行部分更新。然而在内部, `update` API 简单使用 *检索-修改-重建索引* 的处理过程。
-
-```json
-POST company/employee/1/_update
-{
- "doc": {
- "first_name": "sophia",
- "interests": ["sport", "chess"]
- }
-}
-```
-
-#### 更新和冲突
-
-```json
-POST company/employee/1/_update?retry_on_conflict=5
-{
- "doc": {
- "first_name": "sophia",
- "interests": ["sport", "chess"]
- }
-}
-```
-
-### 批量操作
-
-语法:
-
-```json
-{ action: { metadata }}\n
-{ request body }\n
-{ action: { metadata }}\n
-{ request body }\n
-...
-```
-
-action 有 create、delete、index 和 update。metadata 定义了\_index,\_type,_id 等信息。
-
-create:如果文档不存在,那么就创建它。index:创建一个新文档或者替换一个现有的文档。create 和 index 不指定 `_id`的话 ,将会自动生成一个 ID 。
-
-```json
-POST /_bulk
-{ "delete": { "_index": "website", "_type": "blog", "_id": "123" }}
-{ "create": { "_index": "website", "_type": "blog", "_id": "123" }}
-{ "title": "My first blog post" }
-{ "index": { "_index": "website", "_type": "blog" }}
-{ "title": "My second blog post" }
-{ "update": { "_index": "website", "_type": "blog", "_id": "123", "_retry_on_conflict" : 3} }
-{ "doc" : {"title" : "My updated blog post"} }
-```
-
-bulk 请求不是原子的,不能用它来实现事务控制。每个请求是单独处理的,因此一个请求的成功或失败不会影响其他的请求。
-
-**默认的 index 和 type**
-
-```json
-POST /website/_bulk
-{ "index": { "_type": "log" }}
-{ "event": "User logged in" }
-```
-
-
-
-## 分布式文档存储
-
-### 路由文档到分片
-
-文档所在分片的位置通过这个公式计算:`shard = hash(routing) % number_of_primary_shards`,rounting 默认是文档的 _id,可以设置成自定义的值。创建索引的时候就确定好主分片的数量,并且永远不会改变这个数量:因为如果数量变化了,那么所有之前路由的值都会无效,文档也再也找不到了。
-
-
-
-
-## 映射和分析
-
-映射:为了能够将时间域视为时间,数字域视为数字,字符串域视为全文或精确值字符串, Elasticsearch 需要知道每个域中数据的类型。这个信息包含在映射中。
-
-分析:将一块文本分成适合于倒排索引的独立的词条,然后将这些词条统一化为标准格式以提高它们的可搜索性。
-
-### 核心简单域类型
-
-Elasticsearch 支持 如下简单域类型:
-
-- 字符串: `string`
-- 整数 : `byte`, `short`, `integer`, `long`
-- 浮点数: `float`, `double`
-- 布尔型: `boolean`
-- 日期: `date`
-
-当你索引一个新的文档,elasticsearch 会使用动态映射,通过 json 中的基本数据类型,尝试猜测域的类型。
-
-| **JSON type** | **域 type** |
-| ------------------------------ | ----------- |
-| 布尔型: `true` 或者 `false` | `boolean` |
-| 整数: `123` | `long` |
-| 浮点数: `123.45` | `double` |
-| 字符串,有效日期: `2014-09-15` | `date` |
-| 字符串: `foo bar` | `string` |
-
-如果你通过引号( `"123"` )索引一个数字,它会被映射为 `string` 类型,而不是 `long` 。但是,如果这个域已经映射为 `long` ,那么 Elasticsearch 会尝试将这个字符串转化为 long ,如果无法转化,则抛出一个异常。
-
-### 查看映射
-
-`GET /company/_mapping/employee`
-
-返回结果:
-
-```json
-{
- "company": {
- "mappings": {
- "employee": {
- "properties": {
- "age": {
- "type": "long"
- },
- "first_name": {
- "type": "text",
- "fields": {
- "keyword": {
- "type": "keyword",
- "ignore_above": 256
- }
- }
- },
- "hire_date": {
- "type": "date"
- },
- "interests": {
- "type": "text",
- "fields": {
- "keyword": {
- "type": "keyword",
- "ignore_above": 256
- }
- }
- },
- "last_name": {
- "type": "text",
- "fields": {
- "keyword": {
- "type": "keyword",
- "ignore_above": 256
- }
- }
- }
- }
- }
- }
- }
-}
-```
-
-### 自定义映射器
-
-`string` 类型域会被认为包含全文。它们的值在索引前,会通过 一个分析器,针对于这个域的查询在搜索前也会经过一个分析器。string 域映射的两个最重要的属性是 index 和 analyzer。
-
-#### index
-
-`index` 属性控制怎样索引字符串。它有三个值:
-
-- analyzed:默认,首先分析这个域,然后索引它;
-- not_analyzed:索引这个域,索引的是精确值,不会对它进行分析;
-- no:不要索引这个域,这个域不会被搜索到。
-
-设置 tag 域的 index 为 not_analyzed:
-
-```json
-{
- "tag": {
- "type": "string",
- "index": "not_analyzed"
- }
-}
-```
-
-其他简单类型(例如 `long` , `double` , `date` 等)也接受 `index` 参数,但有意义的值只有 `no` 和 `not_analyzed` , 因为它们永远不会被分析。
-
-#### analyzer
-
-对于 `analyzed` 字符串域,用 `analyzer` 属性指定在搜索和索引时使用的分析器。默认, Elasticsearch 使用 `standard` 分析器, 但你可以指定一个内置的分析器替代它,例如 `whitespace` 、 `simple` 和 `english`:
-
-```json
-{
- "tweet": {
- "type": "string",
- "analyzer": "english"
- }
-}
-```
-
-
-### 更新映射
-
-我们可以更新一个映射来添加一个新域,但不能将一个存在的域从 `analyzed` 改为 `not_analyzed` ,否则索引的数据可能会出错,数据不能被正常的搜索。
-
-增加名为 tag 的 not_analyzed 的文本域:
-
-```json
-PUT /gb/_mapping/tweet
-{
- "properties" : {
- "tag" : {
- "type" : "text",
- "index": "not_analyzed"
- }
- }
-}
-```
-
-指定中文分词器:
-
-```json
-PUT /gb/_mapping/tweet
-{
- "properties" : {
- "user": {
- "type": "text",
- "analyzer": "ik_max_word",
- "search_analyzer": "ik_max_word"
- }
- }
-}
-```
-
-`analyzer`是字段文本的分词器,`search_analyzer`是搜索词的分词器。`ik_max_word`分词器是插件`ik`提供的,可以对文本进行最大数量的分词。
-
-
-### 测试映射
-
-```json
-GET /gb/_analyze
-{
- "field": "tag",
- "text": "Black-cats"
-}
-```
-
- `tag` 域产生单独的词条 `Black-cats` 。
-
-### 分析器
-
-分析器的三个功能:
-
-- 字符过滤。字符串按顺序通过每个字符过滤器。它们在分词前整理字符串。一个字符过滤器可以用来去掉HTML或者将 & 转化为 and。
-- 字符串被分词器分成单个的词条
-- token 过滤器。改变词条(Quick 小写),删除词条(a/the/and),增加词条(leap/jump这种同义词)
-
-对特定域和全文域_all 查询字符串时可能会返回不同的结果。
-
-当你查询一个 *全文* 域时, 会对查询字符串应用相同的分析器,以产生正确的搜索词条列表。
-
-当你查询一个 *精确值* 域时,不会分析查询字符串, 而是搜索你指定的精确值。
-
-```json
-GET /_search?q=2014 # 12 results
-GET /_search?q=2014-09-15 # 12 results
-GET /_search?q=date:2014-09-15 # 1 result
-GET /_search?q=date:2014 # 0 results 没有"2014",只有"2014-09-15"
-```
-
-date 域包含一个精确值:单独的词条 `2014-09-15`。
-
-_all 域是一个全文域,所以分词进程将日期转化为三个词条: `2014`, `09`, 和 `15`。
-
-#### 测试分析器
-
-```json
-GET /_analyze
-{
- "analyzer": "standard",
- "text": "Text to analyze"
-}
-```
-
-结果:
-
-```json
-{
- "tokens": [
- {
- "token": "text",
- "start_offset": 0,
- "end_offset": 4,
- "type": "",
- "position": 0
- },
- {
- "token": "to",
- "start_offset": 5,
- "end_offset": 7,
- "type": "",
- "position": 1
- },
- {
- "token": "analyze",
- "start_offset": 8,
- "end_offset": 15,
- "type": "",
- "position": 2
- }
- ]
-}
-```
-
-### 复杂核心域类型
-
-#### 多值域
-
-`{ "tag": [ "search", "nosql" ]}`
-
-### 内部对象
-
-```json
-{
- "tweet": "Elasticsearch is very flexible",
- "user": {
- "id": "@johnsmith",
- "gender": "male",
- "age": 26,
- "name": {
- "full": "John Smith",
- "first": "John",
- "last": "Smith"
- }
- }
-}
-```
-
-Lucene 不理解内部对象。 Lucene 文档是由一组键值对列表组成的。为了能让 Elasticsearch 有效地索引内部类,它把我们的文档转化成这样:
-
-```json
-{
- "tweet": [elasticsearch, flexible, very],
- "user.id": [@johnsmith],
- "user.gender": [male],
- "user.age": [26],
- "user.name.full": [john, smith],
- "user.name.first": [john],
- "user.name.last": [smith]
-}
-```
-
-
-
-
-## 搜索
-
-### 空搜索
-
-`GET /_search`,返回的 hits 数组包含所查询结果的前十个文档。
-
-`GET /_search?timeout=10ms`,在请求超时之前,Elasticsearch 将会返回已经成功从每个分片获取的结果。
-
-
-### 多索引多类型
-
-`GET /c*,g*/_search`,在以 c 开头和 g 开头的索引中搜索所有的类型。
-
-`GET /_all/employee,student/_search?size=1&from=1`,在所有索引中搜索 employee 和 student 类型。elasticsearch 默认一次返回10条结果,size 可以指定返回返回结果数量,from 指定位移。
-
-
-### 轻量搜索
-
-查询 employee 类型的 last_name 字段为 dai 的所有文档:`GET company/employee/_search?q=last_name:dai`
-
-查询包含 dai 的所有文档:`GET /_search?q=dai`
-
-
-
-## 请求体查询
-
-### 空查询
-
-```json
-GET _search
-{
- "query" : {
- "match_all": {}
- }
-}
-```
-
-
-
-
-### 提升权重
-
-我们可以通过指定 `boost` 来控制任何查询语句的相对的权重, `boost` 的默认值为 `1` ,大于 `1` 会提升一个语句的相对权重。
-
-```json
-GET /company/employee/_search
-{
- "query": {
- "bool": {
- "must": {
- "match": {
- "interests": {
- "query": "sport music",
- "operator": "and"
- }
- }
- },
- "should": [
- { "match": {
- "first_name": {
- "query": "tyson",
- "boost": 3
- }
- }},
- { "match": {
- "first_name": {
- "query": "sophia",
- "boost": 2
- }
- }}
- ]
- }
- }
-}
-```
-
-### explain
-
-查询结果说明。
-
-```json
-GET /_validate/query?explain
-{
- "query": {
- "match" : {
- "interests" : "sport shoes"
- }
- }
-}
-```
-
-结果:
-
-```json
-{
- "valid": true,
- "_shards": {
- "total": 3,
- "successful": 3,
- "failed": 0
- },
- "explanations": [
- {
- "index": "class",
- "valid": true,
- "explanation": "interests:sport interests:shoes"
- },
- {
- "index": "company",
- "valid": true,
- "explanation": "interests:sport interests:shoes"
- }
- ]
-}
-```
-
-### 查询和过滤器的区别
-
-查询会计算得分,而过滤不计算得分,过滤器所需处理更少,所以过滤器可以比普通查询更快。而且过滤器可以被缓存。
-
-
-
-## 排序与相关性
-
-有时,_score 相关性评分对你来说并没有意义。例如,下面的查询返回所有 `user_id` 字段包含 `1` 的结果:
-
-```json
-GET /_search
-{
- "query" : {
- "constant_score" : {
- "filter" : {
- "term" : {
- "id" : 1
- }
- }
- }
- }
-}
-```
-
-### 按照字段的值排序
-
-按照 hire_date 排序。
-
-```json
-GET _search
-{
- "query" : {
- "bool" : {
- "filter" : {
- "match" : {
- "first_name" : "tyson"
- }
- }
- }
- },
- "sort" : {
- "hire_date" : { "order" : "desc" }
- }
-}
-```
-
-### 多级排序
-
-首先按第一个条件排序,仅当结果集的第一个 `sort` 值完全相同时才会按照第二个条件进行排序,以此类推。
-
-```json
-GET _search
-{
- "query" : {
- "bool" : {
- "filter" : {
- "match" : {
- "first_name" : "tyson"
- }
- }
- }
- },
- "sort" : {
- "hire_date" : { "order" : "desc" },
- "_score" : { "order" : "desc" }
- }
-}
-```
-
-Query-string 搜索 也支持自定义排序,可以在查询字符串中使用 `sort` 参数:
-
-```js
-GET /_search?sort=hire_date:desc&sort=_score
-```
-
-### 多值字段的排序
-
-对于数字或日期,你可以将多值字段减为单值,这可以通过使用 `min` 、 `max` 、 `avg` 或是 `sum` 排序模式。 例如你可以按照每个 hire_date 字段中的最早日期进行排序,通过以下方法:
-
-```json
-"sort": {
- "hire_date": {
- "order": "asc",
- "mode": "min"
- }
-}
-```
-
-### 字符串排序
-
-为了对字符串字段进行排序,需要用两种方式对同一个字符串进行索引: `analyzed` 用于搜索, `not_analyzed` 用于排序。
-
-用两种方式索引一个单字段:
-
-```json
-"interests": {
- "type": "string",
- "analyzer": "english",
- "fields": {
- "raw": {
- "type": "string",
- "index": "not_analyzed"
- }
- }
-}
-```
-
-interests.raw 子字段是 not_analyzed。interests 字段只在 _source 中出现一次。
-
-使用 interests 字段用于搜索,interests.raw 字段用于排序:
-
-```json
-GET /_search
-{
- "query": {
- "match": {
- "interests": "elasticsearch"
- }
- },
- "sort": "interests.raw"
-}
-```
-
-备注:以全文 `analyzed` 字段排序会消耗大量的内存。
-
-
-
-## 索引管理
-
-### 创建索引
-
-```json
-PUT /my_index
-{
- "settings": { ... any settings ... },
- "mappings": {
- "type_one": { ... any mappings ... },
- "type_two": { ... any mappings ... },
- ...
- }
-}
-```
-
-禁止自动创建索引,你 可以通过在 `config/elasticsearch.yml` 的每个节点下添加下面的配置:
-
-`action.auto_create_index: false`
-
-### 删除索引
-
-`DELETE /company,student`
-
-`DELETE /index_*`
-
-`DELETE /_all` 或 `DELETE /*`
-
-### 索引设置
-
-number_of_shards:每个索引的主分片数,默认值是 `5` 。这个配置在索引创建后不能修改。
-
-number_of_replicas:每个主分片的副本数,默认值是 `1` 。对于活动的索引库,这个配置可以随时修改。
-
-创建只有 一个主分片,没有副本的小索引:
-
-```json
-PUT /my_temp_index
-{
- "settings": {
- "number_of_shards" : 1,
- "number_of_replicas" : 0
- }
-}
-```
-
- 动态修改副本数:
-
-```json
-PUT /my_temp_index/_settings
-{
- "number_of_replicas": 1
-}
-```
-
-### 配置分析器
-
-#### 自定义分析器
-
-,一个分析器组合了三种函数:字符过滤器、分词器和词单元过滤器, 三种函数按照顺序被执行。
-
-```json
-PUT /my_index
-{
- "settings": {
- "analysis": {
- "char_filter": {
- "&_to_and" : {
- "type" : "mapping",
- "mappings" : ["&=>and"]
- }
- },
- "filter": {
- "my_stopwords" : {
- "type" : "stop",
- "stopwords" : ["the", "a"]
- }
- },
- "analyzer": {
- "my_analyzer" : {
- "type" : "custom",
- "char_filter" : ["html_strip", "&_to_and"],
- "tokenizer" : "standard",
- "filter" : ["lowercase", "my_stopwords"]
- }
- }
- }
- }
-}
-```
-
-测试:
-
-```json
-GET /my_index/_analyze
-{
- "analyzer" : "my_analyzer",
- "text" : "The quick & brown fox"
-}
-```
-
-结果:
-
-```json
-{
- "tokens": [
- {
- "token": "quick",
- "start_offset": 4,
- "end_offset": 9,
- "type": "",
- "position": 1
- },
- {
- "token": "and",
- "start_offset": 10,
- "end_offset": 11,
- "type": "",
- "position": 2
- },
- {
- "token": "brown",
- "start_offset": 12,
- "end_offset": 17,
- "type": "",
- "position": 3
- },
- {
- "token": "fox",
- "start_offset": 18,
- "end_offset": 21,
- "type": "",
- "position": 4
- }
- ]
-}
-```
-
-把分析器应用在 string 字段上:
-
-```json
-PUT /my_index/_mapping/my_type
-{
- "properties": {
- "title": {
- "type": "string",
- "analyzer": "my_analyzer"
- }
- }
-}
-```
-
-### 类型和映射
-
-同一个索引下,不同的类型type应该有相同的结构,映射也应该相同。因为 Lucene 会将同一个索引下的所有字段的映射扁平化,相同字段不同映射会导致冲突。
-
-```json
-{
- "data": {
- "mappings": {
- "people": {
- "properties": {
- "name": {
- "type": "string",
- },
- "address": {
- "type": "string"
- }
- }
- },
- "transactions": {
- "properties": {
- "timestamp": {
- "type": "date",
- "format": "strict_date_optional_time"
- },
- "message": {
- "type": "string"
- }
- }
- }
- }
- }
-}
-```
-
-后台 Lucene 将创建一个映射:
-
-```json
-{
- "data": {
- "mappings": {
- "_type": {
- "type": "string",
- "index": "not_analyzed"
- },
- "name": {
- "type": "string"
- }
- "address": {
- "type": "string"
- }
- "timestamp": {
- "type": "long"
- }
- "message": {
- "type": "string"
- }
- }
- }
-}
-```
-
-因此,类型不适合 *完全不同类型的数据* 。如果两个类型的字段集是互不相同的,这就意味着索引中将有一半的数据是空的(字段将是 *稀疏的* ),最终将导致性能问题。在这种情况下,最好是使用两个单独的索引。
-
-
-### 根对象
-
-映射最高的一层被称为根对象。它可能包含以下几项:
-
-- properties 节点,列出文档每个字段的映射
-- 下划线开头的元数据字段,如 \_type,_id 和 \_source
-- 设置项,控制如何动态处理新的字段,例如 `analyzer` 、 `dynamic_date_formats` 和`dynamic_templates`
-- 其他设置,可以同时应用在根对象和其他 `object` 类型的字段上,例如 `enabled` 、 `dynamic` 和 `include_in_all`
-
-
-
-## 分片内部原理
-
-### 倒排索引
-
-倒排索引包含一个有序列表,列表包含所有文档出现过的不重复个体,或称为 *词项* ,对于每一个词项,包含了它所有曾出现过文档的列表。
-
-| Term | doc1 | doc2 | doc3 |
-| ----- | ---- | ---- | ---- |
-| sport | x | | x |
-| music | | x | |
-| chess | x | x | x |
-
-
-
-## 深入搜索
-
-### 精确值查找
-
-term 查询被用于精确值匹配,这些精确值可能是数字、时间、布尔或者那些 `not_analyzed` 的字符串。
-
-```json
-GET /company/employee/_search
-{
- "query" : {
- "term" : {
- "interests" : "listen"
- }
- }
-}
-```
-
-hire_date 在过去四年内的文档:
-
-```json
-GET company/employee/_search
-{
- "query": {
- "constant_score": {
- "filter": {
- "range": {
- "hire_date": {
- "gt": "now-4y"
- }
- }
- }
- }
- }
-}
-```
-
-#### term 查询文本
-
-删除旧索引(因为旧索引字段都是 analyzed 的,它的映射不正确)然后创建一个能正确映射的新索引:
-
-```json
-PUT /my_store
-{
- "mappings" : {
- "products" : {
- "properties": {
- "productID" : {
- "type" : "keyword",//es6废除了string,index是boolean值,keyword不分词,text分词
- }
- }
- }
- }
-}
-```
-
-放进数据:
-
-```json
-POST /my_store/products/_bulk
-{ "index": { "_id": 1 }}
-{ "price" : 10, "productID" : "XHDK-A-1293-#fJ3" }
-{ "index": { "_id": 2 }}
-{ "price" : 20, "productID" : "KDKE-B-9947-#kL5" }
-{ "index": { "_id": 3 }}
-{ "price" : 30, "productID" : "JODL-X-1937-#pV7" }
-{ "index": { "_id": 4 }}
-{ "price" : 30, "productID" : "QQPX-R-3956-#aD8" }
-```
-
-查询文本:
-
-```json
-GET /my_store/products/_search
-{
- "query" : {
- "constant_score" : {
- "filter" : {
- "term" : {
- "productID" : "XHDK-A-1293-#fJ3"
- }
- }
- }
- }
-}
-```
-
-#### terms 查询
-
-允许指定多值进行匹配。如果这个字段包含了指定值中的任何一个值,那么这个文档满足条件。
-
-```json
-GET /company/employee/_search
-{
- "query" : {
- "terms" : {
- "interests" : ["listen", "music", "sport"]
- }
- }
-}
-```
-
-置入 `filter` 语句的常量评分查询:
-
-```json
-GET /my_store/products/_search
-{
- "query": {
- "constant_score": {
- "filter": {
- "terms" : {
- "interests" : ["listen", "music", "sport"]
- }
- },
- "boost": 1.2
- }
- }
-}
-```
-
-interests 为 listen、music 和 sport 的文档会被匹配。
-
-### 全文搜索
-
-#### match 查询
-
-match 查询是对全文进行查询。
-
-如果有多个搜索关键字, Elastic 认为它们是`or`关系。下面例子返回 interests 为 sport 或者 music 的文档。
-
-```json
-GET _search
-{
- "query" : {
- "match": {
- "interests": "sport music"
- }
- }
-}
-```
-
-指定词都必须匹配:
-
-```json
-GET _search
-{
- "query" : {
- "match": {
- "interests": {
- "query": "sport music",
- "operator": "and"
- }
- }
- }
-}
-```
-
-控制匹配的字段数目:
-
-```json
-GET _search
-{
- "query" : {
- "match": {
- "interests": {
- "query": "sport music",
- "minimum_should_match": 2
- }
- }
- }
-}
-```
-
-### range 查询
-
-查询找出那些落在指定区间内的数字或者时间。
-
-#### 数字范围
-
-```json
-GET company/employee/_search
-{
- "query": {
- "constant_score": {
- "filter": {
- "range": {
- "age": {
- "gt": 20,
- "lt": 100
- }
- }
- }
- }
- }
-}
-```
-
-`gt`:大于;`gte`:大于或等于;`lt`:小于;`lte`:小于或等于。
-
-#### 日期范围
-
-```json
-GET company/employee/_search
-{
- "query": {
- "constant_score": {
- "filter": {
- "range": {
- "hire_date": {
- "gt": "2015-01-01",
- "lt": "2019-01-01"
- }
- }
- }
- }
- }
-}
-```
-
-### 分页
-
-请求得到 1 到 3 页的结果:
-
-```json
-GET /_search?size=5
-GET /_search?size=5&from=5
-GET /_search?size=5&from=10
-```
-
-### exists 查询
-
-查询字段 interests 中不为空的文档:
-
-```json
-GET /company/employee/_search
-{
- "query": {
- "constant_score": {
- "filter": {
- "exists": {
- "field": "interests"
- }
- }
- }
- }
-}
-```
-
-字段 interests 为空的文档。es6 没有 missing api,直接用 bool 和 exists 实现。
-
-```json
-GET /company/employee/_search
-{
- "query": {
- "bool": {
- "must_not": {
- "exists": {
- "field": "interests"
- }
- }
- }
- }
-}
-```
-
-### bool 组合查询
-
-bool 查询允许在单独的查询中组合任意数量的查询,适用于将不同查询字符串映射到不同字段的情况。bool 查询接收如下参数:
-
-- must:文档必须匹配这些条件才能被包含进来
-- must_not:文档必须不匹配这些条件才能被包含进来
-- should:如果满足这些语句中的任意语句,将增加 _score,否则没有影响。主要用来修正每个文档的相关性得分
-- filter:必须匹配,但是不影响评分
-
-如果没有 `must` 语句,那么至少需要能够匹配其中的一条 `should` 语句。但,如果存在至少一条 `must` 语句,则对 `should` 语句的匹配没有要求。
-
-```json
-GET /company/employee/_search
-{
- "query" : {
- "bool" : {
- "must" : { "match" : {"first_name" : "tyson"}},
- "must_not" : { "match" : {"last_name" : "tang"}},
- "should" : [
- {"match" : {"interest" : "music"}},
- {"range" : {"age" : { "gte" : 30 }}}
- ]
- }
- }
-}
-```
-
-如果不想因为文档的时间而影响得分,可以添加 filter 过滤掉某个时间段。
-
-```json
-GET _search
-{
- "query" : {
- "bool" : {
- "must" : {
- "match" : { "first_name" : "tyson"}
- },
- "must_not" : {
- "match" : {"last_name" : "tang"}
- },
- "should" : [
- {"match" : { "interests": "sport"}},
- {"range" : { "age" : { "gt" : 20 }}}
- ],
- "filter" : {
- "range" : { "hire_date" : { "gt" : "2015-01-01" }}
- }
- }
- }
-}
-```
-
-可以将查询移到 `bool` 查询的 `filter` 语句中,这样它就自动的转成一个不评分的查询了。
-
-```json
-{
- "bool": {
- "must": { "match": { "title": "how to make millions" }},
- "must_not": { "match": { "tag": "spam" }},
- "should": [
- { "match": { "tag": "starred" }}
- ],
- "filter": {
- "bool": {
- "must": [
- { "range": { "date": { "gte": "2014-01-01" }}},
- { "range": { "price": { "lte": 29.99 }}}
- ],
- "must_not": [
- { "term": { "category": "ebooks" }}
- ]
- }
- }
- }
-}
-```
-
-bool 查询使用`minimum_should_match`控制需要匹配的 should 语句的数量:
-
-```json
-GET /my_index/my_type/_search
-{
- "query": {
- "bool": {
- "should": [
- { "match": { "title": "brown" }},
- { "match": { "title": "fox" }},
- { "match": { "title": "dog" }}
- ],
- "minimum_should_match": 2
- }
- }
-}
-```
-
-### 如何使用 bool 查询
-
-多次 match 查询:
-
-```json
-GET /company/employee/_search
-{
- "query": {
- "match": {
- "interests": "sport music"
- }
- }
-}
-```
-
-等价的 bool 查询(存在`must`,则对 `should` 语句的匹配没有要求,否则至少需要能够匹配其中的一条 `should` 语句):
-
-```json
-GET /company/employee/_search
-{
- "query": {
- "bool": {
- "should": [
- { "term" : { "interests": "sport" } },
- { "term" : { "interests" : "music" } }
- ]
- }
- }
-}
-```
-
-使用 and 操作符,返回所有字段都匹配到的文档:
-
-```json
-GET /company/employee/_search
-{
- "query": {
- "match": {
- "interests": {
- "query": "sport music",
- "operator": "and"
- }
- }
- }
-}
-```
-
-等价的 bool 查询:
-
-```json
-GET /company/employee/_search
-{
- "query": {
- "bool": {
- "must": [
- { "term": { "interests": "sport" }},
- { "term": { "interests": "music" }}
- ]
- }
- }
-}
-```
-
-指定参数 minimum_should_match:
-
-```json
-GET /company/employee/_search
-{
- "query": {
- "match": {
- "interests": {
- "query": "sport music chess",
- "minimum_should_match" : 2
- }
- }
- }
-}
-```
-
-等价的 bool 查询:
-
-```json
-GET /company/employee/_search
-{
- "query": {
- "bool": {
- "should": [
- {"term": { "interests": "sport" }},
- {"term": { "interests": "music" }},
- {"term": { "interests": "chess" }}
- ],
- "minimum_should_match": 2
- }
- }
-}
-```
-
-### constant_score 查询
-
-constant_score 查询返回的文档`score`都是1。它经常用于只需要执行一个 filter 而没有其它查询的情况下。`score`会受到`boost`影响,出现 tyson 的文档`score`为1.2。
-
-```json
-GET _search
-{
- "query" : {
- "constant_score": {
- "filter": {
- "match" : { "first_name" : "tyson"}
- },
- "boost": 1.2
- }
- }
-}
-```
-
-
-
-
-### 多字段搜索
-
-参考自:[best_fields most_fields cross_fields从内在实现看区别——本质就是前两者是以field为中心,后者是词条为中心](https://www.cnblogs.com/bonelee/p/6827068.html)
-
-最佳字段 best_fields:搜索结果中应该返回某一个字段匹配到了最多的关键词的文档。
-
-多数字段 most_fields:返回匹配了更多的字段的文档,尽可能多地匹配文档。ES会为每个字段生成一个match查询,然后将它们包含在一个bool查询中。
-
-跨字段 cross_fields:每个查询的单词都出现在不同的字段中。*cross_fields类型采用了一种以词条为中心(Term-centric)的方法,这种方法和best_fields及most_fields采用的以字段为中心(Field-centric)的方法有很大的区别。它将所有的字段视为一个大的字段,然后在任一字段中搜索每个词条。
-
-#### 多字符串查询
-
-```json
-GET /_search
-{
- "query": {
- "bool": {
- "should": [
- { "match": { // 权重:1/3
- "title": {
- "query": "War and Peace",
- "boost": 2
- }}},
- { "match": { // 权重:1/3
- "author": {
- "query": "Leo Tolstoy",
- "boost": 2
- }}},
- { "bool": { //译者信息用布尔查询,降低评分权重 权重:1/3
- "should": [
- { "match": { "translator": "Constance Garnett" }},
- { "match": { "translator": "Louise Maude" }}
- ]
- }}
- ]
- }
- }
-}
-```
-
-#### multi_match 查询
-
-multi_match 查询可以在多个字段上执行相同的 match 查询。`multi_match` 多匹配查询的类型有三种:`best_fields` 、 `most_fields` 和 `cross_fields` (最佳字段、多数字段、跨字段)。
-
-```json
-GET company/employee/_search
-{
- "query" : {
- "multi_match": {
- "query": "sport shoes",
- "type": "best_fields",
- "fields": [ "interests", "first_name" ],
- "tie_breaker": 0.3,
- "minimum_should_match": 1
- }
- }
-}
-```
-
-模糊匹配:
-
-```json
-GET company/employee/_search
-{
- "query" : {
- "multi_match": {
- "query": "tyson dai",
- "type": "best_fields",
- "fields": "*_name",
- "tie_breaker": 0.3,
- "minimum_should_match": "30%"
- }
- }
-}
-```
-
-提升字段的权重,first_name 字段的 `boost` 值为 `2` :
-
-```json
-GET company/employee/_search
-{
- "query" : {
- "multi_match": {
- "query": "tyson dai",
- "type": "best_fields",
- "fields": ["*_name", "first_name^3"],
- "tie_breaker": 0.3,
- "minimum_should_match": "30%"
- }
- }
-}
-```
-
-#### 多字段映射
-
-对字段索引两次: 一次使用词干模式以及一次非词干模式。
-
-```json
-DELETE /my_index
-
-PUT /my_index
-{
- "settings": { "number_of_shards": 1 },
- "mappings": {
- "my_type": {
- "properties": {
- "title": {
- "type": "string",
- "analyzer": "english",//提取词干
- "fields": {
- "std": {
- "type": "string",
- "analyzer": "standard"
- }
- }
- }
- }
- }
- }
-}
-```
-
-`title` 字段使用 `english` 分析器来提取词干;`title.std` 字段使用 `standard` 标准分析器,所以没有词干提取。
-
-索引文档:
-
-```json
-PUT /my_index/my_type/1
-{ "title": "My rabbit jumps" }
-
-PUT /my_index/my_type/2
-{ "title": "Jumping jack rabbits" }
-```
-
-multi_match 查询:
-
-```json
-GET /my_index/_search
-{
- "query": {
- "multi_match": {
- "query": "jumping rabbits",
- "type": "most_fields",
- "fields": [ "title", "title.std" ]
- }
- }
-}
-```
-
-文档2匹配度更高,因为 title.std 不会提取词干,只有文档2是匹配的。
-
-设置`title` 字段的 `boost` 的值为 `10`,提升 title 字段的权重:
-
-```json
-GET /my_index/_search
-{
- "query": {
- "multi_match": {
- "query": "jumping rabbits",
- "type": "most_fields",
- "fields": [ "title^10", "title.std" ]
- }
- }
-}
-```
-
-#### copy_to 定制组合 field
-
-定制组合 field 与 cross_field 跨字段查询类似,根据两者的实际性能选择具体方案。
-
-创建映射:
-
-```json
-PUT my_index1
-{
- "mappings": {
- "my_type": {
- "properties": {
- "first_name": {
- "type": "keyword",
- "copy_to": "full_name"
- },
- "last_name": {
- "type": "keyword",
- "copy_to": "full_name"
- },
- "full_name": {
- "type": "text",
- "fielddata": true
- }
- }
- }
- }
-}
-```
-
-插入数据:
-
-```json
-PUT my_index1/my_type/1
-{
- "first_name": "John",
- "last_name": "Smith"
-}
-```
-
-校验查询:
-
-```json
-GET my_index1/_search
-{
- "query": {
- "match": {
- "full_name": {
- "query": "John Smith",
- "operator": "and"
- }
- }
- }
-}
-
-```
-
-`copy_to` 设置对 multi-field 无效。如果尝试这样配置映射,Elasticsearch 会抛异常。多字段只是以不同方式简单索引主字段;它们没有自己的数据源。
-
-```json
-PUT /my_index
-{
- "mappings": {
- "person": {
- "properties": {
- "first_name": {
- "type": "string",
- "copy_to": "full_name",
- "fields": {
- "raw": {
- "type": "string",
- "index": "not_analyzed"
- }
- }
- },
- "full_name": {
- "type": "string"
- }
- }
- }
- }
-}
-```
-
-first_name 是主字段,first_name.raw 是多字段。
-
-
-
-## springboot 集成 es
-
-[springboot 集成 es](https://blog.csdn.net/cwenao/article/details/54943505)
-
-[springboot整合elasticsearch5.x以及IK分词器做全文检索](https://blog.csdn.net/chenxihua1/article/details/94546282)
-
-
-
-## mall
-
-创建文档:
-
-```json
-POST /pms/product
-{
- "productSn": "HNTBJ2E080A",
- "brandId": 50,
- "brandName": "海澜之家",
- "productCategoryId": 8,
- "productCategoryName": "T恤",
- "pic": "http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/5ac98b64N70acd82f.jpg!cc_350x449.jpg",
- "name": "HLA海澜之家蓝灰花纹圆领针织布短袖T恤",
- "subTitle": "2018夏季新品短袖T恤男HNTBJ2E080A 蓝灰花纹80 175/92A/L80A 蓝灰花纹80 175/92A/L",
- "keywords": "",
- "price": 98,
- "sale": 0,
- "newStatus": 0,
- "recommandStatus": 0,
- "stock": 100,
- "promotionType": 0,
- "sort": 0,
- "attrValueList": [
- {
- "id": 183,
- "productAttributeId": 24,
- "value": null,
- "type": 1,
- "name": "商品编号"
- },
- {
- "id": 184,
- "productAttributeId": 25,
- "value": "夏季",
- "type": 1,
- "name": "适用季节"
- }
- ]
-}
-```
-
-获取文档映射:`GET /pms/_mapping/product`
-
-按字段查询:`GET /pms/product/_search?q=subTitle:2018`
-
-match查询:
-
-```json
-GET /pms/product/_search
-{
- "query": {
- "match": {
- "brandName": "小米"
- }
- }
-}
-```
-
-
-
-
-
diff --git "a/\344\270\255\351\227\264\344\273\266/\346\266\210\346\201\257\351\230\237\345\210\227\351\235\242\350\257\225\351\242\230.md" "b/\344\270\255\351\227\264\344\273\266/\346\266\210\346\201\257\351\230\237\345\210\227\351\235\242\350\257\225\351\242\230.md"
index adbb65e..633f860 100644
--- "a/\344\270\255\351\227\264\344\273\266/\346\266\210\346\201\257\351\230\237\345\210\227\351\235\242\350\257\225\351\242\230.md"
+++ "b/\344\270\255\351\227\264\344\273\266/\346\266\210\346\201\257\351\230\237\345\210\227\351\235\242\350\257\225\351\242\230.md"
@@ -13,17 +13,51 @@
- 系统可用性降低。引入消息队列之后,如果消息队列挂了,可能会影响到业务系统的可用性。
- 系统复杂性增加。加入了消息队列,要多考虑很多方面的问题,比如:一致性问题、如何保证消息不被重复消费、如何保证消息可靠性传输等。
-## RabbitMQ消息怎么路由?
+## 常见的消息队列对比
-消息路由必须有三部分:**交换器、路由、绑定**。生产者把消息发布到交换器上;绑定决定了消息如何从路由器路由到特定的队列;消息最终到达队列,并被消费者接收。
+| 对比方向 | 概要 |
+| -------- | ------------------------------------------------------------ |
+| 吞吐量 | 万级的 ActiveMQ 和 RabbitMQ 的吞吐量(ActiveMQ 的性能最差)要比 十万级甚至是百万级的 RocketMQ 和 Kafka 低一个数量级。 |
+| 可用性 | 都可以实现高可用。ActiveMQ 和 RabbitMQ 都是基于主从架构实现高可用性。RocketMQ 基于分布式架构。 kafka 也是分布式的,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用 |
+| 时效性 | RabbitMQ 基于 erlang 开发,所以并发能力很强,性能极其好,延时很低,达到微秒级。其他三个都是 ms 级。 |
+| 功能支持 | 除了 Kafka,其他三个功能都较为完备。 Kafka 功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用,是事实上的标准 |
+| 消息丢失 | ActiveMQ 和 RabbitMQ 丢失的可能性非常低, RocketMQ 和 Kafka 理论上不会丢失。 |
-1、消息发布到交换器时,消息将拥有一个路由键(routing key),在消息创建时设定。
+**总结:**
-2、通过队列路由键,可以把队列绑定到交换器上。
+- ActiveMQ 的社区算是比较成熟,但是较目前来说,ActiveMQ 的性能比较差,而且版本迭代很慢,不推荐使用。
+- RabbitMQ 在吞吐量方面虽然稍逊于 Kafka 和 RocketMQ ,但是由于它基于 erlang 开发,所以并发能力很强,性能极其好,延时很低,达到微秒级。但是也因为 RabbitMQ 基于 erlang 开发,所以国内很少有公司有实力做 erlang 源码级别的研究和定制。如果业务场景对并发量要求不是太高(十万级、百万级),那这四种消息队列中,RabbitMQ 一定是你的首选。如果是大数据领域的实时计算、日志采集等场景,用 Kafka 是业内标准的,绝对没问题,社区活跃度很高,绝对不会黄,何况几乎是全世界这个领域的事实性规范。
+- RocketMQ 阿里出品,Java 系开源项目,源代码我们可以直接阅读,然后可以定制自己公司的 MQ,并且 RocketMQ 有阿里巴巴的实际业务场景的实战考验。RocketMQ 社区活跃度相对较为一般,不过也还可以,文档相对来说简单一些,然后接口这块不是按照标准 JMS 规范走的有些系统要迁移需要修改大量代码。还有就是阿里出台的技术,你得做好这个技术万一被抛弃,社区黄掉的风险,那如果你们公司有技术实力我觉得用 RocketMQ 挺好的
+- Kafka 的特点其实很明显,就是仅仅提供较少的核心功能,但是提供超高的吞吐量,ms 级的延迟,极高的可用性以及可靠性,而且分布式可以任意扩展。同时 kafka 最好是支撑较少的 topic 数量即可,保证其超高吞吐量。kafka 唯一的一点劣势是有可能消息重复消费,那么对数据准确性会造成极其轻微的影响,在大数据领域中以及日志采集中,这点轻微影响可以忽略这个特性天然适合大数据实时计算以及日志收集。
-3、消息到达交换器后,RabbitMQ会将消息的路由键与队列的路由键进行匹配(针对不同的交换器有不同的路由规则)。如果能够匹配到队列,则消息会投递到相应队列中。
+## MQ常用协议
-## RabbitMQ如何保证消息的顺序性?
+- **AMQP协议** AMQP即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同开发语言等条件的限制。
+
+ > 优点:可靠、通用
+
+- **MQTT协议** MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是IBM开发的一个即时通讯协议,有可能成为物联网的重要组成部分。该协议支持所有平台,几乎可以把所有联网物品和外部连接起来,被用来当做传感器和致动器(比如通过Twitter让房屋联网)的通信协议。
+
+ > 优点:格式简洁、占用带宽小、移动端通信、PUSH、嵌入式系统
+
+- **STOMP协议** STOMP(Streaming Text Orientated Message Protocol)是流文本定向消息协议,是一种为MOM(Message Oriented Middleware,面向消息的中间件)设计的简单文本协议。STOMP提供一个可互操作的连接格式,允许客户端与任意STOMP消息代理(Broker)进行交互。
+
+ > 优点:命令模式(非topic/queue模式)
+
+- **XMPP协议** XMPP(可扩展消息处理现场协议,Extensible Messaging and Presence Protocol)是基于可扩展标记语言(XML)的协议,多用于即时消息(IM)以及在线现场探测。适用于服务器之间的准即时操作。核心是基于XML流传输,这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息,即使其操作系统和浏览器不同。
+
+ > 优点:通用公开、兼容性强、可扩展、安全性高,但XML编码格式占用带宽大
+
+- **其他基于TCP/IP自定义的协议**:有些特殊框架(如:redis、kafka、zeroMq等)根据自身需要未严格遵循MQ规范,而是基于TCP\IP自行封装了一套协议,通过网络socket接口进行传输,实现了MQ的功能。
+
+## MQ的通讯模式
+
+1. **点对点通讯**:点对点方式是最为传统和常见的通讯方式,它支持一对一、一对多、多对多、多对一等多种配置方式,支持树状、网状等多种拓扑结构。
+2. **多点广播**:MQ适用于不同类型的应用。其中重要的,也是正在发展中的是"多点广播"应用,即能够将消息发送到多个目标站点(Destination List)。可以使用一条MQ指令将单一消息发送到多个目标站点,并确保为每一站点可靠地提供信息。MQ不仅提供了多点广播的功能,而且还拥有智能消息分发功能,在将一条消息发送到同一系统上的多个用户时,MQ将消息的一个复制版本和该系统上接收者的名单发送到目标MQ系统。目标MQ系统在本地复制这些消息,并将它们发送到名单上的队列,从而尽可能减少网络的传输量。
+3. **发布/订阅(Publish/Subscribe)模式**:发布/订阅功能使消息的分发可以突破目的队列地理指向的限制,使消息按照特定的主题甚至内容进行分发,用户或应用程序可以根据主题或内容接收到所需要的消息。发布/订阅功能使得发送者和接收者之间的耦合关系变得更为松散,发送者不必关心接收者的目的地址,而接收者也不必关心消息的发送地址,而只是根据消息的主题进行消息的收发。在MQ家族产品中,MQ Event Broker是专门用于使用发布/订阅技术进行数据通讯的产品,它支持基于队列和直接基于TCP/IP两种方式的发布和订阅。
+4. **集群(Cluster)**:为了简化点对点通讯模式中的系统配置,MQ提供 Cluster 的解决方案。集群类似于一个 域(Domain) ,集群内部的队列管理器之间通讯时,不需要两两之间建立消息通道,而是采用 Cluster 通道与其它成员通讯,从而大大简化了系统配置。此外,集群中的队列管理器之间能够自动进行负载均衡,当某一队列管理器出现故障时,其它队列管理器可以接管它的工作,从而大大提高系统的高可靠性
+
+## 如何保证消息的顺序性?
单线程消费保证消息的顺序性;对消息进行编号,消费者根据编号处理消息。
diff --git "a/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md" "b/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md"
index a125d4b..cca4277 100644
--- "a/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md"
+++ "b/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md"
@@ -22,15 +22,15 @@ Git又是什么呢?简单的说,**Git 是一个管理你的代码历史记
- 创建一个新的仓库,用来存放项目。
- 
+ 
- 或者你在GitHub上看到别人有一个超级无敌有趣的项目,可以直接fork过来,可以理解成复制过来,变成你自己的。之后你想怎么改就怎么改!
- 
+ 
- 然后你可以通过Git命令行`git clone xxx`把项目clone到本地,在本地进行创作。
- 
+ 
- 最后,在本地创作完成,可以使用`git commit -m xxx`提交到本地库,然后使用`git push`把修改推送到GitHub仓库。之后就可以在GitHub上面看到你修改的内容啦~
@@ -42,23 +42,23 @@ Git又是什么呢?简单的说,**Git 是一个管理你的代码历史记
- 评价GitHub项目的两个重要的参数:star和fork。
-
+
比较优秀和热门的项目,star数目和fork数目都会比较多。我们可以根据这两个参数筛选出比较好的项目。使用`关键字 stars:>=xxx forks:>=xxx` 可以筛选出star和fork数目大于xxx的相关项目。
-
+
- 使用 `awesome 关键字`,可以筛选出比较高质量的学习、书籍、工具类或者插件类的集合。
-
+
- 在特定位置搜索关键词。有些关键词出现在项目的不同位置,比如项目名称、项目描述和README等。使用`关键词 in name/description/Readme`,可以搜索到相关的内容。比如使用`spring in name`,可以搜索到在项目名中包含spring的项目。
-
+
- 指定条件搜索关键词。如`tool language:java`搜索到的是包含关键字tool的Java项目,`tool followers:>1000`可以搜索到包含关键字tool,且follower数量大于1000的项目。
-
+
@@ -72,7 +72,7 @@ Git又是什么呢?简单的说,**Git 是一个管理你的代码历史记
- 文档神器。可以为自己的项目建立wiki,可以用markdown语法写wiki;
-
+
- 使用GitHub pages建立个人静态网站,搞一个有自己域名的独立博客,想想都觉得开心。使用GitHub pages的好处是搭建简单而且免费,支持静态脚本,并且可以绑定自己的域名。具体可以参考:[GitHub Pages 建立个人网站详细教程 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/58229299)
diff --git "a/\345\267\245\345\205\267/progit2.md" "b/\345\267\245\345\205\267/progit2.md"
index 68b8c2b..6dde18b 100644
--- "a/\345\267\245\345\205\267/progit2.md"
+++ "b/\345\267\245\345\205\267/progit2.md"
@@ -85,7 +85,7 @@ Git工作流程如下:
Git 的工作流程图如下:
-
+
> 图片来源:https://blog.csdn.net/ThinkWon/article/details/94346816
@@ -103,11 +103,11 @@ Git 的三种状态:已修改(modified)、已暂存(staged)和已提
基本的 Git 工作流程:在工作目录修改文件;暂存文件,将文件快照放到暂存区域;提交更新到本地库。暂存区保存了下次将要提交的文件列表信息,一般在 Git 仓库目录中。
-
+
> 图片来源:`https://img2018.cnblogs.com/blog/1252910/201907/1252910-20190726163829113-2056815874.png`
-
+
## 配置
@@ -156,7 +156,7 @@ git clone https://github.com/...
查看文件状态:`git status`
-
+
> 图片来源:`https://img2018.cnblogs.com/blog/1252910/201907/1252910-20190726163854195-886320537.png`
@@ -262,7 +262,7 @@ git commit -m "add readme.md"
单独执行`git commit`,不带上-m参数,会进入 vim 编辑器界面:
-
+
此时应该这么操作:
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md"
index e9f255a..2378225 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md"
@@ -91,7 +91,7 @@ where blog_id = (
三个表依次嵌套,发现最里层的子查询 `id`最大,最先执行。
-
+
## select_type
@@ -107,13 +107,13 @@ where blog_id = (
查询的表名,并不一定是真实存在的表,有别名显示别名,也可能为临时表。当from子句中有子查询时,table列是 ``的格式,表示当前查询依赖 id为N的查询,会先执行 id为N的查询。
-
+
## partitions
查询时匹配到的分区信息,对于非分区表值为`NULL`,当查询的是分区表时,`partitions`显示分区表命中的分区情况。
-
+
## type
@@ -123,25 +123,25 @@ where blog_id = (
当表仅有一行记录时(系统表),数据量很少,往往不需要进行磁盘IO,速度非常快。比如,Mysql系统表proxies_priv在Mysql服务启动时候已经加载在内存中,对这个表进行查询不需要进行磁盘 IO。
-
+
### const
单表操作的时候,查询使用了主键或者唯一索引。
-
+
### eq_ref
**多表关联**查询的时候,主键和唯一索引作为关联条件。如下图的sql,对于user表(外循环)的每一行,user_role表(内循环)只有一行满足join条件,只要查找到这行记录,就会跳出内循环,继续外循环的下一轮查询。
-
+
### ref
查找条件列使用了索引而且不为主键和唯一索引。虽然使用了索引,但该索引列的值并不唯一,这样即使使用索引查找到了第一条数据,仍然不能停止,要在目标值附近进行小范围扫描。但它的好处是不需要扫全表,因为索引是有序的,即便有重复值,也是在一个非常小的范围内做扫描。
-
+
### ref_or_null
@@ -151,13 +151,13 @@ where blog_id = (
使用了索引合并优化方法,查询使用了两个以上的索引。新建comment表,id为主键,value_id为非唯一索引,执行`explain select content from comment where value_id = 1181000 and id > 1000;`,执行结果显示查询同时使用了id和value_id索引,type列的值为index_merge。
-
+
### range
有范围的索引扫描,相对于index的全索引扫描,它有范围限制,因此要优于index。像between、and、'>'、'<'、in和or都是范围索引扫描。
-
+
### index
@@ -165,17 +165,17 @@ index包括select索引列,order by主键两种情况。
1. order by主键。这种情况会按照索引顺序全表扫描数据,拿到的数据是按照主键排好序的,不需要额外进行排序。
- 
+ 
2. select索引列。type为index,而且extra字段为using index,也称这种情况为索引覆盖。所需要取的数据都在索引列,无需回表查询。
- 
+ 
### all
全表扫描,查询没有用到索引,性能最差。
-
+
## possible_keys
@@ -249,13 +249,13 @@ CREATE TABLE `t_orderdetail` (
查询的列未被索引覆盖,where筛选条件非索引的前导列。对存储引擎返回的结果进行过滤(Post-filter,后过滤),一般发生在MySQL服务器,而不是存储引擎层。
-
+
### using index
查询的列被索引覆盖,并且where筛选条件符合最左前缀原则,通过**索引查找**就能直接找到符合条件的数据,不需要回表查询数据。
-
+
### Using where&Using index
@@ -265,17 +265,17 @@ CREATE TABLE `t_orderdetail` (
- where筛选条件不符合最左前缀原则
- 
+ 
- where筛选条件是索引列前导列的一个范围
- 
+ 
### null
查询的列未被索引覆盖,并且where筛选条件是索引的前导列,也就是用到了索引,但是部分字段未被索引覆盖,必须回表查询这些字段,Extra中为NULL。
-
+
### using index condition
@@ -283,11 +283,11 @@ CREATE TABLE `t_orderdetail` (
不使用ICP的情况(`set optimizer_switch='index_condition_pushdown=off'`),如下图,在步骤4中,没有使用where条件过滤索引:
-
+
使用ICP的情况(`set optimizer_switch='index_condition_pushdown=on'`):
-
+
下面的例子使用了ICP:
@@ -296,11 +296,11 @@ explain select user_id, order_id, order_status
from t_order where user_id > 1 and user_id < 5\G;
```
-
+
关掉ICP之后(`set optimizer_switch='index_condition_pushdown=off'`),可以看到extra列为using where,不会使用索引下推。
-
+
### using temporary
@@ -314,7 +314,7 @@ from t_order where user_id > 1 and user_id < 5\G;
- select 查询字段不全是索引字段
- select 查询字段都是索引字段,但是 order by 字段和索引字段的顺序不一致
-
+
### using join buffer
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md"
index 7caa4e5..4ff8a64 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md"
@@ -138,7 +138,7 @@ B+ 树是基于B 树和叶子节点顺序访问指针进行实现,它具有B
在 B+ 树中,节点中的 key 从左到右递增排列,如果某个指针的左右相邻 key 分别是 keyi 和 keyi+1,则该指针指向节点的所有 key 大于等于 keyi 且小于等于 keyi+1。
-
+
进行查找操作时,首先在根节点进行二分查找,找到key所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出 key 所对应的数据项。
@@ -170,15 +170,15 @@ Index_comment:
如下图,col1 是主键,col2和col3是普通字段。
-
+
下图是主键索引对应的 B+树结构,每个节点对应磁盘的一页。
-
+
对col3 建立一个单列索引,对应的B+树结构:
-
+
### 索引分类
1. 主键索引:名为primary的唯一非空索引,不允许有空值。
@@ -208,7 +208,7 @@ Index_comment:
对(a, b) 建立索引,a 在索引树中是全局有序的,而 b 是全局无序,局部有序(当a相等时,会对b进行比较排序)。直接执行`b = 2`这种查询条件没有办法利用索引。
-
+
从局部来看,当a的值确定的时候,b是有序的。例如a = 1时,b值为1,2是有序的状态。当a=2时候,b的值为1,4也是有序状态。 因此,你执行`a = 1 and b = 2`是a,b字段能用到索引的。而你执行`a > 1 and b = 2`时,a字段能用到索引,b字段用不到索引。因为a的值此时是一个范围,不是固定的,在这个范围内b值不是有序的,因此b字段用不上索引。
@@ -218,7 +218,7 @@ InnoDB使用表的主键构造主键索引树,同时叶子节点中存放的
聚集索引的叶子节点就是整张表的行记录。InnoDB 主键使用的是聚簇索引。聚集索引要比非聚集索引查询效率高很多。
-
+
对于InnoDB来说,聚集索引一般是表中的主键索引,如果表中没有显示指定主键,则会选择表中的第一个不允许为NULL的唯一索引。如果没有主键也没有合适的唯一索引,那么innodb内部会生成一个隐藏的主键作为聚集索引,这个隐藏的主键长度为6个字节,它的值会随着数据的插入自增。
@@ -244,7 +244,7 @@ explain select user_id from user_like where blog_id = 1;
Extra中为`Using where; Using index`, 查询的列被索引覆盖,where筛选条件不符合最左前缀原则,无法通过索引查找找到符合条件的数据,但可以通过**索引扫描**找到符合条件的数据,也不需要回表查询数据。
-
+
### 索引失效
@@ -322,7 +322,7 @@ MEMORY引擎默认使用哈希索引,将键的哈希值和指向数据行的
myisam引擎主键索引和其他索引区别不大,叶子节点都包含索引值和行指针。
innodb引擎二级索引叶子存储的是索引值和主键值(不是行指针),这样可以减少行移动和数据页分裂时二级索引的维护工作。
- 
+ 
@@ -351,7 +351,7 @@ mvcc实现依赖于版本链,版本链是通过表的三个隐藏字段实现
3. 修改当前行的值,生成一个新版本,更新事务id,使回滚指针指向旧版本的记录,这样就形成一条版本链;
4. 记录redo log;
-
+
### read view
@@ -388,14 +388,14 @@ repeatable read:在一个事务范围内,第一次select时更新这个read_
事务a和事务b同时开启事务,事务a插入数据然后提交,事务b执行全表的update,然后执行查询,查到了事务A中添加的数据。
-
+
MySQL如何实现避免幻读:
- 在快照读情况下,MySQL通过mvcc来避免幻读。
- 在当前读情况下,MySQL通过next-key来避免幻读(加行锁和间隙锁来实现的)。
-
+
next-key包括两部分:行锁和间隙锁。行锁是加在索引上的锁,间隙锁是加在索引之间的。
@@ -531,7 +531,7 @@ MySQL主要分为 Server 层和存储引擎层:
- **Server 层**:主要包括连接器、查询缓存、分析器、优化器、执行器等,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图,函数等,还有一个通用的日志模块 binglog 日志模块。
- **存储引擎**: 主要负责数据的存储和读取。server 层通过api与存储引擎进行通信。
-
+
### Server 层基本组件
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 2a8d716..f872daa 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -179,7 +179,7 @@ B+ 树是基于B 树和叶子节点顺序访问指针进行实现,它具有B
在 B+ 树中,节点中的 `key` 从左到右递增排列,如果某个指针的左右相邻 `key` 分别是 keyi 和 keyi+1,则该指针指向节点的所有 `key` 大于等于 keyi 且小于等于 keyi+1。
-
+
进行查找操作时,首先在根节点进行二分查找,找到`key`所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出`key`所对应的数据项。
@@ -228,7 +228,7 @@ Index_comment:
1、**主键索引**:名为primary的唯一非空索引,不允许有空值。
-2、**唯一索引**:索引列中的值必须是唯一的,但是允许为空值。唯一索引和主键索引的区别是:唯一约束的列可以为`null`且可以存在多个`null`值。唯一索引的用途:唯一标识数据库表中的每条记录,主要是用来防止数据重复插入。创建唯一索引的SQL语句如下:
+2、**唯一索引**:索引列中的值必须是唯一的,但是允许为空值。唯一索引和主键索引的区别是:唯一索引字段可以为null且可以存在多个null值,而主键索引字段不可以为null。唯一索引的用途:唯一标识数据库表中的每条记录,主要是用来防止数据重复插入。创建唯一索引的SQL语句如下:
```mysql
ALTER TABLE table_name
@@ -239,6 +239,8 @@ ADD CONSTRAINT constraint_name UNIQUE KEY(column_1,column_2,...);
4、**全文索引**:只能在`CHAR`、`VARCHAR`和`TEXT`类型字段上使用全文索引。
+5、**普通索引**:普通索引是最基本的索引,它没有任何限制,值可以为空。
+
### 什么是最左匹配原则?
如果 SQL 语句中用到了组合索引中的最左边的索引,那么这条 SQL 语句就可以利用这个组合索引去进行匹配。当遇到范围查询(`>`、`<`、`between`、`like`)就会停止匹配,后面的字段不会用到索引。
@@ -249,7 +251,7 @@ ADD CONSTRAINT constraint_name UNIQUE KEY(column_1,column_2,...);
如下图,对(a, b) 建立索引,a 在索引树中是全局有序的,而 b 是全局无序,局部有序(当a相等时,会根据b进行排序)。直接执行`b = 2`这种查询条件无法使用索引。
-
+
当a的值确定的时候,b是有序的。例如`a = 1`时,b值为1,2是有序的状态。当`a = 2`时候,b的值为1,4也是有序状态。 当执行`a = 1 and b = 2`时a和b字段能用到索引。而执行`a > 1 and b = 2`时,a字段能用到索引,b字段用不到索引。因为a的值此时是一个范围,不是固定的,在这个范围内b值不是有序的,因此b字段无法使用索引。
@@ -283,7 +285,7 @@ explain select user_id from user_like where blog_id = 1;
`explain`结果的`Extra`列为`Using where; Using index`, 查询的列被索引覆盖,where筛选条件不符合最左前缀原则,无法通过索引查找找到符合条件的数据,但可以通过**索引扫描**找到符合条件的数据,也不需要回表查询数据。
-
+
### 索引的设计原则?
@@ -393,7 +395,7 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
每条表记录大概是这样的:
-
+
使用事务更新行记录的时候,就会生成版本链,执行过程如下:
@@ -405,15 +407,15 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
1、初始数据如下,其中`DB_ROW_ID`和`DB_ROLL_PTR`为空。
-
+
2、事务A对该行数据做了修改,将`age`修改为12,效果如下:
-
+
3、之后事务B也对该行记录做了修改,将`age`修改为8,效果如下:
-
+
4、此时undo log有两行记录,并且通过回滚指针连在一起。
@@ -433,7 +435,7 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
**前提**:`DATA_TRX_ID` 表示每个数据行的最新的事务ID;`up_limit_id`表示当前快照中的最先开始的事务;`low_limit_id`表示当前快照中的最慢开始的事务,即最后一个事务。
-
+
- 如果`DATA_TRX_ID` < `up_limit_id`:说明在创建`read view`时,修改该数据行的事务已提交,该版本的记录可被当前事务读取到。
- 如果`DATA_TRX_ID` >= `low_limit_id`:说明当前版本的记录的事务是在创建`read view`之后生成的,该版本的数据行不可以被当前事务访问。此时需要通过版本链找到上一个版本,然后重新判断该版本的记录对当前事务的可见性。
@@ -458,7 +460,7 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
1、首先,user表只有两条记录,具体如下:
-
+
2、事务a和事务b同时开启事务`start transaction`;
@@ -476,7 +478,7 @@ update user set user_name = 'a';
5、事务b然后执行查询,查到了事务a中插入的数据。(下图左边是事务b,右边是事务a。事务开始之前只有两条记录,事务a插入一条数据之后,事务b查询出来是三条数据)
-
+
以上就是当前读出现的幻读现象。
@@ -565,7 +567,7 @@ MySQL主要分为 Server 层和存储引擎层:
垂直划分数据库是根据业务进行划分,例如购物场景,可以将库中涉及商品、订单、用户的表分别划分出成一个库,通过降低单库的大小来提高性能。同样的,分表的情况就是将一个大表根据业务功能拆分成一个个子表,例如商品基本信息和商品描述,商品基本信息一般会展示在商品列表,商品描述在商品详情页,可以将商品基本信息和商品描述拆分成两张表。
-
+
**优点**:行记录变小,数据页可以存放更多记录,在查询时减少I/O次数。
@@ -579,7 +581,7 @@ MySQL主要分为 Server 层和存储引擎层:
水平划分是根据一定规则,例如时间或id序列值等进行数据的拆分。比如根据年份来拆分不同的数据库。每个数据库结构一致,但是数据得以拆分,从而提升性能。
-
+
**优点**:单库(表)的数据量得以减少,提高性能;切分出的表结构相同,程序改动较少。
@@ -784,3 +786,101 @@ int(10)中的10表示的是显示数据的长度,而char(10)表示的是存储
- Sorting for order,正在为`ORDER BY`做排序
8. **info**:正在执行的`SQL`语句
+## MySQL查询 limit 1000,10 和limit 10 速度一样快吗?
+
+两种查询方式。对应 `limit offset, size` 和 `limit size` 两种方式。
+
+而其实 `limit size` ,相当于 `limit 0, size`。也就是从0开始取size条数据。
+
+也就是说,两种方式的**区别在于offset是否为0。**
+
+先来看下limit sql的内部执行逻辑。
+
+MySQL内部分为**server层**和**存储引擎层**。一般情况下存储引擎都用innodb。
+
+server层有很多模块,其中需要关注的是**执行器**是用于跟存储引擎打交道的组件。
+
+执行器可以通过调用存储引擎提供的接口,将一行行数据取出,当这些数据完全符合要求(比如满足其他where条件),则会放到**结果集**中,最后返回给调用mysql的**客户端**。
+
+以主键索引的limit执行过程为例:
+
+执行`select * from page order by id limit 0, 10;`,select后面带的是**星号**,也就是要求获得行数据的**所有字段信息。**
+
+server层会调用innodb的接口,在innodb里的主键索引中获取到第0到10条**完整行数据**,依次返回给server层,并放到server层的结果集中,返回给客户端。
+
+把offset搞大点,比如执行的是:`select * from page order by id limit 500000, 10;`
+
+server层会调用innodb的接口,由于这次的offset=500000,会在innodb里的主键索引中获取到第0到(500000 + 10)条**完整行数据**,**返回给server层之后根据offset的值挨个抛弃,最后只留下最后面的size条**,也就是10条数据,放到server层的结果集中,返回给客户端。
+
+可以看出,当offset非0时,server层会从引擎层获取到**很多无用的数据**,而获取的这些无用数据都是要耗时的。
+
+因此,mysql查询中 limit 1000,10 会比 limit 10 更慢。原因是 limit 1000,10 会取出1000+10条数据,并抛弃前1000条,这部分耗时更大。
+
+## 深分页怎么优化?
+
+还是以上面的SQL为空:`select * from page order by id limit 500000, 10;`
+
+**方法一**:
+
+从上面的分析可以看出,当offset非常大时,server层会从引擎层获取到很多无用的数据,而当select后面是*号时,就需要拷贝完整的行信息,**拷贝完整数据**相比**只拷贝行数据里的其中一两个列字段**更耗费时间。
+
+因为前面的offset条数据最后都是不要的,没有必要拷贝完整字段,所以可以将sql语句修改成:
+
+```mysql
+select * from page where id >=(select id from page order by id limit 500000, 1) order by id limit 10;
+```
+
+先执行子查询 `select id from page order by id limit 500000, 1`, 这个操作,其实也是将在innodb中的主键索引中获取到`500000+1`条数据,然后server层会抛弃前500000条,只保留最后一条数据的id。
+
+但不同的地方在于,在返回server层的过程中,只会拷贝数据行内的id这一列,而不会拷贝数据行的所有列,当数据量较大时,这部分的耗时还是比较明显的。
+
+在拿到了上面的id之后,假设这个id正好等于500000,那sql就变成了
+
+```mysql
+select * from page where id >=500000 order by id limit 10;
+```
+
+这样innodb再走一次**主键索引**,通过B+树快速定位到id=500000的行数据,时间复杂度是lg(n),然后向后取10条数据。
+
+**方法二:**
+
+将所有的数据**根据id主键进行排序**,然后分批次取,将当前批次的最大id作为下次筛选的条件进行查询。
+
+```mysql
+select * from page where id > start_id order by id limit 10;
+```
+
+通过主键索引,每次定位到start_id的位置,然后往后遍历10个数据,这样不管数据多大,查询性能都较为稳定。
+
+## B+树一个节点有多大?一千万条数据,B+树多高?
+
+InnoDB存储引擎有自己的最小储存单元——页(Page),一个页的大小是16K。
+
+B+树一个节点的大小设为一页或页的倍数最为合适。因为如果一个节点的大小 < 1页,那么读取这个节点的时候其实读取的还是一页,这样就造成了资源的浪费。
+
+在 MySQL 中 B+ 树的一个节点大小为“1页”,也就是16k。之所以设置为一页,是因为对于大部分业务,一页就足够了:
+
+首先InnoDB的B+树中,**非叶子节点存的是key + 指针**;**叶子节点存的是数据行**。
+
+对于叶子节点,如果一行数据大小为1k,那么一页就能存16条数据。
+
+对于非叶子节点,如果key使用的是bigint,则为8字节,指针在mysql中为6字节,一共是14字节,则16k能存放 16 * 1024 / 14 = 1170 个索引指针。
+
+于是可以算出,对于一颗高度为2的B+树,根节点存储索引指针节点,那么它有1170个叶子节点存储数据,每个叶子节点可以存储16条数据,一共 1170 x 16 = 18720 条数据。而对于高度为3的B+树,就可以存放 1170 x 1170 x 16 = 21902400 条数据(**两千多万条数据**),也就是对于两千多万条的数据,我们只需要**高度为3**的B+树就可以完成,通过主键查询只需要3次IO操作就能查到对应数据。
+
+所以在 InnoDB 中B+树高度一般为3层时,就能满足千万级的数据存储,所以一个节点为1页,也就是16k是比较合理的。
+
+参考:https://www.cnblogs.com/leefreeman/p/8315844.html
+
+## MySQL单表多大进行分库分表?
+
+目前主流的有两种说法:
+
+1. MySQL 单表数据量大于 2000 万行,性能会明显下降,考虑进行分库分表。
+2. 阿里巴巴《Java 开发手册》提出单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。
+
+事实上,这个数值和实际记录的条数无关,而与 MySQL 的配置以及机器的硬件有关。因为,MySQL 为了提高性能,会将表的索引装载到内存中。InnoDB buffer size 足够的情况下,其能完成全加载进内存,查询不会有问题。但是,当单表数据库到达某个量级的上限时,导致内存无法存储其索引,使得之后的 SQL 查询会产生磁盘 IO,从而导致性能下降。当然,这个还有具体的表结构的设计有关,最终导致的问题都是内存限制。
+
+因此,对于分库分表,需要结合实际需求,不宜过度设计,在项目一开始不采用分库与分表设计,而是随着业务的增长,在无法继续优化的情况下,再考虑分库与分表提高系统的性能。对此,阿里巴巴《Java 开发手册》补充到:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。
+
+至于MySQL单表多大进行分库分表,应当根据机器资源进行评估。
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
index 9ad4fb0..9a99fb1 100644
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
+++ "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
@@ -13,7 +13,7 @@
常见的排序算法主要有:冒泡排序、插入排序、选择排序、快速排序、归并排序、堆排序、基数排序。各种排序算法的时间空间复杂度、稳定性见下图。
-
+
## 冒泡排序
diff --git "a/\346\241\206\346\236\266/Mybatis\351\235\242\350\257\225\351\242\230.md" "b/\346\241\206\346\236\266/Mybatis\351\235\242\350\257\225\351\242\230.md"
new file mode 100644
index 0000000..21e911b
--- /dev/null
+++ "b/\346\241\206\346\236\266/Mybatis\351\235\242\350\257\225\351\242\230.md"
@@ -0,0 +1,117 @@
+## Mybatis框架简介
+
+- MyBatis框架是一个开源的数据持久层框架。
+- 它的内部封装了通过JDBC访问数据库的操作,支持普通的SQL查询、存储过程和高级映射,几乎消除了所有的JDBC代码和参数的手工设置以及结果集的检索。
+- MyBatis作为持久层框架,其主要思想是将程序中的大量SQL语句剥离出来,配置在配置文件当中,实现SQL的灵活配置。
+- 这样做的好处是将SQL与程序代码分离,可以在不修改代码的情况下,直接在配置文件当中修改SQL。
+
+## MyBatis框架的优缺点及其适用的场合
+
+**优点**
+
+1. 与JDBC相比,减少了50%以上的代码量。
+2. MyBatis是易学的持久层框架,小巧并且简单易学。
+3. MyBatis相当灵活,不会对应用程序或者数据库的现有设计强加任何影响,SQL写在XML文件里,从程序代码中彻底分离,降低耦合度,便于统一的管理和优化,并可重用。
+4. 提供XML标签,支持编写动态的SQL,满足不同的业务需求。
+5. 提供映射标签,支持对象与数据库的ORM字段关系映射。
+
+**缺点**
+
+1. SQL语句的编写工作量较大,对开发人员编写SQL的能力有一定的要求。
+2. SQL语句依赖于数据库,导致数据库不具有好的移植性,不可以随便更换数据库。
+
+**适用场景**
+
+MyBatis专注于SQL自身,是一个足够灵活的DAO层解决方案。对性能的要求很高,或者需求变化较多的项目,例如Web项目,那么MyBatis是不二的选择。
+
+## Mybatis的工作原理
+
+- 读取MyBatis配置文件:mybatis-config.xml为MyBatis的全局配置文件,配置了MyBatis的运行环境等信息,例如数据库连接信息。
+- 加载映射文件。映射文件即SQL映射文件,该文件中配置了操作数据库的SQL语句,需要在MyBatis配置文件mybatis-config.xml中加载。mybatis-config.xml文件可以加载多个映射文件,每个文件对应数据库中的一张表。
+- 构造会话工厂:通过MyBatis的环境等配置信息构建会话工厂SqlSessionFactory。
+- 创建会话对象:由会话工厂创建SqlSession对象,该对象中包含了执行SQL语句的所有方法。
+- Executor执行器:MyBatis底层定义了一个Executor 接口来操作数据库,它将根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时负责查询缓存的维护。
+- MappedStatement 对象:在Executor接口的执行方法中有一个MappedStatement类型的参数,该参数是对映射信息的封装,用于存储要映射的SQL语句的id、参数等信息。
+- 输入参数映射:输入参数类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输入参数映射过程类似于 JDBC对preparedStatement对象设置参数的过程。
+- 输出结果映射:输出结果类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输出结果映射过程类似于 JDBC对结果集的解析过程。
+
+## Mybatis都有哪些Executor执行器?它们之间的区别是什么?
+
+Mybatis有三种基本的Executor执行器,`SimpleExecutor`、`ReuseExecutor`、`BatchExecutor`。
+
+`SimpleExecutor`:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
+
+`ReuseExecutor`:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。
+
+`BatchExecutor`:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。
+
+作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。
+
+## MyBatis中接口绑定有几种实现方式?
+
+1. 通过注解绑定,在接口的方法上面加上 @Select@Update等注解里面包含Sql语句来绑定(SQL语句比较简单的时候,推荐注解绑定)
+2. 通过xml里面写SQL来绑定, 指定xml映射文件里面的namespace必须为接口的全路径名(SQL语句比较复杂的时候,推荐xml绑定)
+
+## 简述Mybatis的插件运行原理
+
+Mybatis仅可以编写针对 `ParameterHandler`、`ResultSetHandler`、`StatementHandler`、`Executor`这4种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是`InvocationHandler`的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。
+
+编写插件:实现Mybatis的Interceptor接口并复写`intercept()`方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。
+
+## #{}和${}的区别是什么?
+
+#{ } 被解析成预编译语句,预编译之后可以直接执行,不需要重新编译sql。
+
+```mysql
+//sqlMap 中如下的 sql 语句
+select * from user where name = #{name};
+//解析成为预编译语句;编译好SQL语句再取值
+select * from user where name = ?;
+```
+
+${ } 仅仅为一个字符串替换,每次执行sql之前需要进行编译,存在 sql 注入问题。
+
+```mysql
+select * from user where name = '${name}'
+//传递的参数为 "ruhua" 时,解析为如下,然后发送数据库服务器进行编译。取值以后再去编译SQL语句。
+select * from user where name = "ruhua";
+```
+
+## Mybatis的预编译
+
+数据库接受到sql语句之后,需要词法和语义解析,优化sql语句,制定执行计划。这需要花费一些时间。如果一条sql语句需要反复执行,每次都进行语法检查和优化,会浪费很多时间。预编译语句就是将sql语句中的`值用占位符替代`,即将`sql语句模板化`。一次编译、多次运行,省去了解析优化等过程。
+
+mybatis是通过`PreparedStatement`和占位符来实现预编译的。
+
+mybatis底层使用`PreparedStatement`,默认情况下,将对所有的 sql 进行预编译,将#{}替换为?,然后将带有占位符?的sql模板发送至mysql服务器,由服务器对此无参数的sql进行编译后,将编译结果缓存,然后直接执行带有真实参数的sql。
+
+预编译的作用:
+
+1. 预编译阶段可以优化 sql 的执行。预编译之后的 sql 多数情况下可以直接执行,数据库服务器不需要再次编译,可以提升性能。
+2. 预编译语句对象可以重复利用。把一个 sql 预编译后产生的 `PreparedStatement` 对象缓存下来,下次对于同一个sql,可以直接使用这个缓存的 PreparedState 对象。
+3. 防止SQL注入。使用预编译,而其后注入的参数将`不会再进行SQL编译`。也就是说其后注入进来的参数系统将不会认为它会是一条SQL语句,而默认其是一个参数。
+
+ ## 一级缓存和二级缓存
+
+缓存:合理使用缓存是优化中最常见的方法之一,将从数据库中查询出来的数据放入缓存中,下次使用时不必从数据库查询,而是直接从缓存中读取,避免频繁操作数据库,减轻数据库的压力,同时提高系统性能。
+
+**一级缓存是SqlSession级别的缓存**:Mybatis对缓存提供支持,默认情况下只开启一级缓存,一级缓存作用范围为同一个SqlSession。在SQL和参数相同的情况下,我们使用同一个SqlSession对象调用同一个Mapper方法,往往只会执行一次SQL。因为在使用SqlSession第一次查询后,Mybatis会将结果放到缓存中,以后再次查询时,如果没有声明需要刷新,并且缓存没超时的情况下,SqlSession只会取出当前缓存的数据,不会再次发送SQL到数据库。若使用不同的SqlSession,因为不同的SqlSession是相互隔离的,不会使用一级缓存。
+
+**二级缓存是mapper级别的缓存**:可以使缓存在各个SqlSession之间共享。二级缓存默认不开启,需要在mybatis-config.xml开启二级缓存:
+
+```xml
+
+
+
+
+```
+
+并在相应的Mapper.xml文件添加cache标签,表示对哪个mapper 开启缓存:
+
+```xml
+
+```
+
+二级缓存要求返回的POJO必须是可序列化的,即要求实现Serializable接口。
+
+当开启二级缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库。
diff --git "a/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" "b/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
index e9ddb23..dd8b7fb 100644
--- "a/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
+++ "b/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
@@ -28,7 +28,7 @@ SpringBoot实现自动配置原理图解:
> 公众号【程序员大彬】,回复【自动配置】下载高清图片
-
+
在 application.properties 中设置属性 debug=true,可以在控制台查看已启用和未启用的自动配置。
diff --git "a/\346\241\206\346\236\266/SpringCloud\345\276\256\346\234\215\345\212\241\345\256\236\346\210\230.md" "b/\346\241\206\346\236\266/SpringCloud\345\276\256\346\234\215\345\212\241\345\256\236\346\210\230.md"
index ed70b24..6d305f7 100644
--- "a/\346\241\206\346\236\266/SpringCloud\345\276\256\346\234\215\345\212\241\345\256\236\346\210\230.md"
+++ "b/\346\241\206\346\236\266/SpringCloud\345\276\256\346\234\215\345\212\241\345\256\236\346\210\230.md"
@@ -98,10 +98,20 @@ springcloud是一个基于Spring Boot实现的微服务架构开发工具。spri
- Spring Cloud Cluster: 针对ZooKeeper、Redis、Hazelcast、Consul 的选举算法和通用状态模式的实现。
- Spring Cloud Consul: 服务发现与配置管理工具。
- Spring Cloud ZooKeeper: 基于ZooKeeper 的服务发现与配置管理组件。
+- Spring Cloud Security:Spring Security组件封装,提供用户验证和权限验证,一般与Spring Security OAuth2 组一起使用,通过搭建授权服务,验证Token或者JWT这种形式对整个微服务系统进行安全验证
+- Spring Cloud Sleuth:分布式链路追踪组件,他分封装了Dapper、Zipkin、Kibana 的组件
+- Spring Cloud Stream:Spring Cloud框架的数据流操作包,可以封装RabbitMq,ActiveMq,Kafka,Redis等消息组件,利用Spring Cloud Stream可以实现消息的接收和发送
spring-boot-starter-actuator:该模块能够自动为Spring Boot 构建的应用提供一系列用于监控的端点。
+## 说说对SpringBoot 和SpringCloud的理解
+- SpringBoot专注于快速方便的开发单个个体微服务。
+- SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供,配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等集成服务
+- SpringBoot可以离开SpringCloud独立使用开发项目,但是SpringCloud离不开SpringBoot,属于依赖的关系。
+- SpringBoot专注于快速、方便的开发单个微服务个体,SpringCloud关注全局的服务治理框架。
+
+Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系。
## Spring Cloud Eureka
diff --git "a/\346\241\206\346\236\266/SpringMVC\351\235\242\350\257\225\351\242\230.md" "b/\346\241\206\346\236\266/SpringMVC\351\235\242\350\257\225\351\242\230.md"
new file mode 100644
index 0000000..82ac987
--- /dev/null
+++ "b/\346\241\206\346\236\266/SpringMVC\351\235\242\350\257\225\351\242\230.md"
@@ -0,0 +1,88 @@
+## 说说你对 SpringMVC 的理解
+
+SpringMVC是一种基于 Java 的实现MVC设计模型的请求驱动类型的轻量级Web框架,属于Spring框架的一个模块。
+
+它通过一套注解,让一个简单的Java类成为处理请求的控制器,而无须实现任何接口。同时它还支持RESTful编程风格的请求。
+
+## 什么是MVC模式?
+
+MVC的全名是`Model View Controller`,是模型(model)-视图(view)-控制器(controller)的缩写,是一种软件设计典范。它是用一种业务逻辑、数据与界面显示分离的方法来组织代码,将众多的业务逻辑聚集到一个部件里面,在需要改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑,达到减少编码的时间。
+
+View,视图是指用户看到并与之交互的界面。比如由html元素组成的网页界面,或者软件的客户端界面。MVC的好处之一在于它能为应用程序处理很多不同的视图。在视图中其实没有真正的处理发生,它只是作为一种输出数据并允许用户操纵的方式。
+
+model,模型是指模型表示业务规则。在MVC的三个部件中,模型拥有最多的处理任务。被模型返回的数据是中立的,模型与数据格式无关,这样一个模型能为多个视图提供数据,由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性。
+
+controller,控制器是指控制器接受用户的输入并调用模型和视图去完成用户的需求,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。
+
+## SpringMVC 有哪些优点?
+
+1. 与 Spring 集成使用非常方便,生态好。
+2. 配置简单,快速上手。
+3. 支持 RESTful 风格。
+4. 支持各种视图技术,支持各种请求资源映射策略。
+
+## Spring MVC和Struts的区别
+
+1. Spring MVC是基于方法开发,Struts2是基于类开发的。
+ - Spring MVC会将用户请求的URL路径信息与Controller的某个方法进行映射,所有请求参数会注入到对应方法的形参上,生成Handler对象,对象中只有一个方法;
+ - Struts每处理一次请求都会实例一个Action,Action类的所有方法使用的请求参数都是Action类中的成员变量,随着方法增多,整个Action也会变得混乱。
+2. Spring MVC支持单例开发模式,Struts只能使用多例
+
+ - Struts由于只能通过类的成员变量接收参数,故只能使用多例。
+3. Struts2 的核心是基于一个Filter即StrutsPreparedAndExcuteFilter,Spring MVC的核心是基于一个Servlet即DispatcherServlet(前端控制器)。
+4. Struts处理速度稍微比Spring MVC慢,Struts使用了Struts标签,加载数据较慢。
+
+## Spring MVC的工作原理
+
+Spring MVC的工作原理如下:
+
+1. DispatcherServlet 接收用户的请求
+2. 找到用于处理request的 handler 和 Interceptors,构造成 HandlerExecutionChain 执行链
+3. 找到 handler 相对应的 HandlerAdapter
+4. 执行所有注册拦截器的preHandler方法
+5. 调用 HandlerAdapter 的 handle() 方法处理请求,返回 ModelAndView
+6. 倒序执行所有注册拦截器的postHandler方法
+7. 请求视图解析和视图渲染
+
+
+
+## Spring MVC的主要组件?
+
+- 前端控制器(DispatcherServlet):接收用户请求,给用户返回结果。
+- 处理器映射器(HandlerMapping):根据请求的url路径,通过注解或者xml配置,寻找匹配的Handler。
+- 处理器适配器(HandlerAdapter):Handler 的适配器,调用 handler 的方法处理请求。
+- 处理器(Handler):执行相关的请求处理逻辑,并返回相应的数据和视图信息,将其封装到ModelAndView对象中。
+- 视图解析器(ViewResolver):将逻辑视图名解析成真正的视图View。
+- 视图(View):接口类,实现类可支持不同的View类型(JSP、FreeMarker、Excel等)。
+
+## Spring MVC的常用注解由有哪些?
+- @Controller:用于标识此类的实例是一个控制器。
+- @RequestMapping:映射Web请求(访问路径和参数)。
+- @ResponseBody:注解返回数据而不是返回页面
+- @RequestBody:注解实现接收 http 请求的 json 数据,将 json 数据转换为 java 对象。
+- @PathVariable:获得URL中路径变量中的值
+- @RestController:@Controller+@ResponseBody
+
+## Spring MVC的异常处理
+
+可以将异常抛给Spring框架,由Spring框架来处理;我们只需要配置简单的异常处理器,在异常处理器中添视图页面即可。
+
+- 使用系统定义好的异常处理器 SimpleMappingExceptionResolver
+- 使用自定义异常处理器
+- 使用异常处理注解
+
+## SpringMVC 用什么对象从后台向前台传递数据的?
+
+1. 将数据绑定到 request;
+2. 返回 ModelAndView;
+3. 通过ModelMap对象,可以在这个对象里面调用put方法,把对象加到里面,前端就可以通过el表达式拿到;
+4. 绑定数据到 Session中。
+
+## RequestBody和RequestParam的区别
+
+@RequestBody一般处理的是在ajax请求中声明contentType: "application/json; charset=utf-8"时候。也就是json数据或者xml数据。
+
+@RequestParam一般就是在ajax里面没有声明contentType的时候,为默认的`x-www-form-urlencoded`格式时。
+
+
+
diff --git "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md" "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
index 641525e..ae4a031 100644
--- "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
@@ -216,7 +216,7 @@ finishBeanFactoryInitialization(beanFactory);
## Bean的生命周期
-
+
1.调用bean的构造方法创建Bean
diff --git "a/\347\275\221\347\273\234/\347\275\221\347\273\234.md" "b/\347\275\221\347\273\234/\347\275\221\347\273\234.md"
index 60a94c1..0320b99 100644
--- "a/\347\275\221\347\273\234/\347\275\221\347\273\234.md"
+++ "b/\347\275\221\347\273\234/\347\275\221\347\273\234.md"
@@ -333,7 +333,7 @@ HTTP长连接,指的是复用TCP连接。多个HTTP请求可以复用同一个
CDN用户访问流程:
-
+
1.用户向浏览器输入www.web.com这个域名,浏览器第一次发现本地没有dns缓存,则向网站的DNS服务器请求;
diff --git "a/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 48969f2..2fb8b79 100644
--- "a/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -38,7 +38,7 @@
计算机网络体系大致分为三种,OSI七层模型、TCP/IP四层模型和五层模型。一般面试的时候考察比较多的是五层模型。
-
+
五层模型:应用层、传输层、网络层、数据链路层、物理层。
@@ -52,7 +52,7 @@
假设发送端为客户端,接收端为服务端。开始时客户端和服务端的状态都是`CLOSED`。
-
+
1. 第一次握手:客户端向服务端发起建立连接请求,客户端会随机生成一个起始序列号x,客户端向服务端发送的字段中包含标志位`SYN=1`,序列号`seq=x`。第一次握手前客户端的状态为`CLOSE`,第一次握手后客户端的状态为`SYN-SENT`。此时服务端的状态为`LISTEN`。
2. 第二次握手:服务端在收到客户端发来的报文后,会随机生成一个服务端的起始序列号y,然后给客户端回复一段报文,其中包括标志位`SYN=1`,`ACK=1`,序列号`seq=y`,确认号`ack=x+1`。第二次握手前服务端的状态为`LISTEN`,第二次握手后服务端的状态为`SYN-RCVD`,此时客户端的状态为`SYN-SENT`。(其中`SYN=1`表示要和客户端建立一个连接,`ACK=1`表示确认序号有效)
@@ -69,7 +69,7 @@
## 四次挥手
-
+
1. A的应用进程先向其TCP发出连接释放报文段(`FIN=1,seq=u`),并停止再发送数据,主动关闭TCP连接,进入`FIN-WAIT-1`(终止等待1)状态,等待B的确认。
2. B收到连接释放报文段后即发出确认报文段(`ACK=1,ack=u+1,seq=v`),B进入`CLOSE-WAIT`(关闭等待)状态,此时的TCP处于半关闭状态,A到B的连接释放。
@@ -152,7 +152,7 @@ Content-Length:112
## HTTP状态码有哪些?
-
+
## POST和GET的区别?
@@ -194,7 +194,7 @@ HTTP2.0相比HTTP1.1支持的特性:
服务端可以向证书颁发机构CA申请证书,以避免中间人攻击(防止证书被篡改)。证书包含三部分内容:**证书内容、证书签名算法和签名**,签名是为了验证身份。
-
+
服务端把证书传输给浏览器,浏览器从证书里取公钥。证书可以证明该公钥对应本网站。
@@ -216,29 +216,29 @@ HTTP2.0相比HTTP1.1支持的特性:
1. **协商加密算法** 。在`Client Hello`里面客户端会告知服务端自己当前的一些信息,包括客户端要使用的TLS版本,支持的加密算法,要访问的域名,给服务端生成的一个随机数(Nonce)等。需要提前告知服务器想要访问的域名以便服务器发送相应的域名的证书过来。
- 
+ 
2. 服务端响应`Server Hello`,告诉客户端服务端**选中的加密算法**。
- 
+ 
3. 接着服务端给客户端发来了2个证书。第二个证书是第一个证书的签发机构(CA)的证书。
- 
+ 
4. 客户端使用证书的认证机构CA公开发布的RSA公钥**对该证书进行验证**,下图表明证书认证成功。
- 
+ 
5. 验证通过之后,浏览器和服务器通过**密钥交换算法**产生共享的**对称密钥**。
- 
+ 
- 
+ 
6. 开始传输数据,使用同一个对称密钥来加解密。
- 
+ 
## DNS 的解析过程?
@@ -257,7 +257,7 @@ HTTP2.0相比HTTP1.1支持的特性:
4. 服务器**响应请求**,返回响应数据。
5. 浏览器**解析响应内容,进行渲染**,呈现给用户。
-
+
## 什么是cookie和session?
@@ -265,7 +265,7 @@ HTTP2.0相比HTTP1.1支持的特性:
由于HTTP协议是无状态的协议,需要用某种机制来识具体的用户身份,用来跟踪用户的整个会话。常用的会话跟踪技术是cookie与session。
**cookie**就是由服务器发给客户端的特殊信息,而这些信息以文本文件的方式存放在客户端,然后客户端每次向服务器发送请求的时候都会带上这些特殊的信息。说得更具体一些:当用户使用浏览器访问一个支持cookie的网站的时候,用户会提供包括用户名在内的个人信息并且提交至服务器;接着,服务器在向客户端回传相应的超文本的同时也会发回这些个人信息,当然这些信息并不是存放在HTTP响应体中的,而是存放于HTTP响应头;当客户端浏览器接收到来自服务器的响应之后,浏览器会将这些信息存放在一个统一的位置。 自此,客户端再向服务器发送请求的时候,都会把相应的cookie存放在HTTP请求头再次发回至服务器。服务器在接收到来自客户端浏览器的请求之后,就能够通过分析存放于请求头的cookie得到客户端特有的信息,从而动态生成与该客户端相对应的内容。网站的登录界面中“请记住我”这样的选项,就是通过cookie实现的。
-
+
**cookie工作流程**:
@@ -274,7 +274,7 @@ HTTP2.0相比HTTP1.1支持的特性:
3. 下次访问时,浏览器将自动携带cookie数据发送给服务器。
**session原理**:首先浏览器请求服务器访问web站点时,服务器首先会检查这个客户端请求是否已经包含了一个session标识、称为SESSIONID,如果已经包含了一个sessionid则说明以前已经为此客户端创建过session,服务器就按照sessionid把这个session检索出来使用,如果客户端请求不包含session id,则服务器为此客户端创建一个session,并且生成一个与此session相关联的独一无二的sessionid存放到cookie中,这个sessionid将在本次响应中返回到客户端保存,这样在交互的过程中,浏览器端每次请求时,都会带着这个sessionid,服务器根据这个sessionid就可以找得到对应的session。以此来达到共享数据的目的。 这里需要注意的是,session不会随着浏览器的关闭而死亡,而是等待超时时间。
-
+
## Cookie和Session的区别?
@@ -293,7 +293,7 @@ HTTP2.0相比HTTP1.1支持的特性:
TCP 利用滑动窗口实现流量控制。流量控制是为了控制发送方发送速率,保证接收方来得及接收。 TCP会话的双方都各自维护一个发送窗口和一个接收窗口。接收窗口大小取决于应用、系统、硬件的限制。发送窗口则取决于对端通告的接收窗口。接收方发送的确认报文中的window字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将接收方的确认报文window字段设置为 0,则发送方不能发送数据。
-
+
TCP头包含window字段,16bit位,它代表的是窗口的字节容量,最大为65535。这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。接收窗口的大小是约等于发送窗口的大小。
@@ -302,7 +302,7 @@ TCP头包含window字段,16bit位,它代表的是窗口的字节容量,最
防止过多的数据注入到网络中。 几种拥塞控制方法:慢开始( slow-start )、拥塞避免( congestion avoidance )、快重传( fast retransmit )和快恢复( fast recovery )。
-
+
### 慢开始
From 156db24662d9c971173edfdf1c30c36662976135 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 22 May 2022 20:14:47 +0800
Subject: [PATCH 033/112] update
---
Java/JVM.md | 32 ++++++------
...21\351\235\242\350\257\225\351\242\230.md" | 14 +++---
"Java/Java\345\237\272\347\241\200.md" | 10 ++--
...00\351\235\242\350\257\225\351\242\230.md" | 8 +--
...21\351\235\242\350\257\225\351\242\230.md" | 12 ++---
...10\351\235\242\350\257\225\351\242\230.md" | 8 +--
"Java/\345\271\266\345\217\221.md" | 22 ++++-----
"Java/\351\233\206\345\220\210.md" | 8 +--
.../RabbitMQ.md" | 10 ++--
...07\345\215\227\346\200\273\347\273\223.md" | 16 +++---
...is\351\235\242\350\257\225\351\242\230.md" | 10 ++--
.../GitHub\346\214\207\345\215\227.md" | 18 +++----
"\345\267\245\345\205\267/progit2.md" | 10 ++--
...47\350\241\214\350\256\241\345\210\222.md" | 44 ++++++++---------
.../MySQL\350\277\233\351\230\266.md" | 24 ++++-----
...21\351\235\242\350\257\225\351\242\230.md" | 49 ++++++++++++-------
...43\347\240\201\345\256\236\347\216\260.md" | 2 +-
...25\351\242\230\346\200\273\347\273\223.md" | 2 +-
...ng\351\235\242\350\257\225\351\242\230.md" | 2 +-
.../\347\275\221\347\273\234.md" | 2 +-
...21\351\235\242\350\257\225\351\242\230.md" | 34 ++++++-------
21 files changed, 174 insertions(+), 163 deletions(-)
diff --git a/Java/JVM.md b/Java/JVM.md
index 74430f8..05f5634 100644
--- a/Java/JVM.md
+++ b/Java/JVM.md
@@ -90,7 +90,7 @@ Java 内存模型(JMM)是基于共享内存的多线程通信机制。
JVM内存结构 = 类加载器 + 执行引擎 + 运行时数据区域 。
-
+
> 图片来源:深入理解Java虚拟机-周志明
@@ -172,11 +172,11 @@ JDK 1.8 的时候,HotSpot 的永久代被彻底移除了,使用元空间替
运行时常量池是方法区的一部分,在类加载之后,会将编译器生成的各种字面量和符号引号放到运行时常量池。在运行期间动态生成的常量,如 String 类的 intern()方法,也会被放入运行时常量池。
-
+
-
+
-
+
> 图片来源:https://blog.csdn.net/soonfly
@@ -194,11 +194,11 @@ Java 程序通过栈上的 reference 数据来操作堆上的具体对象。对
- 如果使用句柄的话,那么 Java 堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。使用句柄来访问的最大好处是 reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而 reference 本身不需要修改。
-
+
- 直接指针。reference 中存储的直接就是对象的地址。对象包含到对象类型数据的指针,通过这个指针可以访问对象类型数据。使用直接指针访问方式最大的好处就是访问对象速度快,它节省了一次指针定位的时间开销,虚拟机hotspot主要是使用直接指针来访问对象。
-
+
@@ -245,7 +245,7 @@ ClassFile {
加载、验证、准备、解析、初始化、使用和卸载。
-
+
## 类加载的过程
@@ -281,7 +281,7 @@ ClassFile {
一个类加载器收到一个类的加载请求时,它首先不会自己尝试去加载它,而是把这个请求委派给父类加载器去完成,这样层层委派,因此所有的加载请求最终都会传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
-
+
双亲委派模型的好处:可以防止内存中出现多份同样的字节码。如果没有双亲委派模型而是由各个类加载器自行加载的话,如果用户编写了一个java.lang.Object的同名类并放在ClassPath中,多个类加载器都去加载这个类到内存中,系统中将会出现多个不同的Object类,那么类之间的比较结果及类的唯一性将无法保证。
@@ -339,7 +339,7 @@ public abstract class ClassLoader {
堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断那些对象已经死亡(即不能再被任何途径使用的对象)。
-
+
### 引用计数法
@@ -365,7 +365,7 @@ public class ReferenceCountingGc {
通过GC Root对象为起点,从这些节点向下搜索,搜索所走过的路径叫引用链,当一个对象到GC Root没有任何的引用链相连时,说明这个对象是不可用的。
-
+
#### 可作为GC Roots的对象
@@ -469,13 +469,13 @@ public class ReferenceCountingGc {
标记清除算法就是分为“标记”和“清除”两个阶段。标记出所有需要回收的对象,标记结束后统一回收所有被标记的对象。这种垃圾回收算法效率较低,并且会产生大量不连续的空间碎片。
-
+
### 复制清除算法
半区复制,用于新生代垃圾回收。将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。
-
+
特点:实现简单,运行高效,但可用内存缩小为了原来的一半,浪费空间。
@@ -534,7 +534,7 @@ java -XX:+PrintCommandLineFlags -version
单线程收集器,使用一条垃圾收集线程去完成垃圾收集工作,在进行垃圾收集工作的时候必须暂停其他所有的工作线程( "Stop The World" ),直到它收集结束。
-
+
特点:简单高效;内存消耗最小;没有线程交互的开销,单线程收集效率高;需暂停所有的工作线程,用户体验不好。
@@ -542,7 +542,7 @@ java -XX:+PrintCommandLineFlags -version
Serial 收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全一样。
-
+
除了 Serial 收集器外,只有它能与 CMS 收集器配合工作。
@@ -582,7 +582,7 @@ Concurrent Mark Sweep 并发标记清除,目的是获取最短应用停顿时
- 重新标记: 在并发标记期间对象的引用关系可能会变化,需要重新进行标记。此阶段也会stw,停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短。
- 并发清除:清除死亡对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。
-
+
由于在整个过程中耗时最长的并发标记和并发清除阶段中,垃圾收集器线程都可以与用户线程一起工作,所以从总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。
@@ -607,7 +607,7 @@ G1垃圾收集器的目标是用在多核、大内存的机器上,在不同应
G1将整个堆分成相同大小的分区(Region),有四种不同类型的分区:Eden、Survivor、Old和Humongous(大对象)。分区的大小取值范围为1M到32M,都是2的幂次方。Region大小可以通过`-XX:G1HeapRegionSize`参数指定。Humongous区域用于存储大对象。G1认为只要大小超过了一个Region容量一半的对象即可判定为大对象。
-
+
G1 收集器对各个Region回收所获得的空间大小和回收所需时间的经验值进行排序,得到一个优先级列表,每次根据用户设置的最大的回收停顿时间(使用参数-XX:MaxGCPauseMillis指定,默认值是200毫秒),优先处理回收价值最大的 Region。
diff --git "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 7ccae91..e4bddb5 100644
--- "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -49,7 +49,7 @@
JVM内存结构分为5大区域,**程序计数器**、**虚拟机栈**、**本地方法栈**、**堆**、**方法区**。
-
+
### 程序计数器
@@ -185,7 +185,7 @@ ClassFile {
类的加载指的是将类的`class`文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个此类的对象,通过这个对象可以访问到方法区对应的类信息。
-
+
**加载**
@@ -213,7 +213,7 @@ ClassFile {
一个类加载器收到一个类的加载请求时,它首先不会自己尝试去加载它,而是把这个请求**委派**给父类加载器去完成,这样层层委派,因此所有的加载请求最终都会传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
-
+
双亲委派模型的具体实现代码在 `java.lang.ClassLoader`中,此类的 `loadClass()` 方法运行过程如下:先检查类是否已经加载过,如果没有则让父类加载器去加载。当父类加载器加载失败时抛出 `ClassNotFoundException`,此时尝试自己去加载。源码如下:
@@ -312,7 +312,7 @@ public class ReferenceCount {
通过`GC Root`对象为起点,从这些节点向下搜索,搜索所走过的路径叫引用链,当一个对象到`GC Root`没有任何的引用链相连时,说明这个对象是不可用的。
-
+
## 可作为GC Roots的对象有哪些?
@@ -413,7 +413,7 @@ GC(`Garbage Collection`),垃圾回收,是Java与C++的主要区别之一
首先利用可达性去遍历内存,把存活对象和垃圾对象进行标记。标记结束后统一将所有标记的对象回收掉。这种垃圾回收算法效率较低,并且会**产生大量不连续的空间碎片**。
-
+
**复制清除算法**
@@ -425,7 +425,7 @@ GC(`Garbage Collection`),垃圾回收,是Java与C++的主要区别之一
根据老年代的特点提出的一种标记算法,标记过程仍然与`标记-清除`算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。
-
+
**分类收集算法**
@@ -508,7 +508,7 @@ G1垃圾收集器的目标是在不同应用场景中**追求高吞吐量和低
G1将整个堆分成相同大小的分区(`Region`),有四种不同类型的分区:`Eden、Survivor、Old和Humongous`。分区的大小取值范围为 1M 到 32M,都是2的幂次方。分区大小可以通过`-XX:G1HeapRegionSize`参数指定。`Humongous`区域用于存储大对象。G1规定只要大小超过了一个分区容量一半的对象就认为是大对象。
-
+
G1 收集器对各个分区回收所获得的空间大小和回收所需时间的经验值进行排序,得到一个优先级列表,每次根据用户设置的最大回收停顿时间,优先回收价值最大的分区。
diff --git "a/Java/Java\345\237\272\347\241\200.md" "b/Java/Java\345\237\272\347\241\200.md"
index 0f3e5c8..72cff97 100644
--- "a/Java/Java\345\237\272\347\241\200.md"
+++ "b/Java/Java\345\237\272\347\241\200.md"
@@ -976,7 +976,7 @@ Class 类提供了一些方法,可以获取成员变量、成员方法、接
Field提供了类和接口中字段的信息,通过Field类可以动态访问这些字段。下图是Field类提供的一些方法。
-
+
## Method类
@@ -1101,7 +1101,7 @@ public class GenericMethod {
Throwable类是Error和Exception的父类,只有继承于Throwable的类或者其子类才能被抛出。Throwable分为两类:
-
+
- Error:JVM 无法解决的严重问题,如栈溢出(StackOverflowError)、内存溢出(OOM)等。程序无法处理的错误。
@@ -1198,7 +1198,7 @@ public class ExceptionTest {
Java IO流的核心就是对文件的操作,对于字节 、字符类型的输入和输出流。IO流主要分为两大类,字节流和字符流。字节流可以处理任何类型的数据,如图片,视频等,字符流只能处理字符类型的数据。
-
+
> 图片参考:[Java io学习整理](https://zhuanlan.zhihu.com/p/25418336)
@@ -1251,13 +1251,13 @@ OutputStreamWriter:字符到字节的转换,可对读取到的字符数据
同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。
-
+
## NIO
NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了 NIO 框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。
-
+
NIO与IO区别:
diff --git "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
index 095093c..758e026 100644
--- "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
@@ -101,7 +101,7 @@
**JVM** :英文名称(Java Virtual Machine),就是我们耳熟能详的 Java 虚拟机。Java 能够跨平台运行的核心在于 JVM 。
-
+
所有的java程序会首先被编译为.class的类文件,这种类文件可以在虚拟机上执行。也就是说class文件并不直接与机器的操作系统交互,而是经过虚拟机间接与操作系统交互,由虚拟机将程序解释给本地系统执行。
@@ -111,7 +111,7 @@
英文名称(Java Runtime Environment),就是Java 运行时环境。我们编写的Java程序必须要在JRE才能运行。它主要包含两个部分,JVM 和 Java 核心类库。
-
+
JRE是Java的运行环境,并不是一个开发环境,所以没有包含任何开发工具,如编译器和调试器等。
@@ -141,7 +141,7 @@ JRE = JVM + Java 核心类库
JDK = JRE + Java工具 + 编译器 + 调试器
-
+
## 面向对象有哪些特性?
@@ -1030,7 +1030,7 @@ unchecked Exception:
不同的线程干专业的事情,最终每个线程都没空着,系统的吞吐量自然就上去了。
-
+
diff --git "a/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
index cfcb15d..a65d22c 100644
--- "a/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
@@ -64,7 +64,7 @@
### 线程池执行原理?
-
+
1. 当线程池里存活的线程数小于核心线程数`corePoolSize`时,这时对于一个新提交的任务,线程池会创建一个线程去处理任务。当线程池里面存活的线程数小于等于核心线程数`corePoolSize`时,线程池里面的线程会一直存活着,就算空闲时间超过了`keepAliveTime`,线程也不会被销毁,而是一直阻塞在那里一直等待任务队列的任务来执行。
2. 当线程池里面存活的线程数已经等于corePoolSize了,这是对于一个新提交的任务,会被放进任务队列workQueue排队等待执行。
@@ -199,7 +199,7 @@ public static ExecutorService newCachedThreadPool() {
3. 修改 `ScheduledFutureTask` 的 time 变量为下次将要被执行的时间;
4. 把这个修改 time 之后的 `ScheduledFutureTask` 放回 `DelayQueue` 中(`DelayQueue.add()`)。
-
+
适用场景:周期性执行任务的场景,需要限制线程数量的场景。
@@ -225,7 +225,7 @@ public static ExecutorService newCachedThreadPool() {
**终止(TERMINATED)**:表示该线程已经执行完毕。
-
+
> 图片来源:Java并发编程的艺术
@@ -401,7 +401,7 @@ class RunnableDemo implements Runnable {
如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方持有的资源,所以这两个线程就会互相等待而进入死锁状态。
-
+
下面通过例子说明线程死锁,代码来自并发编程之美。
@@ -686,7 +686,7 @@ class SeasonThreadTask implements Runnable{
每个线程都有一个`ThreadLocalMap`(`ThreadLocal`内部类),Map中元素的键为`ThreadLocal`,而值对应线程的变量副本。
-
+
调用`threadLocal.set()`-->调用`getMap(Thread)`-->返回当前线程的`ThreadLocalMap`-->`map.set(this, value)`,this是`threadLocal`本身。源码如下:
@@ -783,7 +783,7 @@ private volatile int state;//共享变量,使用volatile修饰保证线程可
同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态(独占或共享 )构造成为一个节点(Node)并将其加入同步队列并进行自旋,当同步状态释放时,会把首节点中的后继节点对应的线程唤醒,使其再次尝试获取同步状态。
-
+
## ReentrantLock 是如何实现可重入性的?
diff --git "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
index 4a011e4..14acae6 100644
--- "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
@@ -54,9 +54,9 @@ Java集合类主要由两个接口**Collection**和**Map**派生出来的,Coll
Java集合框架图如下:
-
+
-
+
List代表了有序可重复集合,可直接根据元素的索引来访问;Set代表无序不可重复集合,只能根据元素本身来访问;Queue是队列集合。Map代表的是存储key-value对的集合,可根据元素的key来访问value。
@@ -197,7 +197,7 @@ HashMap有扩容机制,就是当达到扩容条件时会进行扩容。扩容
5. 链表的数量大于阈值8,就要转换成红黑树的结构
6. 添加成功后会检查是否需要扩容
-
+
### 红黑树的特点?
@@ -271,7 +271,7 @@ public class TreeMap
TreeMap 的继承结构:
-
+
**TreeMap的特点:**
diff --git "a/Java/\345\271\266\345\217\221.md" "b/Java/\345\271\266\345\217\221.md"
index c9734f5..1f35396 100644
--- "a/Java/\345\271\266\345\217\221.md"
+++ "b/Java/\345\271\266\345\217\221.md"
@@ -87,7 +87,7 @@ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveT
创建新的线程需要获取全局锁,通过这种设计可以尽量避免获取全局锁,当 ThreadPoolExecutor 完成预热之后(当前运行的线程数大于等于 corePoolSize),提交的大部分任务都会被放到 BlockingQueue。
-
+
ThreadPoolExecutor 的通用构造函数:
@@ -326,7 +326,7 @@ public static ExecutorService newCachedThreadPool() {
3. 修改 `ScheduledFutureTask` 的 time 变量为下次将要被执行的时间;
4. 把这个修改 time 之后的 `ScheduledFutureTask` 放回 `DelayQueue` 中(`DelayQueue.add()`)。
-
+
适用场景:周期性执行任务的场景,需要限制线程数量的场景。
@@ -340,7 +340,7 @@ FixedThreadPool和SingleThreadPool。允许请求队列长度为 Integer.MAX_VAL
CachedThreadPool。创建的线程池允许的最大线程数是Integer.MAX_VALUE,当添加任务的速度大于线程池处理任务的速度,可能会创建大量的线程,消耗资源,甚至导致OOM。
-
+
正确示例(阿里巴巴编码规范):
@@ -369,7 +369,7 @@ Java内存模型:线程之间的共享变量存储在主内存里,每个线
1. 线程a把本地内存a中的更新过的共享变量刷新到主内存中去。
2. 线程b到主内存中去读取线程a刷新过的共享变量,然后复制一份到本地内存b中去。
-
+
本地内存是JMM的一个抽象概念,并不真实存在,它包括缓存、写缓冲区、寄存器以及其他硬件和编译器优化。
@@ -392,7 +392,7 @@ Java内存模型:线程之间的共享变量存储在主内存里,每个线
终止(TERMINATED):表示该线程已经执行完毕。
-
+
> 图片来源:Java并发编程的艺术
@@ -680,7 +680,7 @@ public class SynchronizedDemo {
Synchronized底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。
-
+
**方法的同步**不是通过添加monitorenter和monitorexit指令来完成,而是在其常量池中添加了ACC_SYNCHRONIZED标识符。JVM就是根据该标识符来实现方法的同步的:当线程调用方法时,会先检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,说明此方法是同步方法,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他线程无法再获得同一个monitor对象。
@@ -692,7 +692,7 @@ public class SynchronizedMethod {
}
```
-
+
#### 锁的状态
@@ -1077,7 +1077,7 @@ private volatile int state;//共享变量,使用volatile修饰保证线程可
同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态(独占或共享 )构造成为一个节点(Node)并将其加入同步队列并进行自旋,当同步状态释放时,会把首节中的后继节点对应的线程唤醒,使其再次尝试获取同步状态。
-
+
@@ -1112,19 +1112,19 @@ public void conditionSignal() throws InterruptedException {
每个Condition对象都包含着一个等待队列,如果一个线程成功获取了锁之后调用了Condition.await()方法,那么该线程将会释放同步状态、唤醒同步队列中的后继节点,然后构造成节点加入等待队列。只有当线程再次获取Condition相关联的锁之后,才能从await()方法返回。
-
+
> 图片来源:Java并发编程的艺术
在Object的监视器模型上,一个对象拥有一个同步队列和等待队列。Lock通过AQS实现,AQS可以有多个Condition,所以Lock拥有一个同步队列和多个等待队列。
-
+
> 图片来源:Java并发编程的艺术
线程获取了锁之后,调用Condition的signal()方法,会将等待队列的队首节点移到同步队列中,然后该节点的线程会尝试去获取同步状态。成功获取同步状态之后,线程将await()方法返回。
-
+
> 图片来源:Java并发编程的艺术
diff --git "a/Java/\351\233\206\345\220\210.md" "b/Java/\351\233\206\345\220\210.md"
index c7283d5..83fecae 100644
--- "a/Java/\351\233\206\345\220\210.md"
+++ "b/Java/\351\233\206\345\220\210.md"
@@ -51,7 +51,7 @@
以 Map 结尾的类都实现了 Map 接口,其他所有的类都实现了 Collection 接口。
-
+
### HashMap
@@ -88,7 +88,7 @@ put方法流程:
5. 链表的数量大于阈值8,就要转换成红黑树的结构
6. 添加成功后会检查是否需要扩容
-
+
> 参考链接:
>
@@ -121,7 +121,7 @@ HashMap是无序的,迭代HashMap所得到元素的顺序并不是它们最初
LinkedHashMap继承于HashMap,是HashMap和LinkedList的融合体,具备两者的特性。每次put操作都会将entry插入到双向链表的尾部。
-
+
### TreeMap
@@ -136,7 +136,7 @@ public class TreeMap
TreeMap 的继承结构:
-
+
**TreeMap的特点:**
diff --git "a/\344\270\255\351\227\264\344\273\266/RabbitMQ.md" "b/\344\270\255\351\227\264\344\273\266/RabbitMQ.md"
index e57f2b1..cc43200 100644
--- "a/\344\270\255\351\227\264\344\273\266/RabbitMQ.md"
+++ "b/\344\270\255\351\227\264\344\273\266/RabbitMQ.md"
@@ -39,13 +39,13 @@
文章目录:
-
+
# 简介
RabbitMQ是一个由erlang开发的消息队列。消息队列用于应用间的异步协作。
-
+
## 基本概念
@@ -94,19 +94,19 @@ Exchange规则。
direct交换机会将消息路由到binding key 和 routing key完全匹配的队列中。它是完全匹配、单播的模式。
-
+
## fanout
所有发到 fanout 类型交换机的消息都会路由到所有与该交换机绑定的队列上去。fanout 类型转发消息是最快的。
-
+
## topic
topic交换机使用routing key和binding key进行模糊匹配,匹配成功则将消息发送到相应的队列。routing key和binding key都是句点号“. ”分隔的字符串,binding key中可以存在两种特殊字符“*”与“#”,用于做模糊匹配,其中“\*”用于匹配一个单词,“#”用于匹配多个单词。
-
+
## headers
diff --git "a/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md" "b/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
index f329ea8..8d4e0d7 100644
--- "a/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
+++ "b/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
@@ -71,7 +71,7 @@
目录结构如下:
-
+
## 简介
@@ -503,7 +503,7 @@ ziplist是 Redis 为了节约内存而开发的, 由一系列特殊编码的
- 最底层的链表包含所有的元素
- 跳跃表的查找次数近似于层数,时间复杂度为O(logn),插入、删除也为 O(logn)
-
+
#### 对象
@@ -545,7 +545,7 @@ hash类型内部编码有两种:
Redis3.2版本提供了quicklist内部编码,简单地说它是以一个ziplist为节点的linkedlist,它结合了ziplist和linkedlist两者的优势,为列表类型提供了一种更为优秀的内部编码实现。
-
+
使用场景:
@@ -630,7 +630,7 @@ GET #返回文章ID。
3. EXEC命令进行提交事务
-
+
DISCARD:放弃事务,即该事务内的所有命令都将取消
@@ -653,7 +653,7 @@ QUEUED
事务里的命令执行时会读取最新的值:
-
+
### WATCH命令
@@ -800,7 +800,7 @@ SLAVEOF NO ONE //停止接收其他数据库的同步并转化为主数据库。
5. 同步数据集。第一次同步的时候,从数据库启动后会向主数据库发送SYNC命令。主数据库接收到命令后开始在后台保存快照(RDB持久化过程),并将保存快照过程接收到的命令缓存起来。当快照完成后,Redis会将快照文件和缓存的命令发送到从数据库。从数据库接收到后,会载入快照文件并执行缓存的命令。以上过程称为复制初始化。
6. 复制初始化完成后,主数据库每次收到写命令就会将命令同步给从数据库,从而实现主从数据库数据的一致性。
-
+
Redis在2.8及以上版本使用psync命令完成主从数据同步,同步过程分为:全量复制和部分复制。
@@ -932,7 +932,7 @@ redis 127.0.0.1:6379> EVALSHA "232fd51614574cf0867b83d384a5e898cfd24e5a" 0
使用evalsha执行Lua脚本过程如下:
-
+
### lua脚本作用
@@ -1075,7 +1075,7 @@ public void write(String key,Object data){
解决方法:
-
+
> 图片来源:https://tech.it168.com
diff --git "a/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md" "b/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
index 8a0e0d6..d5704d2 100644
--- "a/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
+++ "b/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
@@ -153,7 +153,7 @@ scan的缺点:在scan的过程中如果有键的变化(增加、删除、修
3. EXEC命令进行提交事务
-
+
一个事务范围内某个命令出错不会影响其他命令的执行,不保证原子性:
@@ -224,7 +224,7 @@ Redis支持两种方式的持久化,一种是`RDB`的方式,一种是`AOF`
`bgsave`是主流的触发 RDB 持久化的方式,执行过程如下:
-
+
- 执行`BGSAVE`命令
- Redis 父进程判断当前**是否存在正在执行的子进程**,如果存在,`BGSAVE`命令直接返回。
@@ -269,7 +269,7 @@ appendfsync no //由操作系统决定何时进行同步操作
接下来看一下 AOF 持久化执行流程:
-
+
1. 所有的写入命令会追加到 AOP 缓冲区中。
2. AOF 缓冲区根据对应的策略向硬盘同步。
@@ -334,7 +334,7 @@ SLAVEOF NO ONE //停止接收其他数据库的同步并转化为主数据库。
客户端连接Redis的时候,先连接哨兵,哨兵会告诉客户端Redis主节点的地址,然后客户端连接上Redis并进行后续的操作。当主节点宕机的时候,哨兵监测到主节点宕机,会重新推选出某个表现良好的从节点成为新的主节点,然后通过发布订阅模式通知其他的从服务器,让它们切换主机。
-
+
**工作原理**
@@ -353,7 +353,7 @@ Redis cluster集群节点最小配置6个节点以上(3主3从),其中主
Redis cluster采用**虚拟槽分区**,所有的键根据哈希函数映射到0~16383个整数槽内,每个节点负责维护一部分槽以及槽所映射的键值数据。
-
+
**哈希槽是如何映射到 Redis 实例上的?**
diff --git "a/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md" "b/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md"
index cca4277..a125d4b 100644
--- "a/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md"
+++ "b/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md"
@@ -22,15 +22,15 @@ Git又是什么呢?简单的说,**Git 是一个管理你的代码历史记
- 创建一个新的仓库,用来存放项目。
- 
+ 
- 或者你在GitHub上看到别人有一个超级无敌有趣的项目,可以直接fork过来,可以理解成复制过来,变成你自己的。之后你想怎么改就怎么改!
- 
+ 
- 然后你可以通过Git命令行`git clone xxx`把项目clone到本地,在本地进行创作。
- 
+ 
- 最后,在本地创作完成,可以使用`git commit -m xxx`提交到本地库,然后使用`git push`把修改推送到GitHub仓库。之后就可以在GitHub上面看到你修改的内容啦~
@@ -42,23 +42,23 @@ Git又是什么呢?简单的说,**Git 是一个管理你的代码历史记
- 评价GitHub项目的两个重要的参数:star和fork。
-
+
比较优秀和热门的项目,star数目和fork数目都会比较多。我们可以根据这两个参数筛选出比较好的项目。使用`关键字 stars:>=xxx forks:>=xxx` 可以筛选出star和fork数目大于xxx的相关项目。
-
+
- 使用 `awesome 关键字`,可以筛选出比较高质量的学习、书籍、工具类或者插件类的集合。
-
+
- 在特定位置搜索关键词。有些关键词出现在项目的不同位置,比如项目名称、项目描述和README等。使用`关键词 in name/description/Readme`,可以搜索到相关的内容。比如使用`spring in name`,可以搜索到在项目名中包含spring的项目。
-
+
- 指定条件搜索关键词。如`tool language:java`搜索到的是包含关键字tool的Java项目,`tool followers:>1000`可以搜索到包含关键字tool,且follower数量大于1000的项目。
-
+
@@ -72,7 +72,7 @@ Git又是什么呢?简单的说,**Git 是一个管理你的代码历史记
- 文档神器。可以为自己的项目建立wiki,可以用markdown语法写wiki;
-
+
- 使用GitHub pages建立个人静态网站,搞一个有自己域名的独立博客,想想都觉得开心。使用GitHub pages的好处是搭建简单而且免费,支持静态脚本,并且可以绑定自己的域名。具体可以参考:[GitHub Pages 建立个人网站详细教程 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/58229299)
diff --git "a/\345\267\245\345\205\267/progit2.md" "b/\345\267\245\345\205\267/progit2.md"
index 6dde18b..68b8c2b 100644
--- "a/\345\267\245\345\205\267/progit2.md"
+++ "b/\345\267\245\345\205\267/progit2.md"
@@ -85,7 +85,7 @@ Git工作流程如下:
Git 的工作流程图如下:
-
+
> 图片来源:https://blog.csdn.net/ThinkWon/article/details/94346816
@@ -103,11 +103,11 @@ Git 的三种状态:已修改(modified)、已暂存(staged)和已提
基本的 Git 工作流程:在工作目录修改文件;暂存文件,将文件快照放到暂存区域;提交更新到本地库。暂存区保存了下次将要提交的文件列表信息,一般在 Git 仓库目录中。
-
+
> 图片来源:`https://img2018.cnblogs.com/blog/1252910/201907/1252910-20190726163829113-2056815874.png`
-
+
## 配置
@@ -156,7 +156,7 @@ git clone https://github.com/...
查看文件状态:`git status`
-
+
> 图片来源:`https://img2018.cnblogs.com/blog/1252910/201907/1252910-20190726163854195-886320537.png`
@@ -262,7 +262,7 @@ git commit -m "add readme.md"
单独执行`git commit`,不带上-m参数,会进入 vim 编辑器界面:
-
+
此时应该这么操作:
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md"
index 2378225..e9f255a 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md"
@@ -91,7 +91,7 @@ where blog_id = (
三个表依次嵌套,发现最里层的子查询 `id`最大,最先执行。
-
+
## select_type
@@ -107,13 +107,13 @@ where blog_id = (
查询的表名,并不一定是真实存在的表,有别名显示别名,也可能为临时表。当from子句中有子查询时,table列是 ``的格式,表示当前查询依赖 id为N的查询,会先执行 id为N的查询。
-
+
## partitions
查询时匹配到的分区信息,对于非分区表值为`NULL`,当查询的是分区表时,`partitions`显示分区表命中的分区情况。
-
+
## type
@@ -123,25 +123,25 @@ where blog_id = (
当表仅有一行记录时(系统表),数据量很少,往往不需要进行磁盘IO,速度非常快。比如,Mysql系统表proxies_priv在Mysql服务启动时候已经加载在内存中,对这个表进行查询不需要进行磁盘 IO。
-
+
### const
单表操作的时候,查询使用了主键或者唯一索引。
-
+
### eq_ref
**多表关联**查询的时候,主键和唯一索引作为关联条件。如下图的sql,对于user表(外循环)的每一行,user_role表(内循环)只有一行满足join条件,只要查找到这行记录,就会跳出内循环,继续外循环的下一轮查询。
-
+
### ref
查找条件列使用了索引而且不为主键和唯一索引。虽然使用了索引,但该索引列的值并不唯一,这样即使使用索引查找到了第一条数据,仍然不能停止,要在目标值附近进行小范围扫描。但它的好处是不需要扫全表,因为索引是有序的,即便有重复值,也是在一个非常小的范围内做扫描。
-
+
### ref_or_null
@@ -151,13 +151,13 @@ where blog_id = (
使用了索引合并优化方法,查询使用了两个以上的索引。新建comment表,id为主键,value_id为非唯一索引,执行`explain select content from comment where value_id = 1181000 and id > 1000;`,执行结果显示查询同时使用了id和value_id索引,type列的值为index_merge。
-
+
### range
有范围的索引扫描,相对于index的全索引扫描,它有范围限制,因此要优于index。像between、and、'>'、'<'、in和or都是范围索引扫描。
-
+
### index
@@ -165,17 +165,17 @@ index包括select索引列,order by主键两种情况。
1. order by主键。这种情况会按照索引顺序全表扫描数据,拿到的数据是按照主键排好序的,不需要额外进行排序。
- 
+ 
2. select索引列。type为index,而且extra字段为using index,也称这种情况为索引覆盖。所需要取的数据都在索引列,无需回表查询。
- 
+ 
### all
全表扫描,查询没有用到索引,性能最差。
-
+
## possible_keys
@@ -249,13 +249,13 @@ CREATE TABLE `t_orderdetail` (
查询的列未被索引覆盖,where筛选条件非索引的前导列。对存储引擎返回的结果进行过滤(Post-filter,后过滤),一般发生在MySQL服务器,而不是存储引擎层。
-
+
### using index
查询的列被索引覆盖,并且where筛选条件符合最左前缀原则,通过**索引查找**就能直接找到符合条件的数据,不需要回表查询数据。
-
+
### Using where&Using index
@@ -265,17 +265,17 @@ CREATE TABLE `t_orderdetail` (
- where筛选条件不符合最左前缀原则
- 
+ 
- where筛选条件是索引列前导列的一个范围
- 
+ 
### null
查询的列未被索引覆盖,并且where筛选条件是索引的前导列,也就是用到了索引,但是部分字段未被索引覆盖,必须回表查询这些字段,Extra中为NULL。
-
+
### using index condition
@@ -283,11 +283,11 @@ CREATE TABLE `t_orderdetail` (
不使用ICP的情况(`set optimizer_switch='index_condition_pushdown=off'`),如下图,在步骤4中,没有使用where条件过滤索引:
-
+
使用ICP的情况(`set optimizer_switch='index_condition_pushdown=on'`):
-
+
下面的例子使用了ICP:
@@ -296,11 +296,11 @@ explain select user_id, order_id, order_status
from t_order where user_id > 1 and user_id < 5\G;
```
-
+
关掉ICP之后(`set optimizer_switch='index_condition_pushdown=off'`),可以看到extra列为using where,不会使用索引下推。
-
+
### using temporary
@@ -314,7 +314,7 @@ from t_order where user_id > 1 and user_id < 5\G;
- select 查询字段不全是索引字段
- select 查询字段都是索引字段,但是 order by 字段和索引字段的顺序不一致
-
+
### using join buffer
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md"
index 4ff8a64..7caa4e5 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md"
@@ -138,7 +138,7 @@ B+ 树是基于B 树和叶子节点顺序访问指针进行实现,它具有B
在 B+ 树中,节点中的 key 从左到右递增排列,如果某个指针的左右相邻 key 分别是 keyi 和 keyi+1,则该指针指向节点的所有 key 大于等于 keyi 且小于等于 keyi+1。
-
+
进行查找操作时,首先在根节点进行二分查找,找到key所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出 key 所对应的数据项。
@@ -170,15 +170,15 @@ Index_comment:
如下图,col1 是主键,col2和col3是普通字段。
-
+
下图是主键索引对应的 B+树结构,每个节点对应磁盘的一页。
-
+
对col3 建立一个单列索引,对应的B+树结构:
-
+
### 索引分类
1. 主键索引:名为primary的唯一非空索引,不允许有空值。
@@ -208,7 +208,7 @@ Index_comment:
对(a, b) 建立索引,a 在索引树中是全局有序的,而 b 是全局无序,局部有序(当a相等时,会对b进行比较排序)。直接执行`b = 2`这种查询条件没有办法利用索引。
-
+
从局部来看,当a的值确定的时候,b是有序的。例如a = 1时,b值为1,2是有序的状态。当a=2时候,b的值为1,4也是有序状态。 因此,你执行`a = 1 and b = 2`是a,b字段能用到索引的。而你执行`a > 1 and b = 2`时,a字段能用到索引,b字段用不到索引。因为a的值此时是一个范围,不是固定的,在这个范围内b值不是有序的,因此b字段用不上索引。
@@ -218,7 +218,7 @@ InnoDB使用表的主键构造主键索引树,同时叶子节点中存放的
聚集索引的叶子节点就是整张表的行记录。InnoDB 主键使用的是聚簇索引。聚集索引要比非聚集索引查询效率高很多。
-
+
对于InnoDB来说,聚集索引一般是表中的主键索引,如果表中没有显示指定主键,则会选择表中的第一个不允许为NULL的唯一索引。如果没有主键也没有合适的唯一索引,那么innodb内部会生成一个隐藏的主键作为聚集索引,这个隐藏的主键长度为6个字节,它的值会随着数据的插入自增。
@@ -244,7 +244,7 @@ explain select user_id from user_like where blog_id = 1;
Extra中为`Using where; Using index`, 查询的列被索引覆盖,where筛选条件不符合最左前缀原则,无法通过索引查找找到符合条件的数据,但可以通过**索引扫描**找到符合条件的数据,也不需要回表查询数据。
-
+
### 索引失效
@@ -322,7 +322,7 @@ MEMORY引擎默认使用哈希索引,将键的哈希值和指向数据行的
myisam引擎主键索引和其他索引区别不大,叶子节点都包含索引值和行指针。
innodb引擎二级索引叶子存储的是索引值和主键值(不是行指针),这样可以减少行移动和数据页分裂时二级索引的维护工作。
- 
+ 
@@ -351,7 +351,7 @@ mvcc实现依赖于版本链,版本链是通过表的三个隐藏字段实现
3. 修改当前行的值,生成一个新版本,更新事务id,使回滚指针指向旧版本的记录,这样就形成一条版本链;
4. 记录redo log;
-
+
### read view
@@ -388,14 +388,14 @@ repeatable read:在一个事务范围内,第一次select时更新这个read_
事务a和事务b同时开启事务,事务a插入数据然后提交,事务b执行全表的update,然后执行查询,查到了事务A中添加的数据。
-
+
MySQL如何实现避免幻读:
- 在快照读情况下,MySQL通过mvcc来避免幻读。
- 在当前读情况下,MySQL通过next-key来避免幻读(加行锁和间隙锁来实现的)。
-
+
next-key包括两部分:行锁和间隙锁。行锁是加在索引上的锁,间隙锁是加在索引之间的。
@@ -531,7 +531,7 @@ MySQL主要分为 Server 层和存储引擎层:
- **Server 层**:主要包括连接器、查询缓存、分析器、优化器、执行器等,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图,函数等,还有一个通用的日志模块 binglog 日志模块。
- **存储引擎**: 主要负责数据的存储和读取。server 层通过api与存储引擎进行通信。
-
+
### Server 层基本组件
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index f872daa..8ec0b08 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -179,7 +179,7 @@ B+ 树是基于B 树和叶子节点顺序访问指针进行实现,它具有B
在 B+ 树中,节点中的 `key` 从左到右递增排列,如果某个指针的左右相邻 `key` 分别是 keyi 和 keyi+1,则该指针指向节点的所有 `key` 大于等于 keyi 且小于等于 keyi+1。
-
+
进行查找操作时,首先在根节点进行二分查找,找到`key`所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出`key`所对应的数据项。
@@ -251,7 +251,7 @@ ADD CONSTRAINT constraint_name UNIQUE KEY(column_1,column_2,...);
如下图,对(a, b) 建立索引,a 在索引树中是全局有序的,而 b 是全局无序,局部有序(当a相等时,会根据b进行排序)。直接执行`b = 2`这种查询条件无法使用索引。
-
+
当a的值确定的时候,b是有序的。例如`a = 1`时,b值为1,2是有序的状态。当`a = 2`时候,b的值为1,4也是有序状态。 当执行`a = 1 and b = 2`时a和b字段能用到索引。而执行`a > 1 and b = 2`时,a字段能用到索引,b字段用不到索引。因为a的值此时是一个范围,不是固定的,在这个范围内b值不是有序的,因此b字段无法使用索引。
@@ -285,7 +285,7 @@ explain select user_id from user_like where blog_id = 1;
`explain`结果的`Extra`列为`Using where; Using index`, 查询的列被索引覆盖,where筛选条件不符合最左前缀原则,无法通过索引查找找到符合条件的数据,但可以通过**索引扫描**找到符合条件的数据,也不需要回表查询数据。
-
+
### 索引的设计原则?
@@ -395,7 +395,7 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
每条表记录大概是这样的:
-
+
使用事务更新行记录的时候,就会生成版本链,执行过程如下:
@@ -407,15 +407,15 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
1、初始数据如下,其中`DB_ROW_ID`和`DB_ROLL_PTR`为空。
-
+
2、事务A对该行数据做了修改,将`age`修改为12,效果如下:
-
+
3、之后事务B也对该行记录做了修改,将`age`修改为8,效果如下:
-
+
4、此时undo log有两行记录,并且通过回滚指针连在一起。
@@ -435,7 +435,7 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
**前提**:`DATA_TRX_ID` 表示每个数据行的最新的事务ID;`up_limit_id`表示当前快照中的最先开始的事务;`low_limit_id`表示当前快照中的最慢开始的事务,即最后一个事务。
-
+
- 如果`DATA_TRX_ID` < `up_limit_id`:说明在创建`read view`时,修改该数据行的事务已提交,该版本的记录可被当前事务读取到。
- 如果`DATA_TRX_ID` >= `low_limit_id`:说明当前版本的记录的事务是在创建`read view`之后生成的,该版本的数据行不可以被当前事务访问。此时需要通过版本链找到上一个版本,然后重新判断该版本的记录对当前事务的可见性。
@@ -460,7 +460,7 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
1、首先,user表只有两条记录,具体如下:
-
+
2、事务a和事务b同时开启事务`start transaction`;
@@ -478,7 +478,7 @@ update user set user_name = 'a';
5、事务b然后执行查询,查到了事务a中插入的数据。(下图左边是事务b,右边是事务a。事务开始之前只有两条记录,事务a插入一条数据之后,事务b查询出来是三条数据)
-
+
以上就是当前读出现的幻读现象。
@@ -567,7 +567,7 @@ MySQL主要分为 Server 层和存储引擎层:
垂直划分数据库是根据业务进行划分,例如购物场景,可以将库中涉及商品、订单、用户的表分别划分出成一个库,通过降低单库的大小来提高性能。同样的,分表的情况就是将一个大表根据业务功能拆分成一个个子表,例如商品基本信息和商品描述,商品基本信息一般会展示在商品列表,商品描述在商品详情页,可以将商品基本信息和商品描述拆分成两张表。
-
+
**优点**:行记录变小,数据页可以存放更多记录,在查询时减少I/O次数。
@@ -581,7 +581,7 @@ MySQL主要分为 Server 层和存储引擎层:
水平划分是根据一定规则,例如时间或id序列值等进行数据的拆分。比如根据年份来拆分不同的数据库。每个数据库结构一致,但是数据得以拆分,从而提升性能。
-
+
**优点**:单库(表)的数据量得以减少,提高性能;切分出的表结构相同,程序改动较少。
@@ -852,23 +852,34 @@ select * from page where id > start_id order by id limit 10;
通过主键索引,每次定位到start_id的位置,然后往后遍历10个数据,这样不管数据多大,查询性能都较为稳定。
-## B+树一个节点有多大?一千万条数据,B+树多高?
+## 高度为3的B+树,可以存放多少数据?
-InnoDB存储引擎有自己的最小储存单元——页(Page),一个页的大小是16K。
+InnoDB存储引擎有自己的最小储存单元——页(Page)。
-B+树一个节点的大小设为一页或页的倍数最为合适。因为如果一个节点的大小 < 1页,那么读取这个节点的时候其实读取的还是一页,这样就造成了资源的浪费。
+查询InnoDB页大小的命令如下:
-在 MySQL 中 B+ 树的一个节点大小为“1页”,也就是16k。之所以设置为一页,是因为对于大部分业务,一页就足够了:
+```mysql
+mysql> show global status like 'innodb_page_size';
++------------------+-------+
+| Variable_name | Value |
++------------------+-------+
+| Innodb_page_size | 16384 |
++------------------+-------+
+```
+
+可以看出 innodb 默认的一页大小为 16384B = 16384/1024 = 16kb。
+
+在MySQL中,B+树一个节点的大小设为一页或页的倍数最为合适。因为如果一个节点的大小 < 1页,那么读取这个节点的时候其实读取的还是一页,这样就造成了资源的浪费。
-首先InnoDB的B+树中,**非叶子节点存的是key + 指针**;**叶子节点存的是数据行**。
+B+树中**非叶子节点存的是key + 指针**;**叶子节点存的是数据行**。
对于叶子节点,如果一行数据大小为1k,那么一页就能存16条数据。
-对于非叶子节点,如果key使用的是bigint,则为8字节,指针在mysql中为6字节,一共是14字节,则16k能存放 16 * 1024 / 14 = 1170 个索引指针。
+对于非叶子节点,如果key使用的是bigint,则为8字节,指针在MySQL中为6字节,一共是14字节,则16k能存放 16 * 1024 / 14 = 1170 个索引指针。
于是可以算出,对于一颗高度为2的B+树,根节点存储索引指针节点,那么它有1170个叶子节点存储数据,每个叶子节点可以存储16条数据,一共 1170 x 16 = 18720 条数据。而对于高度为3的B+树,就可以存放 1170 x 1170 x 16 = 21902400 条数据(**两千多万条数据**),也就是对于两千多万条的数据,我们只需要**高度为3**的B+树就可以完成,通过主键查询只需要3次IO操作就能查到对应数据。
-所以在 InnoDB 中B+树高度一般为3层时,就能满足千万级的数据存储,所以一个节点为1页,也就是16k是比较合理的。
+所以在 InnoDB 中B+树高度一般为3层时,就能满足千万级的数据存储。
参考:https://www.cnblogs.com/leefreeman/p/8315844.html
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
index 9a99fb1..9ad4fb0 100644
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
+++ "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
@@ -13,7 +13,7 @@
常见的排序算法主要有:冒泡排序、插入排序、选择排序、快速排序、归并排序、堆排序、基数排序。各种排序算法的时间空间复杂度、稳定性见下图。
-
+
## 冒泡排序
diff --git "a/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" "b/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
index dd8b7fb..e9ddb23 100644
--- "a/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
+++ "b/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
@@ -28,7 +28,7 @@ SpringBoot实现自动配置原理图解:
> 公众号【程序员大彬】,回复【自动配置】下载高清图片
-
+
在 application.properties 中设置属性 debug=true,可以在控制台查看已启用和未启用的自动配置。
diff --git "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md" "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
index ae4a031..641525e 100644
--- "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
@@ -216,7 +216,7 @@ finishBeanFactoryInitialization(beanFactory);
## Bean的生命周期
-
+
1.调用bean的构造方法创建Bean
diff --git "a/\347\275\221\347\273\234/\347\275\221\347\273\234.md" "b/\347\275\221\347\273\234/\347\275\221\347\273\234.md"
index 0320b99..60a94c1 100644
--- "a/\347\275\221\347\273\234/\347\275\221\347\273\234.md"
+++ "b/\347\275\221\347\273\234/\347\275\221\347\273\234.md"
@@ -333,7 +333,7 @@ HTTP长连接,指的是复用TCP连接。多个HTTP请求可以复用同一个
CDN用户访问流程:
-
+
1.用户向浏览器输入www.web.com这个域名,浏览器第一次发现本地没有dns缓存,则向网站的DNS服务器请求;
diff --git "a/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 2fb8b79..48969f2 100644
--- "a/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -38,7 +38,7 @@
计算机网络体系大致分为三种,OSI七层模型、TCP/IP四层模型和五层模型。一般面试的时候考察比较多的是五层模型。
-
+
五层模型:应用层、传输层、网络层、数据链路层、物理层。
@@ -52,7 +52,7 @@
假设发送端为客户端,接收端为服务端。开始时客户端和服务端的状态都是`CLOSED`。
-
+
1. 第一次握手:客户端向服务端发起建立连接请求,客户端会随机生成一个起始序列号x,客户端向服务端发送的字段中包含标志位`SYN=1`,序列号`seq=x`。第一次握手前客户端的状态为`CLOSE`,第一次握手后客户端的状态为`SYN-SENT`。此时服务端的状态为`LISTEN`。
2. 第二次握手:服务端在收到客户端发来的报文后,会随机生成一个服务端的起始序列号y,然后给客户端回复一段报文,其中包括标志位`SYN=1`,`ACK=1`,序列号`seq=y`,确认号`ack=x+1`。第二次握手前服务端的状态为`LISTEN`,第二次握手后服务端的状态为`SYN-RCVD`,此时客户端的状态为`SYN-SENT`。(其中`SYN=1`表示要和客户端建立一个连接,`ACK=1`表示确认序号有效)
@@ -69,7 +69,7 @@
## 四次挥手
-
+
1. A的应用进程先向其TCP发出连接释放报文段(`FIN=1,seq=u`),并停止再发送数据,主动关闭TCP连接,进入`FIN-WAIT-1`(终止等待1)状态,等待B的确认。
2. B收到连接释放报文段后即发出确认报文段(`ACK=1,ack=u+1,seq=v`),B进入`CLOSE-WAIT`(关闭等待)状态,此时的TCP处于半关闭状态,A到B的连接释放。
@@ -152,7 +152,7 @@ Content-Length:112
## HTTP状态码有哪些?
-
+
## POST和GET的区别?
@@ -194,7 +194,7 @@ HTTP2.0相比HTTP1.1支持的特性:
服务端可以向证书颁发机构CA申请证书,以避免中间人攻击(防止证书被篡改)。证书包含三部分内容:**证书内容、证书签名算法和签名**,签名是为了验证身份。
-
+
服务端把证书传输给浏览器,浏览器从证书里取公钥。证书可以证明该公钥对应本网站。
@@ -216,29 +216,29 @@ HTTP2.0相比HTTP1.1支持的特性:
1. **协商加密算法** 。在`Client Hello`里面客户端会告知服务端自己当前的一些信息,包括客户端要使用的TLS版本,支持的加密算法,要访问的域名,给服务端生成的一个随机数(Nonce)等。需要提前告知服务器想要访问的域名以便服务器发送相应的域名的证书过来。
- 
+ 
2. 服务端响应`Server Hello`,告诉客户端服务端**选中的加密算法**。
- 
+ 
3. 接着服务端给客户端发来了2个证书。第二个证书是第一个证书的签发机构(CA)的证书。
- 
+ 
4. 客户端使用证书的认证机构CA公开发布的RSA公钥**对该证书进行验证**,下图表明证书认证成功。
- 
+ 
5. 验证通过之后,浏览器和服务器通过**密钥交换算法**产生共享的**对称密钥**。
- 
+ 
- 
+ 
6. 开始传输数据,使用同一个对称密钥来加解密。
- 
+ 
## DNS 的解析过程?
@@ -257,7 +257,7 @@ HTTP2.0相比HTTP1.1支持的特性:
4. 服务器**响应请求**,返回响应数据。
5. 浏览器**解析响应内容,进行渲染**,呈现给用户。
-
+
## 什么是cookie和session?
@@ -265,7 +265,7 @@ HTTP2.0相比HTTP1.1支持的特性:
由于HTTP协议是无状态的协议,需要用某种机制来识具体的用户身份,用来跟踪用户的整个会话。常用的会话跟踪技术是cookie与session。
**cookie**就是由服务器发给客户端的特殊信息,而这些信息以文本文件的方式存放在客户端,然后客户端每次向服务器发送请求的时候都会带上这些特殊的信息。说得更具体一些:当用户使用浏览器访问一个支持cookie的网站的时候,用户会提供包括用户名在内的个人信息并且提交至服务器;接着,服务器在向客户端回传相应的超文本的同时也会发回这些个人信息,当然这些信息并不是存放在HTTP响应体中的,而是存放于HTTP响应头;当客户端浏览器接收到来自服务器的响应之后,浏览器会将这些信息存放在一个统一的位置。 自此,客户端再向服务器发送请求的时候,都会把相应的cookie存放在HTTP请求头再次发回至服务器。服务器在接收到来自客户端浏览器的请求之后,就能够通过分析存放于请求头的cookie得到客户端特有的信息,从而动态生成与该客户端相对应的内容。网站的登录界面中“请记住我”这样的选项,就是通过cookie实现的。
-
+
**cookie工作流程**:
@@ -274,7 +274,7 @@ HTTP2.0相比HTTP1.1支持的特性:
3. 下次访问时,浏览器将自动携带cookie数据发送给服务器。
**session原理**:首先浏览器请求服务器访问web站点时,服务器首先会检查这个客户端请求是否已经包含了一个session标识、称为SESSIONID,如果已经包含了一个sessionid则说明以前已经为此客户端创建过session,服务器就按照sessionid把这个session检索出来使用,如果客户端请求不包含session id,则服务器为此客户端创建一个session,并且生成一个与此session相关联的独一无二的sessionid存放到cookie中,这个sessionid将在本次响应中返回到客户端保存,这样在交互的过程中,浏览器端每次请求时,都会带着这个sessionid,服务器根据这个sessionid就可以找得到对应的session。以此来达到共享数据的目的。 这里需要注意的是,session不会随着浏览器的关闭而死亡,而是等待超时时间。
-
+
## Cookie和Session的区别?
@@ -293,7 +293,7 @@ HTTP2.0相比HTTP1.1支持的特性:
TCP 利用滑动窗口实现流量控制。流量控制是为了控制发送方发送速率,保证接收方来得及接收。 TCP会话的双方都各自维护一个发送窗口和一个接收窗口。接收窗口大小取决于应用、系统、硬件的限制。发送窗口则取决于对端通告的接收窗口。接收方发送的确认报文中的window字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将接收方的确认报文window字段设置为 0,则发送方不能发送数据。
-
+
TCP头包含window字段,16bit位,它代表的是窗口的字节容量,最大为65535。这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。接收窗口的大小是约等于发送窗口的大小。
@@ -302,7 +302,7 @@ TCP头包含window字段,16bit位,它代表的是窗口的字节容量,最
防止过多的数据注入到网络中。 几种拥塞控制方法:慢开始( slow-start )、拥塞避免( congestion avoidance )、快重传( fast retransmit )和快恢复( fast recovery )。
-
+
### 慢开始
From 1afc40b4f82950c3153ab2753d0bc3a3bfaf30e1 Mon Sep 17 00:00:00 2001
From: tyson
Date: Fri, 3 Jun 2022 14:38:18 +0800
Subject: [PATCH 034/112] =?UTF-8?q?=E8=B0=83=E6=95=B4=E7=9B=AE=E5=BD=95?=
=?UTF-8?q?=E7=BB=93=E6=9E=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../Elasticsearch\345\205\245\351\227\250.md" | 2438 +++++++++++++++++
...is\351\235\242\350\257\225\351\242\230.md" | 2 +
.../\345\256\236\346\210\230\347\257\207.md" | 93 +
...PC\347\232\204\345\216\237\347\220\206.md" | 49 +
...\345\261\200\345\224\257\344\270\200ID.md" | 485 ++++
...03\345\274\217\344\272\213\345\212\241.md" | 77 +
...03\345\274\217\347\274\223\345\255\230.md" | 98 +
...06\345\270\203\345\274\217\351\224\201.md" | 253 ++
.../\345\276\256\346\234\215\345\212\241.md" | 194 ++
...34\347\250\213\350\260\203\347\224\250.md" | 119 +
.../MySQL\345\237\272\347\241\200.md" | 12 +-
...21\351\235\242\350\257\225\351\242\230.md" | 120 +-
...15\347\275\256\344\270\255\345\277\203.md" | 44 +
...15\345\212\241\345\256\236\346\210\230.md" | 2 +-
...72\351\253\230\351\242\221\350\257\215.md" | 65 +
...1\253\230\351\242\221\350\257\215 - wx.md" | 66 +
...65\350\257\235\345\217\267\347\240\201.md" | 82 +
...73\345\275\225\345\216\237\347\220\206.md" | 85 +
...73\347\273\237\350\256\276\350\256\241.md" | 176 ++
...347\273\237\350\256\276\350\256\241old.md" | 24 +
...37\351\235\242\350\257\225\351\242\230.md" | 0
...43\347\240\201\345\256\236\347\216\260.md" | 0
...43\347\240\201\345\256\236\347\216\260.md" | 0
...60\346\215\256\347\273\223\346\236\204.md" | 0
.../\347\256\227\346\263\225.md" | 169 ++
.../TCP IP\345\215\217\350\256\256.md" | 0
.../session\345\222\214cookie.md" | 0
.../\345\233\276\350\247\243HTTP.md" | 0
.../\347\275\221\347\273\234.md" | 0
...21\351\235\242\350\257\225\351\242\230.md" | 0
30 files changed, 4624 insertions(+), 29 deletions(-)
create mode 100644 "\344\270\255\351\227\264\344\273\266/Elasticsearch\345\205\245\351\227\250.md"
create mode 100644 "\345\205\266\344\273\226/\345\256\236\346\210\230\347\257\207.md"
create mode 100644 "\345\210\206\345\270\203\345\274\217/RocketMQ\345\256\236\347\216\260RPC\347\232\204\345\216\237\347\220\206.md"
create mode 100644 "\345\210\206\345\270\203\345\274\217/\345\205\250\345\261\200\345\224\257\344\270\200ID.md"
create mode 100644 "\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md"
create mode 100644 "\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\347\274\223\345\255\230.md"
create mode 100644 "\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\351\224\201.md"
create mode 100644 "\345\210\206\345\270\203\345\274\217/\345\276\256\346\234\215\345\212\241.md"
create mode 100644 "\345\210\206\345\270\203\345\274\217/\350\277\234\347\250\213\350\260\203\347\224\250.md"
create mode 100644 "\346\241\206\346\236\266/Apollo\351\205\215\347\275\256\344\270\255\345\277\203.md"
create mode 100644 "\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\343\200\220\344\274\230\345\214\226\347\211\210\343\200\221\345\246\202\344\275\225\344\273\216\346\265\267\351\207\217\346\225\260\346\215\256\346\211\276\345\207\272\351\253\230\351\242\221\350\257\215.md"
create mode 100644 "\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\345\246\202\344\275\225\344\273\216\346\265\267\351\207\217\346\225\260\346\215\256\346\211\276\345\207\272\351\253\230\351\242\221\350\257\215 - wx.md"
create mode 100644 "\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\347\224\265\350\257\235\345\217\267\347\240\201.md"
create mode 100644 "\347\263\273\347\273\237\350\256\276\350\256\241/\346\211\253\347\240\201\347\231\273\345\275\225\345\216\237\347\220\206.md"
create mode 100644 "\347\263\273\347\273\237\350\256\276\350\256\241/\347\263\273\347\273\237\350\256\276\350\256\241.md"
create mode 100644 "\347\263\273\347\273\237\350\256\276\350\256\241/\347\263\273\347\273\237\350\256\276\350\256\241old.md"
rename "\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md" => "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md" (100%)
rename "\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\344\272\214\345\217\211\346\240\221\345\211\215\345\272\217\343\200\201\344\270\255\345\272\217\343\200\201\345\220\216\345\272\217\343\200\201\345\261\202\345\272\217\351\201\215\345\216\206\344\273\243\347\240\201\345\256\236\347\216\260.md" => "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\344\272\214\345\217\211\346\240\221\345\211\215\345\272\217\343\200\201\344\270\255\345\272\217\343\200\201\345\220\216\345\272\217\343\200\201\345\261\202\345\272\217\351\201\215\345\216\206\344\273\243\347\240\201\345\256\236\347\216\260.md" (100%)
rename "\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md" => "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md" (100%)
rename "\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\225\260\346\215\256\347\273\223\346\236\204.md" => "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\225\260\346\215\256\347\273\223\346\236\204.md" (100%)
create mode 100644 "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\347\256\227\346\263\225.md"
rename "\347\275\221\347\273\234/TCP IP\345\215\217\350\256\256.md" => "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/TCP IP\345\215\217\350\256\256.md" (100%)
rename "\347\275\221\347\273\234/session\345\222\214cookie.md" => "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/session\345\222\214cookie.md" (100%)
rename "\347\275\221\347\273\234/\345\233\276\350\247\243HTTP.md" => "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\345\233\276\350\247\243HTTP.md" (100%)
rename "\347\275\221\347\273\234/\347\275\221\347\273\234.md" => "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\347\275\221\347\273\234.md" (100%)
rename "\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" => "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" (100%)
diff --git "a/\344\270\255\351\227\264\344\273\266/Elasticsearch\345\205\245\351\227\250.md" "b/\344\270\255\351\227\264\344\273\266/Elasticsearch\345\205\245\351\227\250.md"
new file mode 100644
index 0000000..7e2b094
--- /dev/null
+++ "b/\344\270\255\351\227\264\344\273\266/Elasticsearch\345\205\245\351\227\250.md"
@@ -0,0 +1,2438 @@
+
+
+
+
+- [基础](#%E5%9F%BA%E7%A1%80)
+ - [概念](#%E6%A6%82%E5%BF%B5)
+ - [启动和关闭](#%E5%90%AF%E5%8A%A8%E5%92%8C%E5%85%B3%E9%97%AD)
+ - [配置文件](#%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6)
+ - [PUT](#put)
+ - [GET](#get)
+ - [全文搜索](#%E5%85%A8%E6%96%87%E6%90%9C%E7%B4%A2)
+ - [高亮搜索](#%E9%AB%98%E4%BA%AE%E6%90%9C%E7%B4%A2)
+ - [分析](#%E5%88%86%E6%9E%90)
+ - [分布式特性](#%E5%88%86%E5%B8%83%E5%BC%8F%E7%89%B9%E6%80%A7)
+- [集群原理](#%E9%9B%86%E7%BE%A4%E5%8E%9F%E7%90%86)
+ - [术语](#%E6%9C%AF%E8%AF%AD)
+ - [节点](#%E8%8A%82%E7%82%B9)
+ - [分片](#%E5%88%86%E7%89%87)
+ - [集群健康](#%E9%9B%86%E7%BE%A4%E5%81%A5%E5%BA%B7)
+ - [索引](#%E7%B4%A2%E5%BC%95)
+ - [故障转移](#%E6%95%85%E9%9A%9C%E8%BD%AC%E7%A7%BB)
+ - [单机多节点](#%E5%8D%95%E6%9C%BA%E5%A4%9A%E8%8A%82%E7%82%B9)
+- [数据输入与输出](#%E6%95%B0%E6%8D%AE%E8%BE%93%E5%85%A5%E4%B8%8E%E8%BE%93%E5%87%BA)
+ - [文档元数据](#%E6%96%87%E6%A1%A3%E5%85%83%E6%95%B0%E6%8D%AE)
+ - [创建新文档](#%E5%88%9B%E5%BB%BA%E6%96%B0%E6%96%87%E6%A1%A3)
+ - [取回文档](#%E5%8F%96%E5%9B%9E%E6%96%87%E6%A1%A3)
+ - [取回多个文档](#%E5%8F%96%E5%9B%9E%E5%A4%9A%E4%B8%AA%E6%96%87%E6%A1%A3)
+ - [删除文档](#%E5%88%A0%E9%99%A4%E6%96%87%E6%A1%A3)
+ - [检查文档是否存在](#%E6%A3%80%E6%9F%A5%E6%96%87%E6%A1%A3%E6%98%AF%E5%90%A6%E5%AD%98%E5%9C%A8)
+ - [更新文档](#%E6%9B%B4%E6%96%B0%E6%96%87%E6%A1%A3)
+ - [更新和冲突](#%E6%9B%B4%E6%96%B0%E5%92%8C%E5%86%B2%E7%AA%81)
+ - [批量操作](#%E6%89%B9%E9%87%8F%E6%93%8D%E4%BD%9C)
+- [分布式文档存储](#%E5%88%86%E5%B8%83%E5%BC%8F%E6%96%87%E6%A1%A3%E5%AD%98%E5%82%A8)
+ - [路由文档到分片](#%E8%B7%AF%E7%94%B1%E6%96%87%E6%A1%A3%E5%88%B0%E5%88%86%E7%89%87)
+- [映射和分析](#%E6%98%A0%E5%B0%84%E5%92%8C%E5%88%86%E6%9E%90)
+ - [核心简单域类型](#%E6%A0%B8%E5%BF%83%E7%AE%80%E5%8D%95%E5%9F%9F%E7%B1%BB%E5%9E%8B)
+ - [查看映射](#%E6%9F%A5%E7%9C%8B%E6%98%A0%E5%B0%84)
+ - [自定义映射器](#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%98%A0%E5%B0%84%E5%99%A8)
+ - [index](#index)
+ - [analyzer](#analyzer)
+ - [更新映射](#%E6%9B%B4%E6%96%B0%E6%98%A0%E5%B0%84)
+ - [测试映射](#%E6%B5%8B%E8%AF%95%E6%98%A0%E5%B0%84)
+ - [分析器](#%E5%88%86%E6%9E%90%E5%99%A8)
+ - [测试分析器](#%E6%B5%8B%E8%AF%95%E5%88%86%E6%9E%90%E5%99%A8)
+ - [复杂核心域类型](#%E5%A4%8D%E6%9D%82%E6%A0%B8%E5%BF%83%E5%9F%9F%E7%B1%BB%E5%9E%8B)
+ - [多值域](#%E5%A4%9A%E5%80%BC%E5%9F%9F)
+ - [内部对象](#%E5%86%85%E9%83%A8%E5%AF%B9%E8%B1%A1)
+- [搜索](#%E6%90%9C%E7%B4%A2)
+ - [空搜索](#%E7%A9%BA%E6%90%9C%E7%B4%A2)
+ - [多索引多类型](#%E5%A4%9A%E7%B4%A2%E5%BC%95%E5%A4%9A%E7%B1%BB%E5%9E%8B)
+ - [轻量搜索](#%E8%BD%BB%E9%87%8F%E6%90%9C%E7%B4%A2)
+- [请求体查询](#%E8%AF%B7%E6%B1%82%E4%BD%93%E6%9F%A5%E8%AF%A2)
+ - [空查询](#%E7%A9%BA%E6%9F%A5%E8%AF%A2)
+ - [提升权重](#%E6%8F%90%E5%8D%87%E6%9D%83%E9%87%8D)
+ - [explain](#explain)
+ - [查询和过滤器的区别](#%E6%9F%A5%E8%AF%A2%E5%92%8C%E8%BF%87%E6%BB%A4%E5%99%A8%E7%9A%84%E5%8C%BA%E5%88%AB)
+- [排序与相关性](#%E6%8E%92%E5%BA%8F%E4%B8%8E%E7%9B%B8%E5%85%B3%E6%80%A7)
+ - [按照字段的值排序](#%E6%8C%89%E7%85%A7%E5%AD%97%E6%AE%B5%E7%9A%84%E5%80%BC%E6%8E%92%E5%BA%8F)
+ - [多级排序](#%E5%A4%9A%E7%BA%A7%E6%8E%92%E5%BA%8F)
+ - [多值字段的排序](#%E5%A4%9A%E5%80%BC%E5%AD%97%E6%AE%B5%E7%9A%84%E6%8E%92%E5%BA%8F)
+ - [字符串排序](#%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%8E%92%E5%BA%8F)
+- [索引管理](#%E7%B4%A2%E5%BC%95%E7%AE%A1%E7%90%86)
+ - [创建索引](#%E5%88%9B%E5%BB%BA%E7%B4%A2%E5%BC%95)
+ - [删除索引](#%E5%88%A0%E9%99%A4%E7%B4%A2%E5%BC%95)
+ - [索引设置](#%E7%B4%A2%E5%BC%95%E8%AE%BE%E7%BD%AE)
+ - [配置分析器](#%E9%85%8D%E7%BD%AE%E5%88%86%E6%9E%90%E5%99%A8)
+ - [自定义分析器](#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%88%86%E6%9E%90%E5%99%A8)
+ - [类型和映射](#%E7%B1%BB%E5%9E%8B%E5%92%8C%E6%98%A0%E5%B0%84)
+ - [根对象](#%E6%A0%B9%E5%AF%B9%E8%B1%A1)
+- [分片内部原理](#%E5%88%86%E7%89%87%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86)
+ - [倒排索引](#%E5%80%92%E6%8E%92%E7%B4%A2%E5%BC%95)
+- [深入搜索](#%E6%B7%B1%E5%85%A5%E6%90%9C%E7%B4%A2)
+ - [精确值查找](#%E7%B2%BE%E7%A1%AE%E5%80%BC%E6%9F%A5%E6%89%BE)
+ - [term 查询文本](#term-%E6%9F%A5%E8%AF%A2%E6%96%87%E6%9C%AC)
+ - [terms 查询](#terms-%E6%9F%A5%E8%AF%A2)
+ - [全文搜索](#%E5%85%A8%E6%96%87%E6%90%9C%E7%B4%A2-1)
+ - [match 查询](#match-%E6%9F%A5%E8%AF%A2)
+ - [range 查询](#range-%E6%9F%A5%E8%AF%A2)
+ - [数字范围](#%E6%95%B0%E5%AD%97%E8%8C%83%E5%9B%B4)
+ - [日期范围](#%E6%97%A5%E6%9C%9F%E8%8C%83%E5%9B%B4)
+ - [分页](#%E5%88%86%E9%A1%B5)
+ - [exists 查询](#exists-%E6%9F%A5%E8%AF%A2)
+ - [bool 组合查询](#bool-%E7%BB%84%E5%90%88%E6%9F%A5%E8%AF%A2)
+ - [如何使用 bool 查询](#%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8-bool-%E6%9F%A5%E8%AF%A2)
+ - [constant_score 查询](#constant_score-%E6%9F%A5%E8%AF%A2)
+ - [多字段搜索](#%E5%A4%9A%E5%AD%97%E6%AE%B5%E6%90%9C%E7%B4%A2)
+ - [多字符串查询](#%E5%A4%9A%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%9F%A5%E8%AF%A2)
+ - [multi_match 查询](#multi_match-%E6%9F%A5%E8%AF%A2)
+ - [多字段映射](#%E5%A4%9A%E5%AD%97%E6%AE%B5%E6%98%A0%E5%B0%84)
+ - [copy_to 定制组合 field](#copy_to-%E5%AE%9A%E5%88%B6%E7%BB%84%E5%90%88-field)
+- [springboot 集成 es](#springboot-%E9%9B%86%E6%88%90-es)
+- [mall](#mall)
+
+
+
+[Elasticsearch 权威指南](https://www.elastic.co/guide/cn/elasticsearch/guide/current/_talking_to_elasticsearch.html)
+
+## 基础
+
+Elasticsearch 是一个开源的搜索引擎,建立在全文搜索引擎库 lucene 基础之上。Elasticsearch 也是使用 Java 编写的,它的内部使用 Lucene 做索引与搜索,但是它的目的是使全文检索变得简单, 通过隐藏 Lucene 的复杂性,取而代之的提供一套简单一致的 RESTful API。
+
+ES与mysql的对应关系:
+
+- index –> DB
+- type –> Table
+- Document –> row
+
+### 概念
+
+[参考--阮一峰es教程](http://www.ruanyifeng.com/blog/2017/08/elasticsearch.html)
+
+1. node 和 cluster
+
+ Elastic 本质上是一个分布式数据库,允许多台服务器协同工作,每台服务器可以运行多个 Elastic 实例。单个 Elastic 实例称为一个节点(node)。一组节点构成一个集群(cluster)。
+
+2. Index
+
+ 数据库的同义词。每个 Index (即数据库)的名字必须是小写。查看当前节点的所有index:`GET _cat/indices`
+
+3. Type
+
+ 类似于表结构。不同的 Type 应该有相似的结构(schema),举例来说,`id`字段不能在这个组是字符串,在另一个组是数值。这是与关系型数据库的表的区别。性质完全不同的数据(比如`products`和`logs`)应该存成两个 Index,而不是一个 Index 里面的两个 Type。
+
+4. Document
+
+ 单条的记录称为 Document。Document 可以分组,比如`weather`这个 Index 里面,可以按城市分组(北京和上海),这种分组就是 Type。同一个 Index 里面的 Document,不要求有相同的结构(scheme),但是最好保持相同,这样有利于提高搜索效率。
+
+
+### 启动和关闭
+
+不用安装,解压即可。
+
+启动:`bin\elasticsearch.bat`
+
+关闭:ctrl+c/`curl -XPOST http://localhost:9200/_cluster/nodes/_shutdown `关掉整个集群
+
+启动 head 插件:到 head 安装目录下运行`grunt server`
+
+### 配置文件
+
+安装目录config下的 elasticsearch.yml 可以配置集群的信息,如cluster.name 和 node.name。
+
+```yaml
+# ---------------------------------- MyConfig ----------------------------------
+#cluster.name: xxx
+node.name: node-2
+#指明该节点可以是主节点,也可以是数据节点
+node.master: true
+node.data: true
+network.host: 127.0.0.1
+http.port: 9200
+transport.tcp.port: 9300
+```
+
+http.port 是elasticsearch对外提供服务的http端口配置。
+
+transport.tcp.port 指定了elasticsearch集群内数据通讯使用的端口,默认情况下为9300。
+
+
+### PUT
+
+```json
+PUT /company/employee/1
+{
+ "first_name" : "Tyson",
+ "last_name" : "dai",
+ "age" : 23,
+ "interests" : ["sport", "music"],
+ "hire_date" : "2014-01-01"
+}
+```
+
+company:索引名称,employee:类型名称,1是 ID。
+
+### GET
+
+```json
+GET /company/employee/1
+```
+
+搜索所有雇员:
+
+```json
+GET /company/employee/_search
+```
+
+搜索结果如下,雇员数据放在 hits 数组中:
+
+```json
+{
+ "took": 413,
+ "timed_out": false,
+ "_shards": {
+ "total": 5,
+ "successful": 5,
+ "skipped": 0,
+ "failed": 0
+ },
+ "hits": {
+ "total": 2,
+ "max_score": 1,
+ "hits": [
+ {
+ "_index": "company",
+ "_type": "employee",
+ "_id": "2",
+ "_score": 1,
+ "_source": {
+ "first_name": "Lily",
+ "last_name": "dai",
+ "age": 23,
+ "interests": [
+ "sport",
+ "music"
+ ]
+ }
+ },
+ {
+ "_index": "company",
+ "_type": "employee",
+ "_id": "1",
+ "_score": 1,
+ "_source": {
+ "first_name": "Tyson",
+ "last_name": "dai",
+ "age": 23,
+ "interests": [
+ "sport",
+ "music"
+ ]
+ }
+ }
+ ]
+ }
+}
+```
+
+按特定条件搜索:
+
+```json
+GET /company/employee/_search?q=first_name:Tyson
+```
+
+使用查询表达式搜索:
+
+```json
+GET /company/employee/_search
+{
+ "query": {
+ "match": {
+ "first_name": "Tyson"
+ }
+ }
+}
+```
+
+复杂查询:
+
+姓氏为 Dai的雇员,但这次我们只需要年龄大于 20 的。
+
+```json
+GET /company/employee/_search
+{
+ "query": {
+ "bool" : {
+ "must" : {
+ "match" : {
+ "last_name" : "dai"
+ }
+ },
+ "filter": {
+ "range": {
+ "age": {"gt" : 20}
+ }
+ }
+ }
+ }
+}
+```
+
+### 全文搜索
+
+传统数据库确实很难搞定的任务。
+
+```json
+GET /company/employee/_search
+{
+ "query": {
+ "match": {
+ "about": "rock climbing"
+ }
+ }
+}
+```
+
+返回`"about": "rock climbing"`和`"about": "rock albums"`两条记录,默认按照每个文档跟查询的匹配程度排序。
+
+### 高亮搜索
+
+```json
+GET /company/employee/_search
+{
+ "query": {
+ "match_phrase": {
+ "about": "climbing"
+ }
+ },
+ "highlight": {
+ "fields": {
+ "about": {}
+ }
+ }
+}
+```
+
+返回结果多 `highlight` 的部分。这个部分包含了 `about` 属性匹配的文本片段,并以 HTML 标签 `` 封装。
+
+```json
+{
+ "took": 43,
+ "timed_out": false,
+ "_shards": {
+ "total": 5,
+ "successful": 5,
+ "skipped": 0,
+ "failed": 0
+ },
+ "hits": {
+ "total": 1,
+ "max_score": 0.2876821,
+ "hits": [
+ {
+ "_index": "company",
+ "_type": "employee",
+ "_id": "3",
+ "_score": 0.2876821,
+ "_source": {
+ "first_name": "Tyson",
+ "last_name": "dai",
+ "age": 23,
+ "interests": [
+ "sport",
+ "music"
+ ],
+ "about": "rock climbing"
+ },
+ "highlight": {
+ "about": [
+ "rock climbing"
+ ]
+ }
+ }
+ ]
+ }
+}
+```
+
+### 分析
+
+Elasticsearch 有一个功能叫聚合(aggregations),允许我们基于数据生成一些精细的分析结果。聚合与 SQL 中的 `GROUP BY` 类似但更强大。
+
+```json
+GET /company/employee/_search
+{
+ "aggs": {
+ "all_interests": {
+ "terms": { "field": "interests" }
+ }
+ }
+}
+```
+
+直接执行上面的代码会报错,原因是5.x后对排序,聚合这些操作用单独的数据结构(fielddata)缓存到内存里了,需要单独开启。
+
+```json
+PUT company/_mapping/employee/
+{
+ "properties": {
+ "interests": {
+ "type": "text",
+ "fielddata": true
+ }
+ }
+}
+```
+
+搜索结果:
+
+```json
+{
+ ...
+ "hits": { ... },
+ "aggregations": {
+ "all_interests": {
+ "doc_count_error_upper_bound": 0,
+ "sum_other_doc_count": 0,
+ "buckets": [
+ {
+ "key": "music",
+ "doc_count": 4
+ },
+ {
+ "key": "sport",
+ "doc_count": 4
+ }
+ ]
+ }
+ }
+}
+```
+
+如果想知道叫 Tyson的雇员中最受欢迎的兴趣爱好,可以直接添加适当的查询来组合查询:
+
+```json
+GET company/employee/_search
+{
+ "query": {
+ "match": {
+ "first_name": "Tyson"
+ }
+ },
+ "aggs": {
+ "all_interests": {
+ "terms": {
+ "field": "interests"
+ }
+ }
+ }
+}
+```
+
+搜索结果:
+
+```json
+{
+ "took": 33,
+ "timed_out": false,
+ "_shards": {
+ "total": 5,
+ "successful": 5,
+ "skipped": 0,
+ "failed": 0
+ },
+ "hits": {
+ "total": 1,
+ "max_score": 0.2876821,
+ "hits": [
+ {
+ "_index": "company",
+ "_type": "employee",
+ "_id": "3",
+ "_score": 0.2876821,
+ "_source": {
+ "first_name": "Tyson",
+ "last_name": "dai",
+ "age": 23,
+ "interests": [
+ "sport",
+ "music"
+ ],
+ "about": "rock climbing"
+ }
+ }
+ ]
+ },
+ "aggregations": {
+ "all_interests": {
+ "doc_count_error_upper_bound": 0,
+ "sum_other_doc_count": 0,
+ "buckets": [
+ {
+ "key": "music",
+ "doc_count": 1
+ },
+ {
+ "key": "sport",
+ "doc_count": 1
+ }
+ ]
+ }
+ }
+}
+```
+
+聚合还支持分级汇总 。比如,查询特定兴趣爱好员工的平均年龄:
+
+```java
+GET company/employee/_search
+{
+ "aggs": {
+ "all_interests": {
+ "terms": {
+ "field": "interests"
+ },
+ "aggs": {
+ "avg_age": {
+ "avg": {
+ "field": "age"
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+搜索结果:
+
+```json
+"aggregations": {
+ "all_interests": {
+ "doc_count_error_upper_bound": 0,
+ "sum_other_doc_count": 0,
+ "buckets": [
+ {
+ "key": "music",
+ "doc_count": 4,
+ "avg_age": {
+ "value": 18.5
+ }
+ },
+ {
+ "key": "sport",
+ "doc_count": 4,
+ "avg_age": {
+ "value": 18.5
+ }
+ }
+ ]
+ }
+ }
+```
+
+### 分布式特性
+
+Elasticsearch 可以横向扩展至数百(甚至数千)的服务器节点,同时可以处理PB级数据。
+
+## 集群原理
+
+ElasticSearch 的主旨是随时可用和按需扩容。扩容可以通过购买性能更强大( *垂直扩容* ) 或者数量更多的服务器( *水平扩容* )来实现。
+
+### 术语
+
+#### 节点
+
+一个运行中的 Elasticsearch 实例称为节点,而集群是由一个或者多个拥有相同 `cluster.name` 配置的节点组成, 它们共同承担数据和负载的压力。当有节点加入集群中或者从集群中移除节点时,集群将会重新平均分布所有的数据。
+
+当一个节点被选举成为主节点时, 它将负责管理集群范围内的所有变更,例如增加、删除索引,或者增加、删除节点等。 而主节点并不需要涉及到文档级别的变更和搜索等操作,所以当集群只拥有一个主节点的情况下,即使流量的增加它也不会成为瓶颈。 任何节点都可以成为主节点。
+
+我们可以将请求发送到集群中的任何节点,包括主节点。 每个节点都知道任意文档所处的位置,并且能够将我们的请求直接转发到存储我们所需文档的节点。 无论我们将请求发送到哪个节点,它都能负责从各个包含我们所需文档的节点收集回数据,并将最终结果返回給客户端。 Elasticsearch 对这一切的管理都是透明的。
+
+获取节点信息:`http://localhost:9200/_cluster/state/nodes?pretty`,pretty 用于换行。
+
+#### 分片
+
+每个节点可以分配一个或多个分片。分片是数据的容器,文档保存在分片内,分片又被分配到集群内的各个节点里。 当你的集群规模扩大或者缩小时, Elasticsearch 会自动的在各节点中迁移分片,使得数据仍然均匀分布在集群里。
+
+一个分片可以是 *主* 分片或者 *副本* 分片。 索引内任意一个文档都归属于一个主分片,所以主分片的数目决定着索引能够保存的最大数据量。
+
+一个副本分片只是一个主分片的拷贝。 副本分片作为硬件故障时保护数据不丢失的冗余备份,并为搜索和返回文档等读操作提供服务。
+
+### 集群健康
+
+`GET /_cluster/health`
+
+返回内容:
+
+```json
+{
+ "cluster_name": "elasticsearch",
+ "status": "yellow",
+ "timed_out": false,
+ "number_of_nodes": 1,
+ "number_of_data_nodes": 1,
+ "active_primary_shards": 6,
+ "active_shards": 6,
+ "relocating_shards": 0,
+ "initializing_shards": 0,
+ "unassigned_shards": 5,
+ "delayed_unassigned_shards": 0,
+ "number_of_pending_tasks": 0,
+ "number_of_in_flight_fetch": 0,
+ "task_max_waiting_in_queue_millis": 0,
+ "active_shards_percent_as_number": 54.54545454545454
+}
+```
+
+status 字段表示当前集群总体是否正常,它有三个值:
+
+1. green,所有的主分片和副本分片都正常运行。
+
+2. yellow,所有的主分片都正常运行,但不是所有的副本分片都正常运行。
+3. red,有主分片不能正常运行。
+
+### 索引
+
+往 elasticsearch 添加数据时需要用到索引,索引是指向一个或多个分片的逻辑命名空间。一个分片是一个 Lucene 的实例,以及它本身就是一个完整的搜索引擎。
+
+当我们只开启一个节点时,索引在默认情况下会被分配5个主分片,每个主分片拥有一个副本分片。主分片会被分配到这个节点上,而副本分片不会被分配到任何节点。
+
+```json
+{
+ "cluster_name": "elasticsearch",
+ "status": "yellow",
+ ...
+ "unassigned_shards": 5,//5个副本分片都是 unassigned,都没有被分配到任何节点
+ ...
+}
+```
+
+当前我们的集群是正常运行的,但是在硬件故障时有丢失数据的风险。
+
+### 故障转移
+
+可以在同一个目录下开启另一个节点,副本分片会被分配到这个节点上,此时计算有硬件故障也不会丢失数据。
+
+### 单机多节点
+
+node1 的 elasticsearch.yml 做如下配置:
+
+```yaml
+# ---------------------------------- MyConfig ----------------------------------
+#cluster.name: xxx
+node.name: node-1
+#指明该节点可以是主节点,也可以是数据节点
+#node.master: true
+#node.data: true
+network.host: 127.0.0.1
+http.port: 9200
+transport.tcp.port: 9300
+discovery.zen.ping.unicast.hosts: ["127.0.0.1:9300", "127.0.0.1:9301", "127.0.0.1:9302"]
+
+# 解决elasticsearch-head 集群健康值: 未连接问题
+http.cors.enabled: true
+http.cors.allow-origin: "*"
+```
+
+node2 的 elasticsearch.yml 做如下配置:
+
+```yaml
+# ---------------------------------- MyConfig ----------------------------------
+#cluster.name: xxx
+node.name: node-2
+#指明该节点可以是主节点,也可以是数据节点
+#node.master: true
+#node.data: true
+network.host: 127.0.0.1
+http.port: 9201
+transport.tcp.port: 9301
+discovery.zen.ping.unicast.hosts: ["127.0.0.1:9300", "127.0.0.1:9301", "127.0.0.1:9302"]
+
+# 解决elasticsearch-head 集群健康值: 未连接问题
+http.cors.enabled: true
+http.cors.allow-origin: "*"
+```
+
+同理 node3 也做好配置。然后重启 elasticsearch 节点,就可以实现单机多节点集群搭建。
+
+
+
+## 数据输入与输出
+
+### 文档元数据
+
+一个文档不仅包含数据,也包含元数据。三个必须的元数据元素如下:\_index、\_type和_id。
+
+索引名字必须小写,不能以下划线开头,不能包含逗号。
+
+\_type在索引中对数据进行逻辑分区,如产品下面还可以分为很多子类。一个 `_type` 命名可以是大写或者小写,但是不能以下划线或者句号开头,不应该包含逗号, 并且长度限制为256个字符。
+
+### 创建新文档
+
+使用 PUT 请求,需要定义 _id:
+
+```json
+PUT /{index}/{type}/{id}
+{
+ "field": "value",
+ ...
+}
+```
+
+id 为2的文档存在时会报错: document already exists
+
+```json
+PUT company/employee/2/_create
+{
+ "first_name" : "Tyson",
+ "last_name" : "dai",
+ "age" : 23,
+ "interests" : ["sport", "music"]
+}
+```
+
+不存在时才创建:
+
+```json
+PUT company/employee/2?op_type=create
+{
+ "first_name" : "Tyson",
+ "last_name" : "dai",
+ "age" : 23,
+ "interests" : ["sport", "music"]
+}
+```
+
+使用 `POST` 可以让 Elasticsearch 自动生成唯一 的_id。
+
+```json
+POST company/employee
+{
+ "first_name" : "Tyson",
+ "last_name" : "dai",
+ "age" : 23,
+ "interests" : ["sport", "music"]
+}
+```
+
+### 取回文档
+
+返回文档的一部分:
+
+```json
+GET /company/employee/1?_source=first_name,interests
+```
+
+只得到_source 字段(即id为1的整个文档):
+
+```json
+GET /company/employee/1/_source
+```
+
+
+### 取回多个文档
+
+mget api 要求传入一个 docs 数组作为参数,可以通过 _source 指定返回字段。
+
+```json
+GET /_mget
+{
+ "docs": [
+ {
+ "_index": "company",
+ "_type": "employee",
+ "_id": 2
+ },
+ {
+ "_index": "class",
+ "_type": "student",
+ "_id": 1,
+ "_source": "about"
+ }
+ ]
+}
+```
+
+如果获取的文档 _index 和 _type 相同,可以在 url 指定默认的 _index 和 _type,传入一个 ids 数组。
+
+```json
+GET /company/employee/_mget
+{
+ "ids": ["1", "2"]
+}
+```
+
+通过单独请求可以覆盖默认的 _index 和 _type。
+
+```json
+GET /company/employee/_mget
+{
+ "docs" : [
+ { "_id" : 2 },
+ { "_index" : "class", "_type" : "student", "_id" : 1 }
+ ]
+}
+```
+
+### 删除文档
+
+`DELETE company/employee/1`,删除文档,版本号会增加。
+
+```json
+{
+ "_index": "company",
+ "_type": "employee",
+ "_id": "1",
+ "_version": 2,
+ "result": "deleted",
+ "_shards": {
+ "total": 2,
+ "successful": 1,
+ "failed": 0
+ },
+ "_seq_no": 1,
+ "_primary_term": 4
+}
+```
+
+### 检查文档是否存在
+
+`HEAD /company/employee/1`
+
+返回结果:`200 - OK`
+
+### 更新文档
+
+文档是不可变的,不能被修改,只能被替换。 `update` API 必须遵循同样的规则。 从外部来看,我们在一个文档的某个位置进行部分更新。然而在内部, `update` API 简单使用 *检索-修改-重建索引* 的处理过程。
+
+```json
+POST company/employee/1/_update
+{
+ "doc": {
+ "first_name": "sophia",
+ "interests": ["sport", "chess"]
+ }
+}
+```
+
+#### 更新和冲突
+
+```json
+POST company/employee/1/_update?retry_on_conflict=5
+{
+ "doc": {
+ "first_name": "sophia",
+ "interests": ["sport", "chess"]
+ }
+}
+```
+
+### 批量操作
+
+语法:
+
+```json
+{ action: { metadata }}\n
+{ request body }\n
+{ action: { metadata }}\n
+{ request body }\n
+...
+```
+
+action 有 create、delete、index 和 update。metadata 定义了\_index,\_type,_id 等信息。
+
+create:如果文档不存在,那么就创建它。index:创建一个新文档或者替换一个现有的文档。create 和 index 不指定 `_id`的话 ,将会自动生成一个 ID 。
+
+```json
+POST /_bulk
+{ "delete": { "_index": "website", "_type": "blog", "_id": "123" }}
+{ "create": { "_index": "website", "_type": "blog", "_id": "123" }}
+{ "title": "My first blog post" }
+{ "index": { "_index": "website", "_type": "blog" }}
+{ "title": "My second blog post" }
+{ "update": { "_index": "website", "_type": "blog", "_id": "123", "_retry_on_conflict" : 3} }
+{ "doc" : {"title" : "My updated blog post"} }
+```
+
+bulk 请求不是原子的,不能用它来实现事务控制。每个请求是单独处理的,因此一个请求的成功或失败不会影响其他的请求。
+
+**默认的 index 和 type**
+
+```json
+POST /website/_bulk
+{ "index": { "_type": "log" }}
+{ "event": "User logged in" }
+```
+
+
+
+## 分布式文档存储
+
+### 路由文档到分片
+
+文档所在分片的位置通过这个公式计算:`shard = hash(routing) % number_of_primary_shards`,rounting 默认是文档的 _id,可以设置成自定义的值。创建索引的时候就确定好主分片的数量,并且永远不会改变这个数量:因为如果数量变化了,那么所有之前路由的值都会无效,文档也再也找不到了。
+
+
+
+
+## 映射和分析
+
+映射:为了能够将时间域视为时间,数字域视为数字,字符串域视为全文或精确值字符串, Elasticsearch 需要知道每个域中数据的类型。这个信息包含在映射中。
+
+分析:将一块文本分成适合于倒排索引的独立的词条,然后将这些词条统一化为标准格式以提高它们的可搜索性。
+
+### 核心简单域类型
+
+Elasticsearch 支持 如下简单域类型:
+
+- 字符串: `string`
+- 整数 : `byte`, `short`, `integer`, `long`
+- 浮点数: `float`, `double`
+- 布尔型: `boolean`
+- 日期: `date`
+
+当你索引一个新的文档,elasticsearch 会使用动态映射,通过 json 中的基本数据类型,尝试猜测域的类型。
+
+| **JSON type** | **域 type** |
+| ------------------------------ | ----------- |
+| 布尔型: `true` 或者 `false` | `boolean` |
+| 整数: `123` | `long` |
+| 浮点数: `123.45` | `double` |
+| 字符串,有效日期: `2014-09-15` | `date` |
+| 字符串: `foo bar` | `string` |
+
+如果你通过引号( `"123"` )索引一个数字,它会被映射为 `string` 类型,而不是 `long` 。但是,如果这个域已经映射为 `long` ,那么 Elasticsearch 会尝试将这个字符串转化为 long ,如果无法转化,则抛出一个异常。
+
+### 查看映射
+
+`GET /company/_mapping/employee`
+
+返回结果:
+
+```json
+{
+ "company": {
+ "mappings": {
+ "employee": {
+ "properties": {
+ "age": {
+ "type": "long"
+ },
+ "first_name": {
+ "type": "text",
+ "fields": {
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 256
+ }
+ }
+ },
+ "hire_date": {
+ "type": "date"
+ },
+ "interests": {
+ "type": "text",
+ "fields": {
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 256
+ }
+ }
+ },
+ "last_name": {
+ "type": "text",
+ "fields": {
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 256
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+### 自定义映射器
+
+`string` 类型域会被认为包含全文。它们的值在索引前,会通过 一个分析器,针对于这个域的查询在搜索前也会经过一个分析器。string 域映射的两个最重要的属性是 index 和 analyzer。
+
+#### index
+
+`index` 属性控制怎样索引字符串。它有三个值:
+
+- analyzed:默认,首先分析这个域,然后索引它;
+- not_analyzed:索引这个域,索引的是精确值,不会对它进行分析;
+- no:不要索引这个域,这个域不会被搜索到。
+
+设置 tag 域的 index 为 not_analyzed:
+
+```json
+{
+ "tag": {
+ "type": "string",
+ "index": "not_analyzed"
+ }
+}
+```
+
+其他简单类型(例如 `long` , `double` , `date` 等)也接受 `index` 参数,但有意义的值只有 `no` 和 `not_analyzed` , 因为它们永远不会被分析。
+
+#### analyzer
+
+对于 `analyzed` 字符串域,用 `analyzer` 属性指定在搜索和索引时使用的分析器。默认, Elasticsearch 使用 `standard` 分析器, 但你可以指定一个内置的分析器替代它,例如 `whitespace` 、 `simple` 和 `english`:
+
+```json
+{
+ "tweet": {
+ "type": "string",
+ "analyzer": "english"
+ }
+}
+```
+
+
+### 更新映射
+
+我们可以更新一个映射来添加一个新域,但不能将一个存在的域从 `analyzed` 改为 `not_analyzed` ,否则索引的数据可能会出错,数据不能被正常的搜索。
+
+增加名为 tag 的 not_analyzed 的文本域:
+
+```json
+PUT /gb/_mapping/tweet
+{
+ "properties" : {
+ "tag" : {
+ "type" : "text",
+ "index": "not_analyzed"
+ }
+ }
+}
+```
+
+指定中文分词器:
+
+```json
+PUT /gb/_mapping/tweet
+{
+ "properties" : {
+ "user": {
+ "type": "text",
+ "analyzer": "ik_max_word",
+ "search_analyzer": "ik_max_word"
+ }
+ }
+}
+```
+
+`analyzer`是字段文本的分词器,`search_analyzer`是搜索词的分词器。`ik_max_word`分词器是插件`ik`提供的,可以对文本进行最大数量的分词。
+
+
+### 测试映射
+
+```json
+GET /gb/_analyze
+{
+ "field": "tag",
+ "text": "Black-cats"
+}
+```
+
+ `tag` 域产生单独的词条 `Black-cats` 。
+
+### 分析器
+
+分析器的三个功能:
+
+- 字符过滤。字符串按顺序通过每个字符过滤器。它们在分词前整理字符串。一个字符过滤器可以用来去掉HTML或者将 & 转化为 and。
+- 字符串被分词器分成单个的词条
+- token 过滤器。改变词条(Quick 小写),删除词条(a/the/and),增加词条(leap/jump这种同义词)
+
+对特定域和全文域_all 查询字符串时可能会返回不同的结果。
+
+当你查询一个 *全文* 域时, 会对查询字符串应用相同的分析器,以产生正确的搜索词条列表。
+
+当你查询一个 *精确值* 域时,不会分析查询字符串, 而是搜索你指定的精确值。
+
+```json
+GET /_search?q=2014 # 12 results
+GET /_search?q=2014-09-15 # 12 results
+GET /_search?q=date:2014-09-15 # 1 result
+GET /_search?q=date:2014 # 0 results 没有"2014",只有"2014-09-15"
+```
+
+date 域包含一个精确值:单独的词条 `2014-09-15`。
+
+_all 域是一个全文域,所以分词进程将日期转化为三个词条: `2014`, `09`, 和 `15`。
+
+#### 测试分析器
+
+```json
+GET /_analyze
+{
+ "analyzer": "standard",
+ "text": "Text to analyze"
+}
+```
+
+结果:
+
+```json
+{
+ "tokens": [
+ {
+ "token": "text",
+ "start_offset": 0,
+ "end_offset": 4,
+ "type": "",
+ "position": 0
+ },
+ {
+ "token": "to",
+ "start_offset": 5,
+ "end_offset": 7,
+ "type": "",
+ "position": 1
+ },
+ {
+ "token": "analyze",
+ "start_offset": 8,
+ "end_offset": 15,
+ "type": "",
+ "position": 2
+ }
+ ]
+}
+```
+
+### 复杂核心域类型
+
+#### 多值域
+
+`{ "tag": [ "search", "nosql" ]}`
+
+### 内部对象
+
+```json
+{
+ "tweet": "Elasticsearch is very flexible",
+ "user": {
+ "id": "@johnsmith",
+ "gender": "male",
+ "age": 26,
+ "name": {
+ "full": "John Smith",
+ "first": "John",
+ "last": "Smith"
+ }
+ }
+}
+```
+
+Lucene 不理解内部对象。 Lucene 文档是由一组键值对列表组成的。为了能让 Elasticsearch 有效地索引内部类,它把我们的文档转化成这样:
+
+```json
+{
+ "tweet": [elasticsearch, flexible, very],
+ "user.id": [@johnsmith],
+ "user.gender": [male],
+ "user.age": [26],
+ "user.name.full": [john, smith],
+ "user.name.first": [john],
+ "user.name.last": [smith]
+}
+```
+
+
+
+
+## 搜索
+
+### 空搜索
+
+`GET /_search`,返回的 hits 数组包含所查询结果的前十个文档。
+
+`GET /_search?timeout=10ms`,在请求超时之前,Elasticsearch 将会返回已经成功从每个分片获取的结果。
+
+
+### 多索引多类型
+
+`GET /c*,g*/_search`,在以 c 开头和 g 开头的索引中搜索所有的类型。
+
+`GET /_all/employee,student/_search?size=1&from=1`,在所有索引中搜索 employee 和 student 类型。elasticsearch 默认一次返回10条结果,size 可以指定返回返回结果数量,from 指定位移。
+
+
+### 轻量搜索
+
+查询 employee 类型的 last_name 字段为 dai 的所有文档:`GET company/employee/_search?q=last_name:dai`
+
+查询包含 dai 的所有文档:`GET /_search?q=dai`
+
+
+
+## 请求体查询
+
+### 空查询
+
+```json
+GET _search
+{
+ "query" : {
+ "match_all": {}
+ }
+}
+```
+
+
+
+
+### 提升权重
+
+我们可以通过指定 `boost` 来控制任何查询语句的相对的权重, `boost` 的默认值为 `1` ,大于 `1` 会提升一个语句的相对权重。
+
+```json
+GET /company/employee/_search
+{
+ "query": {
+ "bool": {
+ "must": {
+ "match": {
+ "interests": {
+ "query": "sport music",
+ "operator": "and"
+ }
+ }
+ },
+ "should": [
+ { "match": {
+ "first_name": {
+ "query": "tyson",
+ "boost": 3
+ }
+ }},
+ { "match": {
+ "first_name": {
+ "query": "sophia",
+ "boost": 2
+ }
+ }}
+ ]
+ }
+ }
+}
+```
+
+### explain
+
+查询结果说明。
+
+```json
+GET /_validate/query?explain
+{
+ "query": {
+ "match" : {
+ "interests" : "sport shoes"
+ }
+ }
+}
+```
+
+结果:
+
+```json
+{
+ "valid": true,
+ "_shards": {
+ "total": 3,
+ "successful": 3,
+ "failed": 0
+ },
+ "explanations": [
+ {
+ "index": "class",
+ "valid": true,
+ "explanation": "interests:sport interests:shoes"
+ },
+ {
+ "index": "company",
+ "valid": true,
+ "explanation": "interests:sport interests:shoes"
+ }
+ ]
+}
+```
+
+### 查询和过滤器的区别
+
+查询会计算得分,而过滤不计算得分,过滤器所需处理更少,所以过滤器可以比普通查询更快。而且过滤器可以被缓存。
+
+
+
+## 排序与相关性
+
+有时,_score 相关性评分对你来说并没有意义。例如,下面的查询返回所有 `user_id` 字段包含 `1` 的结果:
+
+```json
+GET /_search
+{
+ "query" : {
+ "constant_score" : {
+ "filter" : {
+ "term" : {
+ "id" : 1
+ }
+ }
+ }
+ }
+}
+```
+
+### 按照字段的值排序
+
+按照 hire_date 排序。
+
+```json
+GET _search
+{
+ "query" : {
+ "bool" : {
+ "filter" : {
+ "match" : {
+ "first_name" : "tyson"
+ }
+ }
+ }
+ },
+ "sort" : {
+ "hire_date" : { "order" : "desc" }
+ }
+}
+```
+
+### 多级排序
+
+首先按第一个条件排序,仅当结果集的第一个 `sort` 值完全相同时才会按照第二个条件进行排序,以此类推。
+
+```json
+GET _search
+{
+ "query" : {
+ "bool" : {
+ "filter" : {
+ "match" : {
+ "first_name" : "tyson"
+ }
+ }
+ }
+ },
+ "sort" : {
+ "hire_date" : { "order" : "desc" },
+ "_score" : { "order" : "desc" }
+ }
+}
+```
+
+Query-string 搜索 也支持自定义排序,可以在查询字符串中使用 `sort` 参数:
+
+```js
+GET /_search?sort=hire_date:desc&sort=_score
+```
+
+### 多值字段的排序
+
+对于数字或日期,你可以将多值字段减为单值,这可以通过使用 `min` 、 `max` 、 `avg` 或是 `sum` 排序模式。 例如你可以按照每个 hire_date 字段中的最早日期进行排序,通过以下方法:
+
+```json
+"sort": {
+ "hire_date": {
+ "order": "asc",
+ "mode": "min"
+ }
+}
+```
+
+### 字符串排序
+
+为了对字符串字段进行排序,需要用两种方式对同一个字符串进行索引: `analyzed` 用于搜索, `not_analyzed` 用于排序。
+
+用两种方式索引一个单字段:
+
+```json
+"interests": {
+ "type": "string",
+ "analyzer": "english",
+ "fields": {
+ "raw": {
+ "type": "string",
+ "index": "not_analyzed"
+ }
+ }
+}
+```
+
+interests.raw 子字段是 not_analyzed。interests 字段只在 _source 中出现一次。
+
+使用 interests 字段用于搜索,interests.raw 字段用于排序:
+
+```json
+GET /_search
+{
+ "query": {
+ "match": {
+ "interests": "elasticsearch"
+ }
+ },
+ "sort": "interests.raw"
+}
+```
+
+备注:以全文 `analyzed` 字段排序会消耗大量的内存。
+
+
+
+## 索引管理
+
+### 创建索引
+
+```json
+PUT /my_index
+{
+ "settings": { ... any settings ... },
+ "mappings": {
+ "type_one": { ... any mappings ... },
+ "type_two": { ... any mappings ... },
+ ...
+ }
+}
+```
+
+禁止自动创建索引,你 可以通过在 `config/elasticsearch.yml` 的每个节点下添加下面的配置:
+
+`action.auto_create_index: false`
+
+### 删除索引
+
+`DELETE /company,student`
+
+`DELETE /index_*`
+
+`DELETE /_all` 或 `DELETE /*`
+
+### 索引设置
+
+number_of_shards:每个索引的主分片数,默认值是 `5` 。这个配置在索引创建后不能修改。
+
+number_of_replicas:每个主分片的副本数,默认值是 `1` 。对于活动的索引库,这个配置可以随时修改。
+
+创建只有 一个主分片,没有副本的小索引:
+
+```json
+PUT /my_temp_index
+{
+ "settings": {
+ "number_of_shards" : 1,
+ "number_of_replicas" : 0
+ }
+}
+```
+
+ 动态修改副本数:
+
+```json
+PUT /my_temp_index/_settings
+{
+ "number_of_replicas": 1
+}
+```
+
+### 配置分析器
+
+#### 自定义分析器
+
+,一个分析器组合了三种函数:字符过滤器、分词器和词单元过滤器, 三种函数按照顺序被执行。
+
+```json
+PUT /my_index
+{
+ "settings": {
+ "analysis": {
+ "char_filter": {
+ "&_to_and" : {
+ "type" : "mapping",
+ "mappings" : ["&=>and"]
+ }
+ },
+ "filter": {
+ "my_stopwords" : {
+ "type" : "stop",
+ "stopwords" : ["the", "a"]
+ }
+ },
+ "analyzer": {
+ "my_analyzer" : {
+ "type" : "custom",
+ "char_filter" : ["html_strip", "&_to_and"],
+ "tokenizer" : "standard",
+ "filter" : ["lowercase", "my_stopwords"]
+ }
+ }
+ }
+ }
+}
+```
+
+测试:
+
+```json
+GET /my_index/_analyze
+{
+ "analyzer" : "my_analyzer",
+ "text" : "The quick & brown fox"
+}
+```
+
+结果:
+
+```json
+{
+ "tokens": [
+ {
+ "token": "quick",
+ "start_offset": 4,
+ "end_offset": 9,
+ "type": "",
+ "position": 1
+ },
+ {
+ "token": "and",
+ "start_offset": 10,
+ "end_offset": 11,
+ "type": "",
+ "position": 2
+ },
+ {
+ "token": "brown",
+ "start_offset": 12,
+ "end_offset": 17,
+ "type": "",
+ "position": 3
+ },
+ {
+ "token": "fox",
+ "start_offset": 18,
+ "end_offset": 21,
+ "type": "",
+ "position": 4
+ }
+ ]
+}
+```
+
+把分析器应用在 string 字段上:
+
+```json
+PUT /my_index/_mapping/my_type
+{
+ "properties": {
+ "title": {
+ "type": "string",
+ "analyzer": "my_analyzer"
+ }
+ }
+}
+```
+
+### 类型和映射
+
+同一个索引下,不同的类型type应该有相同的结构,映射也应该相同。因为 Lucene 会将同一个索引下的所有字段的映射扁平化,相同字段不同映射会导致冲突。
+
+```json
+{
+ "data": {
+ "mappings": {
+ "people": {
+ "properties": {
+ "name": {
+ "type": "string",
+ },
+ "address": {
+ "type": "string"
+ }
+ }
+ },
+ "transactions": {
+ "properties": {
+ "timestamp": {
+ "type": "date",
+ "format": "strict_date_optional_time"
+ },
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+后台 Lucene 将创建一个映射:
+
+```json
+{
+ "data": {
+ "mappings": {
+ "_type": {
+ "type": "string",
+ "index": "not_analyzed"
+ },
+ "name": {
+ "type": "string"
+ }
+ "address": {
+ "type": "string"
+ }
+ "timestamp": {
+ "type": "long"
+ }
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+}
+```
+
+因此,类型不适合 *完全不同类型的数据* 。如果两个类型的字段集是互不相同的,这就意味着索引中将有一半的数据是空的(字段将是 *稀疏的* ),最终将导致性能问题。在这种情况下,最好是使用两个单独的索引。
+
+
+### 根对象
+
+映射最高的一层被称为根对象。它可能包含以下几项:
+
+- properties 节点,列出文档每个字段的映射
+- 下划线开头的元数据字段,如 \_type,_id 和 \_source
+- 设置项,控制如何动态处理新的字段,例如 `analyzer` 、 `dynamic_date_formats` 和`dynamic_templates`
+- 其他设置,可以同时应用在根对象和其他 `object` 类型的字段上,例如 `enabled` 、 `dynamic` 和 `include_in_all`
+
+
+
+## 分片内部原理
+
+### 倒排索引
+
+倒排索引包含一个有序列表,列表包含所有文档出现过的不重复个体,或称为 *词项* ,对于每一个词项,包含了它所有曾出现过文档的列表。
+
+| Term | doc1 | doc2 | doc3 |
+| ----- | ---- | ---- | ---- |
+| sport | x | | x |
+| music | | x | |
+| chess | x | x | x |
+
+
+
+## 深入搜索
+
+### 精确值查找
+
+term 查询被用于精确值匹配,这些精确值可能是数字、时间、布尔或者那些 `not_analyzed` 的字符串。
+
+```json
+GET /company/employee/_search
+{
+ "query" : {
+ "term" : {
+ "interests" : "listen"
+ }
+ }
+}
+```
+
+hire_date 在过去四年内的文档:
+
+```json
+GET company/employee/_search
+{
+ "query": {
+ "constant_score": {
+ "filter": {
+ "range": {
+ "hire_date": {
+ "gt": "now-4y"
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+#### term 查询文本
+
+删除旧索引(因为旧索引字段都是 analyzed 的,它的映射不正确)然后创建一个能正确映射的新索引:
+
+```json
+PUT /my_store
+{
+ "mappings" : {
+ "products" : {
+ "properties": {
+ "productID" : {
+ "type" : "keyword",//es6废除了string,index是boolean值,keyword不分词,text分词
+ }
+ }
+ }
+ }
+}
+```
+
+放进数据:
+
+```json
+POST /my_store/products/_bulk
+{ "index": { "_id": 1 }}
+{ "price" : 10, "productID" : "XHDK-A-1293-#fJ3" }
+{ "index": { "_id": 2 }}
+{ "price" : 20, "productID" : "KDKE-B-9947-#kL5" }
+{ "index": { "_id": 3 }}
+{ "price" : 30, "productID" : "JODL-X-1937-#pV7" }
+{ "index": { "_id": 4 }}
+{ "price" : 30, "productID" : "QQPX-R-3956-#aD8" }
+```
+
+查询文本:
+
+```json
+GET /my_store/products/_search
+{
+ "query" : {
+ "constant_score" : {
+ "filter" : {
+ "term" : {
+ "productID" : "XHDK-A-1293-#fJ3"
+ }
+ }
+ }
+ }
+}
+```
+
+#### terms 查询
+
+允许指定多值进行匹配。如果这个字段包含了指定值中的任何一个值,那么这个文档满足条件。
+
+```json
+GET /company/employee/_search
+{
+ "query" : {
+ "terms" : {
+ "interests" : ["listen", "music", "sport"]
+ }
+ }
+}
+```
+
+置入 `filter` 语句的常量评分查询:
+
+```json
+GET /my_store/products/_search
+{
+ "query": {
+ "constant_score": {
+ "filter": {
+ "terms" : {
+ "interests" : ["listen", "music", "sport"]
+ }
+ },
+ "boost": 1.2
+ }
+ }
+}
+```
+
+interests 为 listen、music 和 sport 的文档会被匹配。
+
+### 全文搜索
+
+#### match 查询
+
+match 查询是对全文进行查询。
+
+如果有多个搜索关键字, Elastic 认为它们是`or`关系。下面例子返回 interests 为 sport 或者 music 的文档。
+
+```json
+GET _search
+{
+ "query" : {
+ "match": {
+ "interests": "sport music"
+ }
+ }
+}
+```
+
+指定词都必须匹配:
+
+```json
+GET _search
+{
+ "query" : {
+ "match": {
+ "interests": {
+ "query": "sport music",
+ "operator": "and"
+ }
+ }
+ }
+}
+```
+
+控制匹配的字段数目:
+
+```json
+GET _search
+{
+ "query" : {
+ "match": {
+ "interests": {
+ "query": "sport music",
+ "minimum_should_match": 2
+ }
+ }
+ }
+}
+```
+
+### range 查询
+
+查询找出那些落在指定区间内的数字或者时间。
+
+#### 数字范围
+
+```json
+GET company/employee/_search
+{
+ "query": {
+ "constant_score": {
+ "filter": {
+ "range": {
+ "age": {
+ "gt": 20,
+ "lt": 100
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+`gt`:大于;`gte`:大于或等于;`lt`:小于;`lte`:小于或等于。
+
+#### 日期范围
+
+```json
+GET company/employee/_search
+{
+ "query": {
+ "constant_score": {
+ "filter": {
+ "range": {
+ "hire_date": {
+ "gt": "2015-01-01",
+ "lt": "2019-01-01"
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+### 分页
+
+请求得到 1 到 3 页的结果:
+
+```json
+GET /_search?size=5
+GET /_search?size=5&from=5
+GET /_search?size=5&from=10
+```
+
+### exists 查询
+
+查询字段 interests 中不为空的文档:
+
+```json
+GET /company/employee/_search
+{
+ "query": {
+ "constant_score": {
+ "filter": {
+ "exists": {
+ "field": "interests"
+ }
+ }
+ }
+ }
+}
+```
+
+字段 interests 为空的文档。es6 没有 missing api,直接用 bool 和 exists 实现。
+
+```json
+GET /company/employee/_search
+{
+ "query": {
+ "bool": {
+ "must_not": {
+ "exists": {
+ "field": "interests"
+ }
+ }
+ }
+ }
+}
+```
+
+### bool 组合查询
+
+bool 查询允许在单独的查询中组合任意数量的查询,适用于将不同查询字符串映射到不同字段的情况。bool 查询接收如下参数:
+
+- must:文档必须匹配这些条件才能被包含进来
+- must_not:文档必须不匹配这些条件才能被包含进来
+- should:如果满足这些语句中的任意语句,将增加 _score,否则没有影响。主要用来修正每个文档的相关性得分
+- filter:必须匹配,但是不影响评分
+
+如果没有 `must` 语句,那么至少需要能够匹配其中的一条 `should` 语句。但,如果存在至少一条 `must` 语句,则对 `should` 语句的匹配没有要求。
+
+```json
+GET /company/employee/_search
+{
+ "query" : {
+ "bool" : {
+ "must" : { "match" : {"first_name" : "tyson"}},
+ "must_not" : { "match" : {"last_name" : "tang"}},
+ "should" : [
+ {"match" : {"interest" : "music"}},
+ {"range" : {"age" : { "gte" : 30 }}}
+ ]
+ }
+ }
+}
+```
+
+如果不想因为文档的时间而影响得分,可以添加 filter 过滤掉某个时间段。
+
+```json
+GET _search
+{
+ "query" : {
+ "bool" : {
+ "must" : {
+ "match" : { "first_name" : "tyson"}
+ },
+ "must_not" : {
+ "match" : {"last_name" : "tang"}
+ },
+ "should" : [
+ {"match" : { "interests": "sport"}},
+ {"range" : { "age" : { "gt" : 20 }}}
+ ],
+ "filter" : {
+ "range" : { "hire_date" : { "gt" : "2015-01-01" }}
+ }
+ }
+ }
+}
+```
+
+可以将查询移到 `bool` 查询的 `filter` 语句中,这样它就自动的转成一个不评分的查询了。
+
+```json
+{
+ "bool": {
+ "must": { "match": { "title": "how to make millions" }},
+ "must_not": { "match": { "tag": "spam" }},
+ "should": [
+ { "match": { "tag": "starred" }}
+ ],
+ "filter": {
+ "bool": {
+ "must": [
+ { "range": { "date": { "gte": "2014-01-01" }}},
+ { "range": { "price": { "lte": 29.99 }}}
+ ],
+ "must_not": [
+ { "term": { "category": "ebooks" }}
+ ]
+ }
+ }
+ }
+}
+```
+
+bool 查询使用`minimum_should_match`控制需要匹配的 should 语句的数量:
+
+```json
+GET /my_index/my_type/_search
+{
+ "query": {
+ "bool": {
+ "should": [
+ { "match": { "title": "brown" }},
+ { "match": { "title": "fox" }},
+ { "match": { "title": "dog" }}
+ ],
+ "minimum_should_match": 2
+ }
+ }
+}
+```
+
+### 如何使用 bool 查询
+
+多次 match 查询:
+
+```json
+GET /company/employee/_search
+{
+ "query": {
+ "match": {
+ "interests": "sport music"
+ }
+ }
+}
+```
+
+等价的 bool 查询(存在`must`,则对 `should` 语句的匹配没有要求,否则至少需要能够匹配其中的一条 `should` 语句):
+
+```json
+GET /company/employee/_search
+{
+ "query": {
+ "bool": {
+ "should": [
+ { "term" : { "interests": "sport" } },
+ { "term" : { "interests" : "music" } }
+ ]
+ }
+ }
+}
+```
+
+使用 and 操作符,返回所有字段都匹配到的文档:
+
+```json
+GET /company/employee/_search
+{
+ "query": {
+ "match": {
+ "interests": {
+ "query": "sport music",
+ "operator": "and"
+ }
+ }
+ }
+}
+```
+
+等价的 bool 查询:
+
+```json
+GET /company/employee/_search
+{
+ "query": {
+ "bool": {
+ "must": [
+ { "term": { "interests": "sport" }},
+ { "term": { "interests": "music" }}
+ ]
+ }
+ }
+}
+```
+
+指定参数 minimum_should_match:
+
+```json
+GET /company/employee/_search
+{
+ "query": {
+ "match": {
+ "interests": {
+ "query": "sport music chess",
+ "minimum_should_match" : 2
+ }
+ }
+ }
+}
+```
+
+等价的 bool 查询:
+
+```json
+GET /company/employee/_search
+{
+ "query": {
+ "bool": {
+ "should": [
+ {"term": { "interests": "sport" }},
+ {"term": { "interests": "music" }},
+ {"term": { "interests": "chess" }}
+ ],
+ "minimum_should_match": 2
+ }
+ }
+}
+```
+
+### constant_score 查询
+
+constant_score 查询返回的文档`score`都是1。它经常用于只需要执行一个 filter 而没有其它查询的情况下。`score`会受到`boost`影响,出现 tyson 的文档`score`为1.2。
+
+```json
+GET _search
+{
+ "query" : {
+ "constant_score": {
+ "filter": {
+ "match" : { "first_name" : "tyson"}
+ },
+ "boost": 1.2
+ }
+ }
+}
+```
+
+
+
+
+### 多字段搜索
+
+参考自:[best_fields most_fields cross_fields从内在实现看区别——本质就是前两者是以field为中心,后者是词条为中心](https://www.cnblogs.com/bonelee/p/6827068.html)
+
+最佳字段 best_fields:搜索结果中应该返回某一个字段匹配到了最多的关键词的文档。
+
+多数字段 most_fields:返回匹配了更多的字段的文档,尽可能多地匹配文档。ES会为每个字段生成一个match查询,然后将它们包含在一个bool查询中。
+
+跨字段 cross_fields:每个查询的单词都出现在不同的字段中。*cross_fields类型采用了一种以词条为中心(Term-centric)的方法,这种方法和best_fields及most_fields采用的以字段为中心(Field-centric)的方法有很大的区别。它将所有的字段视为一个大的字段,然后在任一字段中搜索每个词条。
+
+#### 多字符串查询
+
+```json
+GET /_search
+{
+ "query": {
+ "bool": {
+ "should": [
+ { "match": { // 权重:1/3
+ "title": {
+ "query": "War and Peace",
+ "boost": 2
+ }}},
+ { "match": { // 权重:1/3
+ "author": {
+ "query": "Leo Tolstoy",
+ "boost": 2
+ }}},
+ { "bool": { //译者信息用布尔查询,降低评分权重 权重:1/3
+ "should": [
+ { "match": { "translator": "Constance Garnett" }},
+ { "match": { "translator": "Louise Maude" }}
+ ]
+ }}
+ ]
+ }
+ }
+}
+```
+
+#### multi_match 查询
+
+multi_match 查询可以在多个字段上执行相同的 match 查询。`multi_match` 多匹配查询的类型有三种:`best_fields` 、 `most_fields` 和 `cross_fields` (最佳字段、多数字段、跨字段)。
+
+```json
+GET company/employee/_search
+{
+ "query" : {
+ "multi_match": {
+ "query": "sport shoes",
+ "type": "best_fields",
+ "fields": [ "interests", "first_name" ],
+ "tie_breaker": 0.3,
+ "minimum_should_match": 1
+ }
+ }
+}
+```
+
+模糊匹配:
+
+```json
+GET company/employee/_search
+{
+ "query" : {
+ "multi_match": {
+ "query": "tyson dai",
+ "type": "best_fields",
+ "fields": "*_name",
+ "tie_breaker": 0.3,
+ "minimum_should_match": "30%"
+ }
+ }
+}
+```
+
+提升字段的权重,first_name 字段的 `boost` 值为 `2` :
+
+```json
+GET company/employee/_search
+{
+ "query" : {
+ "multi_match": {
+ "query": "tyson dai",
+ "type": "best_fields",
+ "fields": ["*_name", "first_name^3"],
+ "tie_breaker": 0.3,
+ "minimum_should_match": "30%"
+ }
+ }
+}
+```
+
+#### 多字段映射
+
+对字段索引两次: 一次使用词干模式以及一次非词干模式。
+
+```json
+DELETE /my_index
+
+PUT /my_index
+{
+ "settings": { "number_of_shards": 1 },
+ "mappings": {
+ "my_type": {
+ "properties": {
+ "title": {
+ "type": "string",
+ "analyzer": "english",//提取词干
+ "fields": {
+ "std": {
+ "type": "string",
+ "analyzer": "standard"
+ }
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+`title` 字段使用 `english` 分析器来提取词干;`title.std` 字段使用 `standard` 标准分析器,所以没有词干提取。
+
+索引文档:
+
+```json
+PUT /my_index/my_type/1
+{ "title": "My rabbit jumps" }
+
+PUT /my_index/my_type/2
+{ "title": "Jumping jack rabbits" }
+```
+
+multi_match 查询:
+
+```json
+GET /my_index/_search
+{
+ "query": {
+ "multi_match": {
+ "query": "jumping rabbits",
+ "type": "most_fields",
+ "fields": [ "title", "title.std" ]
+ }
+ }
+}
+```
+
+文档2匹配度更高,因为 title.std 不会提取词干,只有文档2是匹配的。
+
+设置`title` 字段的 `boost` 的值为 `10`,提升 title 字段的权重:
+
+```json
+GET /my_index/_search
+{
+ "query": {
+ "multi_match": {
+ "query": "jumping rabbits",
+ "type": "most_fields",
+ "fields": [ "title^10", "title.std" ]
+ }
+ }
+}
+```
+
+#### copy_to 定制组合 field
+
+定制组合 field 与 cross_field 跨字段查询类似,根据两者的实际性能选择具体方案。
+
+创建映射:
+
+```json
+PUT my_index1
+{
+ "mappings": {
+ "my_type": {
+ "properties": {
+ "first_name": {
+ "type": "keyword",
+ "copy_to": "full_name"
+ },
+ "last_name": {
+ "type": "keyword",
+ "copy_to": "full_name"
+ },
+ "full_name": {
+ "type": "text",
+ "fielddata": true
+ }
+ }
+ }
+ }
+}
+```
+
+插入数据:
+
+```json
+PUT my_index1/my_type/1
+{
+ "first_name": "John",
+ "last_name": "Smith"
+}
+```
+
+校验查询:
+
+```json
+GET my_index1/_search
+{
+ "query": {
+ "match": {
+ "full_name": {
+ "query": "John Smith",
+ "operator": "and"
+ }
+ }
+ }
+}
+
+```
+
+`copy_to` 设置对 multi-field 无效。如果尝试这样配置映射,Elasticsearch 会抛异常。多字段只是以不同方式简单索引主字段;它们没有自己的数据源。
+
+```json
+PUT /my_index
+{
+ "mappings": {
+ "person": {
+ "properties": {
+ "first_name": {
+ "type": "string",
+ "copy_to": "full_name",
+ "fields": {
+ "raw": {
+ "type": "string",
+ "index": "not_analyzed"
+ }
+ }
+ },
+ "full_name": {
+ "type": "string"
+ }
+ }
+ }
+ }
+}
+```
+
+first_name 是主字段,first_name.raw 是多字段。
+
+
+
+## springboot 集成 es
+
+[springboot 集成 es](https://blog.csdn.net/cwenao/article/details/54943505)
+
+[springboot整合elasticsearch5.x以及IK分词器做全文检索](https://blog.csdn.net/chenxihua1/article/details/94546282)
+
+
+
+## mall
+
+创建文档:
+
+```json
+POST /pms/product
+{
+ "productSn": "HNTBJ2E080A",
+ "brandId": 50,
+ "brandName": "海澜之家",
+ "productCategoryId": 8,
+ "productCategoryName": "T恤",
+ "pic": "http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180615/5ac98b64N70acd82f.jpg!cc_350x449.jpg",
+ "name": "HLA海澜之家蓝灰花纹圆领针织布短袖T恤",
+ "subTitle": "2018夏季新品短袖T恤男HNTBJ2E080A 蓝灰花纹80 175/92A/L80A 蓝灰花纹80 175/92A/L",
+ "keywords": "",
+ "price": 98,
+ "sale": 0,
+ "newStatus": 0,
+ "recommandStatus": 0,
+ "stock": 100,
+ "promotionType": 0,
+ "sort": 0,
+ "attrValueList": [
+ {
+ "id": 183,
+ "productAttributeId": 24,
+ "value": null,
+ "type": 1,
+ "name": "商品编号"
+ },
+ {
+ "id": 184,
+ "productAttributeId": 25,
+ "value": "夏季",
+ "type": 1,
+ "name": "适用季节"
+ }
+ ]
+}
+```
+
+获取文档映射:`GET /pms/_mapping/product`
+
+按字段查询:`GET /pms/product/_search?q=subTitle:2018`
+
+match查询:
+
+```json
+GET /pms/product/_search
+{
+ "query": {
+ "match": {
+ "brandName": "小米"
+ }
+ }
+}
+```
+
+
+
+
+
diff --git "a/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md" "b/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
index d5704d2..914796d 100644
--- "a/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
+++ "b/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
@@ -435,6 +435,8 @@ Redis cluster采用**虚拟槽分区**,所有的键根据哈希函数映射到
缓存穿透是指查询一个**不存在的数据**,由于缓存是不命中时被动写的,如果从DB查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到DB去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了。
+怎么解决?
+
1. **缓存空值**,不会查数据库。
2. 采用**布隆过滤器**,将所有可能存在的数据哈希到一个足够大的`bitmap`中,查询不存在的数据会被这个`bitmap`拦截掉,从而避免了对`DB`的查询压力。
diff --git "a/\345\205\266\344\273\226/\345\256\236\346\210\230\347\257\207.md" "b/\345\205\266\344\273\226/\345\256\236\346\210\230\347\257\207.md"
new file mode 100644
index 0000000..7d5164d
--- /dev/null
+++ "b/\345\205\266\344\273\226/\345\256\236\346\210\230\347\257\207.md"
@@ -0,0 +1,93 @@
+## OOM问题排查
+
+[记一次OOM问题排查!](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247486106&idx=1&sn=0d528efad06f0a1440e02983bf336bca&chksm=ce98f7dcf9ef7ecafee10115f1d29bfba56dd8bf694122dd2cb69a53f333b2242522f88aa92e&token=1360123733&lang=zh_CN#rd)
+
+
+
+## RT过长,排查思路
+
+[Java诊断工具Arthas](https://mp.weixin.qq.com/s/TnLl2OW9XJLSZihcpgP7VQ)
+
+
+
+## 限流算法
+
+限流是指在系统面临高并发、大流量请求的情况下,限制新的流量对系统的访问,从而保证系统服务的安全性。常用的限流算法有计数器固定窗口算法、滑动窗口算法、漏斗算法和令牌桶算法。
+
+### 计数器固定窗口算法
+
+计数器固定窗口算法是最基础也是最简单的一种限流算法。原理就是对一段固定时间窗口内的请求进行计数,如果请求数超过了阈值,则舍弃该请求;如果没有达到设定的阈值,则接受该请求,且计数加1。当时间窗口结束时,重置计数器为0。
+
+**优点**:实现简单,容易理解。
+
+**缺点**:流量曲线可能不够平滑,有“突刺现象”,这样会有两个问题:
+
+1. **一段时间内(不超过时间窗口)系统服务不可用**。比如窗口大小为1s,限流大小为100,然后恰好在某个窗口的第1ms来了100个请求,然后第2ms-999ms的请求就都会被拒绝,这段时间用户会感觉系统服务不可用。
+2. **窗口切换时可能会产生两倍于阈值流量的请求**。比如窗口大小为1s,限流大小为100,然后恰好在某个窗口的第999ms来了100个请求,窗口前期没有请求,所以这100个请求都会通过。再恰好,下一个窗口的第1ms有来了100个请求,也全部通过了,那也就是在2ms之内通过了200个请求,而我们设定的阈值是100,通过的请求达到了阈值的两倍。
+
+### 计数器滑动窗口算法
+
+计数器滑动窗口算法是计数器固定窗口算法的改进,解决了固定窗口切换时可能会产生两倍于阈值流量请求的缺点。
+
+滑动窗口算法在固定窗口的基础上,将一个计时窗口分成了若干个小窗口,然后每个小窗口维护一个独立的计数器。当请求的时间大于当前窗口的最大时间时,则将计时窗口向前平移一个小窗口。平移时,将第一个小窗口的数据丢弃,然后将第二个小窗口设置为第一个小窗口,同时在最后面新增一个小窗口,将新的请求放在新增的小窗口中。同时要保证整个窗口中所有小窗口的请求数目之后不能超过设定的阈值。
+
+将计时窗口划分成一个小窗口,滑动窗口算法就退化成了固定窗口算法。而滑动窗口算法其实就是对请求数进行了更细粒度的限流,窗口划分的越多,则限流越精准。
+
+**特点分析**
+
+1. 避免了计数器固定窗口算法固定窗口切换时可能会产生两倍于阈值流量请求的问题;
+2. 和漏斗算法相比,新来的请求也能够被处理到,避免了漏斗算法的饥饿问题。
+
+### 漏斗算法
+
+漏斗算法的原理也很容易理解。请求来了之后会首先进到漏斗里,然后漏斗以恒定的速率将请求流出进行处理,从而起到平滑流量的作用。当请求的流量过大时,漏斗达到最大容量时会溢出,此时请求被丢弃。从系统的角度来看,我们不知道什么时候会有请求来,也不知道请求会以多大的速率来,这就给系统的安全性埋下了隐患。但是如果加了一层漏斗算法限流之后,就能够保证请求以恒定的速率流出。在系统看来,请求永远是以平滑的传输速率过来,从而起到了保护系统的作用。
+
+**特点分析**
+
+1. **漏桶的漏出速率是固定的,可以起到整流的作用**。即虽然请求的流量可能具有随机性,忽大忽小,但是经过漏斗算法之后,变成了有固定速率的稳定流量,从而对下游的系统起到保护作用。
+2. **不能解决流量突发的问题**。假如设定的漏斗速率是2个/秒,漏斗限流算法的容量是5。然后突然来了10个请求,受限于漏斗的容量,只有5个请求被接受,另外5个被拒绝。你可能会说,漏斗速率是2个/秒,然后瞬间接受了5个请求,这不就解决了流量突发的问题吗?不,这5个请求只是被接受了,但是没有马上被处理,处理的速度仍然是我们设定的2个/秒,所以没有解决流量突发的问题。而接下来我们要谈的令牌桶算法能够在一定程度上解决流量突发的问题。
+
+### 令牌桶算法
+
+令牌桶算法是对漏斗算法的一种改进,除了能够起到限流的作用外,还允许一定程度的流量突发。在令牌桶算法中,存在一个令牌桶,算法中存在一种机制以恒定的速率向令牌桶中放入令牌。令牌桶也有一定的容量,如果满了令牌就无法放进去了。当请求来时,会首先到令牌桶中去拿令牌,如果拿到了令牌,则该请求会被处理,并消耗掉拿到的令牌;如果令牌桶为空,则该请求会被丢弃。
+
+比如过来10个请求,令牌桶算法和漏斗算法一样,都是接受了5个请求,拒绝了5个请求。与漏斗算法不同的是,令牌桶算法马上处理了这5个请求,处理速度可以认为是5个/秒,超过了我们设定的2个/秒的速率,即**允许一定程度的流量突发**。这一点也是和漏斗算法的主要区别。
+
+**特点分析**
+
+令牌桶算法是对漏桶算法的一种改进,除了能够在限制调用的平均速率的同时还允许一定程度的流量突发。
+
+### 小结
+
+**计数器固定窗口算法**实现简单,容易理解。和漏斗算法相比,新来的请求也能够被马上处理到。但是流量曲线可能不够平滑,有“突刺现象”,在窗口切换时可能会产生两倍于阈值流量的请求。而**计数器滑动窗口算法**作为计数器固定窗口算法的一种改进,有效解决了窗口切换时可能会产生两倍于阈值流量请求的问题。
+
+**漏斗算法**能够对流量起到整流的作用,让随机不稳定的流量以固定的速率流出,但是不能解决**流量突发**的问题。**令牌桶算法**作为漏斗算法的一种改进,除了能够起到平滑流量的作用,还允许一定程度的流量突发。
+
+令牌桶算法一般用于保护自身的系统,对调用者进行限流,保护自身的系统不被突发的流量打垮。如果自身的系统实际的处理能力强于配置的流量限制时,可以允许一定程度的流量突发,使得实际的处理速率高于配置的速率,充分利用系统资源。而漏斗算法一般用于保护第三方的系统,比如自身的系统需要调用第三方的接口,为了保护第三方的系统不被自身的调用打垮,便可以通过漏斗算法进行限流,保证自身的流量平稳的打到第三方的接口上。
+
+参考:https://zhuanlan.zhihu.com/p/228412634
+
+
+
+## 亿级数据分页查询难怎么解决?
+
+无深翻页需求的实时在线应用:禁止深翻页,数据库 offset < N。
+
+确有深翻页需求的离线分析应用:es搜scroll,jdbc自己维护这个读取游标的长链接,持续流式读取。
+
+
+
+## 秒杀场景
+
+并发修改库存,会加行锁。rt较高,tps上不去。两种方案:
+
+1. 分库分表。减小锁粒度
+2. 降低锁的持有时间
+
+可以在内存起一个队列,将用户请求放进队列。起一个异步线程,每200ms处理队列数据,批量扣库存。这样就不用每个请求都去扣减库存,减少行锁持有时间。
+
+每个请求封装一个锁对象(锁里面包含购买数量),丢进队列,调用wait(200)。当扣库存完成后拿到锁对象执行notify,通知用户请求线程继续往下执行。即每个用户线程都会有一个锁对象,用来阻塞和通知,阻塞是最多200ms
+
+
+
+## MySQL表数据量大
diff --git "a/\345\210\206\345\270\203\345\274\217/RocketMQ\345\256\236\347\216\260RPC\347\232\204\345\216\237\347\220\206.md" "b/\345\210\206\345\270\203\345\274\217/RocketMQ\345\256\236\347\216\260RPC\347\232\204\345\216\237\347\220\206.md"
new file mode 100644
index 0000000..383686b
--- /dev/null
+++ "b/\345\210\206\345\270\203\345\274\217/RocketMQ\345\256\236\347\216\260RPC\347\232\204\345\216\237\347\220\206.md"
@@ -0,0 +1,49 @@
+一、背景
+基于RokcetMQ可以实现异步处理、流量削锋、业务解耦,通常是依赖RocketMQ的发布订阅模式。今天分享RocketMQ的新特性,基于Request/Reply模式来实现RPC的功能。该模式是在v4.6作为RocketMQ新特性引入,但在在v4.7.1上才比较完善。
+
+二、设计思路
+从整个数据流的角度上来说,发布/订阅模式中生产者和消费者之间是异步处理的,生产者负责把消息投递到RocketMQ上,而消费者负责处理数据,如果把生产者当做上游系统,消费者是下游系统,那么上下游系统之间是没有任何的状态交流的。而我们知道,RPC上下游系统之间是需要状态交互的,简单来说,要想实现RPC功能,在整个数据链路上,原先上下游系统之间是异步交互的模式,首先需要把异步模式换成同步模式。
+
+异步模式:
+
+把异步模式换成同步模式,需要在生产者发送消息到MQ之后,保持原有的状态,比如可以用一个Map集合去统一维护,等到消费者处理完数据返回响应后,再从Map集合中拿到对应的请求进行处理。其中涉及到怎么去标识一个请求,这里可以用UUID或者雪花id去标记。
+
+同步模式:
+
+RocketMQ整体的处理思路跟上面是类似的,DefaultMQProducerImpl#request负责RPC消息的下发,而DefaultMQPushConsumer中负责消息的消费。具体用法可以看RocketMQ源码example中的RPC部分。
+
+
+三、结构定义
+RocketMQ中是依赖于Message的Properties来区分不同的请求,在调用DefaultMQProducerImpl#request进行消息下发之间会先给消息设置不同的属性,通过属性来保证上下游之间的处理是同一个请求。
+
+设置的属性有:
+
+CORRELATION_ID:消息的标识Id,这里对应是一个UUID
+REPLY_TO_CLIENT:消息下发的客户端Id
+TTL:消息下发的超时时间,单位ms
+
+其实就类似于HTTP请求中的头部内容一样。
+
+之后还会校验一下消息中对应Topic的一个合法性。
+
+四、消息下发
+RocketMQ将下发的客户端封装成RequestResponseFuture,包含客户端Id,请求超时时间,同时根据客户端Id维护在ConcurrentHashMap,调用DefaultMQProducerImpl#sendDefaultImpl下发消息,根据下发消息的回调函数确认消息下发的状态。
+
+消息下发后会调用waitResponse,waitResponse调用CountDownLatch进入阻塞状态,等待消息消费之后的响应。
+
+CountDownLatch中的计数器是1,说明每个请求都会独立隔离阻塞。
+
+
+五、消息响应
+当服务端(消费者)收到消息处理完返回响应时,会调用ReplyMessageProcessor#pushReplyMessage封装响应的内容,处理响应的头部信息和返回body的参数,最终封装成一个PUSH_REPLY_MESSAGE_TO_CLIENT的请求命令发给客户端。
+
+客户端(生产者)收到请求后,会调用ClientRemotingProcessor#processRequest,判断是PUSH_REPLY_MESSAGE_TO_CLIENT命令会调用receiveReplyMessage,将接收到的数据封装成新的消息,接着调用响应处理的处理器。
+
+ClientRemotingProcessor#processReplyMessage中主要做的从消息中获取消息的Id,从ConcurrentHashMap中定位到具体的请求,将返回消息封装到RequestResponseFuture中,同时CountDownLatch的计数值减1,此时线程阻塞状态被释放,之后便将消息响应给到客户端。
+
+六、总结
+所以整体上看来,RocketMQ的Request/Reply模式,其实是利用客户端线程阻塞来换取请求异步变同步以及RocketMQ的回调机制从而间接的实现了RPC效果,但是相比直接RPC调用,数据的链路更长,性能肯定是会有损耗,但是请求会持久化,所以给了重复下发提供了可能。
+
+————————————————
+版权声明:本文为CSDN博主「林风自在」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
+原文链接:https://blog.csdn.net/lveex/article/details/122514893
\ No newline at end of file
diff --git "a/\345\210\206\345\270\203\345\274\217/\345\205\250\345\261\200\345\224\257\344\270\200ID.md" "b/\345\210\206\345\270\203\345\274\217/\345\205\250\345\261\200\345\224\257\344\270\200ID.md"
new file mode 100644
index 0000000..59b6f58
--- /dev/null
+++ "b/\345\210\206\345\270\203\345\274\217/\345\205\250\345\261\200\345\224\257\344\270\200ID.md"
@@ -0,0 +1,485 @@
+传统的单体架构的时候,我们基本是单库然后业务单表的结构。每个业务表的ID一般我们都是从1增,通过`AUTO_INCREMENT=1`设置自增起始值,但是在分布式服务架构模式下分库分表的设计,使得多个库或多个表存储相同的业务数据。这种情况根据数据库的自增ID就会产生相同ID的情况,不能保证主键的唯一性。
+
+
+
+如上图,如果第一个订单存储在 DB1 上则订单 ID 为1,当一个新订单又入库了存储在 DB2 上订单 ID 也为1。我们系统的架构虽然是分布式的,但是在用户层应是无感知的,重复的订单主键显而易见是不被允许的。那么针对分布式系统如何做到主键唯一性呢?
+
+# UUID
+
+UUID (Universally Unique Identifier),通用唯一识别码的缩写。UUID是由一组32位数的16进制数字所构成,所以UUID理论上的总数为 1632=2128,约等于 3.4 x 10^38。也就是说若每纳秒产生1兆个UUID,要花100亿年才会将所有UUID用完。
+
+生成的UUID是由 `8-4-4-4-12`格式的数据组成,其中32个字符和4个连字符' - ',一般我们使用的时候会将连字符删除 `uuid.toString().replaceAll("-","")`。
+
+目前UUID的产生方式有5种版本,每个版本的算法不同,应用范围也不同。
+
+- **基于时间的UUID - 版本1**:
+ 这个一般是通过当前时间,随机数,和本地Mac地址来计算出来,可以通过 `org.apache.logging.log4j.core.util`包中的 `UuidUtil.getTimeBasedUuid()`来使用或者其他包中工具。由于使用了MAC地址,因此能够确保唯一性,但是同时也暴露了MAC地址,私密性不够好。
+- **DCE安全的UUID - 版本2**
+ DCE(Distributed Computing Environment)安全的UUID和基于时间的UUID算法相同,但会把时间戳的前4位置换为POSIX的UID或GID。这个版本的UUID在实际中较少用到。
+- **基于名字的UUID(MD5)- 版本3**
+ 基于名字的UUID通过计算名字和名字空间的MD5散列值得到。这个版本的UUID保证了:相同名字空间中不同名字生成的UUID的唯一性;不同名字空间中的UUID的唯一性;相同名字空间中相同名字的UUID重复生成是相同的。
+- **随机UUID - 版本4**
+ 根据随机数,或者伪随机数生成UUID。这种UUID产生重复的概率是可以计算出来的,但是重复的可能性可以忽略不计,因此该版本也是被经常使用的版本。JDK中使用的就是这个版本。
+- **基于名字的UUID(SHA1) - 版本5**
+ 和基于名字的UUID算法类似,只是散列值计算使用SHA1(Secure Hash Algorithm 1)算法。
+
+我们 Java中 JDK自带的 UUID产生方式就是版本4根据随机数生成的 UUID 和版本3基于名字的 UUID,有兴趣的可以去看看它的源码。
+
+```java
+public static void main(String[] args) {
+
+ //获取一个版本4根据随机字节数组的UUID。
+ UUID uuid = UUID.randomUUID();
+ System.out.println(uuid.toString().replaceAll("-",""));
+
+ //获取一个版本3(基于名称)根据指定的字节数组的UUID。
+ byte[] nbyte = {10, 20, 30};
+ UUID uuidFromBytes = UUID.nameUUIDFromBytes(nbyte);
+ System.out.println(uuidFromBytes.toString().replaceAll("-",""));
+}
+```
+
+得到的UUID结果,
+
+```undefined
+59f51e7ea5ca453bbfaf2c1579f09f1d
+7f49b84d0bbc38e9a493718013baace6
+```
+
+虽然 UUID 生成方便,本地生成没有网络消耗,但是使用起来也有一些缺点,
+
+- 不易于存储:UUID太长,16字节128位,通常以36长度的字符串表示,很多场景不适用。
+- 信息不安全:基于MAC地址生成UUID的算法可能会造成MAC地址泄露,暴露使用者的位置。
+- 对MySQL索引不利:如果作为数据库主键,在InnoDB引擎下,UUID的无序性可能会引起数据位置频繁变动,严重影响性能,可以查阅 Mysql 索引原理 B+树的知识。
+
+# 数据库生成
+
+是不是一定要基于外界的条件才能满足分布式唯一ID的需求呢,我们能不能在我们分布式数据库的基础上获取我们需要的ID?
+
+由于分布式数据库的起始自增值一样所以才会有冲突的情况发生,那么我们将分布式系统中数据库的同一个业务表的自增ID设计成不一样的起始值,然后设置固定的步长,步长的值即为分库的数量或分表的数量。
+
+以MySQL举例,利用给字段设置`auto_increment_increment`和`auto_increment_offset`来保证ID自增。
+
+- auto_increment_offset:表示自增长字段从那个数开始,他的取值范围是1 .. 65535。
+- auto_increment_increment:表示自增长字段每次递增的量,其默认值是1,取值范围是1 .. 65535。
+
+假设有三台机器,则DB1中order表的起始ID值为1,DB2中order表的起始值为2,DB3中order表的起始值为3,它们自增的步长都为3,则它们的ID生成范围如下图所示:
+
+
+
+通过这种方式明显的优势就是依赖于数据库自身不需要其他资源,并且ID号单调自增,可以实现一些对ID有特殊要求的业务。
+
+但是缺点也很明显,首先它强依赖DB,当DB异常时整个系统不可用。虽然配置主从复制可以尽可能的增加可用性,但是数据一致性在特殊情况下难以保证。主从切换时的不一致可能会导致重复发号。还有就是ID发号性能瓶颈限制在单台MySQL的读写性能。
+
+# 使用redis实现
+
+Redis实现分布式唯一ID主要是通过提供像 *INCR* 和 *INCRBY* 这样的自增原子命令,由于Redis自身的单线程的特点所以能保证生成的 ID 肯定是唯一有序的。
+
+但是单机存在性能瓶颈,无法满足高并发的业务需求,所以可以采用集群的方式来实现。集群的方式又会涉及到和数据库集群同样的问题,所以也需要设置分段和步长来实现。
+
+为了避免长期自增后数字过大可以通过与当前时间戳组合起来使用,另外为了保证并发和业务多线程的问题可以采用 Redis + Lua的方式进行编码,保证安全。
+
+Redis 实现分布式全局唯一ID,它的性能比较高,生成的数据是有序的,对排序业务有利,但是同样它依赖于redis,需要系统引进redis组件,增加了系统的配置复杂性。
+
+当然现在Redis的使用性很普遍,所以如果其他业务已经引进了Redis集群,则可以资源利用考虑使用Redis来实现。
+
+# 雪花算法-Snowflake
+
+Snowflake,雪花算法是由Twitter开源的分布式ID生成算法,以划分命名空间的方式将 64-bit位分割成多个部分,每个部分代表不同的含义。而 Java中64bit的整数是Long类型,所以在 Java 中 SnowFlake 算法生成的 ID 就是 long 来存储的。
+
+- 第1位占用1bit,其值始终是0,可看做是符号位不使用。
+- 第2位开始的41位是时间戳,41-bit位可表示2^41个数,每个数代表毫秒,那么雪花算法可用的时间年限是(1L<<41)/(1000L*3600*24*365)=69 年的时间。
+- 中间的10-bit位可表示机器数,即2^10 = 1024台机器,但是一般情况下我们不会部署这么台机器。如果我们对IDC(互联网数据中心)有需求,还可以将 10-bit 分 5-bit 给 IDC,分5-bit给工作机器。这样就可以表示32个IDC,每个IDC下可以有32台机器,具体的划分可以根据自身需求定义。
+- 最后12-bit位是自增序列,可表示2^12 = 4096个数。
+
+这样的划分之后相当于在一毫秒一个数据中心的一台机器上可产生4096个有序的不重复的ID。但是我们 IDC 和机器数肯定不止一个,所以毫秒内能生成的有序ID数是翻倍的。
+
+
+
+Snowflake 的[Twitter官方原版](https://github.com/twitter/snowflake/blob/snowflake-2010/src/main/scala/com/twitter/service/snowflake/IdWorker.scala)是用Scala写的,对Scala语言有研究的同学可以去阅读下,以下是 Java 版本的写法。
+
+```java
+package com.jajian.demo.distribute;
+
+/**
+ * Twitter_Snowflake
+ * SnowFlake的结构如下(每部分用-分开):
+ * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
+ * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0
+ * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
+ * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
+ * 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId
+ * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
+ * 加起来刚好64位,为一个Long型。
+ * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
+ */
+public class SnowflakeDistributeId {
+
+
+ // ==============================Fields===========================================
+ /**
+ * 开始时间截 (2015-01-01)
+ */
+ private final long twepoch = 1420041600000L;
+
+ /**
+ * 机器id所占的位数
+ */
+ private final long workerIdBits = 5L;
+
+ /**
+ * 数据标识id所占的位数
+ */
+ private final long datacenterIdBits = 5L;
+
+ /**
+ * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
+ */
+ private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
+
+ /**
+ * 支持的最大数据标识id,结果是31
+ */
+ private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
+
+ /**
+ * 序列在id中占的位数
+ */
+ private final long sequenceBits = 12L;
+
+ /**
+ * 机器ID向左移12位
+ */
+ private final long workerIdShift = sequenceBits;
+
+ /**
+ * 数据标识id向左移17位(12+5)
+ */
+ private final long datacenterIdShift = sequenceBits + workerIdBits;
+
+ /**
+ * 时间截向左移22位(5+5+12)
+ */
+ private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
+
+ /**
+ * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
+ */
+ private final long sequenceMask = -1L ^ (-1L << sequenceBits);
+
+ /**
+ * 工作机器ID(0~31)
+ */
+ private long workerId;
+
+ /**
+ * 数据中心ID(0~31)
+ */
+ private long datacenterId;
+
+ /**
+ * 毫秒内序列(0~4095)
+ */
+ private long sequence = 0L;
+
+ /**
+ * 上次生成ID的时间截
+ */
+ private long lastTimestamp = -1L;
+
+ //==============================Constructors=====================================
+
+ /**
+ * 构造函数
+ *
+ * @param workerId 工作ID (0~31)
+ * @param datacenterId 数据中心ID (0~31)
+ */
+ public SnowflakeDistributeId(long workerId, long datacenterId) {
+ if (workerId > maxWorkerId || workerId < 0) {
+ throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
+ }
+ if (datacenterId > maxDatacenterId || datacenterId < 0) {
+ throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
+ }
+ this.workerId = workerId;
+ this.datacenterId = datacenterId;
+ }
+
+ // ==============================Methods==========================================
+
+ /**
+ * 获得下一个ID (该方法是线程安全的)
+ *
+ * @return SnowflakeId
+ */
+ public synchronized long nextId() {
+ long timestamp = timeGen();
+
+ //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
+ if (timestamp < lastTimestamp) {
+ throw new RuntimeException(
+ String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
+ }
+
+ //如果是同一时间生成的,则进行毫秒内序列
+ if (lastTimestamp == timestamp) {
+ sequence = (sequence + 1) & sequenceMask;
+ //毫秒内序列溢出
+ if (sequence == 0) {
+ //阻塞到下一个毫秒,获得新的时间戳
+ timestamp = tilNextMillis(lastTimestamp);
+ }
+ }
+ //时间戳改变,毫秒内序列重置
+ else {
+ sequence = 0L;
+ }
+
+ //上次生成ID的时间截
+ lastTimestamp = timestamp;
+
+ //移位并通过或运算拼到一起组成64位的ID
+ return ((timestamp - twepoch) << timestampLeftShift) //
+ | (datacenterId << datacenterIdShift) //
+ | (workerId << workerIdShift) //
+ | sequence;
+ }
+
+ /**
+ * 阻塞到下一个毫秒,直到获得新的时间戳
+ *
+ * @param lastTimestamp 上次生成ID的时间截
+ * @return 当前时间戳
+ */
+ protected long tilNextMillis(long lastTimestamp) {
+ long timestamp = timeGen();
+ while (timestamp <= lastTimestamp) {
+ timestamp = timeGen();
+ }
+ return timestamp;
+ }
+
+ /**
+ * 返回以毫秒为单位的当前时间
+ *
+ * @return 当前时间(毫秒)
+ */
+ protected long timeGen() {
+ return System.currentTimeMillis();
+ }
+}
+```
+
+测试的代码如下
+
+```java
+public static void main(String[] args) {
+ SnowflakeDistributeId idWorker = new SnowflakeDistributeId(0, 0);
+ for (int i = 0; i < 1000; i++) {
+ long id = idWorker.nextId();
+// System.out.println(Long.toBinaryString(id));
+ System.out.println(id);
+ }
+}
+```
+
+雪花算法提供了一个很好的设计思想,雪花算法生成的ID是趋势递增,不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的,而且可以根据自身业务特性分配bit位,非常灵活。
+
+但是雪花算法强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。如果恰巧回退前生成过一些ID,而时间回退后,生成的ID就有可能重复。官方对于此并没有给出解决方案,而是简单的抛错处理,这样会造成在时间被追回之前的这段时间服务不可用。
+
+很多其他类雪花算法也是在此思想上的设计然后改进规避它的缺陷,后面介绍的百度 UidGenerator 和 美团分布式ID生成系统 Leaf 中snowflake模式都是在 snowflake 的基础上演进出来的。
+
+# 百度-UidGenerator
+
+[百度的 UidGenerator](https://github.com/baidu/uid-generator) 是百度开源基于Java语言实现的唯一ID生成器,是在雪花算法 snowflake 的基础上做了一些改进。UidGenerator以组件形式工作在应用项目中, 支持自定义workerId位数和初始化策略,适用于docker等虚拟化环境下实例自动重启、漂移等场景。
+
+在实现上,UidGenerator 提供了两种生成唯一ID方式,分别是 DefaultUidGenerator 和 CachedUidGenerator,官方建议如果有性能考虑的话使用 CachedUidGenerator 方式实现。
+
+UidGenerator 依然是以划分命名空间的方式将 64-bit位分割成多个部分,只不过它的默认划分方式有别于雪花算法 snowflake。它默认是由 1-28-22-13 的格式进行划分。可根据你的业务的情况和特点,自己调整各个字段占用的位数。
+
+- 第1位仍然占用1bit,其值始终是0。
+- 第2位开始的28位是时间戳,28-bit位可表示2^28个数,**这里不再是以毫秒而是以秒为单位**,每个数代表秒则可用(1L<<28)/ (3600*24*365) ≈ 8.51 年的时间。
+- 中间的 workId (数据中心+工作机器,可以其他组成方式)则由 22-bit位组成,可表示 2^22 = 4194304个工作ID。
+- 最后由13-bit位构成自增序列,可表示2^13 = 8192个数。
+
+
+
+其中 workId (机器 id),最多可支持约420w次机器启动。内置实现为在启动时由数据库分配(表名为 WORKER_NODE),默认分配策略为用后即弃,后续可提供复用策略。
+
+```sql
+DROP TABLE IF EXISTS WORKER_NODE;
+CREATE TABLE WORKER_NODE
+(
+ID BIGINT NOT NULL AUTO_INCREMENT COMMENT 'auto increment id',
+HOST_NAME VARCHAR(64) NOT NULL COMMENT 'host name',
+PORT VARCHAR(64) NOT NULL COMMENT 'port',
+TYPE INT NOT NULL COMMENT 'node type: ACTUAL or CONTAINER',
+LAUNCH_DATE DATE NOT NULL COMMENT 'launch date',
+MODIFIED TIMESTAMP NOT NULL COMMENT 'modified time',
+CREATED TIMESTAMP NOT NULL COMMENT 'created time',
+PRIMARY KEY(ID)
+)
+ COMMENT='DB WorkerID Assigner for UID Generator',ENGINE = INNODB;
+```
+
+## DefaultUidGenerator 实现
+
+DefaultUidGenerator 就是正常的根据时间戳和机器位还有序列号的生成方式,和雪花算法很相似,对于时钟回拨也只是抛异常处理。仅有一些不同,如以秒为为单位而不再是毫秒和支持Docker等虚拟化环境。
+
+```java
+protected synchronized long nextId() {
+ long currentSecond = getCurrentSecond();
+
+ // Clock moved backwards, refuse to generate uid
+ if (currentSecond < lastSecond) {
+ long refusedSeconds = lastSecond - currentSecond;
+ throw new UidGenerateException("Clock moved backwards. Refusing for %d seconds", refusedSeconds);
+ }
+
+ // At the same second, increase sequence
+ if (currentSecond == lastSecond) {
+ sequence = (sequence + 1) & bitsAllocator.getMaxSequence();
+ // Exceed the max sequence, we wait the next second to generate uid
+ if (sequence == 0) {
+ currentSecond = getNextSecond(lastSecond);
+ }
+
+ // At the different second, sequence restart from zero
+ } else {
+ sequence = 0L;
+ }
+
+ lastSecond = currentSecond;
+
+ // Allocate bits for UID
+ return bitsAllocator.allocate(currentSecond - epochSeconds, workerId, sequence);
+}
+```
+
+如果你要使用 DefaultUidGenerator 的实现方式的话,以上划分的占用位数可通过 spring 进行参数配置。
+
+```xml
+
+
+
+
+
+
+
+
+
+```
+
+## CachedUidGenerator 实现
+
+而官方建议的性能较高的 CachedUidGenerator 生成方式,是使用 RingBuffer 缓存生成的id。数组每个元素成为一个slot。RingBuffer容量,默认为Snowflake算法中sequence最大值(2^13 = 8192)。可通过 boostPower 配置进行扩容,以提高 RingBuffer 读写吞吐量。
+
+Tail指针、Cursor指针用于环形数组上读写slot:
+
+- Tail指针
+ 表示Producer生产的最大序号(此序号从0开始,持续递增)。Tail不能超过Cursor,即生产者不能覆盖未消费的slot。当Tail已赶上curosr,此时可通过rejectedPutBufferHandler指定PutRejectPolicy
+- Cursor指针
+ 表示Consumer消费到的最小序号(序号序列与Producer序列相同)。Cursor不能超过Tail,即不能消费未生产的slot。当Cursor已赶上tail,此时可通过rejectedTakeBufferHandler指定TakeRejectPolicy
+
+[](https://img2018.cnblogs.com/blog/1162587/201907/1162587-20190707142503899-1530677221.png)
+
+CachedUidGenerator采用了双RingBuffer,Uid-RingBuffer用于存储Uid、Flag-RingBuffer用于存储Uid状态(是否可填充、是否可消费)。
+
+由于数组元素在内存中是连续分配的,可最大程度利用CPU cache以提升性能。但同时会带来「伪共享」FalseSharing问题,为此在Tail、Cursor指针、Flag-RingBuffer中采用了CacheLine 补齐方式。
+
+[](https://img2018.cnblogs.com/blog/1162587/201907/1162587-20190707142450492-1894906450.png)
+
+RingBuffer填充时机
+
+- 初始化预填充
+ RingBuffer初始化时,预先填充满整个RingBuffer。
+- 即时填充
+ Take消费时,即时检查剩余可用slot量(tail - cursor),如小于设定阈值,则补全空闲slots。阈值可通过paddingFactor来进行配置,请参考Quick Start中CachedUidGenerator配置。
+- 周期填充
+ 通过Schedule线程,定时补全空闲slots。可通过scheduleInterval配置,以应用定时填充功能,并指定Schedule时间间隔。
+
+# 美团Leaf
+
+Leaf是美团基础研发平台推出的一个分布式ID生成服务,名字取自德国哲学家、数学家莱布尼茨的著名的一句话:“There are no two identical leaves in the world”,世间不可能存在两片相同的叶子。
+
+Leaf 也提供了两种ID生成的方式,分别是 Leaf-segment 数据库方案和 Leaf-snowflake 方案。
+
+## Leaf-segment 数据库方案
+
+Leaf-segment 数据库方案,是在上文描述的在使用数据库的方案上,做了如下改变:
+
+- 原方案每次获取ID都得读写一次数据库,造成数据库压力大。改为利用proxy server批量获取,每次获取一个segment(step决定大小)号段的值。用完之后再去数据库获取新的号段,可以大大的减轻数据库的压力。
+- 各个业务不同的发号需求用 `biz_tag`字段来区分,每个biz-tag的ID获取相互隔离,互不影响。如果以后有性能需求需要对数据库扩容,不需要上述描述的复杂的扩容操作,只需要对biz_tag分库分表就行。
+
+数据库表设计如下:
+
+```sql
+CREATE TABLE `leaf_alloc` (
+ `biz_tag` varchar(128) NOT NULL DEFAULT '' COMMENT '业务key',
+ `max_id` bigint(20) NOT NULL DEFAULT '1' COMMENT '当前已经分配了的最大id',
+ `step` int(11) NOT NULL COMMENT '初始步长,也是动态调整的最小步长',
+ `description` varchar(256) DEFAULT NULL COMMENT '业务key的描述',
+ `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ PRIMARY KEY (`biz_tag`)
+) ENGINE=InnoDB;
+```
+
+原来获取ID每次都需要写数据库,现在只需要把step设置得足够大,比如1000。那么只有当1000个号被消耗完了之后才会去重新读写一次数据库。读写数据库的频率从1减小到了1/step,大致架构如下图所示:
+
+
+
+同时Leaf-segment 为了解决 TP999(满足千分之九百九十九的网络请求所需要的最低耗时)数据波动大,当号段使用完之后还是会hang在更新数据库的I/O上,TP999 数据会出现偶尔的尖刺的问题,提供了双buffer优化。
+
+简单的说就是,Leaf 取号段的时机是在号段消耗完的时候进行的,也就意味着号段临界点的ID下发时间取决于下一次从DB取回号段的时间,并且在这期间进来的请求也会因为DB号段没有取回来,导致线程阻塞。如果请求DB的网络和DB的性能稳定,这种情况对系统的影响是不大的,但是假如取DB的时候网络发生抖动,或者DB发生慢查询就会导致整个系统的响应时间变慢。
+
+为了DB取号段的过程能够做到无阻塞,不需要在DB取号段的时候阻塞请求线程,即当号段消费到某个点时就异步的把下一个号段加载到内存中,而不需要等到号段用尽的时候才去更新号段。这样做就可以很大程度上的降低系统的 TP999 指标。详细实现如下图所示:
+
+
+
+采用双buffer的方式,Leaf服务内部有两个号段缓存区segment。当前号段已下发10%时,如果下一个号段未更新,则另启一个更新线程去更新下一个号段。当前号段全部下发完后,如果下个号段准备好了则切换到下个号段为当前segment接着下发,循环往复。
+
+- 每个biz-tag都有消费速度监控,通常推荐segment长度设置为服务高峰期发号QPS的600倍(10分钟),这样即使DB宕机,Leaf仍能持续发号10-20分钟不受影响。
+- 每次请求来临时都会判断下个号段的状态,从而更新此号段,所以偶尔的网络抖动不会影响下个号段的更新。
+
+对于这种方案依然存在一些问题,它仍然依赖 DB的稳定性,需要采用主从备份的方式提高 DB的可用性,还有 Leaf-segment方案生成的ID是趋势递增的,这样ID号是可被计算的,例如订单ID生成场景,通过订单id号相减就能大致计算出公司一天的订单量,这个是不能忍受的。
+
+## Leaf-snowflake方案
+
+Leaf-snowflake方案完全沿用 snowflake 方案的bit位设计,对于workerID的分配引入了Zookeeper持久顺序节点的特性自动对snowflake节点配置 wokerID。避免了服务规模较大时,动手配置成本太高的问题。
+
+Leaf-snowflake是按照下面几个步骤启动的:
+
+- 启动Leaf-snowflake服务,连接Zookeeper,在leaf_forever父节点下检查自己是否已经注册过(是否有该顺序子节点)。
+- 如果有注册过直接取回自己的workerID(zk顺序节点生成的int类型ID号),启动服务。
+- 如果没有注册过,就在该父节点下面创建一个持久顺序节点,创建成功后取回顺序号当做自己的workerID号,启动服务。
+
+
+
+为了减少对 Zookeeper的依赖性,会在本机文件系统上缓存一个workerID文件。当ZooKeeper出现问题,恰好机器出现问题需要重启时,能保证服务能够正常启动。
+
+上文阐述过在类 snowflake算法上都存在时钟回拨的问题,Leaf-snowflake在解决时钟回拨的问题上是通过校验自身系统时间与 `leaf_forever/${self}`节点记录时间做比较然后启动报警的措施。
+
+
+
+美团官方建议是由于强依赖时钟,对时间的要求比较敏感,在机器工作时NTP同步也会造成秒级别的回退,建议可以直接关闭NTP同步。要么在时钟回拨的时候直接不提供服务直接返回ERROR_CODE,等时钟追上即可。或者做一层重试,然后上报报警系统,更或者是发现有时钟回拨之后自动摘除本身节点并报警。
+
+在性能上官方提供的数据目前 Leaf 的性能在4C8G 的机器上QPS能压测到近5w/s,TP999 1ms。
+
+# 总结
+
+以上基本列出了所有常用的分布式ID生成方式,其实大致分类的话可以分为两类:
+
+一种是类DB型的,根据设置不同起始值和步长来实现趋势递增,需要考虑服务的容错性和可用性。
+
+另一种是类snowflake型,这种就是将64位划分为不同的段,每段代表不同的涵义,基本就是时间戳、机器ID和序列数。这种方案就是需要考虑时钟回拨的问题以及做一些 buffer的缓冲设计提高性能。
+
+而且可通过将三者(时间戳,机器ID,序列数)划分不同的位数来改变使用寿命和并发数。
+
+例如对于并发数要求不高、期望长期使用的应用,可增加时间戳位数,减少序列数的位数. 例如配置成{"workerBits":23,"timeBits":31,"seqBits":9}时, 可支持28个节点以整体并发量14400 UID/s的速度持续运行68年。
+
+对于节点重启频率频繁、期望长期使用的应用, 可增加工作机器位数和时间戳位数, 减少序列数位数. 例如配置成{"workerBits":27,"timeBits":30,"seqBits":6}时, 可支持37个节点以整体并发量2400 UID/s的速度持续运行34年。
+
+
+
+[参考链接](https://www.cnblogs.com/jajian/p/11101213.html)
\ No newline at end of file
diff --git "a/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" "b/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md"
new file mode 100644
index 0000000..2f9cfa0
--- /dev/null
+++ "b/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md"
@@ -0,0 +1,77 @@
+分布式事务是指事务的参与者,支持事务的服务器,资源服务器以及事务管理器分别位于分布式系统的不同节点之上。通常一个分布式事务中会涉及对多个数据源或业务系统的操作。分布式事务也可以被定义为一种嵌套型的事务,同时也就具有了ACID事务的特性。
+
+## CAP理论
+
+Consistency(一致性):数据一致更新,所有数据变动都是同步的(强一致性)。
+
+Availability(可用性):好的响应性能
+
+Partition tolerance(分区容错性) :可靠性
+
+定理:任何分布式系统只可同时满足二点,没法三者兼顾。
+
+CA系统(放弃P):指将所有数据(或者仅仅是那些与事务相关的数据)都放在一个分布式节点上,就不会存在网络分区。所以强一致性以及可用性得到满足。
+
+CP系统(放弃A):如果要求数据在各个服务器上是强一致的,然而网络分区会导致同步时间无限延长,那么如此一来可用性就得不到保障了。坚持事务ACID(原子性、一致性、隔离性和持久性)的传统数据库以及对结果一致性非常敏感的应用通常会做出这样的选择。
+
+AP系统(放弃C):这里所说的放弃一致性,并不是完全放弃数据一致性,而是放弃数据的强一致性,而保留数据的最终一致性。如果即要求系统高可用又要求分区容错,那么就要放弃一致性了。因为一旦发生网络分区,节点之间将无法通信,为了满足高可用,每个节点只能用本地数据提供服务,这样就会导致数据不一致。一些遵守BASE原则数据库,(如:Cassandra、CouchDB等)往往会放宽对一致性的要求(满足最终一致性即可),一次来获取基本的可用性。
+
+## BASE理论
+
+Basically Available基本可用:指分布式系统在出现不可预知的故障的时候,允许损失部分可用性——但不是系统不可用。
+
+响应时间上的损失:假如正常一个在线搜索0.5秒之内返回,但由于故障(机房断电或网络不通),查询结果的响应时间增加到1—2秒。功能上的损失:如果流量激增或者一个请求需要多个服务间配合,而此时有的服务发生了故障,这时需要进行服务降级,进而保护系统稳定性。
+
+Soft state软状态:允许系统在不同节点的数据副本之间进行数据同步的过程存在延迟。Eventually consistent最终一致:最终数据是一致的就可以了,而不是时时高一致。
+
+BASE思想主要强调基本的可用性,如果你需要High 可用性,也就是纯粹的高性能,那么就要以一致性或容错性为牺牲。
+
+## 实现方案
+
+分布式事务的实现主要有以下 5 种方案:
+
+- XA 方案
+- TCC 方案
+- 可靠消息最终一致性方案
+- 最大努力通知方案
+
+## 2PC/XA方案
+
+所谓的 XA 方案,即:两阶段提交,有一个事务管理器的概念,负责协调多个数据库(资源管理器)的事务,事务管理器先问问各个数据库你准备好了吗?如果每个数据库都回复 ok,那么就正式提交事务,在各个数据库上执行操作;如果任何其中一个数据库回答不 ok,那么就回滚事务。
+
+这种分布式事务方案,比较适合单块应用里,跨多个库的分布式事务,而且因为严重依赖于数据库层面来搞定复杂的事务,效率很低,绝对不适合高并发的场景。
+
+一般来说某个系统内部如果出现跨多个库的这么一个操作,是不合规的。如果你要操作别人的服务的库,你必须是通过调用别的服务的接口来实现,绝对不允许交叉访问别人的数据库。
+
+## TCC强一致性方案
+
+TCC 的全称是:`Try`、`Confirm`、`Cancel`。
+
+- **Try 阶段**:这个阶段说的是对各个服务的资源做检测以及对资源进行 **锁定或者预留**。
+- **Confirm 阶段**:这个阶段说的是在各个服务中执行实际的操作。
+- **Cancel 阶段**:如果任何一个服务的业务方法执行出错,那么这里就需要 **进行补偿**,就是执行已经执行成功的业务逻辑的回滚操作。(把那些执行成功的回滚)
+-
+
+这种方案说实话几乎很少人使用,但是也有使用的场景。因为这个 **事务回滚实际上是严重依赖于你自己写代码来回滚和补偿** 了,会造成补偿代码巨大,非常之恶心。
+
+
+
+## 可靠消息最终一致性方案
+
+基于 MQ 来实现事务。比如阿里的 RocketMQ 就支持消息事务。大概的意思就是:
+
+1. A 系统先发送一个 prepared 消息到 MQ,如果这个 prepared 消息发送失败那么就直接取消操作别执行了;
+2. 如果这个消息发送成功过了,那么接着执行本地事务,如果成功就告诉 MQ 发送确认消息,如果失败就告诉 MQ 回滚消息;
+3. 如果发送了确认消息,那么此时 B 系统会接收到确认消息,然后执行本地的事务;
+4. mq 会自动定时轮询所有 prepared 消息回调你的接口,问你,这个消息是不是本地事务处理失败了,所有没发送确认的消息,是继续重试还是回滚?一般来说这里你就可以查下数据库看之前本地事务是否执行,如果回滚了,那么这里也回滚吧。这个就是避免可能本地事务执行成功了,而确认消息却发送失败了。
+5. 这个方案里,要是系统 B 的事务失败了咋办?重试咯,自动不断重试直到成功,如果实在是不行,要么就是针对重要的资金类业务进行回滚,比如 B 系统本地回滚后,想办法通知系统 A 也回滚;或者是发送报警由人工来手工回滚和补偿。
+
+这个还是比较合适的,目前国内互联网公司大都是这么玩儿的,要不你举用 RocketMQ 支持的,要不你就自己基于类似 ActiveMQ?RabbitMQ?自己封装一套类似的逻辑出来,总之思路就是这样子的。
+
+## 最大努力通知方案
+
+这个方案的大致意思就是:
+
+1. 系统 A 本地事务执行完之后,发送个消息到 MQ;
+2. 这里会有个专门消费 MQ 的最大努力通知服务,这个服务会消费 MQ 然后写入数据库中记录下来,或者是放入个内存队列也可以,接着调用系统 B 的接口;
+3. 要是系统 B 执行成功就 ok 了;要是系统 B 执行失败了,那么最大努力通知服务就定时尝试重新调用系统 B,反复 N 次,最后还是不行就放弃。
\ No newline at end of file
diff --git "a/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\347\274\223\345\255\230.md" "b/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\347\274\223\345\255\230.md"
new file mode 100644
index 0000000..bd1f578
--- /dev/null
+++ "b/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\347\274\223\345\255\230.md"
@@ -0,0 +1,98 @@
+高并发环境下,例如典型的淘宝双11秒杀,几分钟内上亿的用户涌入淘宝,这个时候如果访问不加拦截,让大量的读写请求涌向数据库,由于磁盘的处理速度与内存显然不在一个量级,服务器马上就要宕机。**从减轻数据库的压力和提高系统响应速度两个角度来考虑,都会在数据库之前加一层缓存**,访问压力越大的,在缓存之前就开始 CDN 拦截图片等访问请求。
+
+并且由于最早的单台机器的内存资源以及承载能力有限,如果大量使用本地缓存,也会使相同的数据被不同的节点存储多份,对内存资源造成较大的浪费,因此,才催生出了分布式缓存。
+
+
+
+## 应用场景
+
+1. **页面缓存**:用来缓存Web 页面的内容片段,包括HTML、CSS 和图片等;
+2. **应用对象缓存**:缓存系统作为ORM 框架的二级缓存对外提供服务,目的是减轻数据库的负载压力,加速应用访问;解决分布式Web部署的 session 同步问题,状态缓存.缓存包括Session 会话状态及应用横向扩展时的状态数据等,这类数据一般是难以恢复的,对可用性要求较高,多应用于高可用集群。
+3. **并行处理**:通常涉及大量中间计算结果需要共享;
+4. **云计算领域提供分布式缓存服务**
+
+## 缓存常见的问题
+
+### 缓存穿透
+
+缓存穿透是指查询一个**不存在的数据**,由于缓存是不命中时被动写的,如果从DB查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到DB去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了。
+
+1. **缓存空值**,不会查数据库。
+2. 采用**布隆过滤器**,将所有可能存在的数据哈希到一个足够大的`bitmap`中,查询不存在的数据会被这个`bitmap`拦截掉,从而避免了对`DB`的查询压力。
+
+布隆过滤器的原理:当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。查询时,将元素通过散列函数映射之后会得到k个点,如果这些点有任何一个0,则被检元素一定不在,直接返回;如果都是1,则查询元素很可能存在,就会去查询Redis和数据库。
+
+### 缓存雪崩
+
+缓存雪崩是指在我们设置缓存时采用了相同的过期时间,**导致缓存在某一时刻同时失效**,请求全部转发到DB,DB瞬时压力过重挂掉。
+
+解决方法:在原有的失效时间基础上**增加一个随机值**,使得过期时间分散一些。
+
+### 缓存击穿
+
+缓存击穿:大量的请求同时查询一个 key 时,此时这个 key 正好失效了,就会导致大量的请求都落到数据库。**缓存击穿是查询缓存中失效的 key,而缓存穿透是查询不存在的 key。**
+
+解决方法:加分布式锁,第一个请求的线程可以拿到锁,拿到锁的线程查询到了数据之后设置缓存,其他的线程获取锁失败会等待50ms然后重新到缓存取数据,这样便可以避免大量的请求落到数据库。
+
+```java
+public String get(String key) {
+ String value = redis.get(key);
+ if (value == null) { //缓存值过期
+ String unique_key = systemId + ":" + key;
+ //设置30s的超时
+ if (redis.set(unique_key, 1, 'NX', 'PX', 30000) == 1) { //设置成功
+ value = db.get(key);
+ redis.set(key, value, expire_secs);
+ redis.del(unique_key);
+ } else { //其他线程已经到数据库取值并回写到缓存了,可以重试获取缓存值
+ sleep(50);
+ get(key); //重试
+ }
+ } else {
+ return value;
+ }
+}
+```
+
+### 缓存预热
+
+缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
+
+解决方案:
+
+1. 直接写个缓存刷新页面,上线时手工操作一下;
+2. 数据量不大,可以在项目启动的时候自动进行加载;
+3. 定时刷新缓存;
+
+### 缓存降级
+
+当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
+
+缓存降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
+
+在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:
+
+1. 一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
+2. 警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
+3. 错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
+4. 严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。
+
+服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。
+
+### 如何保证缓存与数据库双写时的数据一致性?
+
+**1、先删除缓存再更新数据库**
+
+进行更新操作时,先删除缓存,然后更新数据库,后续的请求再次读取时,会从数据库读取后再将新数据更新到缓存。
+
+存在的问题:删除缓存数据之后,更新数据库完成之前,这个时间段内如果有新的读请求过来,就会从数据库读取旧数据重新写到缓存中,再次造成不一致,并且后续读的都是旧数据。
+
+**2、先更新数据库再删除缓存**
+
+进行更新操作时,先更新MySQL,成功之后,删除缓存,后续读取请求时再将新数据回写缓存。
+
+存在的问题:更新MySQL和删除缓存这段时间内,请求读取的还是缓存的旧数据,不过等数据库更新完成,就会恢复一致,影响相对比较小。
+
+**3、异步更新缓存**
+
+数据库的更新操作完成后不直接操作缓存,而是把这个操作命令封装成消息扔到消息队列中,然后由Redis自己去消费更新数据,消息队列可以保证数据操作顺序一致性,确保缓存系统的数据正常。
\ No newline at end of file
diff --git "a/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\351\224\201.md" "b/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\351\224\201.md"
new file mode 100644
index 0000000..5645b0b
--- /dev/null
+++ "b/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\351\224\201.md"
@@ -0,0 +1,253 @@
+## 为什么要使用分布式锁
+
+在单机环境下,当存在多个线程可以同时改变某个变量(可变共享变量)时,就会出现线程安全问题。这个问题可以通过 JAVA 提供的 volatile、ReentrantLock、synchronized 以及 concurrent 并发包下一些线程安全的类等来避免。
+
+而在多机部署环境,需要在多进程下保证线程的安全性,Java提供的这些API仅能保证在单个JVM进程内对多线程访问共享资源的线程安全,已经不满足需求了。这时候就需要使用分布式锁来保证线程安全。通过分布式锁,可以保证在分布式部署的应用集群中,同一个方法在同一时间只能被一台机器上的一个线程执行。
+
+## 分布式锁应该具备哪些条件
+
+在分析分布式锁的三种实现方式之前,先了解一下分布式锁应该具备哪些条件:
+
+1. 互斥性。在任意时刻,只有一个客户端能持有锁。
+2. 不会死锁。具备锁失效机制,防止死锁。即使有客户端在持有锁的期间崩溃而没有主动解锁,也要保证后续其他客户端能加锁。
+3. 加锁和解锁必须是同一个客户端。客户端a不能将客户端b的锁解开,即不能误解锁。
+4. 高性能、高可用的获取锁与释放锁。
+5. 具备可重入特性。
+6. 具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。
+
+## 分布式锁的三种实现方式
+
+1. 基于数据库实现分布式锁;
+2. 基于缓存(Redis等)实现分布式锁;
+3. 基于Zookeeper实现分布式锁。
+
+### 基于数据库的实现方式
+
+**悲观锁**
+
+创建一张锁表,然后通过操作该表中的数据来实现加锁和解锁。当要锁住某个方法或资源时,就向该表插入一条记录,表中设置方法名为唯一键,这样多个请求同时提交数据库时,只有一个操作可以成功,判定操作成功的线程获得该方法的锁,可以执行方法内容;想要释放锁的时候就删除这条记录,其他线程就可以继续往数据库中插入数据获取锁。
+
+**乐观锁**
+
+每次更新操作,都觉得不会存在并发冲突,只有更新失败后,才重试。
+
+扣减余额就可以使用这种方案。
+
+具体实现:增加个version字段,每次更新修改,都会自增加一,然后去更新余额时,把查出来的那个版本号,带上条件去更新,如果是上次那个版本号,就更新,如果不是,表示别人并发修改过了,就继续重试。
+
+### 基于Redis的实现方式
+
+#### 简单实现
+
+Redis 2.6.12 之前的版本中采用 setnx + expire 方式实现分布式锁,在 Redis 2.6.12 版本后 setnx 增加了过期时间参数:
+
+```java
+SET lockKey value NX PX expire-time
+```
+
+所以在Redis 2.6.12 版本后,只需要使用setnx就可以实现分布式锁了。
+
+加锁逻辑:
+
+1. setnx争抢key的锁,如果已有key存在,则不作操作,过段时间继续重试,保证只有一个客户端能持有锁。
+2. value设置为 requestId(可以使用机器ip拼接当前线程名称),表示这把锁是哪个请求加的,在解锁的时候需要判断当前请求是否持有锁,防止误解锁。比如客户端A加锁,在执行解锁之前,锁过期了,此时客户端B尝试加锁成功,然后客户端A再执行del()方法,则将客户端B的锁给解除了。
+3. 再用expire给锁加一个过期时间,防止异常导致锁没有释放。
+
+解锁逻辑:
+
+首先获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁。使用lua脚本实现原子操作,保证线程安全。
+
+下面我们通过Jedis(基于java语言的redis客户端)来演示分布式锁的实现。
+
+**Jedis实现分布式锁**
+
+引入Jedis jar包,在pom.xml文件增加代码:
+
+```xml
+
+ redis.clients
+ jedis
+ 2.9.0
+
+```
+
+**加锁**
+
+调用jedis的set()实现加锁,加锁代码如下:
+
+```java
+/**
+ * @description:
+ * @author: 程序员大彬
+ * @time: 2021-08-01 17:13
+ */
+public class RedisTest {
+
+ private static final String LOCK_SUCCESS = "OK";
+ private static final String SET_IF_NOT_EXIST = "NX";
+ private static final String SET_EXPIRE_TIME = "PX";
+
+ @Autowired
+ private JedisPool jedisPool;
+
+ public boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) {
+ Jedis jedis = jedisPool.getResource();
+ String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_EXPIRE_TIME, expireTime);
+
+ if (LOCK_SUCCESS.equals(result)) {
+ return true;
+ }
+ return false;
+ }
+}
+```
+
+各参数说明:
+
+- lockKey:使用key来当锁,需要保证key是唯一的。可以使用系统号拼接自定义的key。
+- requestId:表示这把锁是哪个请求加的,可以使用机器ip拼接当前线程名称。在解锁的时候需要判断当前请求是否持有锁,防止误解锁。比如客户端A加锁,在执行解锁之前,锁过期了,此时客户端B尝试加锁成功,然后客户端A再执行del()方法,则将客户端B的锁给解除了。
+- NX:意思是SET IF NOT EXIST,保证如果已有key存在,则不作操作,过段时间继续重试。NX参数保证只有一个客户端能持有锁。
+- PX:给key加一个过期的设置,具体时间由expireTime决定。
+- expireTime:设置key的过期时间,防止异常导致锁没有释放。
+
+**解锁**
+
+首先需要获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁。这里使用lua脚本实现原子操作,保证线程安全。
+
+使用eval命令执行Lua脚本的时候,不会有其他脚本或 Redis 命令被执行,实现组合命令的原子操作。lua脚本如下:
+
+```
+//KEYS[1]是lockKey,ARGV[1]是requestId
+String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
+Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
+```
+
+Jedis的eval()方法源码如下:
+
+```java
+public Object eval(String script, List keys, List args) {
+ return this.eval(script, keys.size(), getParams(keys, args));
+}
+```
+
+lua脚本的意思是:调用get获取锁(KEYS[1])对应的value值,检查是否与requestId(ARGV[1])相等,如果相等则调用del删除锁。否则返回0。
+
+完整的解锁代码如下:
+
+```java
+/**
+ * @description:
+ * @author: 程序员大彬
+ * @time: 2021-08-01 17:13
+ */
+public class RedisTest {
+ private static final Long RELEASE_SUCCESS = 1L;
+
+ @Autowired
+ private JedisPool jedisPool;
+
+ public boolean releaseDistributedLock(String lockKey, String requestId) {
+ Jedis jedis = jedisPool.getResource();
+ ////KEYS[1]是lockKey,ARGV[1]是requestId
+ String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
+ Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
+
+ if (RELEASE_SUCCESS.equals(result)) {
+ return true;
+ }
+ return false;
+ }
+}
+```
+
+#### RedLock
+
+前面的方案是基于**Redis单机版**的分布式锁讨论,还不是很完美。因为Redis一般都是集群部署的。
+
+如果线程一在`Redis`的`master`节点上拿到了锁,但是加锁的`key`还没同步到`slave`节点。恰好这时,`master`节点发生故障,一个`slave`节点就会升级为`master`节点。线程二就可以顺理成章获取同个`key`的锁啦,但线程一也已经拿到锁了,锁的安全性就没了。
+
+为了解决这个问题,Redis作者antirez提出一种高级的分布式锁算法:**Redlock**。它的核心思想是这样的:
+
+部署多个Redis master,以保证它们不会同时宕掉。并且这些master节点是完全相互独立的,相互之间不存在数据同步。同时,需要确保在这多个master实例上,是与在Redis单实例,使用相同方法来获取和释放锁。
+
+我们假设当前有5个Redis master节点,在5台服务器上面运行这些Redis实例。
+
+RedLock的实现步骤:
+
+1. 获取当前时间,以毫秒为单位。
+2. 按顺序向5个master节点请求加锁。客户端设置网络连接和响应超时时间,并且超时时间要小于锁的失效时间。(假设锁自动失效时间为10秒,则超时时间一般在5-50毫秒之间,我们就假设超时时间是50ms吧)。如果超时,跳过该master节点,尽快去尝试下一个master节点。
+3. 客户端使用当前时间减去开始获取锁时间(即步骤1记录的时间),得到获取锁使用的时间。当且仅当超过一半(N/2+1,这里是5/2+1=3个节点)的Redis master节点都获得锁,并且使用的时间小于锁失效时间时,锁才算获取成功。(如上图,10s> 30ms+40ms+50ms+4m0s+50ms)
+4. 如果取到了锁,key的真正有效时间就变啦,需要减去获取锁所使用的时间。
+5. 如果获取锁失败(没有在至少N/2+1个master实例取到锁,有或者获取锁时间已经超过了有效时间),客户端要在所有的master节点上解锁(即便有些master节点根本就没有加锁成功,也需要解锁,以防止有些漏网之鱼)。
+
+简化下步骤就是:
+
+- 按顺序向5个master节点请求加锁
+- 根据设置的超时时间来判断,是不是要跳过该master节点。
+- 如果大于等于3个节点加锁成功,并且使用的时间小于锁的有效期,即可认定加锁成功啦。
+- 如果获取锁失败,解锁!
+
+### 基于ZooKeeper的实现方式
+
+ZooKeeper是一个为分布式应用提供一致性服务的开源组件,它内部是一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一文件名。基于ZooKeeper实现分布式锁的步骤如下:
+
+(1)创建一个目录mylock;
+(2)线程A想获取锁就在mylock目录下创建临时顺序节点;
+(3)获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁;
+(4)线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点;
+(5)线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。
+
+这里推荐一个Apache的开源库Curator,它是一个ZooKeeper客户端,Curator提供的InterProcessMutex是分布式锁的实现,acquire方法用于获取锁,release方法用于释放锁。
+
+优点:具备高可用、可重入、阻塞锁特性,可解决失效死锁问题。
+
+缺点:因为需要频繁的创建和删除节点,性能上不如Redis方式。
+
+## 三种实现方式对比
+
+**数据库分布式锁实现**
+
+优点:
+
+- 简单,使用方便,不需要引入`Redis、zookeeper`等中间件。
+
+缺点:
+
+- 不适合高并发的场景
+- db操作性能较差;
+
+**Redis分布式锁实现**
+
+优点:
+
+- 性能好,适合高并发场景
+- 较轻量级
+- 有较好的框架支持,如Redisson
+
+缺点:
+
+- 过期时间不好控制
+- 需要考虑锁被别的线程误删场景
+
+**Zookeeper分布式锁实现**
+
+缺点:
+
+- 性能不如redis实现的分布式锁
+- 比较重的分布式锁。
+
+优点:
+
+- 有较好的性能和可靠性
+- 有封装较好的框架,如Curator
+
+**对比汇总**
+
+- 从性能角度(从高到低)Redis > Zookeeper >= 数据库;
+- 从理解的难易程度角度(从低到高)数据库 > Redis > Zookeeper;
+- 从实现的复杂性角度(从低到高)Zookeeper > Redis > 数据库;
+- 从可靠性角度(从高到低)Zookeeper > Redis > 数据库。
+
+## 参考链接
+
+https://mp.weixin.qq.com/s/xQknd6xsVDPBr4TbETTk2A
\ No newline at end of file
diff --git "a/\345\210\206\345\270\203\345\274\217/\345\276\256\346\234\215\345\212\241.md" "b/\345\210\206\345\270\203\345\274\217/\345\276\256\346\234\215\345\212\241.md"
new file mode 100644
index 0000000..99ffb31
--- /dev/null
+++ "b/\345\210\206\345\270\203\345\274\217/\345\276\256\346\234\215\345\212\241.md"
@@ -0,0 +1,194 @@
+## 简介
+
+微服务是将一个原本独立的系统拆分成多个小型服务,这些小型服务都在各自独立的进程中运行,服务和服务之间采用轻量级的通信机制进行协作。每个服务可以被独立的部署到生产环境。
+
+[从单体应用到微服务](https://www.zhihu.com/question/65502802/answer/802678798)
+
+单体系统的缺点:
+
+1. 修改一个小功能,就需要将整个系统重新部署上线,影响其他功能的运行;
+2. 功能模块互相依赖,强耦合,扩展困难。如果出现性能瓶颈,需要对整体应用进行升级,虽然影响性能的可能只是其中一个小模块;
+
+单体系统的优点:
+
+1. 容易部署,程序单一,不存在分布式集群的复杂部署环境;
+2. 容易测试,没有复杂的服务调用关系。
+
+微服务的**优点**:
+
+1. 不同的服务可以使用不同的技术;
+2. 隔离性。一个服务不可用不会导致其他服务不可用;
+3. 可扩展性。某个服务出现性能瓶颈,只需对此服务进行升级即可;
+4. 简化部署。服务的部署是独立的,哪个服务出现问题,只需对此服务进行修改重新部署;
+
+微服务的**缺点**:
+
+1. 网络调用频繁。性能相对函数调用较差。
+2. 运维成本增加。系统由多个独立运行的微服务构成,需要设计一个良好的监控系统对各个微服务的运行状态进行监控。
+
+
+
+## 分布式和微服务的区别
+
+从概念理解,分布式服务架构强调的是服务化以及服务的**分散化**,微服务则更强调服务的**专业化和精细分工**;
+
+从实践的角度来看,**微服务架构通常是分布式服务架构**,反之则未必成立。
+
+一句话概括:分布式:分散部署;微服务:分散能力。
+
+
+
+## 服务划分
+
+横向拆分:按照不同的业务域进行拆分,例如订单、营销、风控、积分资源等。形成独立的业务领域微服务集群。
+
+纵向拆分:把一个业务功能里的不同模块或者组件进行拆分。例如把公共组件拆分成独立的原子服务,下沉到底层,形成相对独立的原子服务层。这样一纵一横,就可以实现业务的服务化拆分。
+
+要做好微服务的分层:梳理和抽取核心应用、公共应用,作为独立的服务下沉到核心和公共能力层,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求
+
+总之,微服务的设计一定要 **渐进式** 的,总的原则是 **服务内部高内聚,服务之间低耦合。**
+
+ ## 微服务设计原则
+**单一职责原则**
+
+意思是每个微服务只需要实现自己的业务逻辑就可以了,比如订单管理模块,它只需要处理订单的业务逻辑就可以了,其它的不必考虑。
+
+**服务自治原则**
+
+意思是每个微服务从开发、测试、运维等都是独立的,包括存储的数据库也都是独立的,自己就有一套完整的流程,我们完全可以把它当成一个项目来对待。不必依赖于其它模块。
+
+**轻量级通信原则**
+
+首先是通信的语言非常的轻量,第二,该通信方式需要是跨语言、跨平台的,之所以要跨平台、跨语言就是为了让每个微服务都有足够的独立性,可以不受技术的钳制。
+
+**接口明确原则**
+
+由于微服务之间可能存在着调用关系,为了尽量避免以后由于某个微服务的接口变化而导致其它微服务都做调整,在设计之初就要考虑到所有情况,让接口尽量做的更通用,更灵活,从而尽量避免其它模块也做调整。
+
+
+
+## 微服务之间是如何通讯的?
+
+**1、RPC**
+
+优点:简单,常见。因为没有中间件代理,系统更简单
+
+缺点:
+
+1. 只支持请求/响应的模式,不支持别的,比如通知、发布/订阅
+2. 降低了可用性,因为客户端和服务端在请求过程中必须都是可用的
+
+**2、消息队列**
+
+除了标准的基于RPC通信的微服务架构,还有基于消息队列通信的微服务架构,这种架构下的微服务采用发送消息(Publish Message)与监听消息(Subscribe Message)的方式来实现彼此之间的交互。
+
+网易的蜂巢平台就采用了基于消息队列的微服务架构设计思路,如下图所示,微服务之间通过RabbitMQ传递消息,实现通信。
+
+与上面几种微服务架构相比,基于消息队列的微服务架构并不多,案例也相对较少,更多地体现为一种与业务相关的设计经验,各家有各家的实现方式,缺乏公认的设计思路与参考架构,也没有形成一个知名的开源平台。因此,如果需要实施这种微服务架构,则基本上需要项目组自己从零开始去设计实现一个微服务架构基础平台,其代价是成本高、风险大。
+
+**优点**:
+
+- 把客户端和服务端解耦,更松耦合提高可用性,因为消息中间件缓存了消息,直到消费者可以消费
+- 支持很多通信机制比如通知、发布/订阅等
+
+**缺点**:
+
+- 缺乏公认的设计思路与参考架构,也没有形成一个知名的开源平台
+- 成本高、风险大
+
+## 熔断器
+
+雪崩效应:假如存在这样的调用链路,a服务->b服务->c服务,当c服务挂了的时候,b服务调用c服务会等待超时,a服务调用b服务也会等待超时,调用方长时间等不到相应而占用线程,如果有大量的请求过来,就会造成线程池打满,导致整个链路的服务奔溃。
+
+为了解决分布式系统的雪崩效应,分布式系统引进了**熔断器机制** 。
+
+当一个服务的处理用户请求的失败次数在一定时间内小于设定的阀值时,熔断器出于关闭状态,服务正常。
+
+当服务处理用户请求失败次数在一定时间内大于设定的阀值时,说明服务出现故障,打开熔断器,这时所有的请求会快速返回失败的错误信息,不执行业务逻辑,从而防止故障的蔓延。
+
+当处于打开状态的熔断器时,一段时间后出于半打开状态,并执行一定数量的请求,剩余的请求会执行快速失败,若执行请求失败了,则继续打开熔断器,若成功了,则将熔断器关闭。
+
+## 服务网关
+
+### 何为网关?
+
+通俗一点的讲:网关就是要去别的网络的时候,把报文首先发送到的那台设备。稍微专业一点的术语,网关就是当前主机的默认路由。
+
+网关一般就是一台路由器,有点像“一个小区中的一个邮局”,小区里面的住户互相是知道怎么走,但是要向外地投递东西就不知道了,怎么办?把地址写好送到本小区的邮局就好了。
+
+那么,怎么区分是“本小区”和“外地小区”的呢?根据IP地址 + 掩码。如果是在一个范围内的,就是本小区(局域网内部),如果掩不住的,就是外地的。
+
+例如,你的机器的IP地址是:192.168.0.2/24,网关是192.168.0.1
+
+如果机器访问的IP地址范围是:192.168.0.1~192.168.0.254的,说明是一个小区的邻居,你的机器就直接发送了(和网关没任何关系)。如果你访问的IP地址不是这个范围的,则就投递到192.168.0.1上,让这台设备来转发。
+
+参考:https://www.zhihu.com/question/362842680/answer/951412213
+
+### 何为API网关
+
+假设你正在开发一个电商网站,那么这里会涉及到很多后端的微服务,比如会员、商品、推荐服务等等。
+
+那么这里就会遇到一个问题,APP/Browser怎么去访问这些后端的服务? 如果业务比较简单的话,可以给每个业务都分配一个独立的域名(`https://service.api.company.com`),但这种方式会有几个问题:
+
+- 每个业务都会需要鉴权、限流、权限校验等逻辑,如果每个业务都各自为战,自己造轮子实现一遍,会很蛋疼,完全可以抽出来,放到一个统一的地方去做。
+- 如果业务量比较简单的话,这种方式前期不会有什么问题,但随着业务越来越复杂,比如淘宝、亚马逊打开一个页面可能会涉及到数百个微服务协同工作,如果每一个微服务都分配一个域名的话,一方面客户端代码会很难维护,涉及到数百个域名
+- 每上线一个新的服务,都需要运维参与,申请域名、配置Nginx等,当上线、下线服务器时,同样也需要运维参与,另外采用域名这种方式,对于环境的隔离也不太友好,调用者需要自己根据域名自己进行判断。
+- 另外还有一个问题,后端每个微服务可能采用了不同的协议,比如HTTP、AMQP、自定义TCP协议等,但是你不可能要求客户端去适配这么多种协议,这是一项非常有挑战的工作,项目会变的非常复杂且很难维护。
+
+更好的方式是采用API网关(也叫做服务网关),实现一个API网关**接管所有的入口流量**,类似Nginx的作用,将所有用户的请求转发给后端的服务器,但网关做的不仅仅只是简单的转发,也会针对流量做一些扩展,比如鉴权、限流、权限、熔断、协议转换、错误码统一、缓存、日志、监控、告警等,这样将通用的逻辑抽出来,由网关统一去做,业务方也能够更专注于业务逻辑,提升迭代的效率。
+
+
+
+通过引入API网关,客户端只需要与API网关交互,而不用与各个业务方的接口分别通讯,但多引入一个组件就多引入了一个潜在的故障点,因此要实现一个高性能、稳定的网关,也会涉及到很多点。
+
+网关层通常以集群的形式存在。并在服务网关层前通常会加上Nginx 用来负载均衡。
+
+**服务网关基本功能**:
+
+
+
+- 智能路由:接收**外部**一切请求,并转发到后端的对外服务。注意:我们只转发外部请求,服务之间的请求不走网关,这就表示全链路追踪、内部服务API监控、内部服务之间调用的容错、智能路由不能在网关完成;当然,也可以将所有的服务调用都走网关,那么几乎所有的功能都可以集成到网关中,但是这样的话,网关的压力会很大,不堪重负。
+- 权限校验:网关可以做一些用户身份认证,权限认证,防止非法请求操作API 接口,对内部服务起到保护作用
+- API监控:监控经过网关的请求,以及网关本身的一些性能指标(gc等)
+- 限流:与监控配合,进行限流操作
+- API日志统一收集:类似于一个aspect切面,记录接口的进入和出去时的相关日志
+
+当然,网关实现这些功能,需要做高可用,否则网关很可能成为架构的瓶颈,最常用的网关组件Zuul、Nginx
+
+## 服务配置统一管理
+
+在微服务架构中,需要有统一管理配置文件的组件,例如:SpringCloud Config组件、阿里的Diamond、百度的Disconf、携程的Apollo等
+
+## 服务链路追踪
+
+在微服务架构中,必须实现分布式链路追踪,去跟进一个请求到底有哪些服务参与、参与顺序,是每个请求链路清晰可见,便于问题快速定位。
+
+常用链路追踪组件有Google的Dapper、Twitter 的Zipkin,以及阿里Eagleeye。
+
+
+
+## 微服务框架
+
+市面常用微服务框架有:Spring Cloud 、Dubbo 、kubernetes
+
+- 从功能模块上考虑,Dubbo缺少很多功能模块,例如网关、链路追踪等
+- 从学习成本上考虑,Dubbo 版本趋于稳定,稳定完善、可以即学即用,难度简单,Spring cloud 基于Spring Boot,需要先掌握Spring Boot ,例外Spring cloud 大多为英文文档,要求学习者有一定的英文阅读能力
+- 从开发风格考虑,Dubbo倾向于xml的配置方式,Spring cloud 基于Spring Boot ,采用基于注解和JavaBean配置方式的敏捷开发
+- 从开发速度上考虑,Spring cloud 具有更高的开发和部署速度
+- 从通信方式上考虑,Spring cloud 基于HTTP Restful 风格,服务于服务之间完全无关、无耦合。Dubbo 基于远程调用,对接口、平台和语言有强依赖性,如果需要实现跨平台,需要有额外的中间件。
+
+所以Dubbo专注于服务治理;Spring Cloud关注于微服务架构生态。
+
+## 其他
+
+### Spring Cloud基础知识
+
+[Spring Cloud基础知识](../框架/SpringCloud微服务实战.md)
+
+### 什么是Service Mesh?
+
+Service Mesh 是微服务时代的 TCP/IP 协议。
+
+参考:https://zhuanlan.zhihu.com/p/61901608
+
+
diff --git "a/\345\210\206\345\270\203\345\274\217/\350\277\234\347\250\213\350\260\203\347\224\250.md" "b/\345\210\206\345\270\203\345\274\217/\350\277\234\347\250\213\350\260\203\347\224\250.md"
new file mode 100644
index 0000000..4f38569
--- /dev/null
+++ "b/\345\210\206\345\270\203\345\274\217/\350\277\234\347\250\213\350\260\203\347\224\250.md"
@@ -0,0 +1,119 @@
+## RPC简介
+
+RPC,英文全名remote procedure call,即远程过程调用。就是说一个应用部署在A服务器上,想要调用B服务器上应用提供的方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。
+
+可以这么说,RPC就是要像调用本地的函数一样去调远程函数。
+
+RPC是一个完整的远程调用方案,它通常包括通信协议和序列化协议。
+
+其中,通信协议包含**http协议**(如gRPC使用http2)、**自定义报文的tcp协议**(如dubbo)。序列化协议包含**基于文本编码**的xml、json,**基于二进制编码**的protobuf、hessian等。
+
+> protobuf 即 Protocol Buffers,是一种轻便高效的结构化数据存储格式,与语言、平台无关,可扩展可序列化。protobuf 性能和效率大幅度优于 JSON、XML 等其他的结构化数据格式。protobuf 是以二进制方式存储,占用空间小,但也带来了可读性差的缺点(二进制协议,因为不可读而难以调试,不好定位问题)。
+>
+> Protobuf序列化协议相比JSON有什么优点?
+>
+> 1:序列化后体积相比Json和XML很小,适合网络传输
+>
+> 2:支持跨平台多语言
+>
+> 3:序列化反序列化速度很快,快于Json的处理速速
+
+**一个完整的RPC过程,都可以用下面这张图来描述**:
+
+> stub说的都是“一小块代码”,通常是有个caller要调用callee的时候,中间需要一些特殊处理的逻辑,就会用这种“小块代码”去做。
+
+
+
+1. 服务消费端(client)以本地调用的方式调用远程服务;
+2. 客户端 Stub(client stub) 接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体(序列化):`RpcRequest`;
+3. 客户端 Stub(client stub) 找到远程服务的地址,并将消息发送到服务提供端;
+4. 服务端 Stub(桩)收到消息将消息反序列化为Java对象: `RpcRequest`;
+5. 服务端 Stub(桩)根据`RpcRequest`中的类、方法、方法参数等信息调用本地的方法;
+6. 服务端 Stub(桩)得到方法执行结果并将组装成能够进行网络传输的消息体:`RpcResponse`(序列化)发送至消费方;
+7. 客户端 Stub(client stub)接收到消息并将消息反序列化为Java对象:`RpcResponse` ,这样也就得到了最终结果。
+
+## RPC 解决了什么问题?
+
+让分布式或者微服务系统中不同服务之间的调用像本地调用一样简单。
+
+## 常见的 RPC 框架有哪些?
+
+- **Dubbo:** Dubbo是 阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成。目前 Dubbo 已经成为 Spring Cloud Alibaba 中的官方组件。
+- **gRPC** :基于HTTP2。gRPC是可以在任何环境中运行的现代开源高性能RPC框架。它可以通过可插拔的支持来有效地连接数据中心内和跨数据中心的服务,以实现负载平衡,跟踪,运行状况检查和身份验证。它也适用于分布式计算的最后一英里,以将设备,移动应用程序和浏览器连接到后端服务。
+- **Hessian:** Hessian是一个轻量级的remoting on http工具,使用简单的方法提供了RMI的功能。 采用的是二进制RPC协议,因为采用的是二进制协议,所以它很适合于发送二进制数据。
+- **Thrift:** Apache Thrift是Facebook开源的跨语言的RPC通信框架,目前已经捐献给Apache基金会管理,由于其跨语言特性和出色的性能,在很多互联网公司得到应用,有能力的公司甚至会基于thrift研发一套分布式服务框架,增加诸如服务注册、服务发现等功能。
+
+## 有了HTTP ,为啥还要用RPC进行服务调用?
+
+首先,RPC是一个完整的远程调用方案,它通常包括通信协议和序列化协议。而HTTP只是一个通信协议,不是一个完整的远程调用方案。这两者不是对等的概念,用来比较不太合适。
+
+RPC框架可以使用 **HTTP协议**作为传输协议或者直接使用**自定义的TCP协议**作为传输协议,使用不同的协议一般也是为了适应不同的场景。
+
+HTTP+Restful,其优势很大。它**可读性好**,且**应用广、跨语言的支持**。
+
+但是使用该方案也有其缺点,这是与其优点相对应的:
+
+- 首先是**有用信息占比少**,毕竟HTTP工作在第七层,包含了大量的HTTP头等信息。
+- 其次是**效率低**,还是因为第七层的缘故,必须按照HTTP协议进行层层封装。
+
+而使用**自定义tcp协议**的话,可以极大地精简了传输内容,这也是为什么有些后端服务之间会采用自定义tcp协议的rpc来进行通信的原因。
+
+## 各种序列化技术
+
+### XML
+
+XML序列化的好处在于可读性好,方便阅读和调试。但是序列化以后的字节码文件比较大,而且效率不高,适用于对性能要求不高,而且QPS较低的企业级内部系统之间的数据交换场景。同时XML又具有语言无关性,所以还可以用于异构系统之间的数据交换和协议。比如我们熟知的WebService,就是采用XML格式对数据进行序列化的。XML序列化/反序列化的实现方式有很多,熟知的方式有XStream和Java自带的XML序列化和反序列化两种。
+
+### JSON
+
+JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,相对于XML来说,JSON的字节流更小,而且可读性也非常好。现在JSON数据格式在企业运用是最普遍的。
+
+JSON序列化常用的开源工具有很多:
+
+1. Jackson(https://github.com/FasterXML/jackson )
+2. 阿里开源的FastJson(https://github.com/alibaba/fastjon)
+3. 谷歌的GSON(https://github.com/google/gson)
+
+这几种json的序列化工具中,jackson与fastjson要比GSON的性能好,但是jackson、GSON的稳定性腰比Fastjson好。而fastjson的优势在于提供的api非常容易使用。
+
+### Hession
+
+Hessian是一个支持跨语言传输的二进制序列化协议,相对于Java默认的序列化机制来说,Hession具有更好的性能和易读性,而且支持多种不同的语言。
+
+实际上Dubbo采用的就是Hessian序列化来实现,只不过Dubbo对Hessian进行了重构,性能更高。
+
+### Avro
+
+Avro是一个数据序列化系统,设计用于支持大批量数据交换的应用。它的主要特点有:支持二进制序列化方式,可以便捷,快速地处理大量数据。动态语言友好,Avro提供的机制是动态语言可以方便的处理Avro数据。
+
+### Kryo
+
+Kryo是一种非常成熟的序列化实现,已经在Hive、Storm中使用的比较广泛,不过它不能夸语言。目前Dubbo已经在2.6版本支持kyro的序列化机制。它的性能要由于之前的hessian2。
+
+### Protobuf
+
+Protobuf是Google的一种数据交换格式,它独立于语言、独立于平台。Google提供了多种语言来实现,比如Java、C、Go、Python,每一种实现都包含了相应语言的编译器和库文件,Protobuf是一个纯粹的表示层协议,可以和各种传输层协议一起使用。
+
+Protobuf使用比较广泛,主要是空间开销小和性能比较好,非常适合用于公司内部对性能要求高的RPC调用。另外由于解析性能比较高,序列化以后数据量相对较少,所以也可以应用在对象的持久化场景中。
+
+但是要使用Protobuf会相对来说麻烦些,因为他有自己的语法,有自己的编译器,如果需要用到的话必须要去投入成本在这个技术的学习中。
+
+Protobuf有个缺点就是要传输每一个类的结构都要生成对应的proto文件,如果某个类发生修改,还得重新生成该类对应的proto文件。
+
+## 序列化技术的选型
+
+### 技术层面
+
+1. 序列化空间开销,也就是序列化产生的结果大小,这个影响到传输性能。
+2. 序列化过程中消耗的时长,序列化消耗时间过长影响到业务的响应时间。
+3. 序列化协议是否支持夸平台,跨语言。因为现在的架构更加灵活,如果存在异构系统通信需求,那么这个是必须要考虑的。
+4. 可扩展性、兼容性,在实际业务开发中,系统往往需要随着需求的快速迭代来实现快速更新,这就要求我们来采用序列化协议具有良好的可扩展性、兼容性,比如现有的序列化数据结构中新增一个业务字段,不会影响到现有的服务。
+5. 技术的流行程度,越流行的技术意味着使用的公司越多,那么很多坑都已经淌过并且得到了解决,技术解决方案也相对成熟。
+6. 学习难度和易用性。
+
+### 选型建议
+
+1. 对性能要求不高的场景,可以采用基于XML的SOAP协议
+2. 性能和间接性有比较高要求的场景,那么Hessian、Protobuf、Thrift、Avro都可以。
+3. 基于前后端分离,或者独立的对外API服务,选用JSON是比较好的,对于调试、可读性都很不错。
+4. Avro设计理念偏于动态类型语言,那么这类的场景使用Avro是可以的。
\ No newline at end of file
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\345\237\272\347\241\200.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\345\237\272\347\241\200.md"
index b569ffc..111ea22 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\345\237\272\347\241\200.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\345\237\272\347\241\200.md"
@@ -457,7 +457,7 @@ WHERE Soundex(cust_contact) = Soundex('Y Lie');
### 日期处理函数
-
+
查找2005年9月的所有订单:
```mysql
@@ -476,7 +476,7 @@ WHERE Year(order_date) = 2005 AND Month(order_date) = 9;
### 数值处理函数
-
+
## 汇总数据
@@ -511,7 +511,7 @@ SELECT * FROM orders
GROUP BY cust_id;
```
-
+
除聚集计算语句外,SELECT语句中的每个列都必须在GROUP BY子句中给出。
@@ -630,7 +630,7 @@ SELECT * FROM role NATURAL JOIN user_role;
返回结果:
-
+
### 内连接
@@ -642,7 +642,7 @@ SELECT * FROM role INNNER JOIN user_role
返回结果:
-
+
join…using(column)按指定的属性做等值连接。
join…on tableA.column1 = tableB.column2 指定条件。
@@ -653,7 +653,7 @@ SELECT * FROM role INNER JOIN user_role ON role.role_id = user_role.role_id
返回结果:
-
+
### 外连接
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 8ec0b08..d829b99 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -132,6 +132,77 @@ set session transaction isolation level read uncommitted;
[互联网项目中mysql应该选什么事务隔离级别](https://zhuanlan.zhihu.com/p/59061106)
+## 编码和字符集的关系
+
+我们平时可以在编辑器上输入各种中文英文字母,但这些都是给人读的,不是给计算机读的,其实计算机真正保存和传输数据都是以**二进制**0101的格式进行的。
+
+那么就需要有一个规则,把中文和英文字母转化为二进制。其中d对应十六进制下的64,它可以转换为01二进制的格式。于是字母和数字就这样一一对应起来了,这就是**ASCII编码**格式。
+
+它用**一个字节**,也就是`8位`来标识字符,基础符号有128个,扩展符号也是128个。也就只能表示下**英文字母和数字**。
+
+这明显不够用。于是,为了标识**中文**,出现了**GB2312**的编码格式。为了标识**希腊语**,出现了**greek**编码格式,为了标识**俄语**,整了**cp866**编码格式。
+
+为了统一它们,于是出现了**Unicode编码格式**,它用了2~4个字节来表示字符,这样理论上所有符号都能被收录进去,并且它还完全兼容ASCII的编码,也就是说,同样是字母d,在ASCII用64表示,在Unicode里还是用64来表示。
+
+但**不同的地方是ASCII编码用1个字节来表示,而Unicode用则两个字节来表示。**
+
+同样都是字母d,unicode比ascii多使用了一个字节,如下:
+
+```mysql
+D ASCII: 01100100
+D Unicode: 00000000 01100100
+```
+
+可以看到,上面的unicode编码,前面的都是0,其实用不上,但还占了个字节,有点浪费。如果我们能做到该隐藏时隐藏,这样就能省下不少空间,按这个思路,就是就有了**UTF-8编码**。
+
+总结一下,按照一定规则把符号和二进制码对应起来,这就是**编码**。而把n多这种已经编码的字符聚在一起,就是我们常说的**字符集**。
+
+比如utf-8字符集就是所有utf-8编码格式的字符的合集。
+
+想看下mysql支持哪些字符集。可以执行 `show charset;`
+
+## utf8和utf8mb4的区别
+
+上面提到utf-8是在unicode的基础上做的优化,既然unicode有办法表示所有字符,那utf-8也一样可以表示所有字符,为了避免混淆,我在后面叫它**大utf8**。
+
+mysql支持的字符集中有utf8和utf8mb4。
+
+先说**utf8mb4**编码,mb4就是**most bytes 4**的意思,从上图最右边的`Maxlen`可以看到,它最大支持用**4个字节**来表示字符,它几乎可以用来表示目前已知的所有的字符。
+
+再说mysql字符集里的**utf8**,它是数据库的**默认字符集**。但注意,**此utf8非彼utf8**,我们叫它**小utf8**字符集。为什么这么说,因为从Maxlen可以看出,它最多支持用3个字节去表示字符,按utf8mb4的命名方式,准确点应该叫它**utf8mb3**。
+
+utf8 就像是阉割版的utf8mb4,只支持部分字符。比如`emoji`表情,它就不支持。
+
+而mysql支持的字符集里,第三列,**collation**,它是指**字符集的比较规则**。
+
+比如,**"debug"和"Debug"**是同一个单词,但它们大小写不同,该不该判为同一个单词呢。
+
+这时候就需要用到collation了。
+
+通过`SHOW COLLATION WHERE Charset = 'utf8mb4';`可以查看到`utf8mb4`下支持什么比较规则。
+
+
+
+如果`collation = utf8mb4_general_ci`,是指使用utf8mb4字符集的前提下,**挨个字符进行比较**(`general`),并且不区分大小写(`_ci,case insensitice`)。
+
+这种情况下,**"debug"和"Debug"是同一个单词**。
+
+如果改成`collation=utf8mb4_bin`,就是指**挨个比较二进制位大小**。
+
+于是**"debug"和"Debug"就不是同一个单词。**
+
+如果改成`collation=utf8mb4_bin`,就是指**挨个比较二进制位大小**。
+
+于是**"debug"和"Debug"就不是同一个单词。**
+
+**那utf8mb4对比utf8有什么劣势吗?**
+
+我们知道数据库表里,字段类型如果是`char(2)`的话,里面的`2`是指**字符个数**,也就是说**不管这张表用的是什么编码的字符集**,都能放上2个字符。
+
+而char又是**固定长度**,为了能放下2个utf8mb4的字符,char会默认保留`2*4(maxlen=4)= 8`个字节的空间。
+
+如果是utf8mb3,则会默认保留 `2 * 3 (maxlen=3) = 6`个字节的空间。也就是说,在这种情况下,**utf8mb4会比utf8mb3多使用一些空间。**
+
## 索引
### 什么是索引?
@@ -510,16 +581,6 @@ select * from table where id<6 for update;--排他锁
2. 根据主键进行查询,查询条件为`like`或者不等于,主键字段产生**表锁**。
3. 根据非索引字段进行查询,会产生**表锁**。
-## 大表怎么优化?
-
-某个表有近千万数据,查询比较慢,如何优化?
-
-当MySQL单表记录数过大时,数据库的性能会明显下降,一些常见的优化措施如下:
-
-* 限定数据的范围。比如:用户在查询历史信息的时候,可以控制在一个月的时间范围内;
-* 读写分离: 经典的数据库拆分方案,主库负责写,从库负责读;
-* 通过分库分表的方式进行优化,主要有垂直拆分和水平拆分。
-
## bin log/redo log/undo log
MySQL日志主要包括查询日志、慢查询日志、事务日志、错误日志、二进制日志等。其中比较重要的是 `bin log`(二进制日志)和 `redo log`(重做日志)和 `undo log`(回滚日志)。
@@ -762,7 +823,7 @@ int(10)中的10表示的是显示数据的长度,而char(10)表示的是存储
数据库中的并发控制是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。乐观锁和悲观锁是并发控制主要采用的技术手段。
-* 悲观锁:假定会发生并发冲突,在查询完数据的时候就把事务锁起来,直到提交事务。实现方式:使用数据库中的锁机制。
+* 悲观锁:假定会发生并发冲突,会对操作的数据进行加锁,直到提交事务,才会释放锁,其他事务才能进行修改。实现方式:使用数据库中的锁机制。
* 乐观锁:假设不会发生并发冲突,只在提交操作时检查是否数据是否被修改过。给表增加`version`字段,在修改提交之前检查`version`与原来取到的`version`值是否相等,若相等,表示数据没有被修改,可以更新,否则,数据为脏数据,不能更新。实现方式:乐观锁一般使用版本号机制或`CAS`算法实现。
## 用过processlist吗?
@@ -804,11 +865,11 @@ server层有很多模块,其中需要关注的是**执行器**是用于跟存
以主键索引的limit执行过程为例:
-执行`select * from page order by id limit 0, 10;`,select后面带的是**星号**,也就是要求获得行数据的**所有字段信息。**
+执行`select * from xxx order by id limit 0, 10;`,select后面带的是**星号**,也就是要求获得行数据的**所有字段信息。**
server层会调用innodb的接口,在innodb里的主键索引中获取到第0到10条**完整行数据**,依次返回给server层,并放到server层的结果集中,返回给客户端。
-把offset搞大点,比如执行的是:`select * from page order by id limit 500000, 10;`
+把offset搞大点,比如执行的是:`select * from xxx order by id limit 500000, 10;`
server层会调用innodb的接口,由于这次的offset=500000,会在innodb里的主键索引中获取到第0到(500000 + 10)条**完整行数据**,**返回给server层之后根据offset的值挨个抛弃,最后只留下最后面的size条**,也就是10条数据,放到server层的结果集中,返回给客户端。
@@ -818,7 +879,7 @@ server层会调用innodb的接口,由于这次的offset=500000,会在innodb
## 深分页怎么优化?
-还是以上面的SQL为空:`select * from page order by id limit 500000, 10;`
+还是以上面的SQL为空:`select * from xxx order by id limit 500000, 10;`
**方法一**:
@@ -826,18 +887,18 @@ server层会调用innodb的接口,由于这次的offset=500000,会在innodb
因为前面的offset条数据最后都是不要的,没有必要拷贝完整字段,所以可以将sql语句修改成:
-```mysql
-select * from page where id >=(select id from page order by id limit 500000, 1) order by id limit 10;
+```
+select * from xxx where id >=(select id from xxx order by id limit 500000, 1) order by id limit 10;
```
-先执行子查询 `select id from page order by id limit 500000, 1`, 这个操作,其实也是将在innodb中的主键索引中获取到`500000+1`条数据,然后server层会抛弃前500000条,只保留最后一条数据的id。
+先执行子查询 `select id from xxx by id limit 500000, 1`, 这个操作,其实也是将在innodb中的主键索引中获取到`500000+1`条数据,然后server层会抛弃前500000条,只保留最后一条数据的id。
但不同的地方在于,在返回server层的过程中,只会拷贝数据行内的id这一列,而不会拷贝数据行的所有列,当数据量较大时,这部分的耗时还是比较明显的。
在拿到了上面的id之后,假设这个id正好等于500000,那sql就变成了
-```mysql
-select * from page where id >=500000 order by id limit 10;
+```
+select * from xxx where id >=500000 order by id limit 10;
```
这样innodb再走一次**主键索引**,通过B+树快速定位到id=500000的行数据,时间复杂度是lg(n),然后向后取10条数据。
@@ -846,9 +907,11 @@ select * from page where id >=500000 order by id limit 10;
将所有的数据**根据id主键进行排序**,然后分批次取,将当前批次的最大id作为下次筛选的条件进行查询。
-```mysql
-select * from page where id > start_id order by id limit 10;
```
+select * from xxx where id > start_id order by id limit 10;
+```
+
+mysql
通过主键索引,每次定位到start_id的位置,然后往后遍历10个数据,这样不管数据多大,查询性能都较为稳定。
@@ -890,8 +953,21 @@ B+树中**非叶子节点存的是key + 指针**;**叶子节点存的是数据
1. MySQL 单表数据量大于 2000 万行,性能会明显下降,考虑进行分库分表。
2. 阿里巴巴《Java 开发手册》提出单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。
-事实上,这个数值和实际记录的条数无关,而与 MySQL 的配置以及机器的硬件有关。因为,MySQL 为了提高性能,会将表的索引装载到内存中。InnoDB buffer size 足够的情况下,其能完成全加载进内存,查询不会有问题。但是,当单表数据库到达某个量级的上限时,导致内存无法存储其索引,使得之后的 SQL 查询会产生磁盘 IO,从而导致性能下降。当然,这个还有具体的表结构的设计有关,最终导致的问题都是内存限制。
+事实上,这个数值和实际记录的条数无关,而与 MySQL 的配置以及机器的硬件有关。因为MySQL为了提高性能,会将表的索引装载到内存中。在InnoDB buffer size 足够的情况下,其能完成全加载进内存,查询不会有问题。但是,当单表数据库到达某个量级的上限时,导致内存无法存储其索引,使得之后的 SQL 查询会产生磁盘 IO,从而导致性能下降。当然,这个还有具体的表结构的设计有关,最终导致的问题都是内存限制。
因此,对于分库分表,需要结合实际需求,不宜过度设计,在项目一开始不采用分库与分表设计,而是随着业务的增长,在无法继续优化的情况下,再考虑分库与分表提高系统的性能。对此,阿里巴巴《Java 开发手册》补充到:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。
至于MySQL单表多大进行分库分表,应当根据机器资源进行评估。
+
+## 大表查询慢怎么优化?
+
+某个表有近千万数据,查询比较慢,如何优化?
+
+当MySQL单表记录数过大时,数据库的性能会明显下降,一些常见的优化措施如下:
+
+* 合理建立索引。在合适的字段上建立索引,例如在WHERE和ORDER BY命令上涉及的列建立索引,可根据EXPLAIN来查看是否用了索引还是全表扫描
+* 建立分区。对关键字段建立水平分区,比如时间字段,若查询条件往往通过时间范围来进行查询,能提升不少性能
+* 利用缓存。利用Redis等缓存热点数据,提高查询效率
+* 限定数据的范围。比如:用户在查询历史信息的时候,可以控制在一个月的时间范围内
+* 读写分离。经典的数据库拆分方案,主库负责写,从库负责读
+* 通过分库分表的方式进行优化,主要有垂直拆分和水平拆分
diff --git "a/\346\241\206\346\236\266/Apollo\351\205\215\347\275\256\344\270\255\345\277\203.md" "b/\346\241\206\346\236\266/Apollo\351\205\215\347\275\256\344\270\255\345\277\203.md"
new file mode 100644
index 0000000..bd248f4
--- /dev/null
+++ "b/\346\241\206\346\236\266/Apollo\351\205\215\347\275\256\344\270\255\345\277\203.md"
@@ -0,0 +1,44 @@
+Apollo从设计之初就立志于成为一个有治理能力的配置发布平台,目前提供了以下的特性
+
+- **统一管理不同环境、不同集群的配置**
+
+- - Apollo提供了一个统一界面集中式管理不同环境(Environment)、不同集群(Cluster)、不同命名空间(Namespace)的配置
+ - 同一份代码部署在不同的集群,可以有不同的配置
+ - 通过命名空间可以很方便地支持多个不同应用共享同一份配置,同时还允许应用对共享的配置进行覆盖
+
+- **热发布--配置修改实时生效** 用户在Apollo修改完配置并发布后,客户端能实时(1秒)接收到最新的配置,并通知到应用程序
+
+- **版本发布管理** 所有的配置发布都有版本概念,从而可以方便地支持配置的回滚
+
+- **灰度发布** 支持配置的灰度发布,比如点了发布后,只对部分应用实例生效,等观察一段时间没问题后再推给所有应用实例
+
+- **权限管理、发布审核、操作审计**
+
+- - 应用和配置的管理都有完善的权限管理机制,对配置的管理还分为了编辑和发布两个环节,从而减少人为的错误
+ - 所有的操作都有审计日志,可以方便地追踪问题
+
+- **客户端配置信息监控** 可以在界面上方便地看到配置在被哪些实例使用
+
+- **提供Java和.Net原生客户端**
+
+- - 提供了Java和.Net的原生客户端,方便应用集成
+ - 支持Spring Placeholder, Annotation和Spring Boot的ConfigurationProperties,方便应用使用(需要Spring 3.1.1+)
+ - 同时提供了Http接口,非Java和.Net应用也可以方便地使用
+
+- 提供开放平台API
+
+- 部署简单
+
+
+
+**基础模型**
+
+- 用户在配置中心对配置进行修改并发布
+- 配置中心通知Apollo客户端有配置更新
+- Apollo客户端从配置中心拉取最新的配置、更新本地配置并通知到应用
+
+
+
+
+
+**配置中心如何做到实时更新并且通知到客户端的?**
\ No newline at end of file
diff --git "a/\346\241\206\346\236\266/SpringCloud\345\276\256\346\234\215\345\212\241\345\256\236\346\210\230.md" "b/\346\241\206\346\236\266/SpringCloud\345\276\256\346\234\215\345\212\241\345\256\236\346\210\230.md"
index 6d305f7..f1f910b 100644
--- "a/\346\241\206\346\236\266/SpringCloud\345\276\256\346\234\215\345\212\241\345\256\236\346\210\230.md"
+++ "b/\346\241\206\346\236\266/SpringCloud\345\276\256\346\234\215\345\212\241\345\256\236\346\210\230.md"
@@ -1543,7 +1543,7 @@ OAuth在"客户端"(云冲印)与"服务提供商"(谷歌)之间,设
"客户端"登录授权层以后,"服务提供商"根据令牌的权限范围和有效期,向"客户端"开放用户储存的资料。
-
+
(A)用户打开客户端以后,客户端要求用户给予授权。
diff --git "a/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\343\200\220\344\274\230\345\214\226\347\211\210\343\200\221\345\246\202\344\275\225\344\273\216\346\265\267\351\207\217\346\225\260\346\215\256\346\211\276\345\207\272\351\253\230\351\242\221\350\257\215.md" "b/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\343\200\220\344\274\230\345\214\226\347\211\210\343\200\221\345\246\202\344\275\225\344\273\216\346\265\267\351\207\217\346\225\260\346\215\256\346\211\276\345\207\272\351\253\230\351\242\221\350\257\215.md"
new file mode 100644
index 0000000..c6ec6e8
--- /dev/null
+++ "b/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\343\200\220\344\274\230\345\214\226\347\211\210\343\200\221\345\246\202\344\275\225\344\273\216\346\265\267\351\207\217\346\225\260\346\215\256\346\211\276\345\207\272\351\253\230\351\242\221\350\257\215.md"
@@ -0,0 +1,65 @@
+本期是【**大厂面试**】系列文章的第**34**期
+
+回复【**手册**】获取大彬精心整理的**大厂面试手册**。
+
+## 面试开始
+
+旁白:通过微信聊天方式模拟现场面试,题目出自腾讯PCG一面
+
+**面试官**:**上回考了你一道场景题,还记得不?**
+
+**面试官**:**题目是这样的,一个1G大小的文件,文件里每一行是一个词,每个词的大小不超过16byte,要求返回出现频率最高的100个词**
+
+**面试官**:**内存大小限制是10M**
+
+**面试官**:**当时你给的解法有问题啊,还好我回去查了下,不然就给蒙混过关了。。**
+
+**面试官**:**你用的是分治的思想,进行哈希取余,放进小文件。**
+
+**面试官**:**但是在极端情况,比如都是同一个词或者某一个词超过10m,小文件会超过10m内存限制啊**
+
+大彬:是的哦,上次的解法不严谨,今天给你介绍另一种解法:
+
+**第一步**,对大文件的单词进行排序,排序之后,相同的单词会紧挨着
+
+排序的步骤如下:
+
+1.1 将文件按照顺序切分成大小不超过2m的小文件,总共500个小文件
+
+1.2 使用10m内存**分别**对500个小文件中的单词进行**排序**
+
+1.3 使用一个大小为500大小的堆,对500个小文件进行**多路排序**,结果写到一个大文件中
+
+其中1.3步骤中,对500个小文件进行多路排序的思路如下:
+
+- 初始化一个最小堆,大小就是小文件的个数500。堆中的每个节点存放每个有序小文件对应的输入流。
+- 按照每个有序文件中的下一行数据对所有文件输入流进行排序,单词小的输入文件流放在堆顶。
+- 拿出堆顶的输入流,并其下一行数据写入到最终排序的文件中,如果拿出来的输入流中还有数据的话,那么将这个输入流再一次添加到堆中。否则说明该文件输入流中没有数据了,那么可以关闭这个流。
+- 循环这个过程,直到所有文件输入流都没有数据为止。
+
+**第二步**:
+
+2.1 初始化一个100个节点的**小顶堆**,用于保存100个出现频率最多的单词
+
+2.2 遍历整个文件,一个单词一个单词的从文件中取出来,并计数
+
+2.3 等到遍历的单词和上一个单词不同的话,那么上一个单词及其频率如果大于堆顶的词的频率,那么放在堆中,否则不放
+
+最终,小顶堆中就是出现频率前100的单词了。
+
+可以看出,这种解法相对之前的解法,更为严谨,就算整个文件都是同一个词,此解法同样有效。
+
+**面试官**:行吧,今天面试就到这里吧
+
+
+
+## 点关注,不迷路
+
+大彬,**非科班出身,自学Java**,校招斩获京东、携程、华为等offer。作为一名转码选手,深感这一路的不易。
+
+希望我的分享能帮助到更多的小伙伴,**我踩过的坑你们不要再踩**。想与大彬交流的话,可以到公众号后台获取大彬的微信~
+
+后台回复『 **笔记**』即可领取大彬斩获大厂offer的**面试笔记**。
+
+
+
diff --git "a/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\345\246\202\344\275\225\344\273\216\346\265\267\351\207\217\346\225\260\346\215\256\346\211\276\345\207\272\351\253\230\351\242\221\350\257\215 - wx.md" "b/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\345\246\202\344\275\225\344\273\216\346\265\267\351\207\217\346\225\260\346\215\256\346\211\276\345\207\272\351\253\230\351\242\221\350\257\215 - wx.md"
new file mode 100644
index 0000000..02d5b41
--- /dev/null
+++ "b/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\345\246\202\344\275\225\344\273\216\346\265\267\351\207\217\346\225\260\346\215\256\346\211\276\345\207\272\351\253\230\351\242\221\350\257\215 - wx.md"
@@ -0,0 +1,66 @@
+本期是【**大厂面试**】系列文章的第**26**期
+
+回复【**手册**】获取大彬精心整理的**大厂面试手册**。
+
+## 面试开始
+
+旁白:通过微信聊天方式模拟现场面试,题目出自腾讯PCG一面
+
+**面试官**:**今天来道场景题**
+
+**面试官**:假如有一个1G大小的文件,文件里每一行是一个词,每个词的大小不超过16byte,要求返回出现频率最高的100个词
+
+**面试官**:内存大小限制是10M
+
+旁白:由于内存限制,我们无法直接将大文件的所有词一次性读到内存中
+
+旁白:可以采用**分治策略**,把一个大文件分解成多个小文件,保证每个文件的大小小于10M,进而直接将单个小文件读取到内存中进行处理
+
+**大彬**:第一步,首先遍历大文件,对遍历到的每个词x,执行 `hash(x) % 500`,将结果为i的词存放到文件f(i)中
+
+**大彬**:遍历结束后,可以得到500个小文件
+
+**大彬**:之所以使用500个小文件,是因为原文件大小为1G,1G/500=2M,每个小文件的大小为2M左右,基本不会超过内存大小10M的限制
+
+**大彬**:第二步,接着统计每个小文件中出现频数最高的100个词
+
+**大彬**:可以使用HashMap来实现,其中key为词,value为该词出现的频率
+
+**大彬**:对于遍历到的词x,如果在map中不存在,则执行 `map.put(x, 1)`
+
+**大彬**:若存在,则执行 `map.put(x, map.get(x)+1)`,将该词出现的次数加1
+
+**大彬**:第三步,在第二步中找出了每个文件出现频率最高的100个词之后,通过维护一个小顶堆来找出所有小文件中出现频率最高的100个词
+
+**大彬**:具体方法是,遍历第一个文件,把第一个文件中出现频率最高的100个词构建成一个小顶堆
+
+**大彬**:如果第一个文件中词的个数小于100,可以继续遍历第二个文件,直到构建好有100个结点的小顶堆为止
+
+**大彬**:继续遍历其他小文件,如果遍历到的词的出现次数大于堆顶上词的出现次数,可以用新遍历到的词替换堆顶的词,然后重新调整这个堆为小顶堆
+
+**大彬**:当遍历完所有小文件后,这个小顶堆中的词就是出现频率最高的100个词
+
+**大彬**:最后总结一下
+
+1. 采用**分治**的思想,进行哈希取余
+2. 使用**HashMap**统计每个小文件单词出现的次数
+3. 使用**小顶堆**,遍历步骤2中的小文件,找出出现频率Top100的单词
+
+面试官:在第二步中,将1G的文件分解到500个小文件,小文件的大小可能超过10M吧?怎么处理?
+
+大彬:如果某个小文件的大小超过10MB了,可以采用相同的方法对这个文件继续分解成g(1)...g(x),直到文件的大小小于10MB为止
+
+**面试官**:good!今天面试就到这吧
+
+
+
+## 点关注,不迷路
+
+大彬,**非科班出身,自学Java**,校招斩获京东、携程、华为等offer。作为一名转码选手,深感这一路的不易。
+
+希望我的分享能帮助到更多的小伙伴,**我踩过的坑你们不要再踩**。想与大彬交流的话,可以到公众号后台获取大彬的微信~
+
+后台回复『 **笔记**』即可领取大彬斩获大厂offer的**面试笔记**。
+
+
+
diff --git "a/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\347\224\265\350\257\235\345\217\267\347\240\201.md" "b/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\347\224\265\350\257\235\345\217\267\347\240\201.md"
new file mode 100644
index 0000000..7a452aa
--- /dev/null
+++ "b/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\347\224\265\350\257\235\345\217\267\347\240\201.md"
@@ -0,0 +1,82 @@
+本期是【**大厂面试**】系列文章的第**35**期
+
+回复【**手册**】获取大彬精心整理的**大厂面试手册**。
+
+## 面试开始
+
+旁白:通过微信聊天方式模拟现场面试,题目出自百度一面
+
+**面试官**:**已知某个文件内包含大量电话号码,每个号码为8位数字,如何统计不同号码的个数?**
+
+旁白:这类题目其实是求解数据重复的问题
+
+大彬:对于这类问题,可以使用**位图法**处理
+
+大彬:8位电话号码可以表示的范围为00000000~99999999。
+
+大彬:如果用 bit表示一个号码,那么总共需要1亿个bit,总共需要大约100MB的内存。
+
+大彬:申请一个位图并初始化为0,然后遍历所有电话号码,把遍历到的电话号码对应的位图中的bit设置为1。
+
+大彬:当遍历完成后,如果bit值为1,则表示这个电话号码在文件中存在,否则这个bit对应的电话号码在文件中不存在。
+
+大彬:所以这个位图中bit值为1的数量就是不同电话号码的个数了。
+
+面试官:那么如何确定电话号码对应的是位图中的哪一位呢?
+
+大彬:嗯,可以使用下面的方法来做电话号码和位图的映射。
+
+```java
+00000000 对应位图最后一位:0×0000…000001。
+00000001 对应位图倒数第二位:0×0000…0000010(1 向左移 1 位)。
+00000002 对应位图倒数第三位:0×0000…0000100(1 向左移 2 位)。
+……
+00000012 对应位图的倒数第十三位:0×0000…0001 0000 0000 0000(1 向左移 12 位)。
+```
+
+大彬:也就是说,电话号码就是1这个数字左移的次数。
+
+**面试官:那用代码怎么实现呢?**
+
+大彬:首先位图可以使用一个int数组来实现。
+
+旁白:在Java中int占用**4byte**
+
+大彬:假设电话号码为 P,而通过电话号码获取位图中对应位置的方法为:
+
+大彬:第一步,因为int整数占用4*8=32bit,通过 **P/32** 就可以计算出该电话号码在 bitmap 数组中的下标,从而可以确定它对应的 bit 在数组中的位置。
+
+大彬:第二步,通过 P%32 就可以计算出这个电话号码在这个int数字中具体的bit的位置。
+
+大彬:只要把1向左移 **P%32** 位,然后把得到的值与这个数组中的值做或运算,就可以把这个电话号码在位图中对应的位设置为1。
+
+大彬:以00000100号码为例
+
+大彬:首先计算数组下标,100 / 32 = 3,得到数组下标位3
+
+大彬:然后计算电话号码在这个int数字中具体的bit的位置,100 % 32 = 4
+
+大彬:取余为0左移1位,故取余为4左移5位,得到000...000100000
+
+大彬:将位图中对应的位设置为 1,即arr[2] = arr[2] **|** 000..00100000
+
+大彬:这就将电话号码映射到了位图的某一位了。
+
+
+
+**面试官**:点赞
+
+**面试官**:今天面试就到这里吧
+
+
+
+## 点关注,不迷路
+
+大彬,**非科班出身,自学Java**,校招斩获京东、携程、华为等offer。作为一名转码选手,深感这一路的不易。
+
+希望我的分享能帮助到更多的小伙伴,**我踩过的坑你们不要再踩**。想与大彬交流的话,可以到公众号后台获取大彬的微信~
+
+后台回复『 **笔记**』即可领取大彬斩获大厂offer的**面试笔记**。
+
+
+
diff --git "a/\347\263\273\347\273\237\350\256\276\350\256\241/\346\211\253\347\240\201\347\231\273\345\275\225\345\216\237\347\220\206.md" "b/\347\263\273\347\273\237\350\256\276\350\256\241/\346\211\253\347\240\201\347\231\273\345\275\225\345\216\237\347\220\206.md"
new file mode 100644
index 0000000..01224d9
--- /dev/null
+++ "b/\347\263\273\347\273\237\350\256\276\350\256\241/\346\211\253\347\240\201\347\231\273\345\275\225\345\216\237\347\220\206.md"
@@ -0,0 +1,85 @@
+本期是【**大厂面试**】系列文章的第**31**期
+
+回复【**手册**】获取大彬精心整理的**大厂面试手册**。
+
+## 面试开始
+
+旁白:通过微信聊天方式模拟现场面试
+
+**面试官**:**看你简历上写了做过扫码登录的功能**
+
+**面试官**:**详细介绍下怎么设计的?**
+
+大彬:嗯,扫码登录功能主要分为三个阶段:**待扫描、已扫描待确认、已确认**。
+
+大彬:整体流程图如下图。
+
+
+
+大彬:下面分阶段来看看设计原理。
+
+**1、待扫描阶段**
+
+首先是待扫描阶段,这个阶段是 PC 端跟服务端的交互过程。
+
+每次用户打开PC端登陆请求,系统返回一个**唯一的二维码ID**,并将二维码ID的信息绘制成二维码返回给用户。
+
+这里的二维码ID一定是唯一的,后续流程会将二维码ID跟身份信息绑定,不唯一的话就会造成你登陆了其他用户的账号或者其他用户登陆你的账号。
+
+此时在 PC 端会启动一个定时器,**轮询查询二维码是否被扫描**。
+
+如果移动端未扫描的话,那么一段时间后二维码将会失效。
+
+这个阶段的交互过程如下图所示。
+
+
+
+**2、已扫描待确认阶段**
+
+第二个阶段是已扫描待确认阶段,主要是移动端跟服务端交互的过程。
+
+首先移动端扫描二维码,获取二维码 ID,然后**将手机端登录的凭证(token)和 二维码 ID 作为参数发送给服务端**
+
+此时的手机在之前已经是登录的,不存在没登录的情况。
+
+服务端接受请求后,会将 token 与二维码 ID 关联,然后会生成一个临时token,这个 token 会返回给移动端,临时 token 用作确认登录的凭证。
+
+PC 端的定时器,会轮询到二维码的状态已经发生变化,会将 PC 端的二维码更新为已扫描,请在手机端确认。
+
+**面试官**:**打断一下,这里为什么要有手机端确认的操作?**
+
+大彬:假设没有确认这个环节,很容易就会被坏人拦截token去冒充登录。所以二维码扫描一定要有这个确认的页面,让用户去确认是否进行登录。
+
+大彬:另外,二维码扫描确认之后,再往用户app或手机等发送登录提醒的通知,告知如果不是本人登录的,则建议用户立即修改密码。
+
+大彬:这个阶段是交互过程如下图所示。
+
+
+
+**3、已确认**
+
+扫码登录的最后阶段,用户点击确认登录,移动端携带上一步骤中获取的临时 token访问服务端。
+
+服务端校对完成后,会更新二维码状态,并且给 PC 端生成一个正式的 token。
+
+后续 PC 端就是持有这个 token 访问服务端。
+
+这个阶段是交互过程如下图所示。
+
+
+
+大彬:以上就是整个扫码登录功能的详细设计!
+
+**面试官**:**点赞!**
+
+
+
+## 点关注,不迷路
+
+大彬,**非科班出身,自学Java**,校招斩获京东、携程、华为等offer。作为一名转码选手,深感这一路的不易。
+
+希望我的分享能帮助到更多的小伙伴,**我踩过的坑你们不要再踩**。想与大彬交流的话,可以到公众号后台获取大彬的微信~
+
+后台回复『 **笔记**』即可领取大彬斩获大厂offer的**面试笔记**。
+
+
diff --git "a/\347\263\273\347\273\237\350\256\276\350\256\241/\347\263\273\347\273\237\350\256\276\350\256\241.md" "b/\347\263\273\347\273\237\350\256\276\350\256\241/\347\263\273\347\273\237\350\256\276\350\256\241.md"
new file mode 100644
index 0000000..036738f
--- /dev/null
+++ "b/\347\263\273\347\273\237\350\256\276\350\256\241/\347\263\273\347\273\237\350\256\276\350\256\241.md"
@@ -0,0 +1,176 @@
+# 超卖问题
+
+先到数据库查询库存,在减库存。不是原子操作,会有超卖问题。
+
+通过加排他锁解决该问题。
+
+- 开始事务。
+- 查询库存,并显式的设置排他锁:SELECT * FROM table_name WHERE … FOR UPDATE
+- 生成订单。
+- 去库存,update会隐式的设置排他锁:UPDATE products SET count=count-1 WHERE id=1
+- commit,释放锁。
+
+也可以通过乐观锁实现。使用版本号实现乐观锁。
+
+假设此时version = 100, num = 1; 100个线程进入到了这里,同时他们select出来版本号都是version = 100。
+
+然后直接update的时候,只有其中一个先update了,同时更新了版本号。
+
+那么其他99个在更新的时候,会发觉version并不等于上次select的version,就说明version被其他线程修改过了,则放弃此次update,重试直到成功。
+
+```mysql
+select version from goods WHERE id= 1001
+update goods set num = num - 1, version = version + 1 WHERE id= 1001 AND num > 0 AND version = @version(上面查到的version);
+```
+
+# 秒杀系统
+
+## 系统的特点
+
+- 高性能:秒杀涉及大量的并发读和并发写,因此支持高并发访问这点非常关键
+- 一致性:秒杀商品减库存的实现方式同样关键,有限数量的商品在同一时刻被很多倍的请求同时来减库存,在大并发更新的过程中都要保证数据的准确性。
+- 高可用:秒杀时会在一瞬间涌入大量的流量,为了避免系统宕机,保证高可用,需要做好流量限制
+
+## 优化思路
+
+- 后端优化:将请求尽量拦截在系统上游
+ - 限流:屏蔽掉无用的流量,允许少部分流量走后端。假设现在库存为 10,有 1000 个购买请求,最终只有 10 个可以成功,99% 的请求都是无效请求
+ - 削峰:秒杀请求在时间上高度集中于某一个时间点,瞬时流量容易压垮系统,因此需要对流量进行削峰处理,缓冲瞬时流量,尽量让服务器对资源进行平缓处理
+ - 异步:将同步请求转换为异步请求,来提高并发量,本质也是削峰处理。在真实的秒杀场景中,有三个核心流程:而这三个核心流程中,真正并发量大的是秒杀功能,下单和支付功能实际并发量很小。所以,我们在设计秒杀系统时,有必要把下单和支付功能从秒杀的主流程中拆分出来,特别是下单功能要做成mq异步处理的。而支付功能,比如支付宝支付,是业务场景本身保证的异步。
+ - 利用缓存:创建订单时,每次都需要先查询判断库存,只有少部分成功的请求才会创建订单,因此可以将商品信息放在缓存中,减少数据库查询
+ - 负载均衡:利用 Nginx 做负载均衡,使用多个服务器并发处理请求,减少单个服务器压力
+- 前端优化:
+ - 限流:使用验证码等,来分散用户的请求
+ - 禁止重复提交:限定每个用户发起一次秒杀后,需等待才可以发起另一次请求,从而减少用户的重复请求
+ - 本地标记:用户成功秒杀到商品后,将提交按钮置灰,禁止用户再次提交请求
+ - 动静分离:使用CDN缓存静态页面资源,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率
+- 防作弊优化:
+ - 隐藏秒杀接口:如果秒杀地址直接暴露,在秒杀开始前可能会被恶意用户来刷接口,因此需要在没到秒杀开始时间不能获取秒杀接口,只有秒杀开始了,才返回秒杀地址 url 和验证 MD5,用户拿到这两个数据才可以进行秒杀
+ - 同一个账号多次发出请求:在前端优化的禁止重复提交可以进行优化;也可以使用 Redis 标志位,每个用户的所有请求都尝试在 Redis 中插入一个 `userId_secondsKill` 标志位,成功插入的才可以执行后续的秒杀逻辑,其他被过滤掉,执行完秒杀逻辑后,删除标志位
+ - 多个账号一次性发出多个请求:一般这种请求都来自同一个 IP 地址,可以检测 IP 的请求频率,如果过于频繁则弹出一个验证码
+ - 多个账号不同 IP 发起不同请求:这种一般都是僵尸账号,检测账号的活跃度或者等级等信息,来进行限制。比如微博抽奖,用 iphone 的年轻女性用户中奖几率更大。通过用户画像限制僵尸号无法参与秒杀或秒杀不能成功
+
+
+
+## 参考资料
+
+[如何设计一个秒杀系统](https://gongfukangee.github.io/2019/06/09/SecondsKill/#%E7%B3%BB%E7%BB%9F%E7%9A%84%E7%89%B9%E7%82%B9)
+
+[秒杀系统如何设计](https://www.teqng.com/2021/09/07/%E9%9D%A2%E9%9C%B8%EF%BC%9A%E7%A7%92%E6%9D%80%E7%B3%BB%E7%BB%9F%E5%A6%82%E4%BD%95%E8%AE%BE%E8%AE%A1%EF%BC%9F/)
+
+# 短链系统
+
+短链服务的鼻祖是TinyURL,是最早提供短链服务的网站,目前国内也有很多短链服务:新浪(t.cn)、百度(dwz.cn)、腾讯(url.cn)等等。
+
+## 短链的好处
+
+- 太长的链接容易被限制长度
+- 短链接看着简洁,长链接看着容易懵
+- 安全,不想暴露参数
+- 可以统一链接转换,当然也可以实现统计点击次数等操作
+
+## 短链的原理
+
+短链是通过服务器重定向到原始链接实现的。
+
+控制台执行命令`curl -i http://t.cn/A6ULvJho`,结果如下:
+
+```html
+HTTP/1.1 302 Found
+Date: Thu, 30 Jul 2020 13:59:13 GMT
+Content-Type: text/html;charset=UTF-8
+Content-Length: 328
+Connection: keep-alive
+Set-Cookie: aliyungf_tc=AQAAAJuaDFpOdQYARlNadFi502DO2kaj; Path=/; HttpOnly
+Server: nginx
+Location: https://www.howardliu.cn/how-to-use-branch-efficiently-in-git/index.html??spm=5176.12825654.gzwmvexct.d118.e9392c4aP1UUdv&scm=20140722.2007.2.1989
+
+
+
+Moved Temporarily
+
+
+Moved Temporarily
+The document has moved here.
+
+
+```
+
+从上面的信息可以看出来,新浪做了 302 跳转,同时为了兼容性,还返回用于手动调整的 HTML 内容。整个交互流程如下:
+
+
+
+## 短链生成方式
+
+据统计,目前全球有 58 亿的网页,Java 中 int 取值最多是 2^32 = 4294967296 < 43 亿 < 58 亿,long 取值是 2^64 > 58 亿。所以如果是用数字的话,int 勉强能够支撑(毕竟不是所有网址都会调用短链服务创建短链),使用 long 就比较保险,但会造成空间浪费。
+
+新浪微博使用 8 位字符串表示原始链接,这种字符串可以理解为数字的 62 进制表示,62^8 = 3521614606208 > 3521 亿 > 58 亿,也就是可以解决目前全球已知的网址。62 进制就是由 10 个数字 + (a-z)26 个小写字母 + (A-Z)26 个大写字母组成的数。
+
+### 哈希算法
+
+对原始链接取 Hash 值,是一种比较简单的思路。有很多现成的算法可以实现,但是有个避不开的问题就是:Hash 碰撞,所以选一个碰撞率低的算法比较重要。
+
+推荐MurmurHash 算法,这种算法是一种非加密型哈希函数,适用于一般的哈希检索操作,目前 Redis,Memcached,Cassandra,HBase,Lucene 都在用这种算法。
+
+对于碰撞问题,最简单的一种思路是,如果发生碰撞,就给原始 URL 附加上特殊字符串,直到躲开碰撞为止。具体操作如下图:
+
+
+
+### 发号器
+
+这个就是不管来的是什么,通过集中的统一发号器,分配一个 ID,这个 ID 就是短链的内容,比如第一个来的就是[https://tinyurl.com/1](https://xie.infoq.cn/link?target=https%3A%2F%2Ftinyurl.com%2F1),第二个就是[https://tinyurl.com/2](https://xie.infoq.cn/link?target=https%3A%2F%2Ftinyurl.com%2F2),以此类推。当然可能一些分布式 ID 算法上来就是很长的一个序号了。为了获取更短路,还可以将其转为 62 进制字符串。
+
+1. Redis 自增:Redis 性能好,单机就能支撑 10W+请求,如果作为发号器,需要考虑 Redis 持久化和灾备。
+2. MySQL 自增主键:这种方案和 Redis 的方案类似,是利用数据库自增主键的提醒实现,保证 ID 不重复且连续自动创建。
+3. Snowflake:这是一种目前应用比较广的 ID 序列生成算法,美团的 Leaf 是对这种算法的封装升级服务。但是这个算法依赖于服务器时钟,如果有时钟回拨,可能会有 ID 冲突。(有人会较真毫秒中的序列值是这个算法的瓶颈,话说回来了,这个算法只是提供了一种思路,如果觉得序列长度不够,自己加就好,但是每秒百万级的服务真的又这么多吗?)
+
+对于统一发号器这种方式,还需要解决的一个问题是:如果同一个原始链接,应该返回相同的短链还是不同的短链?
+
+答案是根据用户、地点等维度,相同的原始链接,返回不同的短链。如果判断维度都相同,则返回相同短链。这样做的好处是,我们可以根据短链的点击、请求信息,做数据统计。对于短链,我们牺牲的只是一些存储和运算,但是收集的信息却是无价的。
+
+## 短链存储
+
+一般这种数据的存储无非就两种:关系型数据库或 NoSQL 数据库。有了上面的创建逻辑,存储就是水到渠成的了。下面给出 MySQL 存储的建表语句:
+
+```mysql
+CREATE TABLE IF NOT EXISTS tiny_url
+(
+ sid INT AUTO_INCREMENT PRIMARY KEY,
+ create_time DATETIME DEFAULT CURRENT_TIMESTAMP NULL,
+ update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP,
+ version INT DEFAULT 0 NULL COMMENT '版本号',
+ tiny_url VARCHAR(10) NULL COMMENT '短链',
+ original_url TEXT NOT NULL COMMENT '原始链接',
+ # 其他附加信息
+ creator_ip INT DEFAULT 0 NOT NULL,
+ creator_user_agent TEXT NOT NULL,
+ # 用户其他信息,用于后续统计,对于这些数据,只要存储影响创建短链的必要字段就行,其他的都可以直接发送到数据服务中
+ instance_id INT DEFAULT 0 NOT NULL,
+ # 创建短链服务实例ID
+ state TINYINT DEFAULT 1 NULL COMMENT '-1无效 1有效'
+);
+```
+
+## 短链请求
+
+存储完成后,接下来就该使用了。
+
+通常的做法是会根据请求的短链字符串,从存储中找到数据,然后返回 HTTP 重定向到原始地址。如果存储使用关系型数据库,对于短链字段一般需要创建索引,而且为了避免数据库成为瓶颈,数据库前面还会通过缓存铺路。而且为了提高缓存合理使用,一般通过 LRU 算法淘汰非热点短链数据。流程如下:
+
+短链请求 -> 布隆过滤器 -> 缓存 -> 数据库
+
+图中的布隆过滤器是为了防止缓存击穿,造成服务器压力过大。
+
+这里还有一个问题:HTTP 返回重定向编码时使用 301 还是 302,为什么新浪微博会返回 302,而不是更加符合语义的 301 跳转?
+
+来看看HTTP状态码中301和302分别是什么含义:
+
+- 301,代表永久重定向。也就是说,浏览器第一次请求拿到重定向地址后,以后的请求,都是直接从浏览器缓存中获取重定向地址,不会再请求短链服务。这样可以有效减少服务请求数,降低服务器负载,但是因为后续浏览器不再向后端发送请求,因此获取不到真实的点击数。
+- 302,代表临时重定向。也就是说,每次浏览器都会向服务器发起请求获取新的地址,虽然会给服务器增加压力,但在硬件过剩的今天,这点压力比起数据简直不值一提。所以,302 重定向才是短链服务的首选。
+
+## 参考链接
+
+[短 URL 系统是怎么设计的?](https://www.zhihu.com/question/29270034)
+
+[系统设计系列之如何设计一个短链服务](https://xie.infoq.cn/article/483fcfbe3f942cb1fa9d9ce20)
+
diff --git "a/\347\263\273\347\273\237\350\256\276\350\256\241/\347\263\273\347\273\237\350\256\276\350\256\241old.md" "b/\347\263\273\347\273\237\350\256\276\350\256\241/\347\263\273\347\273\237\350\256\276\350\256\241old.md"
new file mode 100644
index 0000000..ef3fb83
--- /dev/null
+++ "b/\347\263\273\347\273\237\350\256\276\350\256\241/\347\263\273\347\273\237\350\256\276\350\256\241old.md"
@@ -0,0 +1,24 @@
+1. 设计一个聊天服务
+2. 设计拼车服务
+3. 设计一个URL缩短服务
+4. 设计一个社交媒体服务
+5. 设计一个社交留言板
+6. 设计文件存储服务
+7. 设计一个视频流媒体服务
+8. 设计一个API速率限制器
+9. 设计一个邻近服务器
+10. 设计一个预输入服务
+
+
+
+## 秒杀系统
+
+rabbitmq是为了削峰,如果是有1000件商品参与秒杀,每个商品有10件,那么系统的最大并发就是1万,db扛不住这么大的并发的,如果商品数量更大,这个并发量会更大。
+通过Redis预减库存减少到DB的请求,通过消息队列异步写库缓解数据库的压力。用消息队列来缓冲瞬时流量,把同步的直接调用转换成异步操作。
+
+把判断库存扣减库存的操作封装成lua脚本,实现原子性操作,避免超卖。
+
+
+
+
+
diff --git "a/\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md"
similarity index 100%
rename from "\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md"
rename to "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md"
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\344\272\214\345\217\211\346\240\221\345\211\215\345\272\217\343\200\201\344\270\255\345\272\217\343\200\201\345\220\216\345\272\217\343\200\201\345\261\202\345\272\217\351\201\215\345\216\206\344\273\243\347\240\201\345\256\236\347\216\260.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\344\272\214\345\217\211\346\240\221\345\211\215\345\272\217\343\200\201\344\270\255\345\272\217\343\200\201\345\220\216\345\272\217\343\200\201\345\261\202\345\272\217\351\201\215\345\216\206\344\273\243\347\240\201\345\256\236\347\216\260.md"
similarity index 100%
rename from "\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\344\272\214\345\217\211\346\240\221\345\211\215\345\272\217\343\200\201\344\270\255\345\272\217\343\200\201\345\220\216\345\272\217\343\200\201\345\261\202\345\272\217\351\201\215\345\216\206\344\273\243\347\240\201\345\256\236\347\216\260.md"
rename to "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\344\272\214\345\217\211\346\240\221\345\211\215\345\272\217\343\200\201\344\270\255\345\272\217\343\200\201\345\220\216\345\272\217\343\200\201\345\261\202\345\272\217\351\201\215\345\216\206\344\273\243\347\240\201\345\256\236\347\216\260.md"
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
similarity index 100%
rename from "\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
rename to "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\225\260\346\215\256\347\273\223\346\236\204.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\225\260\346\215\256\347\273\223\346\236\204.md"
similarity index 100%
rename from "\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\225\260\346\215\256\347\273\223\346\236\204.md"
rename to "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\225\260\346\215\256\347\273\223\346\236\204.md"
diff --git "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\347\256\227\346\263\225.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\347\256\227\346\263\225.md"
new file mode 100644
index 0000000..7325e6a
--- /dev/null
+++ "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\347\256\227\346\263\225.md"
@@ -0,0 +1,169 @@
+# 二分查找
+
+```java
+ public int binarySearch(int[] arr, int target) {
+ if (arr == null || arr.length <= 1) {
+ return -1;
+ }
+
+ int left = 0;
+ int right = arr.length - 1;
+
+ while (left <= right) {
+ int mid = (left + right) >>> 1;
+ if (arr[mid] > target) {
+ right = mid - 1;
+ } else if (arr[mid] < target) {
+ left = mid + 1;
+ } else {
+ return mid;
+ }
+ }
+
+ return -1;
+ }
+```
+
+# 归并排序
+
+
+
+## 简单讲下归并排序?
+
+归并排序 (merge sort) 是一类与插入排序、交换排序、选择排序不同的另一种排序方法。归并的含义是将两个或两个以上的有序表合并成一个新的有序表。归并排序有多路归并排序、两路归并排序 , 可用于内排序,也可以用于外排序。
+
+## 说说归并排序怎么实现的?
+
+两路归并排序算法思路是递归处理。每个递归过程涉及三个步骤
+
+- 分解: 把待排序的 n 个元素的序列分解成两个子序列, 每个子序列包括 n/2 个元素
+- 治理: 对每个子序列分别调用归并排序MergeSort, 进行递归操作
+- 合并: 合并两个排好序的子序列,生成排序结果
+
+
+
+代码实现:
+
+```java
+public class MergeSort {
+ public void mergeSort(int[] arr) {
+ if (arr == null || arr.length == 0) {
+ return;
+ }
+ //辅助数组
+ int[] tmpArr = new int[arr.length];
+ mergeSort(arr, tmpArr, 0, arr.length - 1);
+ }
+
+ private void mergeSort(int[] arr, int[] tmpArr, int left, int right) {
+ if (left < right) {
+ int mid = (left + right) >> 1;
+ mergeSort(arr, tmpArr, left, mid);
+ mergeSort(arr, tmpArr, mid + 1, right);
+ merge(arr, tmpArr, left, mid, right);
+ }
+ }
+
+ private void merge(int[] arr, int[] tmpArr, int left, int mid, int right) {
+ int i = left;
+ int j = mid + 1;
+ int tmpIndex = left;
+ while (i <= mid && j <= right) {
+ if (arr[i] < arr[j]) {
+ tmpArr[tmpIndex++] = arr[i];
+ i++;
+ } else {
+ tmpArr[tmpIndex++] = arr[j];
+ j++;
+ }
+ }
+
+ while (i <= mid) {
+ tmpArr[tmpIndex++] = arr[i++];
+ }
+
+ while (j <= right) {
+ tmpArr[tmpIndex++] = arr[j++];
+ }
+
+ for (int m = left; m <= right; m++) {
+ arr[m] = tmpArr[m];
+ }
+ }
+}
+```
+
+## 时间复杂度是多少?
+
+对长度为n的序列,需进行logn次二路归并,每次归并的时间为O(n),故时间复杂度是O(nlgn)。
+
+## 空间复杂度是多少?
+
+归并排序需要辅助空间来暂存两个有序子序列归并的结果,故其辅助空间复杂度为O(n)
+
+# 快速排序
+
+## 了解快速排序吗?
+
+快速排序是由**冒泡排序**改进而得到的,是一种排序执行效率很高的排序算法,它利用**分治法**来对待排序序列进行分治排序,它的思想主要是通过一趟排序将待排记录分隔成独立的两部分,其中的一部分比关键字小,后面一部分比关键字大,然后再对这前后的两部分分别采用这种方式进行排序,通过递归的运算最终达到整个序列有序。
+
+## 快速排序的过程?
+
+1. 在待排序的N个记录中任取一个元素(通常取第一个记录)作为基准,称为基准记录;
+2. 定义两个索引 left 和 right 分别表示首索引和尾索引,key 表示基准值;
+3. 首先,尾索引向前扫描,直到找到比基准值小的记录,并替换首索引对应的值;
+4. 然后,首索引向后扫描,直到找到比基准值大于的记录,并替换尾索引对应的值;
+5. 若在扫描过程中首索引等于尾索引(left = right),则一趟排序结束;将基准值(key)替换首索引所对应的值;
+6. 再进行下一趟排序时,待排序列被分成两个区:[0,left-1]和[righ+1,end]
+7. 对每一个分区重复以上步骤,直到所有分区中的记录都有序,排序完成
+
+代码实现:
+
+```java
+ public void quickSort(int[] arr) {
+ if (arr == null) {
+ return;
+ }
+ quickSortHelper(arr, 0, arr.length - 1);
+ }
+ private void quickSortHelper(int[] arr, int left, int right) {
+ if (left > right) {
+ return;
+ }
+ int tmp = arr[left];
+ int i = left;
+ int j = right;
+ while (i < j) {
+ //j先走,最终循环终止时,j停留的位置就是arr[left]的正确位置
+ //改为i<=j,则会进入死循环,[1,5,5,5,5]->[1] 5 [5,5,5]->[5,5,5],会死循环
+ while (i < j && arr[j] >= tmp) {
+ j--;
+ }
+ while (i < j && arr[i] <= tmp) {
+ i++;
+ }
+ if (i < j) {
+ int tmp1 = arr[i];
+ arr[i] = arr[j];
+ arr[j] = tmp1;
+ } else {
+ break;
+ }
+ }
+
+ //当循环终止的时候,i=j,因为是j先走的,j所在位置的值小于arr[left],交换arr[j]和arr[left]
+ arr[left] = arr[j];
+ arr[j] = tmp;
+
+ quickSortHelper(arr, left, j - 1);
+ quickSortHelper(arr, j + 1, right);
+ }
+```
+
+## 快排为什么比冒泡效率高?
+
+快速排序之所以比较快,是因为相比冒泡排序,每次的交换都是跳跃式的,每次设置一个基准值,将小于基准值的都交换到左边,大于基准值的都交换到右边,这样不会像冒泡一样每次都只交换相邻的两个数,因此比较和交换的此数都变少了,速度自然更高。
+
+## 快排的时间复杂度是多少?
+
+快速排序的平均时间复杂度是O(nlgn),最坏时间复杂度是O(n^2)。
\ No newline at end of file
diff --git "a/\347\275\221\347\273\234/TCP IP\345\215\217\350\256\256.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/TCP IP\345\215\217\350\256\256.md"
similarity index 100%
rename from "\347\275\221\347\273\234/TCP IP\345\215\217\350\256\256.md"
rename to "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/TCP IP\345\215\217\350\256\256.md"
diff --git "a/\347\275\221\347\273\234/session\345\222\214cookie.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/session\345\222\214cookie.md"
similarity index 100%
rename from "\347\275\221\347\273\234/session\345\222\214cookie.md"
rename to "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/session\345\222\214cookie.md"
diff --git "a/\347\275\221\347\273\234/\345\233\276\350\247\243HTTP.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\345\233\276\350\247\243HTTP.md"
similarity index 100%
rename from "\347\275\221\347\273\234/\345\233\276\350\247\243HTTP.md"
rename to "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\345\233\276\350\247\243HTTP.md"
diff --git "a/\347\275\221\347\273\234/\347\275\221\347\273\234.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\347\275\221\347\273\234.md"
similarity index 100%
rename from "\347\275\221\347\273\234/\347\275\221\347\273\234.md"
rename to "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\347\275\221\347\273\234.md"
diff --git "a/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
similarity index 100%
rename from "\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
rename to "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
From 8ef828dd4e63fd24fa28b06b2334ec0f863b1715 Mon Sep 17 00:00:00 2001
From: tyson
Date: Thu, 16 Jun 2022 23:32:40 +0800
Subject: [PATCH 035/112] update
---
...21\351\235\242\350\257\225\351\242\230.md" | 3 +
...00\351\235\242\350\257\225\351\242\230.md" | 8 +-
...21\351\235\242\350\257\225\351\242\230.md" | 8 +-
...10\351\235\242\350\257\225\351\242\230.md" | 4 +
README.md | 12 +-
.../RabbitMQ.md" | 15 +-
...is\351\235\242\350\257\225\351\242\230.md" | 139 +++++++----
...27\351\235\242\350\257\225\351\242\230.md" | 6 +-
...21\351\235\242\350\257\225\351\242\230.md" | 4 +
...is\351\235\242\350\257\225\351\242\230.md" | 42 +++-
...25\351\242\230\346\200\273\347\273\223.md" | 4 +
...VC\351\235\242\350\257\225\351\242\230.md" | 1 +
...ng\351\235\242\350\257\225\351\242\230.md" | 4 +
...37\351\235\242\350\257\225\351\242\230.md" | 232 +++++++++++++++++-
...60\346\215\256\347\273\223\346\236\204.md" | 31 +++
...21\351\235\242\350\257\225\351\242\230.md" | 3 +
16 files changed, 442 insertions(+), 74 deletions(-)
diff --git "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index e4bddb5..49892fd 100644
--- "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -748,3 +748,6 @@ class Person {
- 周志明. 深入理解 Java 虚拟机 [M]. 机械工业出版社
+
+
+
diff --git "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
index 758e026..f477f15 100644
--- "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
@@ -924,7 +924,7 @@ public class Student extends Person {
1、**语法层面**上的区别
-- 抽象类可以有方法实现,而接口的方法中只能是抽象方法;
+- 抽象类可以有方法实现,而接口的方法中只能是抽象方法(Java 8 开始接口方法可以有默认实现);
- 抽象类中的成员变量可以是各种类型的,接口中的成员变量只能是public static final类型;
- 接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
@@ -1163,3 +1163,9 @@ Java语言的关键字,变量修饰符,如果用transient声明一个实例
Java泛型是JDK 5中引⼊的⼀个新特性, 允许在定义类和接口的时候使⽤类型参数。声明的类型参数在使⽤时⽤具体的类型来替换。
泛型最⼤的好处是可以提⾼代码的复⽤性。以List接口为例,我们可以将String、 Integer等类型放⼊List中, 如不⽤泛型, 存放String类型要写⼀个List接口, 存放Integer要写另外⼀个List接口, 泛型可以很好的解决这个问题。
+
+## String为什么不可变?
+
+
+
+
diff --git "a/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
index a65d22c..cf0911c 100644
--- "a/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
@@ -569,8 +569,8 @@ synchronized 修饰的方法并没有 `monitorenter` 指令和 `monitorexit` 指
**相同点**:
-1. 使当前线程暂停运行,把机会交给其他线程
-2. 任何线程在等待期间被中断都会抛出`InterruptedException`
+1. 它们都可以使当前线程暂停运行,把机会交给其他线程
+2. 任何线程在调用wait()和sleep()之后,在等待期间被中断都会抛出`InterruptedException`
**不同点**:
@@ -1146,3 +1146,7 @@ SynchronizedMap一次锁住整张表来保证线程安全,所以每次只能
JDK1.8 ConcurrentHashMap采用CAS和synchronized来保证并发安全。数据结构采用数组+链表/红黑二叉树。synchronized只锁定当前链表或红黑二叉树的首节点,支持并发访问、修改。
另外ConcurrentHashMap使用了一种不同的迭代方式。当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException,取而代之的是在改变时new新的数据从而不影响原有的数据 ,iterator完成后再将头指针替换为新的数据 ,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变。
+
+
+
+
diff --git "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
index 14acae6..f5b3209 100644
--- "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
@@ -595,3 +595,7 @@ http://www.importnew.com/20386.html
https://www.cnblogs.com/yangming1996/p/7997468.html
https://coolshell.cn/articles/9606.htm(HashMap 死循环)
+
+
+
+
diff --git a/README.md b/README.md
index 79ae7ad..eed60e3 100644
--- a/README.md
+++ b/README.md
@@ -137,19 +137,19 @@
# 计算机网络
-[【大厂面试】—— 计算机网络常见面试题总结](网络/计算机网络高频面试题.md) (**知乎1k+收藏!推荐 :+1:**)
+[【大厂面试】—— 计算机网络常见面试题总结](计算机基础/网络/计算机网络高频面试题.md) (**知乎1k+收藏!推荐 :+1:**)
-[session和cookie详解](网络/session和cookie.md)
+[session和cookie详解](计算机基础/网络/session和cookie.md)
# 数据结构与算法
[如何高效的刷LeetCode?](https://www.zhihu.com/question/280279208/answer/2377906738)
-[7种常见的排序算法Java代码实现](数据结构与算法/常见的排序算法Java代码实现.md)
+[7种常见的排序算法Java代码实现](计算机基础/数据结构与算法/常见的排序算法Java代码实现.md)
-[二叉树前序、中序、后序、层序遍历代码实现](数据结构与算法/二叉树前序、中序、后序、层序遍历代码实现.md)
+[二叉树前序、中序、后序、层序遍历代码实现](计算机基础/数据结构与算法/二叉树前序、中序、后序、层序遍历代码实现.md)
-[常见数据结构总结](数据结构与算法/数据结构.md)
+[常见数据结构总结](计算机基础/数据结构与算法/数据结构.md)
# 设计模式
@@ -201,6 +201,8 @@
| 2021.11.25 | 支付宝收款码 | *海 | 1元 | |
| 2021.12.10 | 微信收款码 | 浩*y | 10元 | |
| 2021.12.15 | 微信收款码 | biubiu* | 6.66元 | 好 |
+| 2022.02.17 | 微信收款码 | *齐 | 8元 | |
+| 2022.05.03 | 微信收款码 | *哈 | 2元 | |
diff --git "a/\344\270\255\351\227\264\344\273\266/RabbitMQ.md" "b/\344\270\255\351\227\264\344\273\266/RabbitMQ.md"
index cc43200..4c51d3e 100644
--- "a/\344\270\255\351\227\264\344\273\266/RabbitMQ.md"
+++ "b/\344\270\255\351\227\264\344\273\266/RabbitMQ.md"
@@ -29,18 +29,6 @@
-> 本文已经收录到github仓库,此仓库用于分享Java相关知识总结,包括Java基础、MySQL、Spring Boot、MyBatis、Redis、RabbitMQ、计算机网络、数据结构与算法等等,欢迎大家提pr和star!
->
-> github地址:https://github.com/Tyson0314/Java-learning
->
-> 如果github访问不了,可以访问gitee仓库。
->
-> gitee地址:https://gitee.com/tysondai/Java-learning
-
-文章目录:
-
-
-
# 简介
RabbitMQ是一个由erlang开发的消息队列。消息队列用于应用间的异步协作。
@@ -430,3 +418,6 @@ msg.getMessageProperties().setExpiration("3000");
[线上rabbitmq问题](https://juejin.im/post/6844904088212094983#heading-0)
+
+
+
diff --git "a/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md" "b/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
index 914796d..03a9618 100644
--- "a/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
+++ "b/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
@@ -94,12 +94,6 @@ Redis基于Reactor模式开发了网络事件处理器,这个处理器被称
使用 redis 或 memcached 之类的称为**分布式缓存**,在多实例的情况下,各实例共用一份缓存数据,缓存具有一致性。
-## Redis的内存用完了会怎样?
-
-如果达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回)。
-
-也可以配置内存淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容。
-
## Redis 数据类型有哪些?
**基本数据类型**:
@@ -122,12 +116,6 @@ Redis基于Reactor模式开发了网络事件处理器,这个处理器被称
3、**Geospatial** :主要用于存储地理位置信息,并对存储的信息进行操作,适用场景如定位、附近的人等。
-## keys命令存在的问题?
-
-redis的单线程的。keys指令会导致线程阻塞一段时间,直到执行完毕,服务才能恢复。scan采用渐进式遍历的方式来解决keys命令可能带来的阻塞问题,每次scan命令的时间复杂度是`O(1)`,但是要真正实现keys的功能,需要执行多次scan。
-
-scan的缺点:在scan的过程中如果有键的变化(增加、删除、修改),遍历过程可能会有以下问题:新增的键可能没有遍历到,遍历出了重复的键等情况,也就是说scan并不能保证完整的遍历出来所有的键。
-
## SortedSet和List异同点?
**相同点**:
@@ -142,6 +130,22 @@ scan的缺点:在scan的过程中如果有键的变化(增加、删除、修
3. 列表不能简单的调整某个元素的位置,有序列表可以(更改元素的分数);
4. 有序集合更耗内存。
+## Redis的内存用完了会怎样?
+
+如果达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回)。
+
+也可以配置内存淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容。
+
+## Redis如何做内存优化?
+
+可以好好利用Hash,list,sorted set,set等集合类型数据,因为通常情况下很多小的Key-Value可以用更紧凑的方式存放到一起。尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。比如你的web系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的key,而是应该把这个用户的所有信息存储到一张散列表里面。
+
+## keys命令存在的问题?
+
+redis的单线程的。keys指令会导致线程阻塞一段时间,直到执行完毕,服务才能恢复。scan采用渐进式遍历的方式来解决keys命令可能带来的阻塞问题,每次scan命令的时间复杂度是`O(1)`,但是要真正实现keys的功能,需要执行多次scan。
+
+scan的缺点:在scan的过程中如果有键的变化(增加、删除、修改),遍历过程可能会有以下问题:新增的键可能没有遍历到,遍历出了重复的键等情况,也就是说scan并不能保证完整的遍历出来所有的键。
+
## Redis事务
事务的原理是将一个事务范围内的若干命令发送给Redis,然后再让Redis依次执行这些命令。
@@ -286,7 +290,7 @@ appendfsync no //由操作系统决定何时进行同步操作
1. 对于同一份文件AOF文件比RDB数据快照要大。
2. 数据恢复比较慢。
-## RDB和AOF如何选择?
+### RDB和AOF如何选择?
通常来说,应该同时使用两种持久化方案,以保证数据安全。
@@ -307,16 +311,11 @@ appendfsync no //由操作系统决定何时进行同步操作
**Redis cluster**:服务端分片技术,3.0版本开始正式提供。Redis Cluster并没有使用一致性hash,而是采用slot(槽)的概念,一共分成16384个槽。将请求发送到任意节点,接收到请求的节点会将查询请求发送到正确的节点上执行。主要是针对海量数据+高并发+高可用的场景,如果是海量数据,如果你的数据量很大,那么建议就用Redis cluster,所有主节点的容量总和就是Redis cluster可缓存的数据容量。
-## 主从复制
+## 主从架构
-Redis的复制功能是支持多个数据库之间的数据同步。主数据库可以进行读写操作,当主数据库的数据发生变化时会自动将数据同步到从数据库。从数据库一般是只读的,它会接收主数据库同步过来的数据。一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库。
+单机的 redis,能够承载的 QPS 大概就在上万到几万不等。对于缓存来说,一般都是用来支撑读高并发的。因此架构做成主从(master-slave)架构,一主多从,主负责写,并且将数据复制到其它的 slave 节点,从节点负责读。所有的读请求全部走从节点。这样也可以很轻松实现水平扩容,支撑读高并发。
-```java
-redis-server //启动Redis实例作为主数据库
-redis-server --port 6380 --slaveof 127.0.0.1 6379 //启动另一个实例作为从数据库
-slaveof 127.0.0.1 6379
-SLAVEOF NO ONE //停止接收其他数据库的同步并转化为主数据库。
-```
+Redis的复制功能是支持多个数据库之间的数据同步。主数据库可以进行读写操作,当主数据库的数据发生变化时会自动将数据同步到从数据库。从数据库一般是只读的,它会接收主数据库同步过来的数据。一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库。
**主从复制的原理?**
@@ -355,11 +354,18 @@ Redis cluster采用**虚拟槽分区**,所有的键根据哈希函数映射到

-**哈希槽是如何映射到 Redis 实例上的?**
+**工作原理:**
+
+1. 通过哈希的方式,将数据分片,每个节点均分存储一定哈希槽(哈希值)区间的数据,默认分配了16384 个槽位
+2. 每份数据分片会存储在多个互为主从的多节点上
+3. 数据写入先写主节点,再同步到从节点(支持配置为阻塞同步)
+4. 同一分片多个节点间的数据不保持一致性
+5. 读取数据时,当客户端操作的key没有分配在该节点上时,redis会返回转向指令,指向正确的节点
+6. 扩容时时需要需要把旧节点的数据迁移一部分到新节点
-1. 对键值对的`key`使用 `crc16` 算法计算一个结果
-2. 将结果对 16384 取余,得到的值表示 `key` 对应的哈希槽
-3. 根据该槽信息定位到对应的实例
+在 redis cluster 架构下,每个 redis 要放开两个端口号,比如一个是 6379,另外一个就是 加1w 的端口号,比如 16379。
+
+16379 端口号是用来进行节点间通信的,也就是 cluster bus 的东西,cluster bus 的通信,用来进行故障检测、配置更新、故障转移授权。cluster bus 用了另外一种二进制的协议,`gossip` 协议,用于节点间进行高效的数据交换,占用更少的网络带宽和处理时间。
**优点:**
@@ -374,8 +380,9 @@ Redis cluster采用**虚拟槽分区**,所有的键根据哈希函数映射到
* **事务操作支持有限**,只支持多`key`在同一节点上的事务操作,当多个`key`分布于不同的节点上时无法使用事务功能。
* `key`作为数据分区的最小粒度,不能将一个很大的键值对象如`hash`、`list`等映射到不同的节点。
* **不支持多数据库空间**,单机下的Redis可以支持到16个数据库,集群模式下只能使用1个数据库空间。
+* 只能使用0号数据库。
-### 哈希分区算法有哪些?
+**哈希分区算法有哪些?**
节点取余分区。使用特定的数据,如Redis的键或用户ID,对节点数量N取余:hash(key)%N计算出哈希值,用来决定数据映射到哪一个节点上。
优点是简单性。扩容时通常采用翻倍扩容,避免数据映射全部被打乱导致全量迁移的情况。
@@ -431,7 +438,9 @@ Redis cluster采用**虚拟槽分区**,所有的键根据哈希函数映射到
数据库的更新操作完成后不直接操作缓存,而是把这个操作命令封装成消息扔到消息队列中,然后由Redis自己去消费更新数据,消息队列可以保证数据操作顺序一致性,确保缓存系统的数据正常。
-## 缓存穿透
+## 缓存常见问题
+
+### 缓存穿透
缓存穿透是指查询一个**不存在的数据**,由于缓存是不命中时被动写的,如果从DB查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到DB去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了。
@@ -440,19 +449,27 @@ Redis cluster采用**虚拟槽分区**,所有的键根据哈希函数映射到
1. **缓存空值**,不会查数据库。
2. 采用**布隆过滤器**,将所有可能存在的数据哈希到一个足够大的`bitmap`中,查询不存在的数据会被这个`bitmap`拦截掉,从而避免了对`DB`的查询压力。
-布隆过滤器的原理:当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。查询时,将元素通过散列函数映射之后会得到k个点,如果这些点有任何一个0,则被检元素一定不在,直接返回;如果都是1,则查询元素很可能存在,就会去查询Redis和数据库。
+布隆过滤器的原理:当一个元素被加入集合时,通过K个哈希函数将这个元素映射成一个位数组中的K个点,把它们置为1。查询时,将元素通过哈希函数映射之后会得到k个点,如果这些点有任何一个0,则被检元素一定不在,直接返回;如果都是1,则查询元素很可能存在,就会去查询Redis和数据库。
+
+布隆过滤器一般用于在大数据量的集合中判定某元素是否存在。
-## 缓存雪崩
+### 缓存雪崩
缓存雪崩是指在我们设置缓存时采用了相同的过期时间,**导致缓存在某一时刻同时失效**,请求全部转发到DB,DB瞬时压力过重挂掉。
-解决方法:在原有的失效时间基础上**增加一个随机值**,使得过期时间分散一些。
+解决方法:
-## 缓存击穿
+1. 在原有的失效时间基础上**增加一个随机值**,使得过期时间分散一些。这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
+2. **加锁排队可以起到缓冲的作用**,防止大量的请求同时操作数据库,但它的缺点是**增加了系统的响应时间**,**降低了系统的吞吐量**,牺牲了一部分用户体验。当缓存未查询到时,对要请求的 key 进行加锁,只允许一个线程去数据库中查,其他线程等候排队。
+3. 设置二级缓存。二级缓存指的是除了 Redis 本身的缓存,**再设置一层缓存**,当 Redis 失效之后,先去查询二级缓存。例如可以设置一个本地缓存,在 Redis 缓存失效的时候先去查询本地缓存而非查询数据库。
+
+### 缓存击穿
缓存击穿:大量的请求同时查询一个 key 时,此时这个 key 正好失效了,就会导致大量的请求都落到数据库。**缓存击穿是查询缓存中失效的 key,而缓存穿透是查询不存在的 key。**
-解决方法:加分布式锁,第一个请求的线程可以拿到锁,拿到锁的线程查询到了数据之后设置缓存,其他的线程获取锁失败会等待50ms然后重新到缓存取数据,这样便可以避免大量的请求落到数据库。
+解决方法:
+
+1、**加互斥锁**。在并发的多个请求中,只有第一个请求线程能拿到锁并执行数据库查询操作,其他的线程拿不到锁就阻塞等着,等到第一个线程将数据写入缓存后,直接走缓存。可以使用Redis分布式锁实现,代码如下:
```java
public String get(String key) {
@@ -474,7 +491,9 @@ public String get(String key) {
}
```
-## 缓存预热
+2、**热点数据不过期**。直接将缓存设置为不过期,然后由定时任务去异步加载数据,更新缓存。这种方式适用于比较极端的场景,例如流量特别特别大的场景,使用时需要考虑业务能接受数据不一致的时间,还有就是异常情况的处理,保证缓存可以定时刷新。
+
+### 缓存预热
缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
@@ -484,7 +503,7 @@ public String get(String key) {
2. 数据量不大,可以在项目启动的时候自动进行加载;
3. 定时刷新缓存;
-## 缓存降级
+### 缓存降级
当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
@@ -501,26 +520,15 @@ public String get(String key) {
## Redis 怎么实现消息队列?
-使用一个列表,让生产者将任务使用LPUSH命令放进列表,消费者不断用RPOP从列表取出任务。
-
-> BRPOP和RPOP命令相似,唯一的区别就是当列表没有元素时BRPOP命令会一直阻塞连接,直到有新元素加入。
+使用list类型保存数据信息,rpush生产消息,lpop消费消息,当lpop没有消息时,可以sleep一段时间,然后再检查有没有信息,如果不想sleep的话,可以使用blpop, 在没有信息的时候,会一直阻塞,直到信息的到来。
```java
-BRPOP queue 0 //0表示不限制等待时间
+BLPOP queue 0 //0表示不限制等待时间
```
-**优先级队列**
+> BLPOP和LPOP命令相似,唯一的区别就是当列表没有元素时BLPOP命令会一直阻塞连接,直到有新元素加入。
-如果多个键都有元素,则按照从左到右的顺序取元素。
-
-```java
-BLPOP queue:1 queue:2 queue:3 0
-```
-
-**发布/订阅模式**
-
-`PSUBSCRIBE channel?*` 按照规则订阅。
-`PUNSUBSCRIBE channel?*` 退订通过PSUBSCRIBE命令按照某种规则订阅的频道。其中订阅规则要进行严格的字符串匹配,`PUNSUBSCRIBE *`无法退订`channel?*`规则。
+redis可以通过pub/sub**主题订阅模式**实现一个生产者,多个消费者,当然也存在一定的缺点,当消费者下线时,生产的消息会丢失。
```java
PUBLISH channel1 hi
@@ -528,9 +536,10 @@ SUBSCRIBE channel1
UNSUBSCRIBE channel1 //退订通过SUBSCRIBE命令订阅的频道。
```
-缺点:在消费者下线的情况下,生产的消息会丢失。
+> `PSUBSCRIBE channel?*` 按照规则订阅。
+> `PUNSUBSCRIBE channel?*` 退订通过PSUBSCRIBE命令按照某种规则订阅的频道。其中订阅规则要进行严格的字符串匹配,`PUNSUBSCRIBE *`无法退订`channel?*`规则。
-**延时队列**
+## Redis 怎么实现延时队列
使用sortedset,拿时间戳作为score,消息内容作为key,调用zadd来生产消息,消费者用`zrangebyscore`指令获取N秒之前的数据轮询进行处理。
@@ -606,3 +615,31 @@ Redis 官方站提出了一种权威的基于 Redis 实现分布式锁的方式
2. 避免死锁:最终 client 都可能拿到锁,不会出现死锁的情况,即使原本锁住某资源的 client 挂掉了
3. 容错性:只要大部分 Redis 节点存活就可以正常提供服务
+## Redis大key怎么处理?
+
+通常我们会将含有较大数据或含有大量成员、列表数的Key称之为大Key。
+
+以下是对各个数据类型大key的描述:
+
+- value是STRING类型,它的值超过5MB
+- value是ZSET、Hash、List、Set等集合类型时,它的成员数量超过1w个
+
+上述的定义并不绝对,主要是根据value的成员数量和大小来确定,根据业务场景确定标准。
+
+怎么处理:
+
+1. 当vaule是string时,可以使用序列化、压缩算法将key的大小控制在合理范围内,但是序列化和反序列化都会带来更多时间上的消耗。或者将key进行拆分,一个大key分为不同的部分,记录每个部分的key,使用multiget等操作实现事务读取。
+2. 当value是list/set等集合类型时,根据预估的数据规模来进行分片,不同的元素计算后分到不同的片。
+
+## Redis常见性能问题和解决方案?
+
+1. Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化。
+2. 如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。
+3. 为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内。
+4. 尽量避免在压力较大的主库上增加从库
+5. Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象。
+6. 为了Master的稳定性,主从复制不要用图状结构,用单向链表结构更稳定,即主从关系为:Master<–Slave1<–Slave2<–Slave3…,这样的结构也方便解决单点故障问题,实现Slave对Master的替换,也即,如果Master挂了,可以立马启用Slave1做Master,其他不变。
+
+
+
+
diff --git "a/\344\270\255\351\227\264\344\273\266/\346\266\210\346\201\257\351\230\237\345\210\227\351\235\242\350\257\225\351\242\230.md" "b/\344\270\255\351\227\264\344\273\266/\346\266\210\346\201\257\351\230\237\345\210\227\351\235\242\350\257\225\351\242\230.md"
index 633f860..bfbacf6 100644
--- "a/\344\270\255\351\227\264\344\273\266/\346\266\210\346\201\257\351\230\237\345\210\227\351\235\242\350\257\225\351\242\230.md"
+++ "b/\344\270\255\351\227\264\344\273\266/\346\266\210\346\201\257\351\230\237\345\210\227\351\235\242\350\257\225\351\242\230.md"
@@ -65,4 +65,8 @@
在消息生产时,MQ内部针对每条生产者发送的消息生成一个唯一id,作为去重和幂等的依据(消息投递失败并重传),避免重复的消息进入队列。
-在消息消费时,要求消息体中也要有一全局唯一id作为去重和幂等的依据,避免同一条消息被重复消费。
\ No newline at end of file
+在消息消费时,要求消息体中也要有一全局唯一id作为去重和幂等的依据,避免同一条消息被重复消费。
+
+
+
+
\ No newline at end of file
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index d829b99..e7bfe2d 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -971,3 +971,7 @@ B+树中**非叶子节点存的是key + 指针**;**叶子节点存的是数据
* 限定数据的范围。比如:用户在查询历史信息的时候,可以控制在一个月的时间范围内
* 读写分离。经典的数据库拆分方案,主库负责写,从库负责读
* 通过分库分表的方式进行优化,主要有垂直拆分和水平拆分
+
+
+
+
diff --git "a/\346\241\206\346\236\266/Mybatis\351\235\242\350\257\225\351\242\230.md" "b/\346\241\206\346\236\266/Mybatis\351\235\242\350\257\225\351\242\230.md"
index 21e911b..174db08 100644
--- "a/\346\241\206\346\236\266/Mybatis\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\241\206\346\236\266/Mybatis\351\235\242\350\257\225\351\242\230.md"
@@ -1,10 +1,20 @@
-## Mybatis框架简介
+## Mybatis是什么?
- MyBatis框架是一个开源的数据持久层框架。
- 它的内部封装了通过JDBC访问数据库的操作,支持普通的SQL查询、存储过程和高级映射,几乎消除了所有的JDBC代码和参数的手工设置以及结果集的检索。
- MyBatis作为持久层框架,其主要思想是将程序中的大量SQL语句剥离出来,配置在配置文件当中,实现SQL的灵活配置。
- 这样做的好处是将SQL与程序代码分离,可以在不修改代码的情况下,直接在配置文件当中修改SQL。
+## **ORM是什么**
+
+ORM(Object Relational Mapping),对象关系映射,是一种为了解决关系型数据库数据与简单Java对象(POJO)的映射关系的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系型数据库中。
+
+## 为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?
+
+Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。
+
+而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。
+
## MyBatis框架的优缺点及其适用的场合
**优点**
@@ -52,12 +62,38 @@ Mybatis有三种基本的Executor执行器,`SimpleExecutor`、`ReuseExecutor`
1. 通过注解绑定,在接口的方法上面加上 @Select@Update等注解里面包含Sql语句来绑定(SQL语句比较简单的时候,推荐注解绑定)
2. 通过xml里面写SQL来绑定, 指定xml映射文件里面的namespace必须为接口的全路径名(SQL语句比较复杂的时候,推荐xml绑定)
+## Mybatis 是如何进行分页的?
+
+Mybatis 使用 RowBounds 对象进行分页,它是针对 ResultSet 结果集执行的内存分页,而非物理分页,先把数据都查出来,然后再做分页。
+
+可以在 sql 内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
+
+## 分页插件的基本原理是什么?
+
+分页插件的基本原理是使用 Mybatis 提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 sql,然后重写 sql(SQL 拼接 limit),根据 dialect 方言,添加对应的物理分页语句和物理分页参数,用到了技术 JDK 动态代理,用到了责任链设计模式。
+
## 简述Mybatis的插件运行原理
Mybatis仅可以编写针对 `ParameterHandler`、`ResultSetHandler`、`StatementHandler`、`Executor`这4种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是`InvocationHandler`的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。
编写插件:实现Mybatis的Interceptor接口并复写`intercept()`方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。
+## .如何编写一个插件?
+
+编写插件:实现 Mybatis 的 Interceptor 接口并复写 intercept()方法,然后再给插件编写注解,指定要拦截哪一个接口的哪些方法即可,最后在配置文件中配置你编写的插件。
+
+## .Mybatis 是否支持延迟加载?
+
+Mybatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,association 指的就是一对一,collection 指的就是一对多查询。在 Mybatis 配置文件中,可以配置是否启用延迟加载`lazyLoadingEnabled=true|false`。
+
+## 延迟加载的基本原理是什么?
+
+延迟加载的基本原理是,使用 CGLIB 创建目标对象的代理对象,当调用目标方法时,进入拦截器方法。
+
+比如调用`a.getB().getName()`,拦截器 invoke()方法发现 a.getB()是 null 值,那么就会单独发送事先保存好的查询关联 B 对象的 sql,把 B 查询上来,然后调用`a.setB(b)`,于是 a 的对象 b 属性就有值了,接着完成`a.getB().getName()`方法的调用。
+
+当然了,不光是 Mybatis,几乎所有的包括 Hibernate,支持延迟加载的原理都是一样的。
+
## #{}和${}的区别是什么?
#{ } 被解析成预编译语句,预编译之后可以直接执行,不需要重新编译sql。
@@ -115,3 +151,7 @@ mybatis底层使用`PreparedStatement`,默认情况下,将对所有的 sql
二级缓存要求返回的POJO必须是可序列化的,即要求实现Serializable接口。
当开启二级缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库。
+
+
+
+
diff --git "a/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" "b/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
index e9ddb23..6eba065 100644
--- "a/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
+++ "b/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
@@ -294,3 +294,7 @@ hello.msg=大彬
## @Value原理
@Value的解析就是在bean初始化阶段。BeanPostProcessor定义了bean初始化前后用户可以对bean进行操作的接口方法,它的一个重要实现类`AutowiredAnnotationBeanPostProcessor`为bean中的@Autowired和@Value注解的注入功能提供支持。
+
+
+
+
diff --git "a/\346\241\206\346\236\266/SpringMVC\351\235\242\350\257\225\351\242\230.md" "b/\346\241\206\346\236\266/SpringMVC\351\235\242\350\257\225\351\242\230.md"
index 82ac987..82aca97 100644
--- "a/\346\241\206\346\236\266/SpringMVC\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\241\206\346\236\266/SpringMVC\351\235\242\350\257\225\351\242\230.md"
@@ -86,3 +86,4 @@ Spring MVC的工作原理如下:
+
diff --git "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md" "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
index 641525e..f0f66c0 100644
--- "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
@@ -614,3 +614,7 @@ Spring的Bean默认都是单例的,某些情况下,单例是并发不安全
**4、分布式或微服务的并发安全**
如果还要进一步考虑到微服务或分布式服务的影响,方式3便不合适了。这种情况下可以借助于可以共享某些信息的分布式缓存中间件,如Redis等。这样即可保证同一种服务的不同服务实例都拥有同一份共享信息了。
+
+
+
+
diff --git "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md"
index c068c14..5438b9a 100644
--- "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md"
+++ "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md"
@@ -19,6 +19,25 @@
异步:系统进程用一种走走停停的方式执行,(并不是一下子走完),进程什么时候以怎样的速度向前推进是不可预知的
+## 进程线程
+
+进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间。
+
+线程是比进程更小的执行单位,它是在一个进程中独立的控制流,一个进程可以启动多个线程,每条线程并行执行不同的任务。
+
+**进程和线程的区别如下**:
+
+- 调度:进程是资源管理的基本单位,线程是程序执行的基本单位。
+- 切换:线程上下文切换比进程上下文切换要快得多。
+- 拥有资源: 进程是拥有资源的一个独立单位,线程不拥有系统资源,但是可以访问隶属于进程的资源。
+- 系统开销: 创建或撤销进程时,系统都要为之分配或回收系统资源,如内存空间,I/O设备等,OS所付出的开销显著大于在创建或撤销线程时的开销,进程切换的开销也远大于线程切换的开销。
+
+## 并发和并行
+
+并发就是在一段时间内,多个任务都会被处理;但在某一时刻,只有一个任务在执行。单核处理器可以做到并发。比如有两个进程`A`和`B`,`A`运行一个时间片之后,切换到`B`,`B`运行一个时间片之后又切换到`A`。因为切换速度足够快,所以宏观上表现为在一段时间内能同时运行多个程序。
+
+并行就是在同一时刻,有多个任务在执行。这个需要多核处理器才能完成,在微观上就能同时执行多条指令,不同的程序被放到不同的处理器上运行,这个是物理上的多个进程同时进行。
+
## 多线程相较单线程的好处
1、并发提升程序执行效率
@@ -53,4 +72,215 @@
3、**共享内存**。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
-4、**信号量**。信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
\ No newline at end of file
+4、**信号量**。信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
+
+## 死锁
+
+死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象。若无外力作用,它们都将无法推进下去。
+
+如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方持有的资源,所以这两个线程就会互相等待而进入死锁状态。
+
+
+
+下面通过例子说明线程死锁,代码来自并发编程之美。
+
+```java
+public class DeadLockDemo {
+ private static Object resource1 = new Object();//资源 1
+ private static Object resource2 = new Object();//资源 2
+
+ public static void main(String[] args) {
+ new Thread(() -> {
+ synchronized (resource1) {
+ System.out.println(Thread.currentThread() + "get resource1");
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ System.out.println(Thread.currentThread() + "waiting get resource2");
+ synchronized (resource2) {
+ System.out.println(Thread.currentThread() + "get resource2");
+ }
+ }
+ }, "线程 1").start();
+
+ new Thread(() -> {
+ synchronized (resource2) {
+ System.out.println(Thread.currentThread() + "get resource2");
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ System.out.println(Thread.currentThread() + "waiting get resource1");
+ synchronized (resource1) {
+ System.out.println(Thread.currentThread() + "get resource1");
+ }
+ }
+ }, "线程 2").start();
+ }
+}
+```
+
+代码输出如下:
+
+```java
+Thread[线程 1,5,main]get resource1
+Thread[线程 2,5,main]get resource2
+Thread[线程 1,5,main]waiting get resource2
+Thread[线程 2,5,main]waiting get resource1
+```
+
+线程 A 通过 `synchronized` (resource1) 获得 resource1 的监视器锁,然后通过 `Thread.sleep(1000)`。让线程 A 休眠 1s 为的是让线程 B 得到执行然后获取到 resource2 的监视器锁。线程 A 和线程 B 休眠结束了都开始企图请求获取对方的资源,然后这两个线程就会陷入互相等待的状态,这也就产生了死锁。
+
+## 进程调度策略有哪几种?
+
+- **先来先服务**:非抢占式的调度算法,按照请求的顺序进行调度。有利于长作业,但不利于短作业,因为短作业必须一直等待前面的长作业执行完毕才能执行,而长作业又需要执行很长时间,造成了短作业等待时间过长。另外,对`I/O`密集型进程也不利,因为这种进程每次进行`I/O`操作之后又得重新排队。
+
+- **短作业优先**:非抢占式的调度算法,按估计运行时间最短的顺序进行调度。长作业有可能会饿死,处于一直等待短作业执行完毕的状态。因为如果一直有短作业到来,那么长作业永远得不到调度。
+
+- **最短剩余时间优先**:最短作业优先的抢占式版本,按剩余运行时间的顺序进行调度。 当一个新的作业到达时,其整个运行时间与当前进程的剩余时间作比较。如果新的进程需要的时间更少,则挂起当前进程,运行新的进程。否则新的进程等待。
+
+- **时间片轮转**:将所有就绪进程按 `FCFS` 的原则排成一个队列,每次调度时,把 `CPU` 时间分配给队首进程,该进程可以执行一个时间片。当时间片用完时,由计时器发出时钟中断,调度程序便停止该进程的执行,并将它送往就绪队列的末尾,同时继续把 `CPU` 时间分配给队首的进程。
+
+ 时间片轮转算法的效率和时间片的大小有很大关系:因为进程切换都要保存进程的信息并且载入新进程的信息,如果时间片太小,会导致进程切换得太频繁,在进程切换上就会花过多时间。 而如果时间片过长,那么实时性就不能得到保证。
+
+- **优先级调度**:为每个进程分配一个优先级,按优先级进行调度。为了防止低优先级的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级。
+
+## 进程有哪些状态?
+
+进程一共有`5`种状态,分别是创建、就绪、运行(执行)、终止、阻塞。
+
+
+
+- 运行状态就是进程正在`CPU`上运行。在单处理机环境下,每一时刻最多只有一个进程处于运行状态。
+- 就绪状态就是说进程已处于准备运行的状态,即进程获得了除`CPU`之外的一切所需资源,一旦得到`CPU`即可运行。
+- 阻塞状态就是进程正在等待某一事件而暂停运行,比如等待某资源为可用或等待`I/O`完成。即使`CPU`空闲,该进程也不能运行。
+
+**运行态→阻塞态**:往往是由于等待外设,等待主存等资源分配或等待人工干预而引起的。
+**阻塞态→就绪态**:则是等待的条件已满足,只需分配到处理器后就能运行。
+**运行态→就绪态**:不是由于自身原因,而是由外界原因使运行状态的进程让出处理器,这时候就变成就绪态。例如时间片用完,或有更高优先级的进程来抢占处理器等。
+**就绪态→运行态**:系统按某种策略选中就绪队列中的一个进程占用处理器,此时就变成了运行态。
+
+## 死锁怎么产生?怎么避免?
+
+**死锁产生的四个必要条件**:
+
+- 互斥:一个资源每次只能被一个进程使用
+
+- 请求与保持:一个进程因请求资源而阻塞时,不释放获得的资源
+
+- 不剥夺:进程已获得的资源,在未使用之前,不能强行剥夺
+
+- 循环等待:进程之间循环等待着资源
+
+**避免死锁的方法**:
+
+- 互斥条件不能破坏,因为加锁就是为了保证互斥
+- 一次性申请所有的资源,避免线程占有资源而且在等待其他资源
+- 占有部分资源的线程进一步申请其他资源时,如果申请不到,主动释放它占有的资源
+- 按序申请资源
+
+## 虚拟内存
+
+虚拟存储器就是具有请求调入功能,能从逻辑上对内存容量加以扩充的一种存储器系统,虚拟内存有多次性,对换性和虚拟性三个特征,它可以将程序分多次调入内存,使得在较小的用户空间可以执行较大的用户程序,所以同时容纳更多的进程并发执行,从而提高系统的吞吐量。发生缺页时可以调入一个段也可以调入一个页,取决于内存的存储管理方式。虚拟性表示虚拟内存和物理内存的映射。
+
+Linux下,进程不能直接读写内存物理地址,只能访问【虚拟内存地址】。操作系统会把虚拟内存地址-->物理地址。
+
+虚拟内存解决有限的内存空间加载较大应用程序的问题,根据需要在内存和磁盘之间来回传送数据。
+
+通过段页表的形式,虚拟内存中取一段连续的内存空间映射到主内存中,主内存空间的程序段可以不连续 。
+
+## 什么是分页?
+
+把内存空间划分为**大小相等且固定的块**,作为主存的基本单位。因为程序数据存储在不同的页面中,而页面又离散的分布在内存中,**因此需要一个页表来记录映射关系,以实现从页号到物理块号的映射。**
+
+访问分页系统中内存数据需要**两次的内存访问** (一次是从内存中访问页表,从中找到指定的物理块号,加上页内偏移得到实际物理地址;第二次就是根据第一次得到的物理地址访问内存取出数据)。
+
+## 什么是分段?
+
+**分页是为了提高内存利用率,而分段是为了满足程序员在编写代码的时候的一些逻辑需求(比如数据共享,数据保护,动态链接等)。**
+
+分段内存管理当中,**地址是二维的,一维是段号,二维是段内地址;其中每个段的长度是不一样的,而且每个段内部都是从0开始编址的**。由于分段管理中,每个段内部是连续内存分配,但是段和段之间是离散分配的,因此也存在一个逻辑地址到物理地址的映射关系,相应的就是段表机制。
+
+## 分页和分段有什区别?
+
+- 分页对程序员是透明的,但是分段需要程序员显式划分每个段。
+- 分页的地址空间是一维地址空间,分段是二维的。
+- 页的大小不可变,段的大小可以动态改变。
+- 分页主要用于实现虚拟内存,从而获得更大的地址空间;分段主要是为了使程序和数据可以被划分为逻辑上独立的地址空间并且有助于共享和保护。
+
+## 页面置换算法
+
+**为什么要页面置换:**
+
+因为应用程序是分多次装入内存的,所以运行到一定的时间,一定会发生缺页。地址映射的过程中,如果页面中发现要访问的页面不在内存中,会产生缺页中断。此时操作系统必须在内存里选择一个页面把他移出内存,为即将调入的页面让出空间。选择淘汰哪一页的规则就是页面置换算法
+
+**几种页面置换算法:**
+
+**最佳置换算法(理想)**:将当前页面中在未来最长时间内不会被访问的页置换出去
+
+**先进先出**:淘汰最早调入的页面
+
+**最近最久未使用 LRU**:每个页面有一个t来记录上次页面被访问直到现在,每次置换时置换t值最大的页面(用寄存器或栈实现)
+
+**时钟算法clock**(也被称为最近未使用算法NRU):页面设置访问为,将页面链接为一个环形列表,每个页有一个访问位0/1, 1表示又一次获救的机会,下次循环指针指向它时可以免除此次置换,但是会把访问位置为0, 代表他下次如果碰到循环指针就该被置换了。页面被访问的时候访问位设为1。页面置换的时候,如果当前指针的访问位为0,置换,否则将这个值置为0,循环直到遇到访问位为0的页面。
+
+**改进型Clock算法**:在clock算法的基础上添加一个修改位,优先替换访问位和修改位都是0的页面,其次替换访问位为0修改位为1的页面。
+
+**最少使用算法LFU**:设置寄存器记录页面被访问次数,每次置换当前访问次数最少的。
+
+## 用户态和内核态
+
+内核态:cpu可以访问内存的所有数据,包括外围设备,例如硬盘,网卡,cpu也可以将自己从一个程序切换到另一个程序。
+
+用户态:只能受限的访问内存,且不允许访问外围设备,占用cpu的能力被剥夺,cpu资源可以被其他程序获取。
+
+最大的区别就是权限不同,在运行在用户态下的程序不能直接访问操作系统内核数据结构和程序。
+
+### 为什么要有这两种状态?
+
+内核速度快但是资源有限,能控制的进程数不多,所以需要速度慢一些的用户态协助,但是为了避免用户态被恶意利用,所以限制了用户态程序的权限。
+
+需要限制不同的程序之间的访问能力,防止他们获取别的程序的内存数据,或者获取外围设备的数据,并发送到网络,CPU划分出**两个权限等级** -- 用户态和内核态。
+
+### 什么时候转换
+
+**1、系统调用**:
+
+用户进程主动发起的。用户态进程通过系统调用申请使用操作系统提供的服务程序完成工作,比如fork()就是执行一个创建新进程的系统调用
+
+用户程序使用系统调用,系统调用会转换为内核态并调用操作系统
+
+**2、发生异常**:
+
+会从当前运行进程切换到处理次此异常的内核相关程序中
+
+**3、外围设备的中断:**
+
+所有程序都运行在用户态,但在从硬盘读取数据、或从键盘输入时,这些事情只有操作系统能做,程序需要向操作系统请求以程序的名义来执行这些操作。这个时候用户态程序切换到内核态。
+
+## 什么是缓冲区溢出?有什么危害?
+
+缓冲区溢出是指当计算机向缓冲区填充数据时超出了缓冲区本身的容量,溢出的数据覆盖在合法数据上。
+
+危害有以下两点:
+
+- 程序崩溃,导致拒绝额服务
+- 跳转并且执行一段恶意代码
+
+造成缓冲区溢出的主要原因是程序中没有仔细检查用户输入。
+
+## IO多路复用
+
+**IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。IO多路复用适用如下场合**:
+
+- 当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用。
+- 当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。
+- 如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用。
+- 如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用。
+- 如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。
+- 与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。
+
+
\ No newline at end of file
diff --git "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\225\260\346\215\256\347\273\223\346\236\204.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\225\260\346\215\256\347\273\223\346\236\204.md"
index 6f5ed74..92991c4 100644
--- "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\225\260\346\215\256\347\273\223\346\236\204.md"
+++ "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\225\260\346\215\256\347\273\223\346\236\204.md"
@@ -20,6 +20,37 @@
- 哈希表:提高查找性能
- 红黑树:大致平衡的二叉查找树,相对AVL树,插入删除结点较快,查找性能没有提升
+## 数组
+
+数组的优点:
+
+- 存取速度快
+
+数组的缺点:
+
+- 事先必须知道数组的长度
+- 插入删除元素很慢
+- 空间通常是有限制的
+- 需要大块连续的内存块
+- 插入删除元素的效率很低
+
+## 链表
+
+### 优缺点
+
+优点:
+
+- 空间没有限制
+- 插入删除元素很快
+
+缺点:存取速度很慢
+
+### 分类
+
+- 单向链表 一个节点指向下一个节点。
+- 双向链表 一个节点有两个指针域。
+- 循环链表 能通过任何一个节点找到其他所有的节点,将两种(双向/单向)链表的最后一个结点指向第一个结点从而实现循环
+
## AVL树
diff --git "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 48969f2..c91b807 100644
--- "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -344,3 +344,6 @@ ARP解决了同一个局域网上的主机和路由器IP和MAC地址的解析。
- 源主机收到这个ARP响应数据包后,将得到的目的主机的IP地址和MAC地址添加到自己的ARP列表中,并利用此信息开始数据的传输。
- 如果源主机一直没有收到ARP响应数据包,表示ARP查询失败。
+
+
+
From 4dc2e1c43caabd59ef24f6df046ddc5840275ae1 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 19 Jun 2022 17:15:50 +0800
Subject: [PATCH 036/112] update
---
...21\351\235\242\350\257\225\351\242\230.md" | 47 ---
...00\351\235\242\350\257\225\351\242\230.md" | 55 ---
...21\351\235\242\350\257\225\351\242\230.md" | 54 ---
...10\351\235\242\350\257\225\351\242\230.md" | 50 ---
...07\345\215\227\346\200\273\347\273\223.md" | 75 ----
...is\351\235\242\350\257\225\351\242\230.md" | 39 +-
...PC\347\232\204\345\216\237\347\220\206.md" | 3 +-
...03\345\274\217\344\272\213\345\212\241.md" | 2 +-
...03\345\274\217\347\274\223\345\255\230.md" | 6 +-
...06\345\270\203\345\274\217\351\224\201.md" | 2 +
.../\345\276\256\346\234\215\345\212\241.md" | 7 +-
.../MySQL\345\237\272\347\241\200.md" | 96 -----
...21\351\235\242\350\257\225\351\242\230.md" | 49 ---
...is\351\235\242\350\257\225\351\242\230.md" | 2 -
...15\345\212\241\345\256\236\346\210\230.md" | 58 ---
.../Spring\345\256\236\346\210\230.md" | 65 ----
...ng\351\235\242\350\257\225\351\242\230.md" | 32 --
...06\344\270\216\345\256\236\346\210\230.md" | 62 ---
...72\351\253\230\351\242\221\350\257\215.md" | 65 ----
...1\253\230\351\242\221\350\257\215 - wx.md" | 66 ----
...72\351\253\230\351\242\221\350\257\215.md" | 84 +++++
...65\350\257\235\345\217\267\347\240\201.md" | 82 ----
...01\347\232\204\344\270\252\346\225\260.md" | 55 +++
...73\345\275\225\345\216\237\347\220\206.md" | 4 +-
...37\351\235\242\350\257\225\351\242\230.md" | 56 +--
...43\347\240\201\345\256\236\347\216\260.md" | 148 --------
...43\347\240\201\345\256\236\347\216\260.md" | 214 -----------
...60\346\215\256\347\273\223\346\236\204.md" | 183 +++++----
.../\347\256\227\346\263\225.md" | 353 ++++++++++++++----
.../session\345\222\214cookie.md" | 10 -
.../\345\233\276\350\247\243HTTP.md" | 47 ---
...21\351\235\242\350\257\225\351\242\230.md" | 38 +-
32 files changed, 565 insertions(+), 1544 deletions(-)
delete mode 100644 "\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\343\200\220\344\274\230\345\214\226\347\211\210\343\200\221\345\246\202\344\275\225\344\273\216\346\265\267\351\207\217\346\225\260\346\215\256\346\211\276\345\207\272\351\253\230\351\242\221\350\257\215.md"
delete mode 100644 "\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\345\246\202\344\275\225\344\273\216\346\265\267\351\207\217\346\225\260\346\215\256\346\211\276\345\207\272\351\253\230\351\242\221\350\257\215 - wx.md"
create mode 100644 "\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\345\246\202\344\275\225\344\273\216\346\265\267\351\207\217\346\225\260\346\215\256\346\211\276\345\207\272\351\253\230\351\242\221\350\257\215.md"
delete mode 100644 "\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\347\224\265\350\257\235\345\217\267\347\240\201.md"
create mode 100644 "\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\347\273\237\350\256\241\344\270\215\345\220\214\347\224\265\350\257\235\345\217\267\347\240\201\347\232\204\344\270\252\346\225\260.md"
delete mode 100644 "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\344\272\214\345\217\211\346\240\221\345\211\215\345\272\217\343\200\201\344\270\255\345\272\217\343\200\201\345\220\216\345\272\217\343\200\201\345\261\202\345\272\217\351\201\215\345\216\206\344\273\243\347\240\201\345\256\236\347\216\260.md"
delete mode 100644 "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
diff --git "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 49892fd..d93bc43 100644
--- "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -1,50 +1,3 @@
-
-
-
-
-- [讲一下JVM内存结构?](#%E8%AE%B2%E4%B8%80%E4%B8%8Bjvm%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84)
- - [程序计数器](#%E7%A8%8B%E5%BA%8F%E8%AE%A1%E6%95%B0%E5%99%A8)
- - [虚拟机栈](#%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88)
- - [本地方法栈](#%E6%9C%AC%E5%9C%B0%E6%96%B9%E6%B3%95%E6%A0%88)
- - [堆](#%E5%A0%86)
- - [方法区](#%E6%96%B9%E6%B3%95%E5%8C%BA)
- - [运行时常量池](#%E8%BF%90%E8%A1%8C%E6%97%B6%E5%B8%B8%E9%87%8F%E6%B1%A0)
- - [直接内存](#%E7%9B%B4%E6%8E%A5%E5%86%85%E5%AD%98)
-- [Java对象的定位方式](#java%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%AE%9A%E4%BD%8D%E6%96%B9%E5%BC%8F)
-- [说一下堆栈的区别?](#%E8%AF%B4%E4%B8%80%E4%B8%8B%E5%A0%86%E6%A0%88%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [什么情况下会发生栈溢出?](#%E4%BB%80%E4%B9%88%E6%83%85%E5%86%B5%E4%B8%8B%E4%BC%9A%E5%8F%91%E7%94%9F%E6%A0%88%E6%BA%A2%E5%87%BA)
-- [类文件结构](#%E7%B1%BB%E6%96%87%E4%BB%B6%E7%BB%93%E6%9E%84)
-- [什么是类加载?类加载的过程?](#%E4%BB%80%E4%B9%88%E6%98%AF%E7%B1%BB%E5%8A%A0%E8%BD%BD%E7%B1%BB%E5%8A%A0%E8%BD%BD%E7%9A%84%E8%BF%87%E7%A8%8B)
-- [什么是双亲委派模型?](#%E4%BB%80%E4%B9%88%E6%98%AF%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%A8%A1%E5%9E%8B)
-- [为什么需要双亲委派模型?](#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%A8%A1%E5%9E%8B)
-- [什么是类加载器,类加载器有哪些?](#%E4%BB%80%E4%B9%88%E6%98%AF%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [类的实例化顺序?](#%E7%B1%BB%E7%9A%84%E5%AE%9E%E4%BE%8B%E5%8C%96%E9%A1%BA%E5%BA%8F)
-- [如何判断一个对象是否存活?](#%E5%A6%82%E4%BD%95%E5%88%A4%E6%96%AD%E4%B8%80%E4%B8%AA%E5%AF%B9%E8%B1%A1%E6%98%AF%E5%90%A6%E5%AD%98%E6%B4%BB)
-- [可作为GC Roots的对象有哪些?](#%E5%8F%AF%E4%BD%9C%E4%B8%BAgc-roots%E7%9A%84%E5%AF%B9%E8%B1%A1%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [什么情况下类会被卸载?](#%E4%BB%80%E4%B9%88%E6%83%85%E5%86%B5%E4%B8%8B%E7%B1%BB%E4%BC%9A%E8%A2%AB%E5%8D%B8%E8%BD%BD)
-- [强引用、软引用、弱引用、虚引用是什么,有什么区别?](#%E5%BC%BA%E5%BC%95%E7%94%A8%E8%BD%AF%E5%BC%95%E7%94%A8%E5%BC%B1%E5%BC%95%E7%94%A8%E8%99%9A%E5%BC%95%E7%94%A8%E6%98%AF%E4%BB%80%E4%B9%88%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB)
-- [GC是什么?为什么要GC?](#gc%E6%98%AF%E4%BB%80%E4%B9%88%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81gc)
-- [Minor GC 和 Full GC的区别?](#minor-gc-%E5%92%8C-full-gc%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [内存的分配策略?](#%E5%86%85%E5%AD%98%E7%9A%84%E5%88%86%E9%85%8D%E7%AD%96%E7%95%A5)
-- [Full GC 的触发条件?](#full-gc-%E7%9A%84%E8%A7%A6%E5%8F%91%E6%9D%A1%E4%BB%B6)
-- [垃圾回收算法有哪些?](#%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [有哪些垃圾回收器?](#%E6%9C%89%E5%93%AA%E4%BA%9B%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8)
-- [常用的 JVM 调优的命令都有哪些?](#%E5%B8%B8%E7%94%A8%E7%9A%84-jvm-%E8%B0%83%E4%BC%98%E7%9A%84%E5%91%BD%E4%BB%A4%E9%83%BD%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [对象头了解吗?](#%E5%AF%B9%E8%B1%A1%E5%A4%B4%E4%BA%86%E8%A7%A3%E5%90%97)
-- [main方法执行过程](#main%E6%96%B9%E6%B3%95%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B)
-- [对象创建过程](#%E5%AF%B9%E8%B1%A1%E5%88%9B%E5%BB%BA%E8%BF%87%E7%A8%8B)
-- [如何排查 OOM 的问题?](#%E5%A6%82%E4%BD%95%E6%8E%92%E6%9F%A5-oom-%E7%9A%84%E9%97%AE%E9%A2%98)
-
-
-
-> 首先给大家分享一个github仓库,上面放了**200多本经典的计算机书籍**,包括C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生等,可以star一下,下次找书直接在上面搜索,仓库持续更新中~
->
-> github地址:https://github.com/Tyson0314/java-books
->
-> 如果github访问不了,可以访问gitee仓库。
->
-> gitee地址:https://gitee.com/tysondai/java-books
-
## 讲一下JVM内存结构?
JVM内存结构分为5大区域,**程序计数器**、**虚拟机栈**、**本地方法栈**、**堆**、**方法区**。
diff --git "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
index f477f15..e4e9624 100644
--- "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
@@ -1,58 +1,3 @@
-
-
-
-- [Java的特点](#java%E7%9A%84%E7%89%B9%E7%82%B9)
-- [Java 与 C++ 的区别](#java-%E4%B8%8E-c-%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [面向对象和面向过程的区别?](#%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E5%92%8C%E9%9D%A2%E5%90%91%E8%BF%87%E7%A8%8B%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [JKD和JRE的区别?](#jkd%E5%92%8Cjre%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [面向对象有哪些特性?](#%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E6%9C%89%E5%93%AA%E4%BA%9B%E7%89%B9%E6%80%A7)
-- [Java的基本数据类型有哪些?](#java%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [什么是值传递和引用传递?](#%E4%BB%80%E4%B9%88%E6%98%AF%E5%80%BC%E4%BC%A0%E9%80%92%E5%92%8C%E5%BC%95%E7%94%A8%E4%BC%A0%E9%80%92)
-- [自动装箱和拆箱](#%E8%87%AA%E5%8A%A8%E8%A3%85%E7%AE%B1%E5%92%8C%E6%8B%86%E7%AE%B1)
-- [String 为什么不可变?](#string-%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%8D%E5%8F%AF%E5%8F%98)
-- [String, StringBuffer 和 StringBuilder区别](#string-stringbuffer-%E5%92%8C-stringbuilder%E5%8C%BA%E5%88%AB)
-- [String 类的常用方法有哪些?](#string-%E7%B1%BB%E7%9A%84%E5%B8%B8%E7%94%A8%E6%96%B9%E6%B3%95%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [new String("dabin")会创建几个对象?](#new-stringdabin%E4%BC%9A%E5%88%9B%E5%BB%BA%E5%87%A0%E4%B8%AA%E5%AF%B9%E8%B1%A1)
-- [什么是字符串常量池?](#%E4%BB%80%E4%B9%88%E6%98%AF%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%B8%B8%E9%87%8F%E6%B1%A0)
-- [object常用方法有哪些?](#object%E5%B8%B8%E7%94%A8%E6%96%B9%E6%B3%95%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [讲讲深拷贝和浅拷贝?](#%E8%AE%B2%E8%AE%B2%E6%B7%B1%E6%8B%B7%E8%B4%9D%E5%92%8C%E6%B5%85%E6%8B%B7%E8%B4%9D)
-- [两个对象的hashCode()相同,则 equals()是否也一定为 true?](#%E4%B8%A4%E4%B8%AA%E5%AF%B9%E8%B1%A1%E7%9A%84hashcode%E7%9B%B8%E5%90%8C%E5%88%99-equals%E6%98%AF%E5%90%A6%E4%B9%9F%E4%B8%80%E5%AE%9A%E4%B8%BA-true)
-- [Java创建对象有几种方式?](#java%E5%88%9B%E5%BB%BA%E5%AF%B9%E8%B1%A1%E6%9C%89%E5%87%A0%E7%A7%8D%E6%96%B9%E5%BC%8F)
-- [说说类实例化的顺序](#%E8%AF%B4%E8%AF%B4%E7%B1%BB%E5%AE%9E%E4%BE%8B%E5%8C%96%E7%9A%84%E9%A1%BA%E5%BA%8F)
-- [equals和==有什么区别?](#equals%E5%92%8C%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB)
-- [常见的关键字有哪些?](#%E5%B8%B8%E8%A7%81%E7%9A%84%E5%85%B3%E9%94%AE%E5%AD%97%E6%9C%89%E5%93%AA%E4%BA%9B)
- - [static](#static)
- - [final](#final)
- - [this](#this)
- - [super](#super)
-- [final, finally, finalize 的区别](#final-finally-finalize-%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [final关键字的作用?](#final%E5%85%B3%E9%94%AE%E5%AD%97%E7%9A%84%E4%BD%9C%E7%94%A8)
-- [方法重载和重写的区别?](#%E6%96%B9%E6%B3%95%E9%87%8D%E8%BD%BD%E5%92%8C%E9%87%8D%E5%86%99%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [接口与抽象类区别?](#%E6%8E%A5%E5%8F%A3%E4%B8%8E%E6%8A%BD%E8%B1%A1%E7%B1%BB%E5%8C%BA%E5%88%AB)
-- [常见的Exception有哪些?](#%E5%B8%B8%E8%A7%81%E7%9A%84exception%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [Error和Exception的区别?](#error%E5%92%8Cexception%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [运行时异常和非运行时异常(checked)的区别?](#%E8%BF%90%E8%A1%8C%E6%97%B6%E5%BC%82%E5%B8%B8%E5%92%8C%E9%9D%9E%E8%BF%90%E8%A1%8C%E6%97%B6%E5%BC%82%E5%B8%B8checked%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [throw和throws的区别?](#throw%E5%92%8Cthrows%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [BIO/NIO/AIO区别的区别?](#bionioaio%E5%8C%BA%E5%88%AB%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [守护线程是什么?](#%E5%AE%88%E6%8A%A4%E7%BA%BF%E7%A8%8B%E6%98%AF%E4%BB%80%E4%B9%88)
-- [Java支持多继承吗?](#java%E6%94%AF%E6%8C%81%E5%A4%9A%E7%BB%A7%E6%89%BF%E5%90%97)
-- [如何实现对象克隆?](#%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E5%AF%B9%E8%B1%A1%E5%85%8B%E9%9A%86)
-- [同步和异步的区别?](#%E5%90%8C%E6%AD%A5%E5%92%8C%E5%BC%82%E6%AD%A5%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [阻塞和非阻塞的区别?](#%E9%98%BB%E5%A1%9E%E5%92%8C%E9%9D%9E%E9%98%BB%E5%A1%9E%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [Java8的新特性有哪些?](#java8%E7%9A%84%E6%96%B0%E7%89%B9%E6%80%A7%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [什么是序列化和反序列化?](#%E4%BB%80%E4%B9%88%E6%98%AF%E5%BA%8F%E5%88%97%E5%8C%96%E5%92%8C%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96)
-- [如何实现序列化](#%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E5%BA%8F%E5%88%97%E5%8C%96)
-
-
-
-> 首先给大家分享一个github仓库,上面放了**200多本经典的计算机书籍**,包括C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生等,可以star一下,下次找书直接在上面搜索,仓库持续更新中~
->
-> github地址:https://github.com/Tyson0314/java-books
->
-> 如果github访问不了,可以访问gitee仓库。
->
-> gitee地址:https://gitee.com/tysondai/java-books
-
## Java的特点
**Java是一门面向对象的编程语言。**面向对象和面向过程的区别参考下一个问题。
diff --git "a/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
index cf0911c..e71a0dd 100644
--- "a/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
@@ -1,57 +1,3 @@
-
-
-
-
-- [线程池](#%E7%BA%BF%E7%A8%8B%E6%B1%A0)
- - [为什么使用线程池?](#%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BD%BF%E7%94%A8%E7%BA%BF%E7%A8%8B%E6%B1%A0)
- - [线程池执行原理?](#%E7%BA%BF%E7%A8%8B%E6%B1%A0%E6%89%A7%E8%A1%8C%E5%8E%9F%E7%90%86)
- - [线程池参数有哪些?](#%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%8F%82%E6%95%B0%E6%9C%89%E5%93%AA%E4%BA%9B)
- - [线程池大小怎么设置?](#%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%A4%A7%E5%B0%8F%E6%80%8E%E4%B9%88%E8%AE%BE%E7%BD%AE)
- - [线程池的类型有哪些?适用场景?](#%E7%BA%BF%E7%A8%8B%E6%B1%A0%E7%9A%84%E7%B1%BB%E5%9E%8B%E6%9C%89%E5%93%AA%E4%BA%9B%E9%80%82%E7%94%A8%E5%9C%BA%E6%99%AF)
-- [进程线程](#%E8%BF%9B%E7%A8%8B%E7%BA%BF%E7%A8%8B)
- - [线程的生命周期](#%E7%BA%BF%E7%A8%8B%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F)
- - [讲讲线程中断?](#%E8%AE%B2%E8%AE%B2%E7%BA%BF%E7%A8%8B%E4%B8%AD%E6%96%AD)
- - [创建线程有哪几种方式?](#%E5%88%9B%E5%BB%BA%E7%BA%BF%E7%A8%8B%E6%9C%89%E5%93%AA%E5%87%A0%E7%A7%8D%E6%96%B9%E5%BC%8F)
- - [什么是线程死锁?](#%E4%BB%80%E4%B9%88%E6%98%AF%E7%BA%BF%E7%A8%8B%E6%AD%BB%E9%94%81)
- - [线程死锁怎么产生?怎么避免?](#%E7%BA%BF%E7%A8%8B%E6%AD%BB%E9%94%81%E6%80%8E%E4%B9%88%E4%BA%A7%E7%94%9F%E6%80%8E%E4%B9%88%E9%81%BF%E5%85%8D)
- - [线程run和start的区别?](#%E7%BA%BF%E7%A8%8Brun%E5%92%8Cstart%E7%9A%84%E5%8C%BA%E5%88%AB)
- - [线程都有哪些方法?](#%E7%BA%BF%E7%A8%8B%E9%83%BD%E6%9C%89%E5%93%AA%E4%BA%9B%E6%96%B9%E6%B3%95)
-- [volatile底层原理](#volatile%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86)
-- [synchronized的用法有哪些?](#synchronized%E7%9A%84%E7%94%A8%E6%B3%95%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [synchronized的作用有哪些?](#synchronized%E7%9A%84%E4%BD%9C%E7%94%A8%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [synchronized 底层实现原理?](#synchronized-%E5%BA%95%E5%B1%82%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86)
-- [volatile和synchronized的区别是什么?](#volatile%E5%92%8Csynchronized%E7%9A%84%E5%8C%BA%E5%88%AB%E6%98%AF%E4%BB%80%E4%B9%88)
-- [ReentrantLock和synchronized区别](#reentrantlock%E5%92%8Csynchronized%E5%8C%BA%E5%88%AB)
-- [wait()和sleep()的异同点?](#wait%E5%92%8Csleep%E7%9A%84%E5%BC%82%E5%90%8C%E7%82%B9)
-- [Runnable和Callable有什么区别?](#runnable%E5%92%8Ccallable%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB)
-- [线程执行顺序怎么控制?](#%E7%BA%BF%E7%A8%8B%E6%89%A7%E8%A1%8C%E9%A1%BA%E5%BA%8F%E6%80%8E%E4%B9%88%E6%8E%A7%E5%88%B6)
-- [守护线程是什么?](#%E5%AE%88%E6%8A%A4%E7%BA%BF%E7%A8%8B%E6%98%AF%E4%BB%80%E4%B9%88)
-- [线程间通信方式](#%E7%BA%BF%E7%A8%8B%E9%97%B4%E9%80%9A%E4%BF%A1%E6%96%B9%E5%BC%8F)
-- [ThreadLocal](#threadlocal)
- - [ThreadLocal原理](#threadlocal%E5%8E%9F%E7%90%86)
- - [ThreadLocal内存泄漏的原因?](#threadlocal%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F%E7%9A%84%E5%8E%9F%E5%9B%A0)
- - [ThreadLocal使用场景有哪些?](#threadlocal%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [AQS原理](#aqs%E5%8E%9F%E7%90%86)
-- [ReentrantLock 是如何实现可重入性的?](#reentrantlock-%E6%98%AF%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E5%8F%AF%E9%87%8D%E5%85%A5%E6%80%A7%E7%9A%84)
-- [锁的分类](#%E9%94%81%E7%9A%84%E5%88%86%E7%B1%BB)
- - [公平锁与非公平锁](#%E5%85%AC%E5%B9%B3%E9%94%81%E4%B8%8E%E9%9D%9E%E5%85%AC%E5%B9%B3%E9%94%81)
- - [共享式与独占式锁](#%E5%85%B1%E4%BA%AB%E5%BC%8F%E4%B8%8E%E7%8B%AC%E5%8D%A0%E5%BC%8F%E9%94%81)
- - [悲观锁与乐观锁](#%E6%82%B2%E8%A7%82%E9%94%81%E4%B8%8E%E4%B9%90%E8%A7%82%E9%94%81)
-- [乐观锁有什么问题?](#%E4%B9%90%E8%A7%82%E9%94%81%E6%9C%89%E4%BB%80%E4%B9%88%E9%97%AE%E9%A2%98)
-- [什么是CAS?](#%E4%BB%80%E4%B9%88%E6%98%AFcas)
-- [CAS存在的问题?](#cas%E5%AD%98%E5%9C%A8%E7%9A%84%E9%97%AE%E9%A2%98)
-- [并发工具](#%E5%B9%B6%E5%8F%91%E5%B7%A5%E5%85%B7)
- - [CountDownLatch](#countdownlatch)
- - [CyclicBarrier](#cyclicbarrier)
- - [CyclicBarrier和CountDownLatch区别](#cyclicbarrier%E5%92%8Ccountdownlatch%E5%8C%BA%E5%88%AB)
- - [Semaphore](#semaphore)
-- [原子类](#%E5%8E%9F%E5%AD%90%E7%B1%BB)
- - [基本类型原子类](#%E5%9F%BA%E6%9C%AC%E7%B1%BB%E5%9E%8B%E5%8E%9F%E5%AD%90%E7%B1%BB)
- - [数组类型原子类](#%E6%95%B0%E7%BB%84%E7%B1%BB%E5%9E%8B%E5%8E%9F%E5%AD%90%E7%B1%BB)
- - [引用类型原子类](#%E5%BC%95%E7%94%A8%E7%B1%BB%E5%9E%8B%E5%8E%9F%E5%AD%90%E7%B1%BB)
-
-
-
## 线程池
线程池:一个管理线程的池子。
diff --git "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
index f5b3209..ba6af67 100644
--- "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
@@ -1,53 +1,3 @@
-
-
-
-
-- [常见的集合有哪些?](#%E5%B8%B8%E8%A7%81%E7%9A%84%E9%9B%86%E5%90%88%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [List 、Set和Map 的区别](#list-set%E5%92%8Cmap-%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [ArrayList 了解吗?](#arraylist-%E4%BA%86%E8%A7%A3%E5%90%97)
-- [ArrayList 的扩容机制?](#arraylist-%E7%9A%84%E6%89%A9%E5%AE%B9%E6%9C%BA%E5%88%B6)
-- [怎么在遍历 ArrayList 时移除一个元素?](#%E6%80%8E%E4%B9%88%E5%9C%A8%E9%81%8D%E5%8E%86-arraylist-%E6%97%B6%E7%A7%BB%E9%99%A4%E4%B8%80%E4%B8%AA%E5%85%83%E7%B4%A0)
-- [Arraylist 和 Vector 的区别](#arraylist-%E5%92%8C-vector-%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [Arraylist 与 LinkedList 区别](#arraylist-%E4%B8%8E-linkedlist-%E5%8C%BA%E5%88%AB)
-- [HashMap](#hashmap)
- - [解决hash冲突的办法有哪些?HashMap用的哪种?](#%E8%A7%A3%E5%86%B3hash%E5%86%B2%E7%AA%81%E7%9A%84%E5%8A%9E%E6%B3%95%E6%9C%89%E5%93%AA%E4%BA%9Bhashmap%E7%94%A8%E7%9A%84%E5%93%AA%E7%A7%8D)
- - [使用的hash算法?](#%E4%BD%BF%E7%94%A8%E7%9A%84hash%E7%AE%97%E6%B3%95)
- - [扩容过程?](#%E6%89%A9%E5%AE%B9%E8%BF%87%E7%A8%8B)
- - [put方法流程?](#put%E6%96%B9%E6%B3%95%E6%B5%81%E7%A8%8B)
- - [红黑树的特点?](#%E7%BA%A2%E9%BB%91%E6%A0%91%E7%9A%84%E7%89%B9%E7%82%B9)
- - [为什么使用红黑树而不使用AVL树?](#%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BD%BF%E7%94%A8%E7%BA%A2%E9%BB%91%E6%A0%91%E8%80%8C%E4%B8%8D%E4%BD%BF%E7%94%A8avl%E6%A0%91)
- - [在解决 hash 冲突的时候,为什么选择先用链表,再转红黑树?](#%E5%9C%A8%E8%A7%A3%E5%86%B3-hash-%E5%86%B2%E7%AA%81%E7%9A%84%E6%97%B6%E5%80%99%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89%E6%8B%A9%E5%85%88%E7%94%A8%E9%93%BE%E8%A1%A8%E5%86%8D%E8%BD%AC%E7%BA%A2%E9%BB%91%E6%A0%91)
- - [HashMap 的长度为什么是 2 的幂次方?](#hashmap-%E7%9A%84%E9%95%BF%E5%BA%A6%E4%B8%BA%E4%BB%80%E4%B9%88%E6%98%AF-2-%E7%9A%84%E5%B9%82%E6%AC%A1%E6%96%B9)
- - [HashMap默认加载因子是多少?为什么是 0.75?](#hashmap%E9%BB%98%E8%AE%A4%E5%8A%A0%E8%BD%BD%E5%9B%A0%E5%AD%90%E6%98%AF%E5%A4%9A%E5%B0%91%E4%B8%BA%E4%BB%80%E4%B9%88%E6%98%AF-075)
- - [一般用什么作为HashMap的key?](#%E4%B8%80%E8%88%AC%E7%94%A8%E4%BB%80%E4%B9%88%E4%BD%9C%E4%B8%BAhashmap%E7%9A%84key)
- - [HashMap为什么线程不安全?](#hashmap%E4%B8%BA%E4%BB%80%E4%B9%88%E7%BA%BF%E7%A8%8B%E4%B8%8D%E5%AE%89%E5%85%A8)
- - [HashMap和HashTable的区别?](#hashmap%E5%92%8Chashtable%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [LinkedHashMap底层原理?](#linkedhashmap%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86)
-- [讲一下TreeMap?](#%E8%AE%B2%E4%B8%80%E4%B8%8Btreemap)
-- [HashSet底层原理?](#hashset%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86)
-- [HashSet、LinkedHashSet 和 TreeSet 的区别?](#hashsetlinkedhashset-%E5%92%8C-treeset-%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [什么是fail fast?](#%E4%BB%80%E4%B9%88%E6%98%AFfail-fast)
-- [什么是fail safe?](#%E4%BB%80%E4%B9%88%E6%98%AFfail-safe)
-- [讲一下ArrayDeque?](#%E8%AE%B2%E4%B8%80%E4%B8%8Barraydeque)
-- [哪些集合类是线程安全的?哪些不安全?](#%E5%93%AA%E4%BA%9B%E9%9B%86%E5%90%88%E7%B1%BB%E6%98%AF%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E7%9A%84%E5%93%AA%E4%BA%9B%E4%B8%8D%E5%AE%89%E5%85%A8)
-- [迭代器 Iterator 是什么?](#%E8%BF%AD%E4%BB%A3%E5%99%A8-iterator-%E6%98%AF%E4%BB%80%E4%B9%88)
-- [Iterator 和 ListIterator 有什么区别?](#iterator-%E5%92%8C-listiterator-%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB)
-- [并发容器](#%E5%B9%B6%E5%8F%91%E5%AE%B9%E5%99%A8)
- - [ConcurrentHashMap](#concurrenthashmap)
- - [put执行流程?](#put%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B)
- - [怎么扩容?](#%E6%80%8E%E4%B9%88%E6%89%A9%E5%AE%B9)
- - [ConcurrentHashMap 和 Hashtable 的区别?](#concurrenthashmap-%E5%92%8C-hashtable-%E7%9A%84%E5%8C%BA%E5%88%AB)
- - [CopyOnWrite](#copyonwrite)
- - [ConcurrentLinkedQueue](#concurrentlinkedqueue)
- - [阻塞队列](#%E9%98%BB%E5%A1%9E%E9%98%9F%E5%88%97)
- - [JDK提供的阻塞队列](#jdk%E6%8F%90%E4%BE%9B%E7%9A%84%E9%98%BB%E5%A1%9E%E9%98%9F%E5%88%97)
- - [原理](#%E5%8E%9F%E7%90%86)
-- [参考链接](#%E5%8F%82%E8%80%83%E9%93%BE%E6%8E%A5)
-
-
-
-
-
## 常见的集合有哪些?
Java集合类主要由两个接口**Collection**和**Map**派生出来的,Collection有三个子接口:List、Set、Queue。
diff --git "a/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md" "b/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
index 8d4e0d7..128b9a4 100644
--- "a/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
+++ "b/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
@@ -1,78 +1,3 @@
-
-
-
-
-- [简介](#%E7%AE%80%E4%BB%8B)
- - [优缺点](#%E4%BC%98%E7%BC%BA%E7%82%B9)
- - [io多路复用](#io%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8)
- - [应用场景](#%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF)
- - [Memcached和Redis的区别](#memcached%E5%92%8Credis%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [数据类型](#%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B)
- - [字符串类型](#%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%B1%BB%E5%9E%8B)
- - [SETNX和SETEX](#setnx%E5%92%8Csetex)
- - [keys和scan](#keys%E5%92%8Cscan)
- - [expire](#expire)
- - [type](#type)
- - [散列类型](#%E6%95%A3%E5%88%97%E7%B1%BB%E5%9E%8B)
- - [列表类型](#%E5%88%97%E8%A1%A8%E7%B1%BB%E5%9E%8B)
- - [集合类型](#%E9%9B%86%E5%90%88%E7%B1%BB%E5%9E%8B)
- - [有序集合类型](#%E6%9C%89%E5%BA%8F%E9%9B%86%E5%90%88%E7%B1%BB%E5%9E%8B)
- - [Bitmaps](#bitmaps)
- - [HyperLogLog](#hyperloglog)
- - [数据结构](#%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84)
- - [动态字符串](#%E5%8A%A8%E6%80%81%E5%AD%97%E7%AC%A6%E4%B8%B2)
- - [字典](#%E5%AD%97%E5%85%B8)
- - [整数集合](#%E6%95%B4%E6%95%B0%E9%9B%86%E5%90%88)
- - [压缩列表](#%E5%8E%8B%E7%BC%A9%E5%88%97%E8%A1%A8)
- - [跳表](#%E8%B7%B3%E8%A1%A8)
- - [对象](#%E5%AF%B9%E8%B1%A1)
- - [底层实现](#%E5%BA%95%E5%B1%82%E5%AE%9E%E7%8E%B0)
- - [string](#string)
- - [hash](#hash)
- - [list](#list)
- - [set](#set)
- - [zset](#zset)
- - [使用场景](#%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF)
- - [数据库管理](#%E6%95%B0%E6%8D%AE%E5%BA%93%E7%AE%A1%E7%90%86)
-- [排序](#%E6%8E%92%E5%BA%8F)
-- [事务](#%E4%BA%8B%E5%8A%A1)
- - [WATCH命令](#watch%E5%91%BD%E4%BB%A4)
-- [消息队列](#%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97)
- - [优先级队列](#%E4%BC%98%E5%85%88%E7%BA%A7%E9%98%9F%E5%88%97)
- - [发布/订阅模式](#%E5%8F%91%E5%B8%83%E8%AE%A2%E9%98%85%E6%A8%A1%E5%BC%8F)
- - [延时队列](#%E5%BB%B6%E6%97%B6%E9%98%9F%E5%88%97)
-- [持久化](#%E6%8C%81%E4%B9%85%E5%8C%96)
- - [RDB方式](#rdb%E6%96%B9%E5%BC%8F)
- - [AOF方式](#aof%E6%96%B9%E5%BC%8F)
-- [集群](#%E9%9B%86%E7%BE%A4)
- - [主从复制](#%E4%B8%BB%E4%BB%8E%E5%A4%8D%E5%88%B6)
- - [同步机制](#%E5%90%8C%E6%AD%A5%E6%9C%BA%E5%88%B6)
- - [读写分离](#%E8%AF%BB%E5%86%99%E5%88%86%E7%A6%BB)
- - [从数据库持久化](#%E4%BB%8E%E6%95%B0%E6%8D%AE%E5%BA%93%E6%8C%81%E4%B9%85%E5%8C%96)
- - [哨兵Sentinel](#%E5%93%A8%E5%85%B5sentinel)
- - [作用](#%E4%BD%9C%E7%94%A8)
- - [定时任务](#%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1)
- - [工作原理](#%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86)
- - [cluster](#cluster)
- - [哈希分区算法](#%E5%93%88%E5%B8%8C%E5%88%86%E5%8C%BA%E7%AE%97%E6%B3%95)
- - [故障转移](#%E6%95%85%E9%9A%9C%E8%BD%AC%E7%A7%BB)
-- [LUA脚本](#lua%E8%84%9A%E6%9C%AC)
- - [evalsha](#evalsha)
- - [lua脚本作用](#lua%E8%84%9A%E6%9C%AC%E4%BD%9C%E7%94%A8)
- - [应用场景](#%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF-1)
-- [删除策略](#%E5%88%A0%E9%99%A4%E7%AD%96%E7%95%A5)
-- [其他](#%E5%85%B6%E4%BB%96)
- - [客户端](#%E5%AE%A2%E6%88%B7%E7%AB%AF)
- - [慢查询](#%E6%85%A2%E6%9F%A5%E8%AF%A2)
- - [pipeline](#pipeline)
- - [数据一致性](#%E6%95%B0%E6%8D%AE%E4%B8%80%E8%87%B4%E6%80%A7)
-
-
-
-目录结构如下:
-
-
-
## 简介
Redis是一个高性能的key-value数据库。Redis对数据的操作都是原子性的。
diff --git "a/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md" "b/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
index 03a9618..4db94a9 100644
--- "a/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
+++ "b/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
@@ -1,40 +1,3 @@
-
-
-
-
-- [Redis是什么?](#redis%E6%98%AF%E4%BB%80%E4%B9%88)
-- [Redis优缺点?](#redis%E4%BC%98%E7%BC%BA%E7%82%B9)
-- [Redis为什么这么快?](#redis%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E4%B9%88%E5%BF%AB)
-- [Redis为何选择单线程](#redis%E4%B8%BA%E4%BD%95%E9%80%89%E6%8B%A9%E5%8D%95%E7%BA%BF%E7%A8%8B)
-- [Redis6.0为何引入多线程?](#redis60%E4%B8%BA%E4%BD%95%E5%BC%95%E5%85%A5%E5%A4%9A%E7%BA%BF%E7%A8%8B)
-- [Redis应用场景有哪些?](#redis%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [Memcached和Redis的区别?](#memcached%E5%92%8Credis%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [Redis 数据类型有哪些?](#redis-%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [keys命令存在的问题?](#keys%E5%91%BD%E4%BB%A4%E5%AD%98%E5%9C%A8%E7%9A%84%E9%97%AE%E9%A2%98)
-- [SortedSet和List异同点?](#sortedset%E5%92%8Clist%E5%BC%82%E5%90%8C%E7%82%B9)
-- [Redis事务](#redis%E4%BA%8B%E5%8A%A1)
-- [持久化机制](#%E6%8C%81%E4%B9%85%E5%8C%96%E6%9C%BA%E5%88%B6)
- - [RDB方式](#rdb%E6%96%B9%E5%BC%8F)
- - [AOF方式](#aof%E6%96%B9%E5%BC%8F)
-- [RDB和AOF如何选择?](#rdb%E5%92%8Caof%E5%A6%82%E4%BD%95%E9%80%89%E6%8B%A9)
-- [Redis常见的部署方式有哪些?](#redis%E5%B8%B8%E8%A7%81%E7%9A%84%E9%83%A8%E7%BD%B2%E6%96%B9%E5%BC%8F%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [主从复制](#%E4%B8%BB%E4%BB%8E%E5%A4%8D%E5%88%B6)
-- [哨兵Sentinel](#%E5%93%A8%E5%85%B5sentinel)
-- [Redis cluster](#redis-cluster)
- - [哈希分区算法有哪些?](#%E5%93%88%E5%B8%8C%E5%88%86%E5%8C%BA%E7%AE%97%E6%B3%95%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [过期键的删除策略?](#%E8%BF%87%E6%9C%9F%E9%94%AE%E7%9A%84%E5%88%A0%E9%99%A4%E7%AD%96%E7%95%A5)
-- [内存淘汰策略有哪些?](#%E5%86%85%E5%AD%98%E6%B7%98%E6%B1%B0%E7%AD%96%E7%95%A5%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [如何保证缓存与数据库双写时的数据一致性?](#%E5%A6%82%E4%BD%95%E4%BF%9D%E8%AF%81%E7%BC%93%E5%AD%98%E4%B8%8E%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8F%8C%E5%86%99%E6%97%B6%E7%9A%84%E6%95%B0%E6%8D%AE%E4%B8%80%E8%87%B4%E6%80%A7)
-- [缓存穿透](#%E7%BC%93%E5%AD%98%E7%A9%BF%E9%80%8F)
-- [缓存雪崩](#%E7%BC%93%E5%AD%98%E9%9B%AA%E5%B4%A9)
-- [缓存击穿](#%E7%BC%93%E5%AD%98%E5%87%BB%E7%A9%BF)
-- [Redis 怎么实现消息队列?](#redis-%E6%80%8E%E4%B9%88%E5%AE%9E%E7%8E%B0%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97)
-- [pipeline的作用?](#pipeline%E7%9A%84%E4%BD%9C%E7%94%A8)
-- [LUA脚本](#lua%E8%84%9A%E6%9C%AC)
-- [什么是RedLock?](#%E4%BB%80%E4%B9%88%E6%98%AFredlock)
-
-
-
## Redis是什么?
Redis(`Remote Dictionary Server`)是一个使用 C 语言编写的,高性能非关系型的键值对数据库。与传统数据库不同的是,Redis 的数据是存在内存中的,所以读写速度非常快,被广泛应用于缓存方向。Redis可以将数据写入磁盘中,保证了数据的安全不丢失,而且Redis的操作是原子性的。
@@ -438,6 +401,8 @@ Redis cluster采用**虚拟槽分区**,所有的键根据哈希函数映射到
数据库的更新操作完成后不直接操作缓存,而是把这个操作命令封装成消息扔到消息队列中,然后由Redis自己去消费更新数据,消息队列可以保证数据操作顺序一致性,确保缓存系统的数据正常。
+以上几个方案都不完美,需要根据业务需求,评估哪种方案影响较小,然后选择相应的方案。
+
## 缓存常见问题
### 缓存穿透
diff --git "a/\345\210\206\345\270\203\345\274\217/RocketMQ\345\256\236\347\216\260RPC\347\232\204\345\216\237\347\220\206.md" "b/\345\210\206\345\270\203\345\274\217/RocketMQ\345\256\236\347\216\260RPC\347\232\204\345\216\237\347\220\206.md"
index 383686b..5ecd5d1 100644
--- "a/\345\210\206\345\270\203\345\274\217/RocketMQ\345\256\236\347\216\260RPC\347\232\204\345\216\237\347\220\206.md"
+++ "b/\345\210\206\345\270\203\345\274\217/RocketMQ\345\256\236\347\216\260RPC\347\232\204\345\216\237\347\220\206.md"
@@ -44,6 +44,7 @@ ClientRemotingProcessor#processReplyMessage中主要做的从消息中获取消
六、总结
所以整体上看来,RocketMQ的Request/Reply模式,其实是利用客户端线程阻塞来换取请求异步变同步以及RocketMQ的回调机制从而间接的实现了RPC效果,但是相比直接RPC调用,数据的链路更长,性能肯定是会有损耗,但是请求会持久化,所以给了重复下发提供了可能。
-————————————————
+
+
版权声明:本文为CSDN博主「林风自在」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lveex/article/details/122514893
\ No newline at end of file
diff --git "a/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" "b/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md"
index 2f9cfa0..05ce1f0 100644
--- "a/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md"
+++ "b/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md"
@@ -54,7 +54,7 @@ TCC 的全称是:`Try`、`Confirm`、`Cancel`。
这种方案说实话几乎很少人使用,但是也有使用的场景。因为这个 **事务回滚实际上是严重依赖于你自己写代码来回滚和补偿** 了,会造成补偿代码巨大,非常之恶心。
-
+
## 可靠消息最终一致性方案
diff --git "a/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\347\274\223\345\255\230.md" "b/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\347\274\223\345\255\230.md"
index bd1f578..d19654d 100644
--- "a/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\347\274\223\345\255\230.md"
+++ "b/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\347\274\223\345\255\230.md"
@@ -2,8 +2,6 @@
并且由于最早的单台机器的内存资源以及承载能力有限,如果大量使用本地缓存,也会使相同的数据被不同的节点存储多份,对内存资源造成较大的浪费,因此,才催生出了分布式缓存。
-
-
## 应用场景
1. **页面缓存**:用来缓存Web 页面的内容片段,包括HTML、CSS 和图片等;
@@ -95,4 +93,6 @@ public String get(String key) {
**3、异步更新缓存**
-数据库的更新操作完成后不直接操作缓存,而是把这个操作命令封装成消息扔到消息队列中,然后由Redis自己去消费更新数据,消息队列可以保证数据操作顺序一致性,确保缓存系统的数据正常。
\ No newline at end of file
+数据库的更新操作完成后不直接操作缓存,而是把这个操作命令封装成消息扔到消息队列中,然后由Redis自己去消费更新数据,消息队列可以保证数据操作顺序一致性,确保缓存系统的数据正常。
+
+以上几个方案都不完美,需要根据业务需求,评估哪种方案影响较小,然后选择相应的方案。
\ No newline at end of file
diff --git "a/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\351\224\201.md" "b/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\351\224\201.md"
index 5645b0b..8a65dd1 100644
--- "a/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\351\224\201.md"
+++ "b/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\351\224\201.md"
@@ -248,6 +248,8 @@ ZooKeeper是一个为分布式应用提供一致性服务的开源组件,它
- 从实现的复杂性角度(从低到高)Zookeeper > Redis > 数据库;
- 从可靠性角度(从高到低)Zookeeper > Redis > 数据库。
+
+
## 参考链接
https://mp.weixin.qq.com/s/xQknd6xsVDPBr4TbETTk2A
\ No newline at end of file
diff --git "a/\345\210\206\345\270\203\345\274\217/\345\276\256\346\234\215\345\212\241.md" "b/\345\210\206\345\270\203\345\274\217/\345\276\256\346\234\215\345\212\241.md"
index 99ffb31..7f52c58 100644
--- "a/\345\210\206\345\270\203\345\274\217/\345\276\256\346\234\215\345\212\241.md"
+++ "b/\345\210\206\345\270\203\345\274\217/\345\276\256\346\234\215\345\212\241.md"
@@ -1,4 +1,4 @@
-## 简介
+## 什么是微服务?
微服务是将一个原本独立的系统拆分成多个小型服务,这些小型服务都在各自独立的进程中运行,服务和服务之间采用轻量级的通信机制进行协作。每个服务可以被独立的部署到生产环境。
@@ -38,7 +38,7 @@
-## 服务划分
+## 服务怎么划分?
横向拆分:按照不同的业务域进行拆分,例如订单、营销、风控、积分资源等。形成独立的业务领域微服务集群。
@@ -145,7 +145,7 @@
**服务网关基本功能**:
-
+
- 智能路由:接收**外部**一切请求,并转发到后端的对外服务。注意:我们只转发外部请求,服务之间的请求不走网关,这就表示全链路追踪、内部服务API监控、内部服务之间调用的容错、智能路由不能在网关完成;当然,也可以将所有的服务调用都走网关,那么几乎所有的功能都可以集成到网关中,但是这样的话,网关的压力会很大,不堪重负。
- 权限校验:网关可以做一些用户身份认证,权限认证,防止非法请求操作API 接口,对内部服务起到保护作用
@@ -191,4 +191,3 @@ Service Mesh 是微服务时代的 TCP/IP 协议。
参考:https://zhuanlan.zhihu.com/p/61901608
-
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\345\237\272\347\241\200.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\345\237\272\347\241\200.md"
index 111ea22..13d9ab1 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\345\237\272\347\241\200.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\345\237\272\347\241\200.md"
@@ -1,99 +1,3 @@
-
-
-
-
-- [简介](#%E7%AE%80%E4%BB%8B)
- - [数据类型](#%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B)
- - [整型](#%E6%95%B4%E5%9E%8B)
- - [浮点型](#%E6%B5%AE%E7%82%B9%E5%9E%8B)
- - [定点数](#%E5%AE%9A%E7%82%B9%E6%95%B0)
- - [字符串](#%E5%AD%97%E7%AC%A6%E4%B8%B2)
- - [二进制数据(BLOB)](#%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%95%B0%E6%8D%AEblob)
- - [日期时间类型](#%E6%97%A5%E6%9C%9F%E6%97%B6%E9%97%B4%E7%B1%BB%E5%9E%8B)
-- [基本命令](#%E5%9F%BA%E6%9C%AC%E5%91%BD%E4%BB%A4)
- - [启动](#%E5%90%AF%E5%8A%A8)
- - [数据库操作](#%E6%95%B0%E6%8D%AE%E5%BA%93%E6%93%8D%E4%BD%9C)
- - [表](#%E8%A1%A8)
- - [检索](#%E6%A3%80%E7%B4%A2)
- - [排序](#%E6%8E%92%E5%BA%8F)
- - [过滤](#%E8%BF%87%E6%BB%A4)
- - [不匹配检查:](#%E4%B8%8D%E5%8C%B9%E9%85%8D%E6%A3%80%E6%9F%A5)
- - [范围查询:](#%E8%8C%83%E5%9B%B4%E6%9F%A5%E8%AF%A2)
- - [空值检查](#%E7%A9%BA%E5%80%BC%E6%A3%80%E6%9F%A5)
- - [计算次序](#%E8%AE%A1%E7%AE%97%E6%AC%A1%E5%BA%8F)
- - [IN 操作符](#in-%E6%93%8D%E4%BD%9C%E7%AC%A6)
- - [NOT操作符](#not%E6%93%8D%E4%BD%9C%E7%AC%A6)
- - [LIKE操作符](#like%E6%93%8D%E4%BD%9C%E7%AC%A6)
- - [LIMIT](#limit)
- - [正则表达式](#%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F)
-- [计算字段](#%E8%AE%A1%E7%AE%97%E5%AD%97%E6%AE%B5)
- - [拼接字段](#%E6%8B%BC%E6%8E%A5%E5%AD%97%E6%AE%B5)
- - [计算字段](#%E8%AE%A1%E7%AE%97%E5%AD%97%E6%AE%B5-1)
-- [函数](#%E5%87%BD%E6%95%B0)
- - [文本处理](#%E6%96%87%E6%9C%AC%E5%A4%84%E7%90%86)
- - [日期处理函数](#%E6%97%A5%E6%9C%9F%E5%A4%84%E7%90%86%E5%87%BD%E6%95%B0)
- - [数值处理函数](#%E6%95%B0%E5%80%BC%E5%A4%84%E7%90%86%E5%87%BD%E6%95%B0)
-- [汇总数据](#%E6%B1%87%E6%80%BB%E6%95%B0%E6%8D%AE)
- - [聚集函数](#%E8%81%9A%E9%9B%86%E5%87%BD%E6%95%B0)
-- [分组](#%E5%88%86%E7%BB%84)
- - [过滤分组](#%E8%BF%87%E6%BB%A4%E5%88%86%E7%BB%84)
- - [having和where区别](#having%E5%92%8Cwhere%E5%8C%BA%E5%88%AB)
- - [SELECT 子句顺序](#select-%E5%AD%90%E5%8F%A5%E9%A1%BA%E5%BA%8F)
-- [子查询](#%E5%AD%90%E6%9F%A5%E8%AF%A2)
-- [连接](#%E8%BF%9E%E6%8E%A5)
- - [内连接](#%E5%86%85%E8%BF%9E%E6%8E%A5)
- - [自连接](#%E8%87%AA%E8%BF%9E%E6%8E%A5)
- - [自然连接](#%E8%87%AA%E7%84%B6%E8%BF%9E%E6%8E%A5)
- - [内连接](#%E5%86%85%E8%BF%9E%E6%8E%A5-1)
- - [外连接](#%E5%A4%96%E8%BF%9E%E6%8E%A5)
- - [多表连接](#%E5%A4%9A%E8%A1%A8%E8%BF%9E%E6%8E%A5)
-- [组合查询](#%E7%BB%84%E5%90%88%E6%9F%A5%E8%AF%A2)
- - [UNION](#union)
-- [全文搜索](#%E5%85%A8%E6%96%87%E6%90%9C%E7%B4%A2)
-- [表操作](#%E8%A1%A8%E6%93%8D%E4%BD%9C)
- - [创建表](#%E5%88%9B%E5%BB%BA%E8%A1%A8)
- - [更新表](#%E6%9B%B4%E6%96%B0%E8%A1%A8)
- - [约束](#%E7%BA%A6%E6%9D%9F)
- - [删除表](#%E5%88%A0%E9%99%A4%E8%A1%A8)
- - [重命名表](#%E9%87%8D%E5%91%BD%E5%90%8D%E8%A1%A8)
-- [列操作](#%E5%88%97%E6%93%8D%E4%BD%9C)
- - [插入数据](#%E6%8F%92%E5%85%A5%E6%95%B0%E6%8D%AE)
- - [更新数据](#%E6%9B%B4%E6%96%B0%E6%95%B0%E6%8D%AE)
- - [删除数据](#%E5%88%A0%E9%99%A4%E6%95%B0%E6%8D%AE)
- - [truncate、delete与drop区别](#truncatedelete%E4%B8%8Edrop%E5%8C%BA%E5%88%AB)
-- [引擎](#%E5%BC%95%E6%93%8E)
-- [视图](#%E8%A7%86%E5%9B%BE)
- - [应用](#%E5%BA%94%E7%94%A8)
- - [限制](#%E9%99%90%E5%88%B6)
- - [语法](#%E8%AF%AD%E6%B3%95)
- - [简化复杂连接](#%E7%AE%80%E5%8C%96%E5%A4%8D%E6%9D%82%E8%BF%9E%E6%8E%A5)
- - [更新视图](#%E6%9B%B4%E6%96%B0%E8%A7%86%E5%9B%BE)
-- [存储过程](#%E5%AD%98%E5%82%A8%E8%BF%87%E7%A8%8B)
- - [创建](#%E5%88%9B%E5%BB%BA)
- - [调用](#%E8%B0%83%E7%94%A8)
- - [删除](#%E5%88%A0%E9%99%A4)
- - [参数](#%E5%8F%82%E6%95%B0)
- - [实例](#%E5%AE%9E%E4%BE%8B)
- - [检查](#%E6%A3%80%E6%9F%A5)
-- [游标](#%E6%B8%B8%E6%A0%87)
- - [创建游标](#%E5%88%9B%E5%BB%BA%E6%B8%B8%E6%A0%87)
- - [使用游标](#%E4%BD%BF%E7%94%A8%E6%B8%B8%E6%A0%87)
-- [触发器](#%E8%A7%A6%E5%8F%91%E5%99%A8)
- - [创建](#%E5%88%9B%E5%BB%BA-1)
- - [删除](#%E5%88%A0%E9%99%A4-1)
- - [使用触发器](#%E4%BD%BF%E7%94%A8%E8%A7%A6%E5%8F%91%E5%99%A8)
-- [事务处理](#%E4%BA%8B%E5%8A%A1%E5%A4%84%E7%90%86)
- - [保留点](#%E4%BF%9D%E7%95%99%E7%82%B9)
-- [权限](#%E6%9D%83%E9%99%90)
- - [管理用户](#%E7%AE%A1%E7%90%86%E7%94%A8%E6%88%B7)
-- [优化性能](#%E4%BC%98%E5%8C%96%E6%80%A7%E8%83%BD)
-- [索引](#%E7%B4%A2%E5%BC%95)
- - [创建索引](#%E5%88%9B%E5%BB%BA%E7%B4%A2%E5%BC%95)
- - [删除索引](#%E5%88%A0%E9%99%A4%E7%B4%A2%E5%BC%95)
- - [查看索引](#%E6%9F%A5%E7%9C%8B%E7%B4%A2%E5%BC%95)
-
-
-
## 简介
SQL 结构化查询语言。
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index e7bfe2d..6f12608 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -1,52 +1,3 @@
-
-
-
-
-- [事务的四大特性?](#%E4%BA%8B%E5%8A%A1%E7%9A%84%E5%9B%9B%E5%A4%A7%E7%89%B9%E6%80%A7)
-- [数据库的三大范式](#%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9A%84%E4%B8%89%E5%A4%A7%E8%8C%83%E5%BC%8F)
-- [事务隔离级别有哪些?](#%E4%BA%8B%E5%8A%A1%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [索引](#%E7%B4%A2%E5%BC%95)
- - [什么是索引?](#%E4%BB%80%E4%B9%88%E6%98%AF%E7%B4%A2%E5%BC%95)
- - [索引的优缺点?](#%E7%B4%A2%E5%BC%95%E7%9A%84%E4%BC%98%E7%BC%BA%E7%82%B9)
- - [索引的作用?](#%E7%B4%A2%E5%BC%95%E7%9A%84%E4%BD%9C%E7%94%A8)
- - [什么情况下需要建索引?](#%E4%BB%80%E4%B9%88%E6%83%85%E5%86%B5%E4%B8%8B%E9%9C%80%E8%A6%81%E5%BB%BA%E7%B4%A2%E5%BC%95)
- - [什么情况下不建索引?](#%E4%BB%80%E4%B9%88%E6%83%85%E5%86%B5%E4%B8%8B%E4%B8%8D%E5%BB%BA%E7%B4%A2%E5%BC%95)
- - [索引的数据结构](#%E7%B4%A2%E5%BC%95%E7%9A%84%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84)
- - [Hash索引和B+树索引的区别?](#hash%E7%B4%A2%E5%BC%95%E5%92%8Cb%E6%A0%91%E7%B4%A2%E5%BC%95%E7%9A%84%E5%8C%BA%E5%88%AB)
- - [为什么B+树比B树更适合实现数据库索引?](#%E4%B8%BA%E4%BB%80%E4%B9%88b%E6%A0%91%E6%AF%94b%E6%A0%91%E6%9B%B4%E9%80%82%E5%90%88%E5%AE%9E%E7%8E%B0%E6%95%B0%E6%8D%AE%E5%BA%93%E7%B4%A2%E5%BC%95)
- - [索引有什么分类?](#%E7%B4%A2%E5%BC%95%E6%9C%89%E4%BB%80%E4%B9%88%E5%88%86%E7%B1%BB)
- - [什么是最左匹配原则?](#%E4%BB%80%E4%B9%88%E6%98%AF%E6%9C%80%E5%B7%A6%E5%8C%B9%E9%85%8D%E5%8E%9F%E5%88%99)
- - [什么是聚集索引?](#%E4%BB%80%E4%B9%88%E6%98%AF%E8%81%9A%E9%9B%86%E7%B4%A2%E5%BC%95)
- - [什么是覆盖索引?](#%E4%BB%80%E4%B9%88%E6%98%AF%E8%A6%86%E7%9B%96%E7%B4%A2%E5%BC%95)
- - [索引的设计原则?](#%E7%B4%A2%E5%BC%95%E7%9A%84%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99)
- - [索引什么时候会失效?](#%E7%B4%A2%E5%BC%95%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99%E4%BC%9A%E5%A4%B1%E6%95%88)
- - [什么是前缀索引?](#%E4%BB%80%E4%B9%88%E6%98%AF%E5%89%8D%E7%BC%80%E7%B4%A2%E5%BC%95)
-- [常见的存储引擎有哪些?](#%E5%B8%B8%E8%A7%81%E7%9A%84%E5%AD%98%E5%82%A8%E5%BC%95%E6%93%8E%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [MyISAM和InnoDB的区别?](#myisam%E5%92%8Cinnodb%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [MVCC 实现原理?](#mvcc-%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86)
-- [快照读和当前读](#%E5%BF%AB%E7%85%A7%E8%AF%BB%E5%92%8C%E5%BD%93%E5%89%8D%E8%AF%BB)
-- [共享锁和排他锁](#%E5%85%B1%E4%BA%AB%E9%94%81%E5%92%8C%E6%8E%92%E4%BB%96%E9%94%81)
-- [大表怎么优化?](#%E5%A4%A7%E8%A1%A8%E6%80%8E%E4%B9%88%E4%BC%98%E5%8C%96)
-- [bin log/redo log/undo log](#bin-logredo-logundo-log)
-- [bin log和redo log有什么区别?](#bin-log%E5%92%8Credo-log%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB)
-- [讲一下MySQL架构?](#%E8%AE%B2%E4%B8%80%E4%B8%8Bmysql%E6%9E%B6%E6%9E%84)
-- [分库分表](#%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8)
-- [什么是分区表?](#%E4%BB%80%E4%B9%88%E6%98%AF%E5%88%86%E5%8C%BA%E8%A1%A8)
-- [分区表类型](#%E5%88%86%E5%8C%BA%E8%A1%A8%E7%B1%BB%E5%9E%8B)
-- [分区的问题?](#%E5%88%86%E5%8C%BA%E7%9A%84%E9%97%AE%E9%A2%98)
-- [查询语句执行流程?](#%E6%9F%A5%E8%AF%A2%E8%AF%AD%E5%8F%A5%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B)
-- [更新语句执行过程?](#%E6%9B%B4%E6%96%B0%E8%AF%AD%E5%8F%A5%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B)
-- [exist和in的区别?](#exist%E5%92%8Cin%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [MySQL中int(10)和char(10)的区别? ](#mysql%E4%B8%ADint10%E5%92%8Cchar10%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [truncate、delete与drop区别?](#truncatedelete%E4%B8%8Edrop%E5%8C%BA%E5%88%AB)
-- [having和where区别?](#having%E5%92%8Cwhere%E5%8C%BA%E5%88%AB)
-- [什么是MySQL主从同步?](#%E4%BB%80%E4%B9%88%E6%98%AFmysql%E4%B8%BB%E4%BB%8E%E5%90%8C%E6%AD%A5)
-- [为什么要做主从同步?](#%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E5%81%9A%E4%B8%BB%E4%BB%8E%E5%90%8C%E6%AD%A5)
-- [乐观锁和悲观锁是什么?](#%E4%B9%90%E8%A7%82%E9%94%81%E5%92%8C%E6%82%B2%E8%A7%82%E9%94%81%E6%98%AF%E4%BB%80%E4%B9%88)
-- [用过processlist吗?](#%E7%94%A8%E8%BF%87processlist%E5%90%97)
-
-
-
## 事务的四大特性?
**事务特性ACID**:**原子性**(`Atomicity`)、**一致性**(`Consistency`)、**隔离性**(`Isolation`)、**持久性**(`Durability`)。
diff --git "a/\346\241\206\346\236\266/Mybatis\351\235\242\350\257\225\351\242\230.md" "b/\346\241\206\346\236\266/Mybatis\351\235\242\350\257\225\351\242\230.md"
index 174db08..49f4bc0 100644
--- "a/\346\241\206\346\236\266/Mybatis\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\241\206\346\236\266/Mybatis\351\235\242\350\257\225\351\242\230.md"
@@ -76,8 +76,6 @@ Mybatis 使用 RowBounds 对象进行分页,它是针对 ResultSet 结果集
Mybatis仅可以编写针对 `ParameterHandler`、`ResultSetHandler`、`StatementHandler`、`Executor`这4种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是`InvocationHandler`的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。
-编写插件:实现Mybatis的Interceptor接口并复写`intercept()`方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。
-
## .如何编写一个插件?
编写插件:实现 Mybatis 的 Interceptor 接口并复写 intercept()方法,然后再给插件编写注解,指定要拦截哪一个接口的哪些方法即可,最后在配置文件中配置你编写的插件。
diff --git "a/\346\241\206\346\236\266/SpringCloud\345\276\256\346\234\215\345\212\241\345\256\236\346\210\230.md" "b/\346\241\206\346\236\266/SpringCloud\345\276\256\346\234\215\345\212\241\345\256\236\346\210\230.md"
index f1f910b..7a75bb4 100644
--- "a/\346\241\206\346\236\266/SpringCloud\345\276\256\346\234\215\345\212\241\345\256\236\346\210\230.md"
+++ "b/\346\241\206\346\236\266/SpringCloud\345\276\256\346\234\215\345\212\241\345\256\236\346\210\230.md"
@@ -1,61 +1,3 @@
-
-
-**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
-
-- [基础知识](#%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86)
-- [Spring Cloud Eureka](#spring-cloud-eureka)
- - [服务注册与发现](#%E6%9C%8D%E5%8A%A1%E6%B3%A8%E5%86%8C%E4%B8%8E%E5%8F%91%E7%8E%B0)
- - [代码实例](#%E4%BB%A3%E7%A0%81%E5%AE%9E%E4%BE%8B)
- - [高可用注册中心](#%E9%AB%98%E5%8F%AF%E7%94%A8%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83)
-- [Spring Cloud Ribbon](#spring-cloud-ribbon)
- - [RestTemplate](#resttemplate)
- - [代码实例](#%E4%BB%A3%E7%A0%81%E5%AE%9E%E4%BE%8B-1)
-- [Spring Cloud Hystrix](#spring-cloud-hystrix)
- - [代码实例](#%E4%BB%A3%E7%A0%81%E5%AE%9E%E4%BE%8B-2)
-- [Spring Cloud Feign](#spring-cloud-feign)
- - [负载均衡功能](#%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E5%8A%9F%E8%83%BD)
- - [服务降级功能](#%E6%9C%8D%E5%8A%A1%E9%99%8D%E7%BA%A7%E5%8A%9F%E8%83%BD)
-- [Spring Cloud Zuul](#spring-cloud-zuul)
- - [路由规则](#%E8%B7%AF%E7%94%B1%E8%A7%84%E5%88%99)
- - [访问前缀](#%E8%AE%BF%E9%97%AE%E5%89%8D%E7%BC%80)
- - [header过滤](#header%E8%BF%87%E6%BB%A4)
- - [重定向](#%E9%87%8D%E5%AE%9A%E5%90%91)
- - [查看路由信息](#%E6%9F%A5%E7%9C%8B%E8%B7%AF%E7%94%B1%E4%BF%A1%E6%81%AF)
- - [过滤器](#%E8%BF%87%E6%BB%A4%E5%99%A8)
- - [过滤器类型](#%E8%BF%87%E6%BB%A4%E5%99%A8%E7%B1%BB%E5%9E%8B)
- - [自定义过滤器](#%E8%87%AA%E5%AE%9A%E4%B9%89%E8%BF%87%E6%BB%A4%E5%99%A8)
- - [禁用过滤器](#%E7%A6%81%E7%94%A8%E8%BF%87%E6%BB%A4%E5%99%A8)
- - [Ribbon和Hystrix的支持](#ribbon%E5%92%8Chystrix%E7%9A%84%E6%94%AF%E6%8C%81)
- - [常用配置](#%E5%B8%B8%E7%94%A8%E9%85%8D%E7%BD%AE)
-- [Spring Cloud Gateway](#spring-cloud-gateway)
- - [配置路由](#%E9%85%8D%E7%BD%AE%E8%B7%AF%E7%94%B1)
- - [Route Predicate](#route-predicate)
- - [After Route Predicate](#after-route-predicate)
- - [Cookie Route Predicate](#cookie-route-predicate)
- - [Header Route Predicate](#header-route-predicate)
- - [Host Route Predicate](#host-route-predicate)
- - [Route Filter](#route-filter)
- - [AddRequestParameter GatewayFilter](#addrequestparameter-gatewayfilter)
- - [StripPrefix GatewayFilter](#stripprefix-gatewayfilter)
- - [PrefixPate GatewayFilter](#prefixpate-gatewayfilter)
- - [Hystrix GatewayFilter](#hystrix-gatewayfilter)
- - [RequestRateLimiter GatewayFilter](#requestratelimiter-gatewayfilter)
- - [Retry GatewayFilter](#retry-gatewayfilter)
- - [结合注册中心使用](#%E7%BB%93%E5%90%88%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83%E4%BD%BF%E7%94%A8)
- - [使用动态路由](#%E4%BD%BF%E7%94%A8%E5%8A%A8%E6%80%81%E8%B7%AF%E7%94%B1)
- - [使用过滤器](#%E4%BD%BF%E7%94%A8%E8%BF%87%E6%BB%A4%E5%99%A8)
-- [Spring Cloud Config](#spring-cloud-config)
- - [准备配置信息](#%E5%87%86%E5%A4%87%E9%85%8D%E7%BD%AE%E4%BF%A1%E6%81%AF)
- - [配置中心](#%E9%85%8D%E7%BD%AE%E4%B8%AD%E5%BF%83)
- - [获取配置信息](#%E8%8E%B7%E5%8F%96%E9%85%8D%E7%BD%AE%E4%BF%A1%E6%81%AF)
- - [刷新配置](#%E5%88%B7%E6%96%B0%E9%85%8D%E7%BD%AE)
- - [安全认证](#%E5%AE%89%E5%85%A8%E8%AE%A4%E8%AF%81)
- - [配置中心集群](#%E9%85%8D%E7%BD%AE%E4%B8%AD%E5%BF%83%E9%9B%86%E7%BE%A4)
-- [Spring Cloud Bus](#spring-cloud-bus)
-- [Spring Cloud Security](#spring-cloud-security)
-
-
-
## 基础知识
微服务是将一个原本独立的系统拆分成多个小型服务,这些小型服务都在各自独立的进程中运行。
diff --git "a/\346\241\206\346\236\266/Spring\345\256\236\346\210\230.md" "b/\346\241\206\346\236\266/Spring\345\256\236\346\210\230.md"
index b7eb212..f56fe59 100644
--- "a/\346\241\206\346\236\266/Spring\345\256\236\346\210\230.md"
+++ "b/\346\241\206\346\236\266/Spring\345\256\236\346\210\230.md"
@@ -1,68 +1,3 @@
-
-
-
-
-- [Spring的核心](#spring%E7%9A%84%E6%A0%B8%E5%BF%83)
-- [装配bean](#%E8%A3%85%E9%85%8Dbean)
- - [自动装配bean](#%E8%87%AA%E5%8A%A8%E8%A3%85%E9%85%8Dbean)
- - [验证自动装配](#%E9%AA%8C%E8%AF%81%E8%87%AA%E5%8A%A8%E8%A3%85%E9%85%8D)
- - [通过Java代码装配bean](#%E9%80%9A%E8%BF%87java%E4%BB%A3%E7%A0%81%E8%A3%85%E9%85%8Dbean)
- - [通过xml装配bean](#%E9%80%9A%E8%BF%87xml%E8%A3%85%E9%85%8Dbean)
- - [构造器注入](#%E6%9E%84%E9%80%A0%E5%99%A8%E6%B3%A8%E5%85%A5)
- - [属性注入](#%E5%B1%9E%E6%80%A7%E6%B3%A8%E5%85%A5)
- - [混合配置](#%E6%B7%B7%E5%90%88%E9%85%8D%E7%BD%AE)
- - [在JavaConfig中引用xml配置](#%E5%9C%A8javaconfig%E4%B8%AD%E5%BC%95%E7%94%A8xml%E9%85%8D%E7%BD%AE)
- - [在xml配置中引用JavaConfig](#%E5%9C%A8xml%E9%85%8D%E7%BD%AE%E4%B8%AD%E5%BC%95%E7%94%A8javaconfig)
-- [高级装配](#%E9%AB%98%E7%BA%A7%E8%A3%85%E9%85%8D)
- - [自动装配的歧义性](#%E8%87%AA%E5%8A%A8%E8%A3%85%E9%85%8D%E7%9A%84%E6%AD%A7%E4%B9%89%E6%80%A7)
- - [标识首选的bean](#%E6%A0%87%E8%AF%86%E9%A6%96%E9%80%89%E7%9A%84bean)
- - [限定自动装配的bean](#%E9%99%90%E5%AE%9A%E8%87%AA%E5%8A%A8%E8%A3%85%E9%85%8D%E7%9A%84bean)
- - [创建自定义的限定符](#%E5%88%9B%E5%BB%BA%E8%87%AA%E5%AE%9A%E4%B9%89%E7%9A%84%E9%99%90%E5%AE%9A%E7%AC%A6)
- - [bean的作用域](#bean%E7%9A%84%E4%BD%9C%E7%94%A8%E5%9F%9F)
- - [运行时值注入](#%E8%BF%90%E8%A1%8C%E6%97%B6%E5%80%BC%E6%B3%A8%E5%85%A5)
- - [注入外部的值](#%E6%B3%A8%E5%85%A5%E5%A4%96%E9%83%A8%E7%9A%84%E5%80%BC)
- - [使用Spring表达式语言进行装配](#%E4%BD%BF%E7%94%A8spring%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%AF%AD%E8%A8%80%E8%BF%9B%E8%A1%8C%E8%A3%85%E9%85%8D)
-- [面向切面](#%E9%9D%A2%E5%90%91%E5%88%87%E9%9D%A2)
- - [AOP术语](#aop%E6%9C%AF%E8%AF%AD)
- - [Spring对aop的支持](#spring%E5%AF%B9aop%E7%9A%84%E6%94%AF%E6%8C%81)
- - [通过切面选择连接点](#%E9%80%9A%E8%BF%87%E5%88%87%E9%9D%A2%E9%80%89%E6%8B%A9%E8%BF%9E%E6%8E%A5%E7%82%B9)
- - [使用注解创建切面](#%E4%BD%BF%E7%94%A8%E6%B3%A8%E8%A7%A3%E5%88%9B%E5%BB%BA%E5%88%87%E9%9D%A2)
- - [定义切面](#%E5%AE%9A%E4%B9%89%E5%88%87%E9%9D%A2)
- - [创建环绕通知](#%E5%88%9B%E5%BB%BA%E7%8E%AF%E7%BB%95%E9%80%9A%E7%9F%A5)
- - [处理通知中的参数](#%E5%A4%84%E7%90%86%E9%80%9A%E7%9F%A5%E4%B8%AD%E7%9A%84%E5%8F%82%E6%95%B0)
- - [通过注解引入新功能](#%E9%80%9A%E8%BF%87%E6%B3%A8%E8%A7%A3%E5%BC%95%E5%85%A5%E6%96%B0%E5%8A%9F%E8%83%BD)
- - [在xml中声明切面](#%E5%9C%A8xml%E4%B8%AD%E5%A3%B0%E6%98%8E%E5%88%87%E9%9D%A2)
- - [声明通知](#%E5%A3%B0%E6%98%8E%E9%80%9A%E7%9F%A5)
- - [创建环绕通知](#%E5%88%9B%E5%BB%BA%E7%8E%AF%E7%BB%95%E9%80%9A%E7%9F%A5-1)
- - [为通知传递参数](#%E4%B8%BA%E9%80%9A%E7%9F%A5%E4%BC%A0%E9%80%92%E5%8F%82%E6%95%B0)
- - [通过切面引入新的功能](#%E9%80%9A%E8%BF%87%E5%88%87%E9%9D%A2%E5%BC%95%E5%85%A5%E6%96%B0%E7%9A%84%E5%8A%9F%E8%83%BD)
- - [注入AspectJ切面](#%E6%B3%A8%E5%85%A5aspectj%E5%88%87%E9%9D%A2)
-- [后端中的 Spring](#%E5%90%8E%E7%AB%AF%E4%B8%AD%E7%9A%84-spring)
- - [数据访问模板化](#%E6%95%B0%E6%8D%AE%E8%AE%BF%E9%97%AE%E6%A8%A1%E6%9D%BF%E5%8C%96)
- - [配置数据源](#%E9%85%8D%E7%BD%AE%E6%95%B0%E6%8D%AE%E6%BA%90)
- - [在 Spring 中集成 Hibernate](#%E5%9C%A8-spring-%E4%B8%AD%E9%9B%86%E6%88%90-hibernate)
- - [Spring 与 Java 持久化 API](#spring-%E4%B8%8E-java-%E6%8C%81%E4%B9%85%E5%8C%96--api)
- - [配置实体管理器工厂](#%E9%85%8D%E7%BD%AE%E5%AE%9E%E4%BD%93%E7%AE%A1%E7%90%86%E5%99%A8%E5%B7%A5%E5%8E%82)
- - [编写 JPA Repository](#%E7%BC%96%E5%86%99-jpa-repository)
- - [借助 Spring Data 实现自动化的 JPA Repository](#%E5%80%9F%E5%8A%A9-spring-data-%E5%AE%9E%E7%8E%B0%E8%87%AA%E5%8A%A8%E5%8C%96%E7%9A%84-jpa-repository)
- - [Spring Data JPA 的核心接口](#spring-data-jpa-%E7%9A%84%E6%A0%B8%E5%BF%83%E6%8E%A5%E5%8F%A3)
- - [定义数据访问层](#%E5%AE%9A%E4%B9%89%E6%95%B0%E6%8D%AE%E8%AE%BF%E9%97%AE%E5%B1%82)
- - [定义查询方法](#%E5%AE%9A%E4%B9%89%E6%9F%A5%E8%AF%A2%E6%96%B9%E6%B3%95)
- - [混合自定义的功能](#%E6%B7%B7%E5%90%88%E8%87%AA%E5%AE%9A%E4%B9%89%E7%9A%84%E5%8A%9F%E8%83%BD)
-- [Spring Security](#spring-security)
- - [基于内存的用户存储](#%E5%9F%BA%E4%BA%8E%E5%86%85%E5%AD%98%E7%9A%84%E7%94%A8%E6%88%B7%E5%AD%98%E5%82%A8)
- - [基于数据库表进行认证](#%E5%9F%BA%E4%BA%8E%E6%95%B0%E6%8D%AE%E5%BA%93%E8%A1%A8%E8%BF%9B%E8%A1%8C%E8%AE%A4%E8%AF%81)
- - [拦截请求](#%E6%8B%A6%E6%88%AA%E8%AF%B7%E6%B1%82)
- - [使用Spring表达式进行安全保护](#%E4%BD%BF%E7%94%A8spring%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%BF%9B%E8%A1%8C%E5%AE%89%E5%85%A8%E4%BF%9D%E6%8A%A4)
- - [安全通道](#%E5%AE%89%E5%85%A8%E9%80%9A%E9%81%93)
- - [认证用户](#%E8%AE%A4%E8%AF%81%E7%94%A8%E6%88%B7)
- - [方法级别安全](#%E6%96%B9%E6%B3%95%E7%BA%A7%E5%88%AB%E5%AE%89%E5%85%A8)
- - [@Secured](#secured)
- - [@RolesAllowed](#rolesallowed)
- - [使用表达式实现方法级别的安全性](#%E4%BD%BF%E7%94%A8%E8%A1%A8%E8%BE%BE%E5%BC%8F%E5%AE%9E%E7%8E%B0%E6%96%B9%E6%B3%95%E7%BA%A7%E5%88%AB%E7%9A%84%E5%AE%89%E5%85%A8%E6%80%A7)
-
-
-
## Spring的核心
相对于EJB(Enterprise JaveBean),Spring提供了更加轻量级和简单的编程模型。
diff --git "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md" "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
index f0f66c0..697a649 100644
--- "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
@@ -1,35 +1,3 @@
-
-
-
-
-- [Spring的优点](#spring%E7%9A%84%E4%BC%98%E7%82%B9)
-- [Spring 用到了哪些设计模式?](#spring-%E7%94%A8%E5%88%B0%E4%BA%86%E5%93%AA%E4%BA%9B%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F)
-- [什么是AOP?](#%E4%BB%80%E4%B9%88%E6%98%AFaop)
-- [AOP有哪些实现方式?](#aop%E6%9C%89%E5%93%AA%E4%BA%9B%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F)
-- [JDK动态代理和CGLIB动态代理的区别?](#jdk%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E5%92%8Ccglib%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [Spring AOP相关术语](#spring-aop%E7%9B%B8%E5%85%B3%E6%9C%AF%E8%AF%AD)
-- [Spring通知有哪些类型?](#spring%E9%80%9A%E7%9F%A5%E6%9C%89%E5%93%AA%E4%BA%9B%E7%B1%BB%E5%9E%8B)
-- [什么是IOC?](#%E4%BB%80%E4%B9%88%E6%98%AFioc)
-- [IOC的优点是什么?](#ioc%E7%9A%84%E4%BC%98%E7%82%B9%E6%98%AF%E4%BB%80%E4%B9%88)
-- [什么是依赖注入?](#%E4%BB%80%E4%B9%88%E6%98%AF%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5)
-- [IOC容器初始化过程?](#ioc%E5%AE%B9%E5%99%A8%E5%88%9D%E5%A7%8B%E5%8C%96%E8%BF%87%E7%A8%8B)
-- [Bean的生命周期](#bean%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F)
-- [BeanFactory和FactoryBean的区别?](#beanfactory%E5%92%8Cfactorybean%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [Bean注入容器有哪些方式?](#bean%E6%B3%A8%E5%85%A5%E5%AE%B9%E5%99%A8%E6%9C%89%E5%93%AA%E4%BA%9B%E6%96%B9%E5%BC%8F)
-- [Bean的作用域](#bean%E7%9A%84%E4%BD%9C%E7%94%A8%E5%9F%9F)
-- [Spring自动装配的方式有哪些?](#spring%E8%87%AA%E5%8A%A8%E8%A3%85%E9%85%8D%E7%9A%84%E6%96%B9%E5%BC%8F%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [@Autowired和@Resource的区别?](#autowired%E5%92%8Cresource%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [@Qualifier 注解有什么作用](#qualifier-%E6%B3%A8%E8%A7%A3%E6%9C%89%E4%BB%80%E4%B9%88%E4%BD%9C%E7%94%A8)
-- [@Bean和@Component有什么区别?](#bean%E5%92%8Ccomponent%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB)
-- [@Component、@Controller、@Repositor和@Service 的区别?](#componentcontrollerrepositor%E5%92%8Cservice-%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [Spring 事务实现方式有哪些?](#spring-%E4%BA%8B%E5%8A%A1%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [有哪些事务传播行为?](#%E6%9C%89%E5%93%AA%E4%BA%9B%E4%BA%8B%E5%8A%A1%E4%BC%A0%E6%92%AD%E8%A1%8C%E4%B8%BA)
-- [Spring怎么解决循环依赖的问题?](#spring%E6%80%8E%E4%B9%88%E8%A7%A3%E5%86%B3%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96%E7%9A%84%E9%97%AE%E9%A2%98)
-- [Spring启动过程](#spring%E5%90%AF%E5%8A%A8%E8%BF%87%E7%A8%8B)
-- [Spring 的单例 Bean 是否有线程安全问题?](#spring-%E7%9A%84%E5%8D%95%E4%BE%8B-bean-%E6%98%AF%E5%90%A6%E6%9C%89%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E9%97%AE%E9%A2%98)
-
-
-
## Spring的优点
- 通过控制反转和依赖注入实现**松耦合**。
diff --git "a/\346\241\206\346\236\266/\346\267\261\345\205\245\346\265\205\345\207\272Mybatis\346\212\200\346\234\257\345\216\237\347\220\206\344\270\216\345\256\236\346\210\230.md" "b/\346\241\206\346\236\266/\346\267\261\345\205\245\346\265\205\345\207\272Mybatis\346\212\200\346\234\257\345\216\237\347\220\206\344\270\216\345\256\236\346\210\230.md"
index 1c51b46..bc1d6ca 100644
--- "a/\346\241\206\346\236\266/\346\267\261\345\205\245\346\265\205\345\207\272Mybatis\346\212\200\346\234\257\345\216\237\347\220\206\344\270\216\345\256\236\346\210\230.md"
+++ "b/\346\241\206\346\236\266/\346\267\261\345\205\245\346\265\205\345\207\272Mybatis\346\212\200\346\234\257\345\216\237\347\220\206\344\270\216\345\256\236\346\210\230.md"
@@ -1,65 +1,3 @@
-
-
-
-
-- [Mybatis简介](#mybatis%E7%AE%80%E4%BB%8B)
- - [传统的JDBC编程](#%E4%BC%A0%E7%BB%9F%E7%9A%84jdbc%E7%BC%96%E7%A8%8B)
- - [Hibernate与Mybatis](#hibernate%E4%B8%8Emybatis)
-- [Mybatis入门](#mybatis%E5%85%A5%E9%97%A8)
- - [SqlSessionFactory](#sqlsessionfactory)
- - [SqlSession](#sqlsession)
- - [映射器Mapper](#%E6%98%A0%E5%B0%84%E5%99%A8mapper)
- - [Mybatis组件的生命周期](#mybatis%E7%BB%84%E4%BB%B6%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F)
-- [配置](#%E9%85%8D%E7%BD%AE)
- - [properties](#properties)
- - [typeAliases](#typealiases)
- - [typeHandler](#typehandler)
- - [自定义typeHandler](#%E8%87%AA%E5%AE%9A%E4%B9%89typehandler)
- - [枚举类型typeHandler](#%E6%9E%9A%E4%B8%BE%E7%B1%BB%E5%9E%8Btypehandler)
- - [自定义枚举类typeHandler](#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%9E%9A%E4%B8%BE%E7%B1%BBtypehandler)
- - [objectFactory](#objectfactory)
- - [environments](#environments)
- - [数据库事务](#%E6%95%B0%E6%8D%AE%E5%BA%93%E4%BA%8B%E5%8A%A1)
-- [映射器](#%E6%98%A0%E5%B0%84%E5%99%A8)
- - [select元素](#select%E5%85%83%E7%B4%A0)
- - [自动映射](#%E8%87%AA%E5%8A%A8%E6%98%A0%E5%B0%84)
- - [传递多个参数](#%E4%BC%A0%E9%80%92%E5%A4%9A%E4%B8%AA%E5%8F%82%E6%95%B0)
- - [使用resultMap映射结果集](#%E4%BD%BF%E7%94%A8resultmap%E6%98%A0%E5%B0%84%E7%BB%93%E6%9E%9C%E9%9B%86)
- - [insert元素](#insert%E5%85%83%E7%B4%A0)
- - [主键回填](#%E4%B8%BB%E9%94%AE%E5%9B%9E%E5%A1%AB)
- - [自增主键](#%E8%87%AA%E5%A2%9E%E4%B8%BB%E9%94%AE)
- - [非自增主键](#%E9%9D%9E%E8%87%AA%E5%A2%9E%E4%B8%BB%E9%94%AE)
- - [update和delete元素](#update%E5%92%8Cdelete%E5%85%83%E7%B4%A0)
- - [sql元素](#sql%E5%85%83%E7%B4%A0)
- - [resultMap元素](#resultmap%E5%85%83%E7%B4%A0)
- - [级联](#%E7%BA%A7%E8%81%94)
- - [association一对一级联](#association%E4%B8%80%E5%AF%B9%E4%B8%80%E7%BA%A7%E8%81%94)
- - [collection一对多级联](#collection%E4%B8%80%E5%AF%B9%E5%A4%9A%E7%BA%A7%E8%81%94)
- - [discriminator鉴别器级联](#discriminator%E9%89%B4%E5%88%AB%E5%99%A8%E7%BA%A7%E8%81%94)
- - [延迟加载](#%E5%BB%B6%E8%BF%9F%E5%8A%A0%E8%BD%BD)
-- [动态SQL](#%E5%8A%A8%E6%80%81sql)
- - [if元素](#if%E5%85%83%E7%B4%A0)
- - [choose元素](#choose%E5%85%83%E7%B4%A0)
- - [where元素](#where%E5%85%83%E7%B4%A0)
- - [set元素](#set%E5%85%83%E7%B4%A0)
- - [foreach元素](#foreach%E5%85%83%E7%B4%A0)
- - [bind元素](#bind%E5%85%83%E7%B4%A0)
-- [Mybatis-Spring应用](#mybatis-spring%E5%BA%94%E7%94%A8)
-- [实用场景](#%E5%AE%9E%E7%94%A8%E5%9C%BA%E6%99%AF)
- - [批量更新](#%E6%89%B9%E9%87%8F%E6%9B%B4%E6%96%B0)
- - [存储过程](#%E5%AD%98%E5%82%A8%E8%BF%87%E7%A8%8B)
- - [in和out参数](#in%E5%92%8Cout%E5%8F%82%E6%95%B0)
- - [游标](#%E6%B8%B8%E6%A0%87)
- - [分页](#%E5%88%86%E9%A1%B5)
- - [RowBounds分页](#rowbounds%E5%88%86%E9%A1%B5)
-- [预编译](#%E9%A2%84%E7%BC%96%E8%AF%91)
-- [缓存](#%E7%BC%93%E5%AD%98)
- - [一级缓存和二级缓存](#%E4%B8%80%E7%BA%A7%E7%BC%93%E5%AD%98%E5%92%8C%E4%BA%8C%E7%BA%A7%E7%BC%93%E5%AD%98)
-- [原理](#%E5%8E%9F%E7%90%86)
-- [优缺点](#%E4%BC%98%E7%BC%BA%E7%82%B9)
-
-
-
## Mybatis简介
JDBC定义了连接数据库的接口规范,每个数据库厂商都会提供具体的实现,JDBC是一种典型的桥接模式。
diff --git "a/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\343\200\220\344\274\230\345\214\226\347\211\210\343\200\221\345\246\202\344\275\225\344\273\216\346\265\267\351\207\217\346\225\260\346\215\256\346\211\276\345\207\272\351\253\230\351\242\221\350\257\215.md" "b/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\343\200\220\344\274\230\345\214\226\347\211\210\343\200\221\345\246\202\344\275\225\344\273\216\346\265\267\351\207\217\346\225\260\346\215\256\346\211\276\345\207\272\351\253\230\351\242\221\350\257\215.md"
deleted file mode 100644
index c6ec6e8..0000000
--- "a/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\343\200\220\344\274\230\345\214\226\347\211\210\343\200\221\345\246\202\344\275\225\344\273\216\346\265\267\351\207\217\346\225\260\346\215\256\346\211\276\345\207\272\351\253\230\351\242\221\350\257\215.md"
+++ /dev/null
@@ -1,65 +0,0 @@
-本期是【**大厂面试**】系列文章的第**34**期
-
-回复【**手册**】获取大彬精心整理的**大厂面试手册**。
-
-## 面试开始
-
-旁白:通过微信聊天方式模拟现场面试,题目出自腾讯PCG一面
-
-**面试官**:**上回考了你一道场景题,还记得不?**
-
-**面试官**:**题目是这样的,一个1G大小的文件,文件里每一行是一个词,每个词的大小不超过16byte,要求返回出现频率最高的100个词**
-
-**面试官**:**内存大小限制是10M**
-
-**面试官**:**当时你给的解法有问题啊,还好我回去查了下,不然就给蒙混过关了。。**
-
-**面试官**:**你用的是分治的思想,进行哈希取余,放进小文件。**
-
-**面试官**:**但是在极端情况,比如都是同一个词或者某一个词超过10m,小文件会超过10m内存限制啊**
-
-大彬:是的哦,上次的解法不严谨,今天给你介绍另一种解法:
-
-**第一步**,对大文件的单词进行排序,排序之后,相同的单词会紧挨着
-
-排序的步骤如下:
-
-1.1 将文件按照顺序切分成大小不超过2m的小文件,总共500个小文件
-
-1.2 使用10m内存**分别**对500个小文件中的单词进行**排序**
-
-1.3 使用一个大小为500大小的堆,对500个小文件进行**多路排序**,结果写到一个大文件中
-
-其中1.3步骤中,对500个小文件进行多路排序的思路如下:
-
-- 初始化一个最小堆,大小就是小文件的个数500。堆中的每个节点存放每个有序小文件对应的输入流。
-- 按照每个有序文件中的下一行数据对所有文件输入流进行排序,单词小的输入文件流放在堆顶。
-- 拿出堆顶的输入流,并其下一行数据写入到最终排序的文件中,如果拿出来的输入流中还有数据的话,那么将这个输入流再一次添加到堆中。否则说明该文件输入流中没有数据了,那么可以关闭这个流。
-- 循环这个过程,直到所有文件输入流都没有数据为止。
-
-**第二步**:
-
-2.1 初始化一个100个节点的**小顶堆**,用于保存100个出现频率最多的单词
-
-2.2 遍历整个文件,一个单词一个单词的从文件中取出来,并计数
-
-2.3 等到遍历的单词和上一个单词不同的话,那么上一个单词及其频率如果大于堆顶的词的频率,那么放在堆中,否则不放
-
-最终,小顶堆中就是出现频率前100的单词了。
-
-可以看出,这种解法相对之前的解法,更为严谨,就算整个文件都是同一个词,此解法同样有效。
-
-**面试官**:行吧,今天面试就到这里吧
-
-
-
-## 点关注,不迷路
-
-大彬,**非科班出身,自学Java**,校招斩获京东、携程、华为等offer。作为一名转码选手,深感这一路的不易。
-
-希望我的分享能帮助到更多的小伙伴,**我踩过的坑你们不要再踩**。想与大彬交流的话,可以到公众号后台获取大彬的微信~
-
-后台回复『 **笔记**』即可领取大彬斩获大厂offer的**面试笔记**。
-
-
-
diff --git "a/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\345\246\202\344\275\225\344\273\216\346\265\267\351\207\217\346\225\260\346\215\256\346\211\276\345\207\272\351\253\230\351\242\221\350\257\215 - wx.md" "b/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\345\246\202\344\275\225\344\273\216\346\265\267\351\207\217\346\225\260\346\215\256\346\211\276\345\207\272\351\253\230\351\242\221\350\257\215 - wx.md"
deleted file mode 100644
index 02d5b41..0000000
--- "a/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\345\246\202\344\275\225\344\273\216\346\265\267\351\207\217\346\225\260\346\215\256\346\211\276\345\207\272\351\253\230\351\242\221\350\257\215 - wx.md"
+++ /dev/null
@@ -1,66 +0,0 @@
-本期是【**大厂面试**】系列文章的第**26**期
-
-回复【**手册**】获取大彬精心整理的**大厂面试手册**。
-
-## 面试开始
-
-旁白:通过微信聊天方式模拟现场面试,题目出自腾讯PCG一面
-
-**面试官**:**今天来道场景题**
-
-**面试官**:假如有一个1G大小的文件,文件里每一行是一个词,每个词的大小不超过16byte,要求返回出现频率最高的100个词
-
-**面试官**:内存大小限制是10M
-
-旁白:由于内存限制,我们无法直接将大文件的所有词一次性读到内存中
-
-旁白:可以采用**分治策略**,把一个大文件分解成多个小文件,保证每个文件的大小小于10M,进而直接将单个小文件读取到内存中进行处理
-
-**大彬**:第一步,首先遍历大文件,对遍历到的每个词x,执行 `hash(x) % 500`,将结果为i的词存放到文件f(i)中
-
-**大彬**:遍历结束后,可以得到500个小文件
-
-**大彬**:之所以使用500个小文件,是因为原文件大小为1G,1G/500=2M,每个小文件的大小为2M左右,基本不会超过内存大小10M的限制
-
-**大彬**:第二步,接着统计每个小文件中出现频数最高的100个词
-
-**大彬**:可以使用HashMap来实现,其中key为词,value为该词出现的频率
-
-**大彬**:对于遍历到的词x,如果在map中不存在,则执行 `map.put(x, 1)`
-
-**大彬**:若存在,则执行 `map.put(x, map.get(x)+1)`,将该词出现的次数加1
-
-**大彬**:第三步,在第二步中找出了每个文件出现频率最高的100个词之后,通过维护一个小顶堆来找出所有小文件中出现频率最高的100个词
-
-**大彬**:具体方法是,遍历第一个文件,把第一个文件中出现频率最高的100个词构建成一个小顶堆
-
-**大彬**:如果第一个文件中词的个数小于100,可以继续遍历第二个文件,直到构建好有100个结点的小顶堆为止
-
-**大彬**:继续遍历其他小文件,如果遍历到的词的出现次数大于堆顶上词的出现次数,可以用新遍历到的词替换堆顶的词,然后重新调整这个堆为小顶堆
-
-**大彬**:当遍历完所有小文件后,这个小顶堆中的词就是出现频率最高的100个词
-
-**大彬**:最后总结一下
-
-1. 采用**分治**的思想,进行哈希取余
-2. 使用**HashMap**统计每个小文件单词出现的次数
-3. 使用**小顶堆**,遍历步骤2中的小文件,找出出现频率Top100的单词
-
-面试官:在第二步中,将1G的文件分解到500个小文件,小文件的大小可能超过10M吧?怎么处理?
-
-大彬:如果某个小文件的大小超过10MB了,可以采用相同的方法对这个文件继续分解成g(1)...g(x),直到文件的大小小于10MB为止
-
-**面试官**:good!今天面试就到这吧
-
-
-
-## 点关注,不迷路
-
-大彬,**非科班出身,自学Java**,校招斩获京东、携程、华为等offer。作为一名转码选手,深感这一路的不易。
-
-希望我的分享能帮助到更多的小伙伴,**我踩过的坑你们不要再踩**。想与大彬交流的话,可以到公众号后台获取大彬的微信~
-
-后台回复『 **笔记**』即可领取大彬斩获大厂offer的**面试笔记**。
-
-
-
diff --git "a/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\345\246\202\344\275\225\344\273\216\346\265\267\351\207\217\346\225\260\346\215\256\346\211\276\345\207\272\351\253\230\351\242\221\350\257\215.md" "b/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\345\246\202\344\275\225\344\273\216\346\265\267\351\207\217\346\225\260\346\215\256\346\211\276\345\207\272\351\253\230\351\242\221\350\257\215.md"
new file mode 100644
index 0000000..885806d
--- /dev/null
+++ "b/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\345\246\202\344\275\225\344\273\216\346\265\267\351\207\217\346\225\260\346\215\256\346\211\276\345\207\272\351\253\230\351\242\221\350\257\215.md"
@@ -0,0 +1,84 @@
+大家好,我是大彬~
+
+今天分享一道面试常考的海量数据场景题。
+
+## 题目描述
+
+假如有一个**1G**大小的文件,文件里每一行是一个词,每个词的大小不超过**16byte**,要求返回出现频率最高的100个词。内存大小限制是**10M**
+
+## 解法1
+
+由于内存限制,我们无法直接将大文件的所有词一次性读到内存中。
+
+可以采用**分治策略**,把一个大文件分解成多个小文件,保证每个文件的大小小于10M,进而直接将单个小文件读取到内存中进行处理。
+
+**第一步**,首先遍历大文件,对遍历到的每个词x,执行 `hash(x) % 500`,将结果为i的词存放到文件f(i)中,遍历结束后,可以得到500个小文件,每个小文件的大小为2M左右;
+
+**第二步**,接着统计每个小文件中出现频数最高的100个词。可以使用HashMap来实现,其中key为词,value为该词出现的频率。
+
+对于遍历到的词x,如果在map中不存在,则执行 `map.put(x, 1)。`
+
+若存在,则执行 `map.put(x, map.get(x)+1)`,将该词出现的次数加1。
+
+**第三步**,在第二步中找出了每个文件出现频率最高的100个词之后,通过维护一个**小顶堆**来找出所有小文件中出现频率最高的100个词。
+
+具体方法是,遍历第一个文件,把第一个文件中出现频率最高的100个词构建成一个小顶堆。
+
+如果第一个文件中词的个数小于100,可以继续遍历第二个文件,直到构建好有100个结点的小顶堆为止。
+
+继续遍历其他小文件,如果遍历到的词的出现次数大于堆顶上词的出现次数,可以用新遍历到的词替换堆顶的词,然后重新调整这个堆为小顶堆。
+
+当遍历完所有小文件后,这个小顶堆中的词就是出现频率最高的100个词。
+
+总结一下,这种解法的主要思路如下:
+
+1. 采用**分治**的思想,进行哈希取余
+2. 使用**HashMap**统计每个小文件单词出现的次数
+3. 使用**小顶堆**,遍历步骤2中的小文件,找出词频top100的单词
+
+但是很容易可以发现问题,在第二步中,如果这个1G的大文件中有某个词词频过高,可能导致小文件大小超过10m。这种情况下该怎么处理呢?
+
+接下来看另外一种解法。
+
+## 解法2
+
+**第一步**:使用多路归并排序对大文件进行排序,这样相同的单词肯定是紧挨着的
+
+多路归并排序对大文件进行排序的步骤如下:
+
+① 将文件按照顺序切分成大小不超过2m的小文件,总共500个小文件
+
+② 使用10MB内存**分别**对 500 个小文件中的单词进行**排序**
+
+③ 使用一个大小为500大小的堆,对500个小文件进行**多路排序**,结果写到一个大文件中
+
+其中第三步,对500个小文件进行多路排序的思路如下:
+
+- 初始化一个最小堆,大小就是有序小文件的个数500。堆中的每个节点存放每个有序小文件对应的输入流。
+- 按照每个有序文件中的下一行数据对所有文件输入流进行排序,单词小的输入文件流放在堆顶。
+- 拿出堆顶的输入流,并其下一行数据写入到最终排序的文件中,如果拿出来的输入流中还有数据的话,那么将这个输入流再一次添加到栈中。否则说明该文件输入流中没有数据了,那么可以关闭这个流。
+- 循环这个过程,直到所有文件输入流都没有数据为止。
+
+**第二步**:
+
+① 初始化一个100个节点的**小顶堆**,用于保存100个出现频率最多的单词
+
+② 遍历整个文件,一个单词一个单词的从文件中取出来,并计数
+
+③ 等到遍历的单词和上一个单词不同的话,那么上一个单词及其频率如果大于堆顶的词的频率,那么放在堆中,否则不放
+
+最终,小顶堆中就是出现频率前100的单词了。
+
+解法2相对解法1,更加严谨,如果某个词词频过高或者整个文件都是同一个词的话,解法1不适用。
+
+
+## 点关注,不迷路
+
+大彬,**非科班出身,自学Java**,校招斩获京东、携程、华为等offer。作为一名转码选手,深感这一路的不易。
+
+希望我的分享能帮助到更多的小伙伴,**我踩过的坑你们不要再踩**。想与大彬交流的话,可以到公众号后台获取大彬的微信~
+
+后台回复『 **笔记**』即可领取大彬斩获大厂offer的**面试笔记**。
+
+
+
diff --git "a/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\347\224\265\350\257\235\345\217\267\347\240\201.md" "b/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\347\224\265\350\257\235\345\217\267\347\240\201.md"
deleted file mode 100644
index 7a452aa..0000000
--- "a/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\347\224\265\350\257\235\345\217\267\347\240\201.md"
+++ /dev/null
@@ -1,82 +0,0 @@
-本期是【**大厂面试**】系列文章的第**35**期
-
-回复【**手册**】获取大彬精心整理的**大厂面试手册**。
-
-## 面试开始
-
-旁白:通过微信聊天方式模拟现场面试,题目出自百度一面
-
-**面试官**:**已知某个文件内包含大量电话号码,每个号码为8位数字,如何统计不同号码的个数?**
-
-旁白:这类题目其实是求解数据重复的问题
-
-大彬:对于这类问题,可以使用**位图法**处理
-
-大彬:8位电话号码可以表示的范围为00000000~99999999。
-
-大彬:如果用 bit表示一个号码,那么总共需要1亿个bit,总共需要大约100MB的内存。
-
-大彬:申请一个位图并初始化为0,然后遍历所有电话号码,把遍历到的电话号码对应的位图中的bit设置为1。
-
-大彬:当遍历完成后,如果bit值为1,则表示这个电话号码在文件中存在,否则这个bit对应的电话号码在文件中不存在。
-
-大彬:所以这个位图中bit值为1的数量就是不同电话号码的个数了。
-
-面试官:那么如何确定电话号码对应的是位图中的哪一位呢?
-
-大彬:嗯,可以使用下面的方法来做电话号码和位图的映射。
-
-```java
-00000000 对应位图最后一位:0×0000…000001。
-00000001 对应位图倒数第二位:0×0000…0000010(1 向左移 1 位)。
-00000002 对应位图倒数第三位:0×0000…0000100(1 向左移 2 位)。
-……
-00000012 对应位图的倒数第十三位:0×0000…0001 0000 0000 0000(1 向左移 12 位)。
-```
-
-大彬:也就是说,电话号码就是1这个数字左移的次数。
-
-**面试官:那用代码怎么实现呢?**
-
-大彬:首先位图可以使用一个int数组来实现。
-
-旁白:在Java中int占用**4byte**
-
-大彬:假设电话号码为 P,而通过电话号码获取位图中对应位置的方法为:
-
-大彬:第一步,因为int整数占用4*8=32bit,通过 **P/32** 就可以计算出该电话号码在 bitmap 数组中的下标,从而可以确定它对应的 bit 在数组中的位置。
-
-大彬:第二步,通过 P%32 就可以计算出这个电话号码在这个int数字中具体的bit的位置。
-
-大彬:只要把1向左移 **P%32** 位,然后把得到的值与这个数组中的值做或运算,就可以把这个电话号码在位图中对应的位设置为1。
-
-大彬:以00000100号码为例
-
-大彬:首先计算数组下标,100 / 32 = 3,得到数组下标位3
-
-大彬:然后计算电话号码在这个int数字中具体的bit的位置,100 % 32 = 4
-
-大彬:取余为0左移1位,故取余为4左移5位,得到000...000100000
-
-大彬:将位图中对应的位设置为 1,即arr[2] = arr[2] **|** 000..00100000
-
-大彬:这就将电话号码映射到了位图的某一位了。
-
-
-
-**面试官**:点赞
-
-**面试官**:今天面试就到这里吧
-
-
-
-## 点关注,不迷路
-
-大彬,**非科班出身,自学Java**,校招斩获京东、携程、华为等offer。作为一名转码选手,深感这一路的不易。
-
-希望我的分享能帮助到更多的小伙伴,**我踩过的坑你们不要再踩**。想与大彬交流的话,可以到公众号后台获取大彬的微信~
-
-后台回复『 **笔记**』即可领取大彬斩获大厂offer的**面试笔记**。
-
-
-
diff --git "a/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\347\273\237\350\256\241\344\270\215\345\220\214\347\224\265\350\257\235\345\217\267\347\240\201\347\232\204\344\270\252\346\225\260.md" "b/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\347\273\237\350\256\241\344\270\215\345\220\214\347\224\265\350\257\235\345\217\267\347\240\201\347\232\204\344\270\252\346\225\260.md"
new file mode 100644
index 0000000..5b480ae
--- /dev/null
+++ "b/\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\347\273\237\350\256\241\344\270\215\345\220\214\347\224\265\350\257\235\345\217\267\347\240\201\347\232\204\344\270\252\346\225\260.md"
@@ -0,0 +1,55 @@
+大家好,我是大彬~
+
+海量数据题目是面试经常会考的题型,今天给大家分享一道百度二面的题目。
+
+## 题目描述
+
+**已知某个文件内包含大量电话号码,每个号码为8位数字,如何统计不同号码的个数?**
+
+## 思路分析
+
+这类题目其实是求解数据重复的问题。对于这类问题,可以使用**位图法**处理
+
+8位电话号码可以表示的范围为00000000~99999999。如果用 bit表示一个号码,那么总共需要1亿个bit,总共需要大约**10MB**的内存。
+
+申请一个位图并初始化为0,然后遍历所有电话号码,**把遍历到的电话号码对应的位图中的bit设置为1**。当遍历完成后,如果bit值为1,则表示这个电话号码在文件中存在,否则这个bit对应的电话号码在文件中不存在。
+
+最后这个**位图中bit值为1的数量**就是不同电话号码的个数了。
+
+那么如何确定电话号码对应的是位图中的哪一位呢?
+
+可以使用下面的方法来做**电话号码和位图的映射**。
+
+```java
+00000000 对应位图最后一位:0×0000…000001。
+00000001 对应位图倒数第二位:0×0000…0000010(1 向左移 1 位)。
+00000002 对应位图倒数第三位:0×0000…0000100(1 向左移 2 位)。
+……
+00000012 对应位图的倒数第十三位:0×0000…0001 0000 0000 0000(1 向左移 12 位)。
+```
+
+也就是说,电话号码就是1这个数字左移的次数。
+
+## 具体实现
+
+首先位图可以使用一个**int数组**来实现(在Java中int占用**4byte**)。
+
+假设电话号码为 P,而通过电话号码获取位图中对应位置的方法为:
+
+**第一步**,因为int整数占用4*8=32bit,通过 **P/32** 就可以计算出该电话号码在 bitmap 数组中的下标,从而可以确定它对应的 bit 在数组中的位置。
+
+**第二步**,通过 **P%32** 就可以计算出这个电话号码在这个int数字中具体的bit的位置。只要把1向左移 **P%32** 位,然后把得到的值与这个数组中的值做或运算,就可以把这个电话号码在位图中对应的位设置为1。
+
+以00000100号码为例。
+
+1. 首先计算数组下标,100 / 32 = 3,得到数组下标位3。
+2. 然后计算电话号码在这个int数字中具体的bit的位置,100 % 32 = 4。取余为0左移1位,故取余为4左移5位,得到000...000010000
+3. 将位图中对应的位设置为 1,即arr[2] = arr[2] **|** 000..00010000。
+4. 这就将电话号码映射到了位图的某一位了。
+
+
+
+最后,统计位图中bit值为1的数量,便能得到不同电话号码的个数了。
+
+
+
diff --git "a/\347\263\273\347\273\237\350\256\276\350\256\241/\346\211\253\347\240\201\347\231\273\345\275\225\345\216\237\347\220\206.md" "b/\347\263\273\347\273\237\350\256\276\350\256\241/\346\211\253\347\240\201\347\231\273\345\275\225\345\216\237\347\220\206.md"
index 01224d9..93f6591 100644
--- "a/\347\263\273\347\273\237\350\256\276\350\256\241/\346\211\253\347\240\201\347\231\273\345\275\225\345\216\237\347\220\206.md"
+++ "b/\347\263\273\347\273\237\350\256\276\350\256\241/\346\211\253\347\240\201\347\231\273\345\275\225\345\216\237\347\220\206.md"
@@ -54,7 +54,7 @@ PC 端的定时器,会轮询到二维码的状态已经发生变化,会将 P
大彬:这个阶段是交互过程如下图所示。
-
+
**3、已确认**
@@ -66,7 +66,7 @@ PC 端的定时器,会轮询到二维码的状态已经发生变化,会将 P
这个阶段是交互过程如下图所示。
-
+
大彬:以上就是整个扫码登录功能的详细设计!
diff --git "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md"
index 5438b9a..c8e073c 100644
--- "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md"
+++ "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md"
@@ -1,14 +1,3 @@
-## 操作系统里的内存碎片怎么理解?
-
-内存碎片通常分为内部碎片和外部碎片:
-
-1. 内部碎片是由于采用固定大小的内存分区,当一个进程不能完全使用分给它的固定内存区域时就会产生内部碎片。通常内部碎片难以完全避免
-2. 外部碎片是由于某些未分配的连续内存区域太小,以至于不能满足任意进程的内存分配请求,从而不能被进程利用的内存区域。
-
-## 有什么解决办法?
-
-现在普遍采取的内存分配方式是段页式内存分配。将内存分为不同的段,再将每一段分成固定大小的页。通过页表机制,使段内的页可以不必连续处于同一内存区域。
-
## 操作系统的四个特性?
并发:同一段时间内多个程序执行(与并行区分,并行指的是同一时刻有多个事件,多处理器系统可以使程序并行执行)
@@ -74,13 +63,13 @@
4、**信号量**。信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
-## 死锁
+## 什么是死锁?
死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象。若无外力作用,它们都将无法推进下去。
如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方持有的资源,所以这两个线程就会互相等待而进入死锁状态。
-
+
下面通过例子说明线程死锁,代码来自并发编程之美。
@@ -134,6 +123,25 @@ Thread[线程 2,5,main]waiting get resource1
线程 A 通过 `synchronized` (resource1) 获得 resource1 的监视器锁,然后通过 `Thread.sleep(1000)`。让线程 A 休眠 1s 为的是让线程 B 得到执行然后获取到 resource2 的监视器锁。线程 A 和线程 B 休眠结束了都开始企图请求获取对方的资源,然后这两个线程就会陷入互相等待的状态,这也就产生了死锁。
+## 死锁怎么产生?怎么避免?
+
+**死锁产生的四个必要条件**:
+
+- 互斥:一个资源每次只能被一个进程使用
+
+- 请求与保持:一个进程因请求资源而阻塞时,不释放获得的资源
+
+- 不剥夺:进程已获得的资源,在未使用之前,不能强行剥夺
+
+- 循环等待:进程之间循环等待着资源
+
+**避免死锁的方法**:
+
+- 互斥条件不能破坏,因为加锁就是为了保证互斥
+- 一次性申请所有的资源,避免线程占有资源而且在等待其他资源
+- 占有部分资源的线程进一步申请其他资源时,如果申请不到,主动释放它占有的资源
+- 按序申请资源
+
## 进程调度策略有哪几种?
- **先来先服务**:非抢占式的调度算法,按照请求的顺序进行调度。有利于长作业,但不利于短作业,因为短作业必须一直等待前面的长作业执行完毕才能执行,而长作业又需要执行很长时间,造成了短作业等待时间过长。另外,对`I/O`密集型进程也不利,因为这种进程每次进行`I/O`操作之后又得重新排队。
@@ -163,24 +171,16 @@ Thread[线程 2,5,main]waiting get resource1
**运行态→就绪态**:不是由于自身原因,而是由外界原因使运行状态的进程让出处理器,这时候就变成就绪态。例如时间片用完,或有更高优先级的进程来抢占处理器等。
**就绪态→运行态**:系统按某种策略选中就绪队列中的一个进程占用处理器,此时就变成了运行态。
-## 死锁怎么产生?怎么避免?
-
-**死锁产生的四个必要条件**:
-
-- 互斥:一个资源每次只能被一个进程使用
-
-- 请求与保持:一个进程因请求资源而阻塞时,不释放获得的资源
+## 操作系统里的内存碎片怎么理解?
-- 不剥夺:进程已获得的资源,在未使用之前,不能强行剥夺
+内存碎片通常分为内部碎片和外部碎片:
-- 循环等待:进程之间循环等待着资源
+1. 内部碎片是由于采用固定大小的内存分区,当一个进程不能完全使用分给它的固定内存区域时就会产生内部碎片。通常内部碎片难以完全避免
+2. 外部碎片是由于某些未分配的连续内存区域太小,以至于不能满足任意进程的内存分配请求,从而不能被进程利用的内存区域。
-**避免死锁的方法**:
+**有什么解决办法**?
-- 互斥条件不能破坏,因为加锁就是为了保证互斥
-- 一次性申请所有的资源,避免线程占有资源而且在等待其他资源
-- 占有部分资源的线程进一步申请其他资源时,如果申请不到,主动释放它占有的资源
-- 按序申请资源
+现在普遍采取的内存分配方式是段页式内存分配。将内存分为不同的段,再将每一段分成固定大小的页。通过页表机制,使段内的页可以不必连续处于同一内存区域。
## 虚拟内存
@@ -283,4 +283,6 @@ Linux下,进程不能直接读写内存物理地址,只能访问【虚拟内
- 如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。
- 与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。
+
+

\ No newline at end of file
diff --git "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\344\272\214\345\217\211\346\240\221\345\211\215\345\272\217\343\200\201\344\270\255\345\272\217\343\200\201\345\220\216\345\272\217\343\200\201\345\261\202\345\272\217\351\201\215\345\216\206\344\273\243\347\240\201\345\256\236\347\216\260.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\344\272\214\345\217\211\346\240\221\345\211\215\345\272\217\343\200\201\344\270\255\345\272\217\343\200\201\345\220\216\345\272\217\343\200\201\345\261\202\345\272\217\351\201\215\345\216\206\344\273\243\347\240\201\345\256\236\347\216\260.md"
deleted file mode 100644
index c0ff353..0000000
--- "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\344\272\214\345\217\211\346\240\221\345\211\215\345\272\217\343\200\201\344\270\255\345\272\217\343\200\201\345\220\216\345\272\217\343\200\201\345\261\202\345\272\217\351\201\215\345\216\206\344\273\243\347\240\201\345\256\236\347\216\260.md"
+++ /dev/null
@@ -1,148 +0,0 @@
-
-
-**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
-
-- [前序遍历](#%E5%89%8D%E5%BA%8F%E9%81%8D%E5%8E%86)
-- [中序遍历](#%E4%B8%AD%E5%BA%8F%E9%81%8D%E5%8E%86)
-- [后序遍历](#%E5%90%8E%E5%BA%8F%E9%81%8D%E5%8E%86)
-- [层序遍历](#%E5%B1%82%E5%BA%8F%E9%81%8D%E5%8E%86)
-
-
-
-二叉树的先序、中序和后序属于深度优先遍历DFS,层次遍历属于广度优先遍历BFS。
-
-
-
-# 前序遍历
-
-```java
-class Solution {
- //方法1
- public List preorderTraversal1(TreeNode root) {
- List result = new ArrayList<>();
- LinkedList ll = new LinkedList<>(); //类型声明List改为LinkedList,List没有addFirst()/removeFirst()方法
-
- while (root != null || !ll.isEmpty()) {
- while (root != null) {
- result.add(root.val);
- ll.addFirst(root);
- root = root.left;
- }
- root = ll.removeFirst();
- root = root.right;
- }
-
- return result;
- }
- //方法2
- public List preorderTraversal2(TreeNode root) {
- List result = new ArrayList<>();
- if(root == null) {
- return result;
- }
-
- Stack s = new Stack<>();
- s.push(root);
- while(!s.isEmpty()) {
- TreeNode node = s.pop();
- result.add(node.val);
- if(node.right != null) {
- s.push(node.right);//先压右,再压左
- }
- if(node.left != null) {
- s.push(node.left);
- }
- }
-
- return result;
- }
-}
-```
-
-# 中序遍历
-
-```java
- public List inorderTraversal(TreeNode root) {
- List res = new ArrayList<>();
- Deque deque = new ArrayDeque<>();
-
- while (!deque.isEmpty() || root != null) {
- while (root != null) {
- deque.push(root);
- root = root.left;
- }
- root = deque.pop();
- res.add(root.val);
- root = root.right;
- }
-
- return res;
- }
-```
-
-# 后序遍历
-
-使用 null 作为标志位,访问到 null 说明此次递归调用结束。
-
-```java
-class Solution {
- public List postorderTraversal(TreeNode root) {
- List res = new LinkedList<>();
- if (root == null) {
- return res;
- }
-
- Stack stack = new Stack<>();
- stack.push(root);
- while (!stack.isEmpty()) {
- root = stack.pop();
- if (root != null) {
- stack.push(root);//最后访问
- stack.push(null);
- if (root.right != null) {
- stack.push(root.right);
- }
- if (root.left != null) {
- stack.push(root.left);
- }
- } else { //值为null说明此次递归调用结束,将节点值存进结果
- res.add(stack.pop().val);
- }
- }
-
- return res;
- }
-}
-```
-
-# 层序遍历
-
-```
-class Solution {
- public List> levelOrder(TreeNode root) {
- List> res = new ArrayList<>();
- LinkedList queue = new LinkedList<>();
- if (root == null) {
- return res;
- }
- queue.addLast(root);
- while (!queue.isEmpty()) {
- List levelList = new LinkedList<>();
- int size = queue.size();
- while (size-- > 0) {
- root = queue.removeFirst();
- levelList.add(root.val);
- if (root.left != null) {
- queue.addLast(root.left);
- }
- if (root.right != null) {
- queue.addLast(root.right);
- }
- }
- res.add(levelList);
- }
- return res;
- }
-}
-```
-
diff --git "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
deleted file mode 100644
index 9ad4fb0..0000000
--- "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\347\232\204\346\216\222\345\272\217\347\256\227\346\263\225Java\344\273\243\347\240\201\345\256\236\347\216\260.md"
+++ /dev/null
@@ -1,214 +0,0 @@
-
-
-
-- [冒泡排序](#%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F)
-- [插入排序](#%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F)
-- [选择排序](#%E9%80%89%E6%8B%A9%E6%8E%92%E5%BA%8F)
-- [基数排序](#%E5%9F%BA%E6%95%B0%E6%8E%92%E5%BA%8F)
-- [快速排序](#%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F)
-- [归并排序](#%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F)
-- [堆排序](#%E5%A0%86%E6%8E%92%E5%BA%8F)
-
-
-
-常见的排序算法主要有:冒泡排序、插入排序、选择排序、快速排序、归并排序、堆排序、基数排序。各种排序算法的时间空间复杂度、稳定性见下图。
-
-
-
-## 冒泡排序
-
-```java
- public void bubbleSort(int[] arr) {
- if (arr == null) {
- return;
- }
- boolean flag;
- for (int i = arr.length - 1; i > 0; i--) {
- flag = false;
- for (int j = 0; j < i; j++) {
- if (arr[j] > arr[j + 1]) {
- int tmp = arr[j];
- arr[j] = arr[j + 1];
- arr[j + 1] = tmp;
- flag = true;
- }
- }
- if (!flag) {
- return;
- }
- }
- }
-```
-
-## 插入排序
-
-```java
- public void insertSort(int[] arr) {
- if (arr == null) {
- return;
- }
- for (int i = 1; i < arr.length; i++) {
- int tmp = arr[i];
- int j = i;
- for (; j > 0 && tmp < arr[j - 1]; j--) {
- arr[j] = arr[j - 1];
- }
- arr[j] = tmp;
- }
- }
-```
-
-## 选择排序
-
-```java
- public void selectionSort(int[] arr) {
- if (arr == null) {
- return;
- }
- for (int i = 0; i < arr.length - 1; i++) {
- for (int j = i + 1; j < arr.length; j++) {
- if (arr[i] > arr[j]) {
- int tmp = arr[i];
- arr[i] = arr[j];
- arr[j] = tmp;
- }
- }
- }
- }
-```
-
-## 基数排序
-
-在基数排序中,因为没有比较操作,所以在时间复杂上,最好的情况与最坏的情况在时间上是一致的,均为 O(d * (n + r))。d 为位数,r 为基数,n 为原数组个数。
-
-
-## 快速排序
-
-```java
- public void quickSort(int[] arr) {
- if (arr == null) {
- return;
- }
- quickSortHelper(arr, 0, arr.length - 1);
- }
- private void quickSortHelper(int[] arr, int left, int right) {
- if (left > right) {
- return;
- }
- int tmp = arr[left];
- int i = left;
- int j = right;
- while (i < j) {
- //j先走,最终循环终止时,j停留的位置就是arr[left]的正确位置
- //改为i<=j,则会进入死循环,[1,5,5,5,5]->[1] 5 [5,5,5]->[5,5,5],会死循环
- while (i < j && arr[j] >= tmp) {
- j--;
- }
- while (i < j && arr[i] <= tmp) {
- i++;
- }
- if (i < j) {
- int tmp1 = arr[i];
- arr[i] = arr[j];
- arr[j] = tmp1;
- } else {
- break;
- }
- }
-
- //当循环终止的时候,i=j,因为是j先走的,j所在位置的值小于arr[left],交换arr[j]和arr[left]
- arr[left] = arr[j];
- arr[j] = tmp;
-
- quickSortHelper(arr, left, j - 1);
- quickSortHelper(arr, j + 1, right);
- }
-```
-
-## 归并排序
-
-
-
-```java
-public class MergeSort {
- public void mergeSort(int[] arr) {
- if (arr == null || arr.length == 0) {
- return;
- }
- //辅助数组
- int[] tmpArr = new int[arr.length];
- mergeSort(arr, tmpArr, 0, arr.length - 1);
- }
-
- private void mergeSort(int[] arr, int[] tmpArr, int left, int right) {
- if (left < right) {
- int mid = (left + right) >> 1;
- mergeSort(arr, tmpArr, left, mid);
- mergeSort(arr, tmpArr, mid + 1, right);
- merge(arr, tmpArr, left, mid, right);
- }
- }
-
- private void merge(int[] arr, int[] tmpArr, int left, int mid, int right) {
- int i = left;
- int j = mid + 1;
- int tmpIndex = left;
- while (i <= mid && j <= right) {
- if (arr[i] < arr[j]) {
- tmpArr[tmpIndex++] = arr[i];
- i++;
- } else {
- tmpArr[tmpIndex++] = arr[j];
- j++;
- }
- }
-
- while (i <= mid) {
- tmpArr[tmpIndex++] = arr[i++];
- }
-
- while (j <= right) {
- tmpArr[tmpIndex++] = arr[j++];
- }
-
- for (int m = left; m <= right; m++) {
- arr[m] = tmpArr[m];
- }
- }
-}
-```
-
-## 堆排序
-
-堆是具有下列性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
-
-
-
-**Top大问题**解决思路:使用一个固定大小的**最小堆**,当堆满后,每次添加数据的时候与堆顶元素比较,若小于堆顶元素,则舍弃,若大于堆顶元素,则删除堆顶元素,添加新增元素,对堆进行重新排序。
-
-对于n个数,取Top m个数,时间复杂度为O(nlogm),这样在n较大情况下,是优于nlogn(其他排序算法)的时间复杂度的。
-
-PriorityQueue 是一种基于优先级堆的优先级队列。每次从队列中取出的是具有最高优先权的元素。如果不提供Comparator的话,优先队列中元素默认按自然顺序排列,也就是数字默认是小的在队列头。优先级队列用数组实现,但是数组大小可以动态增加,容量无限。
-
-```java
-//找出前k个最大数,采用小顶堆实现
-public static int[] findKMax(int[] nums, int k) {
- PriorityQueue pq = new PriorityQueue<>(k);//队列默认自然顺序排列,小顶堆,不必重写compare
-
- for (int num : nums) {
- if (pq.size() < k) {
- pq.offer(num);
- } else if (pq.peek() < num) {//如果堆顶元素 < 新数,则删除堆顶,加入新数入堆
- pq.poll();
- pq.offer(num);
- }
- }
-
- int[] result = new int[k];
- for (int i = 0; i < k&&!pq.isEmpty(); i++) {
- result[i] = pq.poll();
- }
- return result;
-}
-```
-
diff --git "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\225\260\346\215\256\347\273\223\346\236\204.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\225\260\346\215\256\347\273\223\346\236\204.md"
index 92991c4..9bb6945 100644
--- "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\225\260\346\215\256\347\273\223\346\236\204.md"
+++ "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\225\260\346\215\256\347\273\223\346\236\204.md"
@@ -1,16 +1,3 @@
-
-
-
-
-- [各种数据结构应用场景](#%E5%90%84%E7%A7%8D%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF)
-- [AVL树](#avl%E6%A0%91)
-- [B树](#b%E6%A0%91)
-- [红黑树](#%E7%BA%A2%E9%BB%91%E6%A0%91)
-- [图](#%E5%9B%BE)
-- [二分查找](#%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE)
-
-
-
## 各种数据结构应用场景
- 栈:逆序输出;语法检查,符号成对判断;方法调用
@@ -36,8 +23,6 @@
## 链表
-### 优缺点
-
优点:
- 空间没有限制
@@ -45,15 +30,52 @@
缺点:存取速度很慢
-### 分类
+**分类**
- 单向链表 一个节点指向下一个节点。
- 双向链表 一个节点有两个指针域。
- 循环链表 能通过任何一个节点找到其他所有的节点,将两种(双向/单向)链表的最后一个结点指向第一个结点从而实现循环
+## 哈希表
+
+散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
+
+## 栈
+
+我们把类似于弹夹那种先进后出的数据结构称为栈,栈是限定**仅在表尾进行插入和删除操作**的线性表,我们把允许插入和删除的一端称为栈顶,另一端称为栈底,不含任何数据元素的栈称为空栈,栈又称后进后出的线性表,简称LIFO结构。
+
+栈的特殊之处在于限制了这个线性表的**插入和删除位置,它始终只在栈顶进行**。这也就使得:栈底是固定的,最先进栈的只能在栈底。
+
+栈的插入操作,叫做进栈;栈的删除操作叫做出栈。
+
+## 队列
+
+队列是只允许**在一端进行插入操作,而在另一端进行删除操作**的线性表,队列是一种先进先出的线性表,简称FIFO,允许插入的一端称为队尾(Rear),允许删除的一端称为队头(Front)。向队中插入元素称为进队,新元素进队后成为新的队尾元素;向队中删除元素称为出队,元素出队后,其后继元素就成为新的队头元素。
+## 树
- ## AVL树
+树是一种数据结构,它看上去像一棵 "圣诞树",它的根在上,叶朝下。
+
+**树有多个节点(node),用以储存元素。某些节点之间存在一定的关系,用连线表示,连线称为边(edge)。边的上端节点称为父节点,下端称为子节点。树像是一个不断分叉的树根。**
+
+### 二叉树
+
+最多有两棵子树的树被称为二叉树
+
+满二叉树: 二叉树中所有非叶子结点的度都是2,且叶子结点都在同一层次上
+
+完全二叉树: 如果一个二叉树与满二叉树前m个节点的结构相同,这样的二叉树被称为完全二叉树
+
+### 二叉查找树
+
+指一棵空树或者具有下列性质的二叉树。
+
+- 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
+- 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
+- 任意节点的左、右子树也分别为二叉查找树;
+- 没有键值相等的节点。
+
+### AVL树
平衡二叉搜索树,它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1。
@@ -67,21 +89,14 @@

+### B树
-
-## B树
-
-也称B-树,属于多叉树又名平衡多路查找树。
-
-规则:
+也称B-树,属于多叉树又名平衡多路查找树。规则:
- 1<子节点数<=m,m代表一个树节点最多有多少个查找路径
- 每个节点最多有m-1个关键字,非根节点至少有m/2个关键字,根节点最少可以只有1个关键字
- 每个节点都有指针指向子节点,指针个数=关键字个数+1,叶子节点指针指向null
-三叉树:
-
-
B-树的特性:
1. 关键字集合分布在整颗树中;
@@ -95,54 +110,58 @@ B-和B+树的区别
- B+树的非叶子结点不包含data,叶子结点使用链表连接,便于区间查找和遍历。B-树需要遍历整棵树,范围查询性能没有B+树好。
- B-树的非树节点存放数据和索引,搜索可能在非叶子结点结束,访问更快。
-
-
-## 红黑树
+### 红黑树
红黑树是对AVL树的优化,只要求部分平衡,用非严格的平衡来换取增删节点时候旋转次数的降低,提高了插入和删除的性能。查找性能并没有提高,查找的时间复杂度是O(logn)。红黑树通过左旋、右旋和变色维持平衡。
对于插入节点,AVL和红黑树都是最多两次旋转来实现平衡。对于删除节点,avl需要维护从被删除节点到根节点root这条路径上所有节点的平衡,旋转的量级为O(logN),而红黑树最多只需旋转3次。
-- 特性:
- (1) 每个节点或者是黑色,或者是红色。
- (2) 根节点和叶子节点是黑色,叶子节点为空。
- (4)红色节点的子节点必须是黑色的。
- (5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点,保证没有一条路径会比其他路径长一倍。
+特性:
+(1) 每个节点或者是黑色,或者是红色。
+(2) 根节点和叶子节点是黑色,叶子节点为空。
+(4)红色节点的子节点必须是黑色的。
+(5) 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点,保证没有一条路径会比其他路径长一倍。
+
+
-- 优点:相比avl树,红黑树插入删除的效率更高。红黑树维持红黑性质所做的红黑变换和旋转的开销,相较于avl树维持平衡的开销要小得多。
+优点:相比avl树,红黑树插入删除的效率更高。红黑树维持红黑性质所做的红黑变换和旋转的开销,相较于avl树维持平衡的开销要小得多。
-- 应用:主要用来存储有序的数据,Java中的TreeSet和TreeMap都是通过红黑树实现的。
- 
+应用:主要用来存储有序的数据,Java中的TreeSet和TreeMap都是通过红黑树实现的。
## 图
图由顶点集(vertex set)和边集(edge set)所组成。
-
+
+
图的存储结构有邻接矩阵、邻接表和边集数组三种。
-邻接矩阵
-
-邻接表
-
+
+1、邻接矩阵
+
+
+
+2、邻接表
+
+
下面给出建立图的邻接表中所使用的边结点类的定义
```java
- public class EdgeNode { //定义邻接表中的边结点类型
- int adjvex; //邻接点域
- int weight; //边的权值域,假定为整型,对于无权图,边的权值为1
- EdgeNode next; //指向下一个边结点的链接域
- public EdgeNode(int adj, EdgeNode nt)
- { //对无权图中的边结点进行初始化
- adjvex=adj; next=nt; weight=1;
- }
- public EdgeNode(int adj, int wgt, EdgeNode nt)
- { //对有权图中的边结点进行初始化
- adjvex=adj; next=nt; weight=wgt;
- }
- }
+public class EdgeNode { //定义邻接表中的边结点类型
+ int adjvex; //邻接点域
+ int weight; //边的权值域,假定为整型,对于无权图,边的权值为1
+ EdgeNode next; //指向下一个边结点的链接域
+ public EdgeNode(int adj, EdgeNode nt)
+ { //对无权图中的边结点进行初始化
+ adjvex=adj; next=nt; weight=1;
+ }
+ public EdgeNode(int adj, int wgt, EdgeNode nt)
+ { //对有权图中的边结点进行初始化
+ adjvex=adj; next=nt; weight=wgt;
+ }
+}
```
图的接口类定义如下:
@@ -170,43 +189,43 @@ public interface Graph
深度优先遍历
```java
- private void dfs(int i, boolean[] visited) {
- System.out.printl(i + " ");
- visited[i] = true;
- EdgeNode p = a[i];
- while(p != null) {
- int j = p.adjvex;
- if(!visited[j]) {
- dfs(j, visited);
- }
- p = p.next;
+private void dfs(int i, boolean[] visited) {
+ System.out.printl(i + " ");
+ visited[i] = true;
+ EdgeNode p = a[i];
+ while(p != null) {
+ int j = p.adjvex;
+ if(!visited[j]) {
+ dfs(j, visited);
}
+ p = p.next;
}
+}
```
广度优先搜索
```java
- private void bfs(int i, boolean[] visited) {
- LinkedList queue = new LinkedList<>();
- System.out.print(i + " ");
- visited[i] = true;
- queue.offer(i);
- while(!queue.isEmpty()) {
- int k = queue.poll();
- EdgeNode p = a[k];
-
- while(p != null) {
- int j = p.adjvex;
- if(!visited[j]) {
- System.out.print(j + " ");
- visited[j] = true;
- queue.offer(j);
- }
- p = p.next;
+private void bfs(int i, boolean[] visited) {
+ LinkedList queue = new LinkedList<>();
+ System.out.print(i + " ");
+ visited[i] = true;
+ queue.offer(i);
+ while(!queue.isEmpty()) {
+ int k = queue.poll();
+ EdgeNode p = a[k];
+
+ while(p != null) {
+ int j = p.adjvex;
+ if(!visited[j]) {
+ System.out.print(j + " ");
+ visited[j] = true;
+ queue.offer(j);
}
+ p = p.next;
}
}
+}
```
diff --git "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\347\256\227\346\263\225.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\347\256\227\346\263\225.md"
index 7325e6a..7100651 100644
--- "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\347\256\227\346\263\225.md"
+++ "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\347\256\227\346\263\225.md"
@@ -1,113 +1,220 @@
-# 二分查找
+## 二叉树的遍历
+
+二叉树的先序、中序和后序属于深度优先遍历DFS,层次遍历属于广度优先遍历BFS。
+
+
+
+### 前序遍历
```java
- public int binarySearch(int[] arr, int target) {
- if (arr == null || arr.length <= 1) {
- return -1;
+class Solution {
+ //方法1
+ public List preorderTraversal1(TreeNode root) {
+ List result = new ArrayList<>();
+ LinkedList ll = new LinkedList<>(); //类型声明List改为LinkedList,List没有addFirst()/removeFirst()方法
+
+ while (root != null || !ll.isEmpty()) {
+ while (root != null) {
+ result.add(root.val);
+ ll.addFirst(root);
+ root = root.left;
+ }
+ root = ll.removeFirst();
+ root = root.right;
}
- int left = 0;
- int right = arr.length - 1;
+ return result;
+ }
+ //方法2
+ public List preorderTraversal2(TreeNode root) {
+ List result = new ArrayList<>();
+ if(root == null) {
+ return result;
+ }
+
+ Stack s = new Stack<>();
+ s.push(root);
+ while(!s.isEmpty()) {
+ TreeNode node = s.pop();
+ result.add(node.val);
+ if(node.right != null) {
+ s.push(node.right);//先压右,再压左
+ }
+ if(node.left != null) {
+ s.push(node.left);
+ }
+ }
+
+ return result;
+ }
+}
+```
- while (left <= right) {
- int mid = (left + right) >>> 1;
- if (arr[mid] > target) {
- right = mid - 1;
- } else if (arr[mid] < target) {
- left = mid + 1;
- } else {
- return mid;
+### 中序遍历
+
+```java
+ public List inorderTraversal(TreeNode root) {
+ List res = new ArrayList<>();
+ Deque deque = new ArrayDeque<>();
+
+ while (!deque.isEmpty() || root != null) {
+ while (root != null) {
+ deque.push(root);
+ root = root.left;
}
+ root = deque.pop();
+ res.add(root.val);
+ root = root.right;
}
- return -1;
+ return res;
}
```
-# 归并排序
+### 后序遍历
+使用 null 作为标志位,访问到 null 说明此次递归调用结束。
+```java
+class Solution {
+ public List postorderTraversal(TreeNode root) {
+ List res = new LinkedList<>();
+ if (root == null) {
+ return res;
+ }
-## 简单讲下归并排序?
+ Stack stack = new Stack<>();
+ stack.push(root);
+ while (!stack.isEmpty()) {
+ root = stack.pop();
+ if (root != null) {
+ stack.push(root);//最后访问
+ stack.push(null);
+ if (root.right != null) {
+ stack.push(root.right);
+ }
+ if (root.left != null) {
+ stack.push(root.left);
+ }
+ } else { //值为null说明此次递归调用结束,将节点值存进结果
+ res.add(stack.pop().val);
+ }
+ }
-归并排序 (merge sort) 是一类与插入排序、交换排序、选择排序不同的另一种排序方法。归并的含义是将两个或两个以上的有序表合并成一个新的有序表。归并排序有多路归并排序、两路归并排序 , 可用于内排序,也可以用于外排序。
+ return res;
+ }
+}
+```
-## 说说归并排序怎么实现的?
+### 层序遍历
-两路归并排序算法思路是递归处理。每个递归过程涉及三个步骤
+```
+class Solution {
+ public List> levelOrder(TreeNode root) {
+ List> res = new ArrayList<>();
+ LinkedList queue = new LinkedList<>();
+ if (root == null) {
+ return res;
+ }
+ queue.addLast(root);
+ while (!queue.isEmpty()) {
+ List levelList = new LinkedList<>();
+ int size = queue.size();
+ while (size-- > 0) {
+ root = queue.removeFirst();
+ levelList.add(root.val);
+ if (root.left != null) {
+ queue.addLast(root.left);
+ }
+ if (root.right != null) {
+ queue.addLast(root.right);
+ }
+ }
+ res.add(levelList);
+ }
+ return res;
+ }
+}
+```
-- 分解: 把待排序的 n 个元素的序列分解成两个子序列, 每个子序列包括 n/2 个元素
-- 治理: 对每个子序列分别调用归并排序MergeSort, 进行递归操作
-- 合并: 合并两个排好序的子序列,生成排序结果
+## 排序算法
-
+常见的排序算法主要有:冒泡排序、插入排序、选择排序、快速排序、归并排序、堆排序、基数排序。各种排序算法的时间空间复杂度、稳定性见下图。
-代码实现:
+
+
+### 冒泡排序
```java
-public class MergeSort {
- public void mergeSort(int[] arr) {
- if (arr == null || arr.length == 0) {
+ public void bubbleSort(int[] arr) {
+ if (arr == null) {
return;
}
- //辅助数组
- int[] tmpArr = new int[arr.length];
- mergeSort(arr, tmpArr, 0, arr.length - 1);
- }
-
- private void mergeSort(int[] arr, int[] tmpArr, int left, int right) {
- if (left < right) {
- int mid = (left + right) >> 1;
- mergeSort(arr, tmpArr, left, mid);
- mergeSort(arr, tmpArr, mid + 1, right);
- merge(arr, tmpArr, left, mid, right);
+ boolean flag;
+ for (int i = arr.length - 1; i > 0; i--) {
+ flag = false;
+ for (int j = 0; j < i; j++) {
+ if (arr[j] > arr[j + 1]) {
+ int tmp = arr[j];
+ arr[j] = arr[j + 1];
+ arr[j + 1] = tmp;
+ flag = true;
+ }
+ }
+ if (!flag) {
+ return;
+ }
}
}
+```
- private void merge(int[] arr, int[] tmpArr, int left, int mid, int right) {
- int i = left;
- int j = mid + 1;
- int tmpIndex = left;
- while (i <= mid && j <= right) {
- if (arr[i] < arr[j]) {
- tmpArr[tmpIndex++] = arr[i];
- i++;
- } else {
- tmpArr[tmpIndex++] = arr[j];
- j++;
+### 插入排序
+
+```java
+ public void insertSort(int[] arr) {
+ if (arr == null) {
+ return;
+ }
+ for (int i = 1; i < arr.length; i++) {
+ int tmp = arr[i];
+ int j = i;
+ for (; j > 0 && tmp < arr[j - 1]; j--) {
+ arr[j] = arr[j - 1];
}
+ arr[j] = tmp;
}
+ }
+```
- while (i <= mid) {
- tmpArr[tmpIndex++] = arr[i++];
- }
+### 选择排序
- while (j <= right) {
- tmpArr[tmpIndex++] = arr[j++];
+```java
+ public void selectionSort(int[] arr) {
+ if (arr == null) {
+ return;
}
-
- for (int m = left; m <= right; m++) {
- arr[m] = tmpArr[m];
+ for (int i = 0; i < arr.length - 1; i++) {
+ for (int j = i + 1; j < arr.length; j++) {
+ if (arr[i] > arr[j]) {
+ int tmp = arr[i];
+ arr[i] = arr[j];
+ arr[j] = tmp;
+ }
+ }
}
}
-}
```
-## 时间复杂度是多少?
-
-对长度为n的序列,需进行logn次二路归并,每次归并的时间为O(n),故时间复杂度是O(nlgn)。
-
-## 空间复杂度是多少?
+### 基数排序
-归并排序需要辅助空间来暂存两个有序子序列归并的结果,故其辅助空间复杂度为O(n)
+在基数排序中,因为没有比较操作,所以在时间复杂上,最好的情况与最坏的情况在时间上是一致的,均为 O(d * (n + r))。d 为位数,r 为基数,n 为原数组个数。
+
-# 快速排序
-
-## 了解快速排序吗?
+### 快速排序
快速排序是由**冒泡排序**改进而得到的,是一种排序执行效率很高的排序算法,它利用**分治法**来对待排序序列进行分治排序,它的思想主要是通过一趟排序将待排记录分隔成独立的两部分,其中的一部分比关键字小,后面一部分比关键字大,然后再对这前后的两部分分别采用这种方式进行排序,通过递归的运算最终达到整个序列有序。
-## 快速排序的过程?
+快速排序的过程如下:
1. 在待排序的N个记录中任取一个元素(通常取第一个记录)作为基准,称为基准记录;
2. 定义两个索引 left 和 right 分别表示首索引和尾索引,key 表示基准值;
@@ -117,7 +224,11 @@ public class MergeSort {
6. 再进行下一趟排序时,待排序列被分成两个区:[0,left-1]和[righ+1,end]
7. 对每一个分区重复以上步骤,直到所有分区中的记录都有序,排序完成
-代码实现:
+快排为什么比冒泡效率高?
+
+快速排序之所以比较快,是因为相比冒泡排序,每次的交换都是跳跃式的,每次设置一个基准值,将小于基准值的都交换到左边,大于基准值的都交换到右边,这样不会像冒泡一样每次都只交换相邻的两个数,因此比较和交换的此数都变少了,速度自然更高。
+
+快速排序的平均时间复杂度是O(nlgn),最坏时间复杂度是O(n^2)。
```java
public void quickSort(int[] arr) {
@@ -160,10 +271,104 @@ public class MergeSort {
}
```
-## 快排为什么比冒泡效率高?
+### 归并排序
+
+归并排序 (merge sort) 是一类与插入排序、交换排序、选择排序不同的另一种排序方法。归并的含义是将两个或两个以上的有序表合并成一个新的有序表。归并排序有多路归并排序、两路归并排序 , 可用于内排序,也可以用于外排序。
+
+两路归并排序算法思路是递归处理。每个递归过程涉及三个步骤
+
+- 分解: 把待排序的 n 个元素的序列分解成两个子序列, 每个子序列包括 n/2 个元素
+- 治理: 对每个子序列分别调用归并排序MergeSort, 进行递归操作
+- 合并: 合并两个排好序的子序列,生成排序结果
+
+
+
+时间复杂度:对长度为n的序列,需进行logn次二路归并,每次归并的时间为O(n),故时间复杂度是O(nlgn)。
+
+空间复杂度:归并排序需要辅助空间来暂存两个有序子序列归并的结果,故其辅助空间复杂度为O(n)
+
+```java
+public class MergeSort {
+ public void mergeSort(int[] arr) {
+ if (arr == null || arr.length == 0) {
+ return;
+ }
+ //辅助数组
+ int[] tmpArr = new int[arr.length];
+ mergeSort(arr, tmpArr, 0, arr.length - 1);
+ }
+
+ private void mergeSort(int[] arr, int[] tmpArr, int left, int right) {
+ if (left < right) {
+ int mid = (left + right) >> 1;
+ mergeSort(arr, tmpArr, left, mid);
+ mergeSort(arr, tmpArr, mid + 1, right);
+ merge(arr, tmpArr, left, mid, right);
+ }
+ }
+
+ private void merge(int[] arr, int[] tmpArr, int left, int mid, int right) {
+ int i = left;
+ int j = mid + 1;
+ int tmpIndex = left;
+ while (i <= mid && j <= right) {
+ if (arr[i] < arr[j]) {
+ tmpArr[tmpIndex++] = arr[i];
+ i++;
+ } else {
+ tmpArr[tmpIndex++] = arr[j];
+ j++;
+ }
+ }
+
+ while (i <= mid) {
+ tmpArr[tmpIndex++] = arr[i++];
+ }
+
+ while (j <= right) {
+ tmpArr[tmpIndex++] = arr[j++];
+ }
+
+ for (int m = left; m <= right; m++) {
+ arr[m] = tmpArr[m];
+ }
+ }
+}
+```
+
+### 堆排序
+
+堆是具有下列性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
+
+
+
+**Top大问题**解决思路:使用一个固定大小的**最小堆**,当堆满后,每次添加数据的时候与堆顶元素比较,若小于堆顶元素,则舍弃,若大于堆顶元素,则删除堆顶元素,添加新增元素,对堆进行重新排序。
+
+对于n个数,取Top m个数,时间复杂度为O(nlogm),这样在n较大情况下,是优于nlogn(其他排序算法)的时间复杂度的。
+
+PriorityQueue 是一种基于优先级堆的优先级队列。每次从队列中取出的是具有最高优先权的元素。如果不提供Comparator的话,优先队列中元素默认按自然顺序排列,也就是数字默认是小的在队列头。优先级队列用数组实现,但是数组大小可以动态增加,容量无限。
+
+```java
+//找出前k个最大数,采用小顶堆实现
+public static int[] findKMax(int[] nums, int k) {
+ PriorityQueue pq = new PriorityQueue<>(k);//队列默认自然顺序排列,小顶堆,不必重写compare
+
+ for (int num : nums) {
+ if (pq.size() < k) {
+ pq.offer(num);
+ } else if (pq.peek() < num) {//如果堆顶元素 < 新数,则删除堆顶,加入新数入堆
+ pq.poll();
+ pq.offer(num);
+ }
+ }
+
+ int[] result = new int[k];
+ for (int i = 0; i < k&&!pq.isEmpty(); i++) {
+ result[i] = pq.poll();
+ }
+ return result;
+}
+```
-快速排序之所以比较快,是因为相比冒泡排序,每次的交换都是跳跃式的,每次设置一个基准值,将小于基准值的都交换到左边,大于基准值的都交换到右边,这样不会像冒泡一样每次都只交换相邻的两个数,因此比较和交换的此数都变少了,速度自然更高。
-## 快排的时间复杂度是多少?
-快速排序的平均时间复杂度是O(nlgn),最坏时间复杂度是O(n^2)。
\ No newline at end of file
diff --git "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/session\345\222\214cookie.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/session\345\222\214cookie.md"
index da09566..6cc4584 100644
--- "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/session\345\222\214cookie.md"
+++ "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/session\345\222\214cookie.md"
@@ -1,13 +1,3 @@
-
-
-**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
-
-- [cookie](#cookie)
-- [session](#session)
-- [区别](#%E5%8C%BA%E5%88%AB)
-
-
-
由于HTTP协议是无状态的协议,需要用某种机制来识具体的用户身份,用来跟踪用户的整个会话。常用的会话跟踪技术是cookie与session。
## cookie
diff --git "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\345\233\276\350\247\243HTTP.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\345\233\276\350\247\243HTTP.md"
index 642e7c3..abf68ff 100644
--- "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\345\233\276\350\247\243HTTP.md"
+++ "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\345\233\276\350\247\243HTTP.md"
@@ -1,50 +1,3 @@
-
-
-
-
-- [简介](#%E7%AE%80%E4%BB%8B)
- - [URI和URL](#uri%E5%92%8Curl)
- - [URI 格式](#uri-%E6%A0%BC%E5%BC%8F)
-- [简单的HTTP协议](#%E7%AE%80%E5%8D%95%E7%9A%84http%E5%8D%8F%E8%AE%AE)
- - [请求](#%E8%AF%B7%E6%B1%82)
- - [响应](#%E5%93%8D%E5%BA%94)
- - [首部](#%E9%A6%96%E9%83%A8)
- - [通用首部字段](#%E9%80%9A%E7%94%A8%E9%A6%96%E9%83%A8%E5%AD%97%E6%AE%B5)
- - [请求首部字段](#%E8%AF%B7%E6%B1%82%E9%A6%96%E9%83%A8%E5%AD%97%E6%AE%B5)
- - [响应首部字段](#%E5%93%8D%E5%BA%94%E9%A6%96%E9%83%A8%E5%AD%97%E6%AE%B5)
- - [实体首部字段](#%E5%AE%9E%E4%BD%93%E9%A6%96%E9%83%A8%E5%AD%97%E6%AE%B5)
- - [Cookie相关的首部](#cookie%E7%9B%B8%E5%85%B3%E7%9A%84%E9%A6%96%E9%83%A8)
- - [请求URI定位资源](#%E8%AF%B7%E6%B1%82uri%E5%AE%9A%E4%BD%8D%E8%B5%84%E6%BA%90)
- - [HTTP方法](#http%E6%96%B9%E6%B3%95)
- - [持久连接](#%E6%8C%81%E4%B9%85%E8%BF%9E%E6%8E%A5)
- - [Cookie](#cookie)
- - [编码提升传输速率](#%E7%BC%96%E7%A0%81%E6%8F%90%E5%8D%87%E4%BC%A0%E8%BE%93%E9%80%9F%E7%8E%87)
- - [multipart 多部分对象集合](#multipart-%E5%A4%9A%E9%83%A8%E5%88%86%E5%AF%B9%E8%B1%A1%E9%9B%86%E5%90%88)
- - [范围请求](#%E8%8C%83%E5%9B%B4%E8%AF%B7%E6%B1%82)
- - [内容协商返回最合适的内容](#%E5%86%85%E5%AE%B9%E5%8D%8F%E5%95%86%E8%BF%94%E5%9B%9E%E6%9C%80%E5%90%88%E9%80%82%E7%9A%84%E5%86%85%E5%AE%B9)
-- [状态码](#%E7%8A%B6%E6%80%81%E7%A0%81)
- - [2xx 成功](#2xx-%E6%88%90%E5%8A%9F)
- - [3xx 重定向](#3xx-%E9%87%8D%E5%AE%9A%E5%90%91)
- - [4xx 客户端错误](#4xx-%E5%AE%A2%E6%88%B7%E7%AB%AF%E9%94%99%E8%AF%AF)
- - [5xx 服务器错误](#5xx-%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%94%99%E8%AF%AF)
-- [Web 服务器](#web-%E6%9C%8D%E5%8A%A1%E5%99%A8)
- - [代理](#%E4%BB%A3%E7%90%86)
- - [网关](#%E7%BD%91%E5%85%B3)
- - [隧道](#%E9%9A%A7%E9%81%93)
-- [HTTPS](#https)
- - [公开密钥加密](#%E5%85%AC%E5%BC%80%E5%AF%86%E9%92%A5%E5%8A%A0%E5%AF%86)
-- [认证机制](#%E8%AE%A4%E8%AF%81%E6%9C%BA%E5%88%B6)
- - [BASIC 认证](#basic-%E8%AE%A4%E8%AF%81)
- - [DIGEST 认证](#digest-%E8%AE%A4%E8%AF%81)
- - [SSL 客户端认证](#ssl-%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%AE%A4%E8%AF%81)
- - [基于表单认证](#%E5%9F%BA%E4%BA%8E%E8%A1%A8%E5%8D%95%E8%AE%A4%E8%AF%81)
-- [基于 HTTP 的功能追加协议](#%E5%9F%BA%E4%BA%8E-http-%E7%9A%84%E5%8A%9F%E8%83%BD%E8%BF%BD%E5%8A%A0%E5%8D%8F%E8%AE%AE)
- - [WebSocket](#websocket)
- - [Web 服务器管理文件的 WebDAV](#web-%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%AE%A1%E7%90%86%E6%96%87%E4%BB%B6%E7%9A%84-webdav)
-- [Web 的攻击技术](#web-%E7%9A%84%E6%94%BB%E5%87%BB%E6%8A%80%E6%9C%AF)
-
-
-
## 简介
访问baidu.com的过程:
diff --git "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index c91b807..3d82083 100644
--- "a/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -1,39 +1,3 @@
-
-
-
-
-- [网络分层结构](#%E7%BD%91%E7%BB%9C%E5%88%86%E5%B1%82%E7%BB%93%E6%9E%84)
-- [三次握手](#%E4%B8%89%E6%AC%A1%E6%8F%A1%E6%89%8B)
-- [两次握手可以吗?](#%E4%B8%A4%E6%AC%A1%E6%8F%A1%E6%89%8B%E5%8F%AF%E4%BB%A5%E5%90%97)
-- [四次挥手](#%E5%9B%9B%E6%AC%A1%E6%8C%A5%E6%89%8B)
-- [第四次挥手为什么要等待2MSL?](#%E7%AC%AC%E5%9B%9B%E6%AC%A1%E6%8C%A5%E6%89%8B%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E7%AD%89%E5%BE%852msl)
-- [为什么是四次挥手?](#%E4%B8%BA%E4%BB%80%E4%B9%88%E6%98%AF%E5%9B%9B%E6%AC%A1%E6%8C%A5%E6%89%8B)
-- [TCP有哪些特点?](#tcp%E6%9C%89%E5%93%AA%E4%BA%9B%E7%89%B9%E7%82%B9)
-- [TCP和UDP的区别?](#tcp%E5%92%8Cudp%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [HTTP协议的特点?](#http%E5%8D%8F%E8%AE%AE%E7%9A%84%E7%89%B9%E7%82%B9)
-- [HTTP报文格式](#http%E6%8A%A5%E6%96%87%E6%A0%BC%E5%BC%8F)
-- [HTTP状态码有哪些?](#http%E7%8A%B6%E6%80%81%E7%A0%81%E6%9C%89%E5%93%AA%E4%BA%9B)
-- [POST和GET的区别?](#post%E5%92%8Cget%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [HTTP长连接和短连接?](#http%E9%95%BF%E8%BF%9E%E6%8E%A5%E5%92%8C%E7%9F%AD%E8%BF%9E%E6%8E%A5)
-- [HTTP1.1和 HTTP2.0的区别?](#http11%E5%92%8C-http20%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [HTTPS与HTTP的区别?](#https%E4%B8%8Ehttp%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [什么是数字证书?](#%E4%BB%80%E4%B9%88%E6%98%AF%E6%95%B0%E5%AD%97%E8%AF%81%E4%B9%A6)
-- [HTTPS原理](#https%E5%8E%9F%E7%90%86)
-- [DNS 的解析过程?](#dns-%E7%9A%84%E8%A7%A3%E6%9E%90%E8%BF%87%E7%A8%8B)
-- [浏览器中输入URL返回页面过程?](#%E6%B5%8F%E8%A7%88%E5%99%A8%E4%B8%AD%E8%BE%93%E5%85%A5url%E8%BF%94%E5%9B%9E%E9%A1%B5%E9%9D%A2%E8%BF%87%E7%A8%8B)
-- [什么是cookie和session?](#%E4%BB%80%E4%B9%88%E6%98%AFcookie%E5%92%8Csession)
-- [Cookie和Session的区别?](#cookie%E5%92%8Csession%E7%9A%84%E5%8C%BA%E5%88%AB)
-- [什么是对称加密和非对称加密?](#%E4%BB%80%E4%B9%88%E6%98%AF%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86%E5%92%8C%E9%9D%9E%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86)
-- [滑动窗口机制](#%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E6%9C%BA%E5%88%B6)
-- [详细讲一下拥塞控制?](#%E8%AF%A6%E7%BB%86%E8%AE%B2%E4%B8%80%E4%B8%8B%E6%8B%A5%E5%A1%9E%E6%8E%A7%E5%88%B6)
- - [慢开始](#%E6%85%A2%E5%BC%80%E5%A7%8B)
- - [拥塞避免](#%E6%8B%A5%E5%A1%9E%E9%81%BF%E5%85%8D)
- - [快重传](#%E5%BF%AB%E9%87%8D%E4%BC%A0)
- - [快恢复](#%E5%BF%AB%E6%81%A2%E5%A4%8D)
-- [ARP协议](#arp%E5%8D%8F%E8%AE%AE)
-
-
-
## 网络分层结构
计算机网络体系大致分为三种,OSI七层模型、TCP/IP四层模型和五层模型。一般面试的时候考察比较多的是五层模型。
@@ -265,6 +229,7 @@ HTTP2.0相比HTTP1.1支持的特性:
由于HTTP协议是无状态的协议,需要用某种机制来识具体的用户身份,用来跟踪用户的整个会话。常用的会话跟踪技术是cookie与session。
**cookie**就是由服务器发给客户端的特殊信息,而这些信息以文本文件的方式存放在客户端,然后客户端每次向服务器发送请求的时候都会带上这些特殊的信息。说得更具体一些:当用户使用浏览器访问一个支持cookie的网站的时候,用户会提供包括用户名在内的个人信息并且提交至服务器;接着,服务器在向客户端回传相应的超文本的同时也会发回这些个人信息,当然这些信息并不是存放在HTTP响应体中的,而是存放于HTTP响应头;当客户端浏览器接收到来自服务器的响应之后,浏览器会将这些信息存放在一个统一的位置。 自此,客户端再向服务器发送请求的时候,都会把相应的cookie存放在HTTP请求头再次发回至服务器。服务器在接收到来自客户端浏览器的请求之后,就能够通过分析存放于请求头的cookie得到客户端特有的信息,从而动态生成与该客户端相对应的内容。网站的登录界面中“请记住我”这样的选项,就是通过cookie实现的。
+

**cookie工作流程**:
@@ -274,6 +239,7 @@ HTTP2.0相比HTTP1.1支持的特性:
3. 下次访问时,浏览器将自动携带cookie数据发送给服务器。
**session原理**:首先浏览器请求服务器访问web站点时,服务器首先会检查这个客户端请求是否已经包含了一个session标识、称为SESSIONID,如果已经包含了一个sessionid则说明以前已经为此客户端创建过session,服务器就按照sessionid把这个session检索出来使用,如果客户端请求不包含session id,则服务器为此客户端创建一个session,并且生成一个与此session相关联的独一无二的sessionid存放到cookie中,这个sessionid将在本次响应中返回到客户端保存,这样在交互的过程中,浏览器端每次请求时,都会带着这个sessionid,服务器根据这个sessionid就可以找得到对应的session。以此来达到共享数据的目的。 这里需要注意的是,session不会随着浏览器的关闭而死亡,而是等待超时时间。
+

## Cookie和Session的区别?
From 8f8e07cd359bc88430495d0aeed3d3a417aa7107 Mon Sep 17 00:00:00 2001
From: tyson
Date: Tue, 5 Jul 2022 08:28:01 +0800
Subject: [PATCH 037/112] =?UTF-8?q?=E6=9B=B4=E6=94=B9=E7=9B=AE=E5=BD=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Java/JVM.md | 32 +-
...21\351\235\242\350\257\225\351\242\230.md" | 16 +-
...a8\346\226\260\347\211\271\346\200\247.md" | 0
"Java/Java\345\237\272\347\241\200.md" | 10 +-
...00\351\235\242\350\257\225\351\242\230.md" | 33 +-
...21\351\235\242\350\257\225\351\242\230.md" | 14 +-
...10\351\235\242\350\257\225\351\242\230.md" | 10 +-
"Java/\345\271\266\345\217\221.md" | 22 +-
"Java/\351\233\206\345\220\210.md" | 8 +-
README.md | 30 +-
...07\345\215\227\346\200\273\347\273\223.md" | 14 +-
...is\351\235\242\350\257\225\351\242\230.md" | 12 +-
...23\345\255\230\345\207\273\347\251\277.md" | 0
...\345\261\200\345\224\257\344\270\200ID.md" | 16 +-
.../\345\276\256\346\234\215\345\212\241.md" | 4 +-
...34\347\250\213\350\260\203\347\224\250.md" | 2 +-
.../GitHub\346\214\207\345\215\227.md" | 18 +-
"\345\267\245\345\205\267/progit2.md" | 10 +-
.../MySQL\345\237\272\347\241\200.md" | 12 +-
...47\350\241\214\350\256\241\345\210\222.md" | 44 +--
.../MySQL\350\277\233\351\230\266.md" | 24 +-
...21\351\235\242\350\257\225\351\242\230.md" | 28 +-
...is\351\235\242\350\257\225\351\242\230.md" | 2 +-
...25\351\242\230\346\200\273\347\273\223.md" | 4 +-
...15\345\212\241\345\256\236\346\210\230.md" | 2 +-
...VC\351\235\242\350\257\225\351\242\230.md" | 2 +-
...ng\351\235\242\350\257\225\351\242\230.md" | 356 +++++++++++++++++-
...72\351\253\230\351\242\221\350\257\215.md" | 2 +-
...01\347\232\204\344\270\252\346\225\260.md" | 2 +-
.../RabbitMQ.md" | 10 +-
...73\344\277\241\351\230\237\345\210\227.md" | 0
...27\351\235\242\350\257\225\351\242\230.md" | 2 +-
...73\345\275\225\345\216\237\347\220\206.md" | 6 +-
...73\347\273\237\350\256\276\350\256\241.md" | 4 +-
...347\273\237\350\256\276\350\256\241old.md" | 2 +-
35 files changed, 550 insertions(+), 203 deletions(-)
rename Java/Java8.md => "Java/Java8\346\226\260\347\211\271\346\200\247.md" (100%)
rename "\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md" => "Redis/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md" (98%)
rename "\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md" => "Redis/Redis\351\235\242\350\257\225\351\242\230.md" (98%)
rename "\344\270\255\351\227\264\344\273\266/\347\274\223\345\255\230\347\251\277\351\200\217\343\200\201\347\274\223\345\255\230\351\233\252\345\264\251\343\200\201\347\274\223\345\255\230\345\207\273\347\251\277.md" => "Redis/\347\274\223\345\255\230\347\251\277\351\200\217\343\200\201\347\274\223\345\255\230\351\233\252\345\264\251\343\200\201\347\274\223\345\255\230\345\207\273\347\251\277.md" (100%)
rename "\344\270\255\351\227\264\344\273\266/RabbitMQ.md" => "\346\266\210\346\201\257\351\230\237\345\210\227/RabbitMQ.md" (98%)
rename "\344\270\255\351\227\264\344\273\266/\346\255\273\344\277\241\351\230\237\345\210\227.md" => "\346\266\210\346\201\257\351\230\237\345\210\227/\346\255\273\344\277\241\351\230\237\345\210\227.md" (100%)
rename "\344\270\255\351\227\264\344\273\266/\346\266\210\346\201\257\351\230\237\345\210\227\351\235\242\350\257\225\351\242\230.md" => "\346\266\210\346\201\257\351\230\237\345\210\227/\346\266\210\346\201\257\351\230\237\345\210\227\351\235\242\350\257\225\351\242\230.md" (99%)
diff --git a/Java/JVM.md b/Java/JVM.md
index 05f5634..b19e730 100644
--- a/Java/JVM.md
+++ b/Java/JVM.md
@@ -90,7 +90,7 @@ Java 内存模型(JMM)是基于共享内存的多线程通信机制。
JVM内存结构 = 类加载器 + 执行引擎 + 运行时数据区域 。
-
+
> 图片来源:深入理解Java虚拟机-周志明
@@ -172,11 +172,11 @@ JDK 1.8 的时候,HotSpot 的永久代被彻底移除了,使用元空间替
运行时常量池是方法区的一部分,在类加载之后,会将编译器生成的各种字面量和符号引号放到运行时常量池。在运行期间动态生成的常量,如 String 类的 intern()方法,也会被放入运行时常量池。
-
+
-
+
-
+
> 图片来源:https://blog.csdn.net/soonfly
@@ -194,11 +194,11 @@ Java 程序通过栈上的 reference 数据来操作堆上的具体对象。对
- 如果使用句柄的话,那么 Java 堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。使用句柄来访问的最大好处是 reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而 reference 本身不需要修改。
-
+
- 直接指针。reference 中存储的直接就是对象的地址。对象包含到对象类型数据的指针,通过这个指针可以访问对象类型数据。使用直接指针访问方式最大的好处就是访问对象速度快,它节省了一次指针定位的时间开销,虚拟机hotspot主要是使用直接指针来访问对象。
-
+
@@ -245,7 +245,7 @@ ClassFile {
加载、验证、准备、解析、初始化、使用和卸载。
-
+
## 类加载的过程
@@ -281,7 +281,7 @@ ClassFile {
一个类加载器收到一个类的加载请求时,它首先不会自己尝试去加载它,而是把这个请求委派给父类加载器去完成,这样层层委派,因此所有的加载请求最终都会传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
-
+
双亲委派模型的好处:可以防止内存中出现多份同样的字节码。如果没有双亲委派模型而是由各个类加载器自行加载的话,如果用户编写了一个java.lang.Object的同名类并放在ClassPath中,多个类加载器都去加载这个类到内存中,系统中将会出现多个不同的Object类,那么类之间的比较结果及类的唯一性将无法保证。
@@ -339,7 +339,7 @@ public abstract class ClassLoader {
堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断那些对象已经死亡(即不能再被任何途径使用的对象)。
-
+
### 引用计数法
@@ -365,7 +365,7 @@ public class ReferenceCountingGc {
通过GC Root对象为起点,从这些节点向下搜索,搜索所走过的路径叫引用链,当一个对象到GC Root没有任何的引用链相连时,说明这个对象是不可用的。
-
+
#### 可作为GC Roots的对象
@@ -469,13 +469,13 @@ public class ReferenceCountingGc {
标记清除算法就是分为“标记”和“清除”两个阶段。标记出所有需要回收的对象,标记结束后统一回收所有被标记的对象。这种垃圾回收算法效率较低,并且会产生大量不连续的空间碎片。
-
+
### 复制清除算法
半区复制,用于新生代垃圾回收。将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。
-
+
特点:实现简单,运行高效,但可用内存缩小为了原来的一半,浪费空间。
@@ -534,7 +534,7 @@ java -XX:+PrintCommandLineFlags -version
单线程收集器,使用一条垃圾收集线程去完成垃圾收集工作,在进行垃圾收集工作的时候必须暂停其他所有的工作线程( "Stop The World" ),直到它收集结束。
-
+
特点:简单高效;内存消耗最小;没有线程交互的开销,单线程收集效率高;需暂停所有的工作线程,用户体验不好。
@@ -542,7 +542,7 @@ java -XX:+PrintCommandLineFlags -version
Serial 收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全一样。
-
+
除了 Serial 收集器外,只有它能与 CMS 收集器配合工作。
@@ -582,7 +582,7 @@ Concurrent Mark Sweep 并发标记清除,目的是获取最短应用停顿时
- 重新标记: 在并发标记期间对象的引用关系可能会变化,需要重新进行标记。此阶段也会stw,停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短。
- 并发清除:清除死亡对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。
-
+
由于在整个过程中耗时最长的并发标记和并发清除阶段中,垃圾收集器线程都可以与用户线程一起工作,所以从总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。
@@ -607,7 +607,7 @@ G1垃圾收集器的目标是用在多核、大内存的机器上,在不同应
G1将整个堆分成相同大小的分区(Region),有四种不同类型的分区:Eden、Survivor、Old和Humongous(大对象)。分区的大小取值范围为1M到32M,都是2的幂次方。Region大小可以通过`-XX:G1HeapRegionSize`参数指定。Humongous区域用于存储大对象。G1认为只要大小超过了一个Region容量一半的对象即可判定为大对象。
-
+
G1 收集器对各个Region回收所获得的空间大小和回收所需时间的经验值进行排序,得到一个优先级列表,每次根据用户设置的最大的回收停顿时间(使用参数-XX:MaxGCPauseMillis指定,默认值是200毫秒),优先处理回收价值最大的 Region。
diff --git "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index d93bc43..3f2af40 100644
--- "a/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -2,7 +2,7 @@
JVM内存结构分为5大区域,**程序计数器**、**虚拟机栈**、**本地方法栈**、**堆**、**方法区**。
-
+
### 程序计数器
@@ -138,7 +138,7 @@ ClassFile {
类的加载指的是将类的`class`文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个此类的对象,通过这个对象可以访问到方法区对应的类信息。
-
+
**加载**
@@ -166,7 +166,7 @@ ClassFile {
一个类加载器收到一个类的加载请求时,它首先不会自己尝试去加载它,而是把这个请求**委派**给父类加载器去完成,这样层层委派,因此所有的加载请求最终都会传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
-
+
双亲委派模型的具体实现代码在 `java.lang.ClassLoader`中,此类的 `loadClass()` 方法运行过程如下:先检查类是否已经加载过,如果没有则让父类加载器去加载。当父类加载器加载失败时抛出 `ClassNotFoundException`,此时尝试自己去加载。源码如下:
@@ -265,7 +265,7 @@ public class ReferenceCount {
通过`GC Root`对象为起点,从这些节点向下搜索,搜索所走过的路径叫引用链,当一个对象到`GC Root`没有任何的引用链相连时,说明这个对象是不可用的。
-
+
## 可作为GC Roots的对象有哪些?
@@ -366,7 +366,7 @@ GC(`Garbage Collection`),垃圾回收,是Java与C++的主要区别之一
首先利用可达性去遍历内存,把存活对象和垃圾对象进行标记。标记结束后统一将所有标记的对象回收掉。这种垃圾回收算法效率较低,并且会**产生大量不连续的空间碎片**。
-
+
**复制清除算法**
@@ -378,7 +378,7 @@ GC(`Garbage Collection`),垃圾回收,是Java与C++的主要区别之一
根据老年代的特点提出的一种标记算法,标记过程仍然与`标记-清除`算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。
-
+
**分类收集算法**
@@ -461,7 +461,7 @@ G1垃圾收集器的目标是在不同应用场景中**追求高吞吐量和低
G1将整个堆分成相同大小的分区(`Region`),有四种不同类型的分区:`Eden、Survivor、Old和Humongous`。分区的大小取值范围为 1M 到 32M,都是2的幂次方。分区大小可以通过`-XX:G1HeapRegionSize`参数指定。`Humongous`区域用于存储大对象。G1规定只要大小超过了一个分区容量一半的对象就认为是大对象。
-
+
G1 收集器对各个分区回收所获得的空间大小和回收所需时间的经验值进行排序,得到一个优先级列表,每次根据用户设置的最大回收停顿时间,优先回收价值最大的分区。
@@ -703,4 +703,4 @@ class Person {
-
+
diff --git a/Java/Java8.md "b/Java/Java8\346\226\260\347\211\271\346\200\247.md"
similarity index 100%
rename from Java/Java8.md
rename to "Java/Java8\346\226\260\347\211\271\346\200\247.md"
diff --git "a/Java/Java\345\237\272\347\241\200.md" "b/Java/Java\345\237\272\347\241\200.md"
index 72cff97..3a08dc1 100644
--- "a/Java/Java\345\237\272\347\241\200.md"
+++ "b/Java/Java\345\237\272\347\241\200.md"
@@ -976,7 +976,7 @@ Class 类提供了一些方法,可以获取成员变量、成员方法、接
Field提供了类和接口中字段的信息,通过Field类可以动态访问这些字段。下图是Field类提供的一些方法。
-
+
## Method类
@@ -1101,7 +1101,7 @@ public class GenericMethod {
Throwable类是Error和Exception的父类,只有继承于Throwable的类或者其子类才能被抛出。Throwable分为两类:
-
+
- Error:JVM 无法解决的严重问题,如栈溢出(StackOverflowError)、内存溢出(OOM)等。程序无法处理的错误。
@@ -1198,7 +1198,7 @@ public class ExceptionTest {
Java IO流的核心就是对文件的操作,对于字节 、字符类型的输入和输出流。IO流主要分为两大类,字节流和字符流。字节流可以处理任何类型的数据,如图片,视频等,字符流只能处理字符类型的数据。
-
+
> 图片参考:[Java io学习整理](https://zhuanlan.zhihu.com/p/25418336)
@@ -1251,13 +1251,13 @@ OutputStreamWriter:字符到字节的转换,可对读取到的字符数据
同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。
-
+
## NIO
NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了 NIO 框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。
-
+
NIO与IO区别:
diff --git "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
index e4e9624..04f0e86 100644
--- "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
@@ -46,7 +46,7 @@
**JVM** :英文名称(Java Virtual Machine),就是我们耳熟能详的 Java 虚拟机。Java 能够跨平台运行的核心在于 JVM 。
-
+
所有的java程序会首先被编译为.class的类文件,这种类文件可以在虚拟机上执行。也就是说class文件并不直接与机器的操作系统交互,而是经过虚拟机间接与操作系统交互,由虚拟机将程序解释给本地系统执行。
@@ -56,7 +56,7 @@
英文名称(Java Runtime Environment),就是Java 运行时环境。我们编写的Java程序必须要在JRE才能运行。它主要包含两个部分,JVM 和 Java 核心类库。
-
+
JRE是Java的运行环境,并不是一个开发环境,所以没有包含任何开发工具,如编译器和调试器等。
@@ -86,7 +86,7 @@ JRE = JVM + Java 核心类库
JDK = JRE + Java工具 + 编译器 + 调试器
-
+
## 面向对象有哪些特性?
@@ -232,7 +232,11 @@ private static class IntegerCache {
## String 为什么不可变?
-先看下Java8 String类的源码:
+先看看什么是不可变的对象。
+
+如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的。不能改变状态的意思是,不能改变对象内的成员变量,包括基本数据类型的值不能改变,引用类型的变量不能指向其他的对象,引用类型指向的对象的状态也不能改变。
+
+接着来看Java8 String类的源码:
```java
public final class String
@@ -245,12 +249,27 @@ public final class String
}
```
-String类是final的,它的所有成员变量也都是final的。为什么是final的?
+从源码可以看出,String对象其实在内部就是一个个字符,存储在这个value数组里面的。
+
+value数组用final修饰,final 修饰的变量,值不能被修改。因此value不可以指向其他对象。
+
+String类内部所有的字段都是私有的,也就是被private修饰。而且String没有对外提供修改内部状态的方法,因此value数组不能改变。
+
+所以,String是不可变的。
+
+那为什么String要设计成不可变的?
+
+主要有以下几点原因:
1. **线程安全**。同一个字符串实例可以被多个线程共享,因为字符串不可变,本身就是线程安全的。
2. **支持hash映射和缓存。**因为String的hash值经常会使用到,比如作为 Map 的键,不可变的特性使得 hash 值也不会变,不需要重新计算。
+3. **出于安全考虑**。网络地址URL、文件路径path、密码通常情况下都是以String类型保存,假若String不是固定不变的,将会引起各种安全隐患。比如将密码用String的类型保存,那么它将一直留在内存中,直到垃圾收集器把它清除。假如String类不是固定不变的,那么这个密码可能会被改变,导致出现安全隐患。
3. **字符串常量池优化**。String对象创建之后,会缓存到字符串常量池中,下次需要创建同样的对象时,可以直接返回缓存的引用。
+既然我们的String是不可变的,它内部还有很多substring, replace, replaceAll这些操作的方法。这些方法好像会改变String对象?怎么解释呢?
+
+其实不是的,我们每次调用replace等方法,其实会在堆内存中创建了一个新的对象。然后其value数组引用指向不同的对象。
+
## String, StringBuffer 和 StringBuilder区别
**1. 可变性**
@@ -975,7 +994,7 @@ unchecked Exception:
不同的线程干专业的事情,最终每个线程都没空着,系统的吞吐量自然就上去了。
-
+
@@ -1113,4 +1132,4 @@ Java泛型是JDK 5中引⼊的⼀个新特性, 允许在定义类和接口的
-
+
diff --git "a/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
index e71a0dd..022a2fc 100644
--- "a/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md"
@@ -10,7 +10,7 @@
### 线程池执行原理?
-
+
1. 当线程池里存活的线程数小于核心线程数`corePoolSize`时,这时对于一个新提交的任务,线程池会创建一个线程去处理任务。当线程池里面存活的线程数小于等于核心线程数`corePoolSize`时,线程池里面的线程会一直存活着,就算空闲时间超过了`keepAliveTime`,线程也不会被销毁,而是一直阻塞在那里一直等待任务队列的任务来执行。
2. 当线程池里面存活的线程数已经等于corePoolSize了,这是对于一个新提交的任务,会被放进任务队列workQueue排队等待执行。
@@ -145,7 +145,7 @@ public static ExecutorService newCachedThreadPool() {
3. 修改 `ScheduledFutureTask` 的 time 变量为下次将要被执行的时间;
4. 把这个修改 time 之后的 `ScheduledFutureTask` 放回 `DelayQueue` 中(`DelayQueue.add()`)。
-
+
适用场景:周期性执行任务的场景,需要限制线程数量的场景。
@@ -171,7 +171,7 @@ public static ExecutorService newCachedThreadPool() {
**终止(TERMINATED)**:表示该线程已经执行完毕。
-
+
> 图片来源:Java并发编程的艺术
@@ -347,7 +347,7 @@ class RunnableDemo implements Runnable {
如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方持有的资源,所以这两个线程就会互相等待而进入死锁状态。
-
+
下面通过例子说明线程死锁,代码来自并发编程之美。
@@ -632,7 +632,7 @@ class SeasonThreadTask implements Runnable{
每个线程都有一个`ThreadLocalMap`(`ThreadLocal`内部类),Map中元素的键为`ThreadLocal`,而值对应线程的变量副本。
-
+
调用`threadLocal.set()`-->调用`getMap(Thread)`-->返回当前线程的`ThreadLocalMap`-->`map.set(this, value)`,this是`threadLocal`本身。源码如下:
@@ -729,7 +729,7 @@ private volatile int state;//共享变量,使用volatile修饰保证线程可
同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态(独占或共享 )构造成为一个节点(Node)并将其加入同步队列并进行自旋,当同步状态释放时,会把首节点中的后继节点对应的线程唤醒,使其再次尝试获取同步状态。
-
+
## ReentrantLock 是如何实现可重入性的?
@@ -1095,4 +1095,4 @@ JDK1.8 ConcurrentHashMap采用CAS和synchronized来保证并发安全。数据
-
+
diff --git "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
index ba6af67..5af404d 100644
--- "a/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md"
@@ -4,9 +4,9 @@ Java集合类主要由两个接口**Collection**和**Map**派生出来的,Coll
Java集合框架图如下:
-
+
-
+
List代表了有序可重复集合,可直接根据元素的索引来访问;Set代表无序不可重复集合,只能根据元素本身来访问;Queue是队列集合。Map代表的是存储key-value对的集合,可根据元素的key来访问value。
@@ -147,7 +147,7 @@ HashMap有扩容机制,就是当达到扩容条件时会进行扩容。扩容
5. 链表的数量大于阈值8,就要转换成红黑树的结构
6. 添加成功后会检查是否需要扩容
-
+
### 红黑树的特点?
@@ -221,7 +221,7 @@ public class TreeMap
TreeMap 的继承结构:
-
+
**TreeMap的特点:**
@@ -548,4 +548,4 @@ https://coolshell.cn/articles/9606.htm(HashMap 死循环)
-
+
diff --git "a/Java/\345\271\266\345\217\221.md" "b/Java/\345\271\266\345\217\221.md"
index 1f35396..65ea9c9 100644
--- "a/Java/\345\271\266\345\217\221.md"
+++ "b/Java/\345\271\266\345\217\221.md"
@@ -87,7 +87,7 @@ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveT
创建新的线程需要获取全局锁,通过这种设计可以尽量避免获取全局锁,当 ThreadPoolExecutor 完成预热之后(当前运行的线程数大于等于 corePoolSize),提交的大部分任务都会被放到 BlockingQueue。
-
+
ThreadPoolExecutor 的通用构造函数:
@@ -326,7 +326,7 @@ public static ExecutorService newCachedThreadPool() {
3. 修改 `ScheduledFutureTask` 的 time 变量为下次将要被执行的时间;
4. 把这个修改 time 之后的 `ScheduledFutureTask` 放回 `DelayQueue` 中(`DelayQueue.add()`)。
-
+
适用场景:周期性执行任务的场景,需要限制线程数量的场景。
@@ -340,7 +340,7 @@ FixedThreadPool和SingleThreadPool。允许请求队列长度为 Integer.MAX_VAL
CachedThreadPool。创建的线程池允许的最大线程数是Integer.MAX_VALUE,当添加任务的速度大于线程池处理任务的速度,可能会创建大量的线程,消耗资源,甚至导致OOM。
-
+
正确示例(阿里巴巴编码规范):
@@ -369,7 +369,7 @@ Java内存模型:线程之间的共享变量存储在主内存里,每个线
1. 线程a把本地内存a中的更新过的共享变量刷新到主内存中去。
2. 线程b到主内存中去读取线程a刷新过的共享变量,然后复制一份到本地内存b中去。
-
+
本地内存是JMM的一个抽象概念,并不真实存在,它包括缓存、写缓冲区、寄存器以及其他硬件和编译器优化。
@@ -392,7 +392,7 @@ Java内存模型:线程之间的共享变量存储在主内存里,每个线
终止(TERMINATED):表示该线程已经执行完毕。
-
+
> 图片来源:Java并发编程的艺术
@@ -680,7 +680,7 @@ public class SynchronizedDemo {
Synchronized底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。
-
+
**方法的同步**不是通过添加monitorenter和monitorexit指令来完成,而是在其常量池中添加了ACC_SYNCHRONIZED标识符。JVM就是根据该标识符来实现方法的同步的:当线程调用方法时,会先检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,说明此方法是同步方法,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他线程无法再获得同一个monitor对象。
@@ -692,7 +692,7 @@ public class SynchronizedMethod {
}
```
-
+
#### 锁的状态
@@ -1077,7 +1077,7 @@ private volatile int state;//共享变量,使用volatile修饰保证线程可
同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态(独占或共享 )构造成为一个节点(Node)并将其加入同步队列并进行自旋,当同步状态释放时,会把首节中的后继节点对应的线程唤醒,使其再次尝试获取同步状态。
-
+
@@ -1112,19 +1112,19 @@ public void conditionSignal() throws InterruptedException {
每个Condition对象都包含着一个等待队列,如果一个线程成功获取了锁之后调用了Condition.await()方法,那么该线程将会释放同步状态、唤醒同步队列中的后继节点,然后构造成节点加入等待队列。只有当线程再次获取Condition相关联的锁之后,才能从await()方法返回。
-
+
> 图片来源:Java并发编程的艺术
在Object的监视器模型上,一个对象拥有一个同步队列和等待队列。Lock通过AQS实现,AQS可以有多个Condition,所以Lock拥有一个同步队列和多个等待队列。
-
+
> 图片来源:Java并发编程的艺术
线程获取了锁之后,调用Condition的signal()方法,会将等待队列的队首节点移到同步队列中,然后该节点的线程会尝试去获取同步状态。成功获取同步状态之后,线程将await()方法返回。
-
+
> 图片来源:Java并发编程的艺术
diff --git "a/Java/\351\233\206\345\220\210.md" "b/Java/\351\233\206\345\220\210.md"
index 83fecae..d626f12 100644
--- "a/Java/\351\233\206\345\220\210.md"
+++ "b/Java/\351\233\206\345\220\210.md"
@@ -51,7 +51,7 @@
以 Map 结尾的类都实现了 Map 接口,其他所有的类都实现了 Collection 接口。
-
+
### HashMap
@@ -88,7 +88,7 @@ put方法流程:
5. 链表的数量大于阈值8,就要转换成红黑树的结构
6. 添加成功后会检查是否需要扩容
-
+
> 参考链接:
>
@@ -121,7 +121,7 @@ HashMap是无序的,迭代HashMap所得到元素的顺序并不是它们最初
LinkedHashMap继承于HashMap,是HashMap和LinkedList的融合体,具备两者的特性。每次put操作都会将entry插入到双向链表的尾部。
-
+
### TreeMap
@@ -136,7 +136,7 @@ public class TreeMap
TreeMap 的继承结构:
-
+
**TreeMap的特点:**
diff --git a/README.md b/README.md
index eed60e3..21e4c06 100644
--- a/README.md
+++ b/README.md
@@ -16,17 +16,17 @@
如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的**公众号『 程序员大彬 』**,后台回复『 PDF 』可以**下载最新版本的大厂高频面试题目PDF版本**。
-
+
个人公众号
-
-
-
-
-
-
+
+
+
+
+
+
@@ -90,9 +90,9 @@
## Redis
-1. [【大厂面试】——Redis30问](中间件/Redis面试题.md)(牛客高赞,推荐 :+1:)
-2. [Redis分布式锁(推荐 :+1:)](中间件/Redis分布式锁.md)
-4. [缓存穿透、缓存雪崩、缓存击穿](中间件/缓存穿透、缓存雪崩、缓存击穿.md)
+1. [【大厂面试】——Redis30问](Redis/Redis面试题.md)(牛客高赞,推荐 :+1:)
+2. [Redis分布式锁(推荐 :+1:)](Redis/Redis分布式锁.md)
+4. [缓存穿透、缓存雪崩、缓存击穿](Redis/缓存穿透、缓存雪崩、缓存击穿.md)
# 框架
@@ -131,9 +131,9 @@
## RabbitMQ
-1. [消息队列面试题](中间件/消息队列面试题.md)
-1. [RabbitMQ核心知识总结](中间件/RabbitMQ.md) (推荐 :+1:)
-2. [死信队列](中间件/死信队列.md)
+1. [消息队列面试题](消息队列/消息队列面试题.md)
+1. [RabbitMQ核心知识总结](消息队列/RabbitMQ.md) (推荐 :+1:)
+2. [死信队列](消息队列/死信队列.md)
# 计算机网络
@@ -180,7 +180,7 @@
如果想进**技术、面试交流群**,可以扫描下方二维码加我微信,**备注加群**,我拉你进群,群里有BAT大佬,互相学习~
-

+
@@ -191,7 +191,7 @@
| 微信 | 支付宝 |
| ----------------------------------------------------------- | ------------------------------------------------------------ |
-|  |  |
+|  |  |
每笔赞赏我会在下面记录下来,感谢你们,我会更加努力,砥砺前行~
diff --git "a/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md" "b/Redis/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
similarity index 98%
rename from "\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
rename to "Redis/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
index 128b9a4..a3b0f32 100644
--- "a/\344\270\255\351\227\264\344\273\266/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
+++ "b/Redis/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md"
@@ -428,7 +428,7 @@ ziplist是 Redis 为了节约内存而开发的, 由一系列特殊编码的
- 最底层的链表包含所有的元素
- 跳跃表的查找次数近似于层数,时间复杂度为O(logn),插入、删除也为 O(logn)
-
+
#### 对象
@@ -470,7 +470,7 @@ hash类型内部编码有两种:
Redis3.2版本提供了quicklist内部编码,简单地说它是以一个ziplist为节点的linkedlist,它结合了ziplist和linkedlist两者的优势,为列表类型提供了一种更为优秀的内部编码实现。
-
+
使用场景:
@@ -555,7 +555,7 @@ GET #返回文章ID。
3. EXEC命令进行提交事务
-
+
DISCARD:放弃事务,即该事务内的所有命令都将取消
@@ -578,7 +578,7 @@ QUEUED
事务里的命令执行时会读取最新的值:
-
+
### WATCH命令
@@ -725,7 +725,7 @@ SLAVEOF NO ONE //停止接收其他数据库的同步并转化为主数据库。
5. 同步数据集。第一次同步的时候,从数据库启动后会向主数据库发送SYNC命令。主数据库接收到命令后开始在后台保存快照(RDB持久化过程),并将保存快照过程接收到的命令缓存起来。当快照完成后,Redis会将快照文件和缓存的命令发送到从数据库。从数据库接收到后,会载入快照文件并执行缓存的命令。以上过程称为复制初始化。
6. 复制初始化完成后,主数据库每次收到写命令就会将命令同步给从数据库,从而实现主从数据库数据的一致性。
-
+
Redis在2.8及以上版本使用psync命令完成主从数据同步,同步过程分为:全量复制和部分复制。
@@ -857,7 +857,7 @@ redis 127.0.0.1:6379> EVALSHA "232fd51614574cf0867b83d384a5e898cfd24e5a" 0
使用evalsha执行Lua脚本过程如下:
-
+
### lua脚本作用
@@ -1000,7 +1000,7 @@ public void write(String key,Object data){
解决方法:
-
+
> 图片来源:https://tech.it168.com
diff --git "a/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md" "b/Redis/Redis\351\235\242\350\257\225\351\242\230.md"
similarity index 98%
rename from "\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
rename to "Redis/Redis\351\235\242\350\257\225\351\242\230.md"
index 4db94a9..b9a05f4 100644
--- "a/\344\270\255\351\227\264\344\273\266/Redis\351\235\242\350\257\225\351\242\230.md"
+++ "b/Redis/Redis\351\235\242\350\257\225\351\242\230.md"
@@ -120,7 +120,7 @@ scan的缺点:在scan的过程中如果有键的变化(增加、删除、修
3. EXEC命令进行提交事务
-
+
一个事务范围内某个命令出错不会影响其他命令的执行,不保证原子性:
@@ -191,7 +191,7 @@ Redis支持两种方式的持久化,一种是`RDB`的方式,一种是`AOF`
`bgsave`是主流的触发 RDB 持久化的方式,执行过程如下:
-
+
- 执行`BGSAVE`命令
- Redis 父进程判断当前**是否存在正在执行的子进程**,如果存在,`BGSAVE`命令直接返回。
@@ -236,7 +236,7 @@ appendfsync no //由操作系统决定何时进行同步操作
接下来看一下 AOF 持久化执行流程:
-
+
1. 所有的写入命令会追加到 AOP 缓冲区中。
2. AOF 缓冲区根据对应的策略向硬盘同步。
@@ -296,7 +296,7 @@ Redis的复制功能是支持多个数据库之间的数据同步。主数据库
客户端连接Redis的时候,先连接哨兵,哨兵会告诉客户端Redis主节点的地址,然后客户端连接上Redis并进行后续的操作。当主节点宕机的时候,哨兵监测到主节点宕机,会重新推选出某个表现良好的从节点成为新的主节点,然后通过发布订阅模式通知其他的从服务器,让它们切换主机。
-
+
**工作原理**
@@ -315,7 +315,7 @@ Redis cluster集群节点最小配置6个节点以上(3主3从),其中主
Redis cluster采用**虚拟槽分区**,所有的键根据哈希函数映射到0~16383个整数槽内,每个节点负责维护一部分槽以及槽所映射的键值数据。
-
+
**工作原理:**
@@ -607,4 +607,4 @@ Redis 官方站提出了一种权威的基于 Redis 实现分布式锁的方式
-
+
diff --git "a/\344\270\255\351\227\264\344\273\266/\347\274\223\345\255\230\347\251\277\351\200\217\343\200\201\347\274\223\345\255\230\351\233\252\345\264\251\343\200\201\347\274\223\345\255\230\345\207\273\347\251\277.md" "b/Redis/\347\274\223\345\255\230\347\251\277\351\200\217\343\200\201\347\274\223\345\255\230\351\233\252\345\264\251\343\200\201\347\274\223\345\255\230\345\207\273\347\251\277.md"
similarity index 100%
rename from "\344\270\255\351\227\264\344\273\266/\347\274\223\345\255\230\347\251\277\351\200\217\343\200\201\347\274\223\345\255\230\351\233\252\345\264\251\343\200\201\347\274\223\345\255\230\345\207\273\347\251\277.md"
rename to "Redis/\347\274\223\345\255\230\347\251\277\351\200\217\343\200\201\347\274\223\345\255\230\351\233\252\345\264\251\343\200\201\347\274\223\345\255\230\345\207\273\347\251\277.md"
diff --git "a/\345\210\206\345\270\203\345\274\217/\345\205\250\345\261\200\345\224\257\344\270\200ID.md" "b/\345\210\206\345\270\203\345\274\217/\345\205\250\345\261\200\345\224\257\344\270\200ID.md"
index 59b6f58..0871efa 100644
--- "a/\345\210\206\345\270\203\345\274\217/\345\205\250\345\261\200\345\224\257\344\270\200ID.md"
+++ "b/\345\210\206\345\270\203\345\274\217/\345\205\250\345\261\200\345\224\257\344\270\200ID.md"
@@ -1,6 +1,6 @@
传统的单体架构的时候,我们基本是单库然后业务单表的结构。每个业务表的ID一般我们都是从1增,通过`AUTO_INCREMENT=1`设置自增起始值,但是在分布式服务架构模式下分库分表的设计,使得多个库或多个表存储相同的业务数据。这种情况根据数据库的自增ID就会产生相同ID的情况,不能保证主键的唯一性。
-
+
如上图,如果第一个订单存储在 DB1 上则订单 ID 为1,当一个新订单又入库了存储在 DB2 上订单 ID 也为1。我们系统的架构虽然是分布式的,但是在用户层应是无感知的,重复的订单主键显而易见是不被允许的。那么针对分布式系统如何做到主键唯一性呢?
@@ -65,7 +65,7 @@ public static void main(String[] args) {
假设有三台机器,则DB1中order表的起始ID值为1,DB2中order表的起始值为2,DB3中order表的起始值为3,它们自增的步长都为3,则它们的ID生成范围如下图所示:
-
+
通过这种方式明显的优势就是依赖于数据库自身不需要其他资源,并且ID号单调自增,可以实现一些对ID有特殊要求的业务。
@@ -94,7 +94,7 @@ Snowflake,雪花算法是由Twitter开源的分布式ID生成算法,以划
这样的划分之后相当于在一毫秒一个数据中心的一台机器上可产生4096个有序的不重复的ID。但是我们 IDC 和机器数肯定不止一个,所以毫秒内能生成的有序ID数是翻倍的。
-
+
Snowflake 的[Twitter官方原版](https://github.com/twitter/snowflake/blob/snowflake-2010/src/main/scala/com/twitter/service/snowflake/IdWorker.scala)是用Scala写的,对Scala语言有研究的同学可以去阅读下,以下是 Java 版本的写法。
@@ -303,7 +303,7 @@ UidGenerator 依然是以划分命名空间的方式将 64-bit位分割成多个
- 中间的 workId (数据中心+工作机器,可以其他组成方式)则由 22-bit位组成,可表示 2^22 = 4194304个工作ID。
- 最后由13-bit位构成自增序列,可表示2^13 = 8192个数。
-
+
其中 workId (机器 id),最多可支持约420w次机器启动。内置实现为在启动时由数据库分配(表名为 WORKER_NODE),默认分配策略为用后即弃,后续可提供复用策略。
@@ -427,7 +427,7 @@ CREATE TABLE `leaf_alloc` (
原来获取ID每次都需要写数据库,现在只需要把step设置得足够大,比如1000。那么只有当1000个号被消耗完了之后才会去重新读写一次数据库。读写数据库的频率从1减小到了1/step,大致架构如下图所示:
-
+
同时Leaf-segment 为了解决 TP999(满足千分之九百九十九的网络请求所需要的最低耗时)数据波动大,当号段使用完之后还是会hang在更新数据库的I/O上,TP999 数据会出现偶尔的尖刺的问题,提供了双buffer优化。
@@ -435,7 +435,7 @@ CREATE TABLE `leaf_alloc` (
为了DB取号段的过程能够做到无阻塞,不需要在DB取号段的时候阻塞请求线程,即当号段消费到某个点时就异步的把下一个号段加载到内存中,而不需要等到号段用尽的时候才去更新号段。这样做就可以很大程度上的降低系统的 TP999 指标。详细实现如下图所示:
-
+
采用双buffer的方式,Leaf服务内部有两个号段缓存区segment。当前号段已下发10%时,如果下一个号段未更新,则另启一个更新线程去更新下一个号段。当前号段全部下发完后,如果下个号段准备好了则切换到下个号段为当前segment接着下发,循环往复。
@@ -454,13 +454,13 @@ Leaf-snowflake是按照下面几个步骤启动的:
- 如果有注册过直接取回自己的workerID(zk顺序节点生成的int类型ID号),启动服务。
- 如果没有注册过,就在该父节点下面创建一个持久顺序节点,创建成功后取回顺序号当做自己的workerID号,启动服务。
-
+
为了减少对 Zookeeper的依赖性,会在本机文件系统上缓存一个workerID文件。当ZooKeeper出现问题,恰好机器出现问题需要重启时,能保证服务能够正常启动。
上文阐述过在类 snowflake算法上都存在时钟回拨的问题,Leaf-snowflake在解决时钟回拨的问题上是通过校验自身系统时间与 `leaf_forever/${self}`节点记录时间做比较然后启动报警的措施。
-
+
美团官方建议是由于强依赖时钟,对时间的要求比较敏感,在机器工作时NTP同步也会造成秒级别的回退,建议可以直接关闭NTP同步。要么在时钟回拨的时候直接不提供服务直接返回ERROR_CODE,等时钟追上即可。或者做一层重试,然后上报报警系统,更或者是发现有时钟回拨之后自动摘除本身节点并报警。
diff --git "a/\345\210\206\345\270\203\345\274\217/\345\276\256\346\234\215\345\212\241.md" "b/\345\210\206\345\270\203\345\274\217/\345\276\256\346\234\215\345\212\241.md"
index 7f52c58..c48afc0 100644
--- "a/\345\210\206\345\270\203\345\274\217/\345\276\256\346\234\215\345\212\241.md"
+++ "b/\345\210\206\345\270\203\345\274\217/\345\276\256\346\234\215\345\212\241.md"
@@ -137,7 +137,7 @@
更好的方式是采用API网关(也叫做服务网关),实现一个API网关**接管所有的入口流量**,类似Nginx的作用,将所有用户的请求转发给后端的服务器,但网关做的不仅仅只是简单的转发,也会针对流量做一些扩展,比如鉴权、限流、权限、熔断、协议转换、错误码统一、缓存、日志、监控、告警等,这样将通用的逻辑抽出来,由网关统一去做,业务方也能够更专注于业务逻辑,提升迭代的效率。
-
+
通过引入API网关,客户端只需要与API网关交互,而不用与各个业务方的接口分别通讯,但多引入一个组件就多引入了一个潜在的故障点,因此要实现一个高性能、稳定的网关,也会涉及到很多点。
@@ -145,7 +145,7 @@
**服务网关基本功能**:
-
+
- 智能路由:接收**外部**一切请求,并转发到后端的对外服务。注意:我们只转发外部请求,服务之间的请求不走网关,这就表示全链路追踪、内部服务API监控、内部服务之间调用的容错、智能路由不能在网关完成;当然,也可以将所有的服务调用都走网关,那么几乎所有的功能都可以集成到网关中,但是这样的话,网关的压力会很大,不堪重负。
- 权限校验:网关可以做一些用户身份认证,权限认证,防止非法请求操作API 接口,对内部服务起到保护作用
diff --git "a/\345\210\206\345\270\203\345\274\217/\350\277\234\347\250\213\350\260\203\347\224\250.md" "b/\345\210\206\345\270\203\345\274\217/\350\277\234\347\250\213\350\260\203\347\224\250.md"
index 4f38569..beade88 100644
--- "a/\345\210\206\345\270\203\345\274\217/\350\277\234\347\250\213\350\260\203\347\224\250.md"
+++ "b/\345\210\206\345\270\203\345\274\217/\350\277\234\347\250\213\350\260\203\347\224\250.md"
@@ -22,7 +22,7 @@ RPC是一个完整的远程调用方案,它通常包括通信协议和序列
> stub说的都是“一小块代码”,通常是有个caller要调用callee的时候,中间需要一些特殊处理的逻辑,就会用这种“小块代码”去做。
-
+
1. 服务消费端(client)以本地调用的方式调用远程服务;
2. 客户端 Stub(client stub) 接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体(序列化):`RpcRequest`;
diff --git "a/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md" "b/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md"
index a125d4b..d78291f 100644
--- "a/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md"
+++ "b/\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md"
@@ -22,15 +22,15 @@ Git又是什么呢?简单的说,**Git 是一个管理你的代码历史记
- 创建一个新的仓库,用来存放项目。
- 
+ 
- 或者你在GitHub上看到别人有一个超级无敌有趣的项目,可以直接fork过来,可以理解成复制过来,变成你自己的。之后你想怎么改就怎么改!
- 
+ 
- 然后你可以通过Git命令行`git clone xxx`把项目clone到本地,在本地进行创作。
- 
+ 
- 最后,在本地创作完成,可以使用`git commit -m xxx`提交到本地库,然后使用`git push`把修改推送到GitHub仓库。之后就可以在GitHub上面看到你修改的内容啦~
@@ -42,23 +42,23 @@ Git又是什么呢?简单的说,**Git 是一个管理你的代码历史记
- 评价GitHub项目的两个重要的参数:star和fork。
-
+
比较优秀和热门的项目,star数目和fork数目都会比较多。我们可以根据这两个参数筛选出比较好的项目。使用`关键字 stars:>=xxx forks:>=xxx` 可以筛选出star和fork数目大于xxx的相关项目。
-
+
- 使用 `awesome 关键字`,可以筛选出比较高质量的学习、书籍、工具类或者插件类的集合。
-
+
- 在特定位置搜索关键词。有些关键词出现在项目的不同位置,比如项目名称、项目描述和README等。使用`关键词 in name/description/Readme`,可以搜索到相关的内容。比如使用`spring in name`,可以搜索到在项目名中包含spring的项目。
-
+
- 指定条件搜索关键词。如`tool language:java`搜索到的是包含关键字tool的Java项目,`tool followers:>1000`可以搜索到包含关键字tool,且follower数量大于1000的项目。
-
+
@@ -72,7 +72,7 @@ Git又是什么呢?简单的说,**Git 是一个管理你的代码历史记
- 文档神器。可以为自己的项目建立wiki,可以用markdown语法写wiki;
-
+
- 使用GitHub pages建立个人静态网站,搞一个有自己域名的独立博客,想想都觉得开心。使用GitHub pages的好处是搭建简单而且免费,支持静态脚本,并且可以绑定自己的域名。具体可以参考:[GitHub Pages 建立个人网站详细教程 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/58229299)
diff --git "a/\345\267\245\345\205\267/progit2.md" "b/\345\267\245\345\205\267/progit2.md"
index 68b8c2b..a79a027 100644
--- "a/\345\267\245\345\205\267/progit2.md"
+++ "b/\345\267\245\345\205\267/progit2.md"
@@ -85,7 +85,7 @@ Git工作流程如下:
Git 的工作流程图如下:
-
+
> 图片来源:https://blog.csdn.net/ThinkWon/article/details/94346816
@@ -103,11 +103,11 @@ Git 的三种状态:已修改(modified)、已暂存(staged)和已提
基本的 Git 工作流程:在工作目录修改文件;暂存文件,将文件快照放到暂存区域;提交更新到本地库。暂存区保存了下次将要提交的文件列表信息,一般在 Git 仓库目录中。
-
+
> 图片来源:`https://img2018.cnblogs.com/blog/1252910/201907/1252910-20190726163829113-2056815874.png`
-
+
## 配置
@@ -156,7 +156,7 @@ git clone https://github.com/...
查看文件状态:`git status`
-
+
> 图片来源:`https://img2018.cnblogs.com/blog/1252910/201907/1252910-20190726163854195-886320537.png`
@@ -262,7 +262,7 @@ git commit -m "add readme.md"
单独执行`git commit`,不带上-m参数,会进入 vim 编辑器界面:
-
+
此时应该这么操作:
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\345\237\272\347\241\200.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\345\237\272\347\241\200.md"
index 13d9ab1..8c66844 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\345\237\272\347\241\200.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\345\237\272\347\241\200.md"
@@ -361,7 +361,7 @@ WHERE Soundex(cust_contact) = Soundex('Y Lie');
### 日期处理函数
-
+
查找2005年9月的所有订单:
```mysql
@@ -380,7 +380,7 @@ WHERE Year(order_date) = 2005 AND Month(order_date) = 9;
### 数值处理函数
-
+
## 汇总数据
@@ -415,7 +415,7 @@ SELECT * FROM orders
GROUP BY cust_id;
```
-
+
除聚集计算语句外,SELECT语句中的每个列都必须在GROUP BY子句中给出。
@@ -534,7 +534,7 @@ SELECT * FROM role NATURAL JOIN user_role;
返回结果:
-
+
### 内连接
@@ -546,7 +546,7 @@ SELECT * FROM role INNNER JOIN user_role
返回结果:
-
+
join…using(column)按指定的属性做等值连接。
join…on tableA.column1 = tableB.column2 指定条件。
@@ -557,7 +557,7 @@ SELECT * FROM role INNER JOIN user_role ON role.role_id = user_role.role_id
返回结果:
-
+
### 外连接
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md"
index e9f255a..eaffa77 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md"
@@ -91,7 +91,7 @@ where blog_id = (
三个表依次嵌套,发现最里层的子查询 `id`最大,最先执行。
-
+
## select_type
@@ -107,13 +107,13 @@ where blog_id = (
查询的表名,并不一定是真实存在的表,有别名显示别名,也可能为临时表。当from子句中有子查询时,table列是 `
`的格式,表示当前查询依赖 id为N的查询,会先执行 id为N的查询。
-
+
## partitions
查询时匹配到的分区信息,对于非分区表值为`NULL`,当查询的是分区表时,`partitions`显示分区表命中的分区情况。
-
+
## type
@@ -123,25 +123,25 @@ where blog_id = (
当表仅有一行记录时(系统表),数据量很少,往往不需要进行磁盘IO,速度非常快。比如,Mysql系统表proxies_priv在Mysql服务启动时候已经加载在内存中,对这个表进行查询不需要进行磁盘 IO。
-
+
### const
单表操作的时候,查询使用了主键或者唯一索引。
-
+
### eq_ref
**多表关联**查询的时候,主键和唯一索引作为关联条件。如下图的sql,对于user表(外循环)的每一行,user_role表(内循环)只有一行满足join条件,只要查找到这行记录,就会跳出内循环,继续外循环的下一轮查询。
-
+
### ref
查找条件列使用了索引而且不为主键和唯一索引。虽然使用了索引,但该索引列的值并不唯一,这样即使使用索引查找到了第一条数据,仍然不能停止,要在目标值附近进行小范围扫描。但它的好处是不需要扫全表,因为索引是有序的,即便有重复值,也是在一个非常小的范围内做扫描。
-
+
### ref_or_null
@@ -151,13 +151,13 @@ where blog_id = (
使用了索引合并优化方法,查询使用了两个以上的索引。新建comment表,id为主键,value_id为非唯一索引,执行`explain select content from comment where value_id = 1181000 and id > 1000;`,执行结果显示查询同时使用了id和value_id索引,type列的值为index_merge。
-
+
### range
有范围的索引扫描,相对于index的全索引扫描,它有范围限制,因此要优于index。像between、and、'>'、'<'、in和or都是范围索引扫描。
-
+
### index
@@ -165,17 +165,17 @@ index包括select索引列,order by主键两种情况。
1. order by主键。这种情况会按照索引顺序全表扫描数据,拿到的数据是按照主键排好序的,不需要额外进行排序。
- 
+ 
2. select索引列。type为index,而且extra字段为using index,也称这种情况为索引覆盖。所需要取的数据都在索引列,无需回表查询。
- 
+ 
### all
全表扫描,查询没有用到索引,性能最差。
-
+
## possible_keys
@@ -249,13 +249,13 @@ CREATE TABLE `t_orderdetail` (
查询的列未被索引覆盖,where筛选条件非索引的前导列。对存储引擎返回的结果进行过滤(Post-filter,后过滤),一般发生在MySQL服务器,而不是存储引擎层。
-
+
### using index
查询的列被索引覆盖,并且where筛选条件符合最左前缀原则,通过**索引查找**就能直接找到符合条件的数据,不需要回表查询数据。
-
+
### Using where&Using index
@@ -265,17 +265,17 @@ CREATE TABLE `t_orderdetail` (
- where筛选条件不符合最左前缀原则
- 
+ 
- where筛选条件是索引列前导列的一个范围
- 
+ 
### null
查询的列未被索引覆盖,并且where筛选条件是索引的前导列,也就是用到了索引,但是部分字段未被索引覆盖,必须回表查询这些字段,Extra中为NULL。
-
+
### using index condition
@@ -283,11 +283,11 @@ CREATE TABLE `t_orderdetail` (
不使用ICP的情况(`set optimizer_switch='index_condition_pushdown=off'`),如下图,在步骤4中,没有使用where条件过滤索引:
-
+
使用ICP的情况(`set optimizer_switch='index_condition_pushdown=on'`):
-
+
下面的例子使用了ICP:
@@ -296,11 +296,11 @@ explain select user_id, order_id, order_status
from t_order where user_id > 1 and user_id < 5\G;
```
-
+
关掉ICP之后(`set optimizer_switch='index_condition_pushdown=off'`),可以看到extra列为using where,不会使用索引下推。
-
+
### using temporary
@@ -314,7 +314,7 @@ from t_order where user_id > 1 and user_id < 5\G;
- select 查询字段不全是索引字段
- select 查询字段都是索引字段,但是 order by 字段和索引字段的顺序不一致
-
+
### using join buffer
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md"
index 7caa4e5..d66e18c 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md"
@@ -138,7 +138,7 @@ B+ 树是基于B 树和叶子节点顺序访问指针进行实现,它具有B
在 B+ 树中,节点中的 key 从左到右递增排列,如果某个指针的左右相邻 key 分别是 keyi 和 keyi+1,则该指针指向节点的所有 key 大于等于 keyi 且小于等于 keyi+1。
-
+
进行查找操作时,首先在根节点进行二分查找,找到key所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出 key 所对应的数据项。
@@ -170,15 +170,15 @@ Index_comment:
如下图,col1 是主键,col2和col3是普通字段。
-
+
下图是主键索引对应的 B+树结构,每个节点对应磁盘的一页。
-
+
对col3 建立一个单列索引,对应的B+树结构:
-
+
### 索引分类
1. 主键索引:名为primary的唯一非空索引,不允许有空值。
@@ -208,7 +208,7 @@ Index_comment:
对(a, b) 建立索引,a 在索引树中是全局有序的,而 b 是全局无序,局部有序(当a相等时,会对b进行比较排序)。直接执行`b = 2`这种查询条件没有办法利用索引。
-
+
从局部来看,当a的值确定的时候,b是有序的。例如a = 1时,b值为1,2是有序的状态。当a=2时候,b的值为1,4也是有序状态。 因此,你执行`a = 1 and b = 2`是a,b字段能用到索引的。而你执行`a > 1 and b = 2`时,a字段能用到索引,b字段用不到索引。因为a的值此时是一个范围,不是固定的,在这个范围内b值不是有序的,因此b字段用不上索引。
@@ -218,7 +218,7 @@ InnoDB使用表的主键构造主键索引树,同时叶子节点中存放的
聚集索引的叶子节点就是整张表的行记录。InnoDB 主键使用的是聚簇索引。聚集索引要比非聚集索引查询效率高很多。
-
+
对于InnoDB来说,聚集索引一般是表中的主键索引,如果表中没有显示指定主键,则会选择表中的第一个不允许为NULL的唯一索引。如果没有主键也没有合适的唯一索引,那么innodb内部会生成一个隐藏的主键作为聚集索引,这个隐藏的主键长度为6个字节,它的值会随着数据的插入自增。
@@ -244,7 +244,7 @@ explain select user_id from user_like where blog_id = 1;
Extra中为`Using where; Using index`, 查询的列被索引覆盖,where筛选条件不符合最左前缀原则,无法通过索引查找找到符合条件的数据,但可以通过**索引扫描**找到符合条件的数据,也不需要回表查询数据。
-
+
### 索引失效
@@ -322,7 +322,7 @@ MEMORY引擎默认使用哈希索引,将键的哈希值和指向数据行的
myisam引擎主键索引和其他索引区别不大,叶子节点都包含索引值和行指针。
innodb引擎二级索引叶子存储的是索引值和主键值(不是行指针),这样可以减少行移动和数据页分裂时二级索引的维护工作。
- 
+ 
@@ -351,7 +351,7 @@ mvcc实现依赖于版本链,版本链是通过表的三个隐藏字段实现
3. 修改当前行的值,生成一个新版本,更新事务id,使回滚指针指向旧版本的记录,这样就形成一条版本链;
4. 记录redo log;
-
+
### read view
@@ -388,14 +388,14 @@ repeatable read:在一个事务范围内,第一次select时更新这个read_
事务a和事务b同时开启事务,事务a插入数据然后提交,事务b执行全表的update,然后执行查询,查到了事务A中添加的数据。
-
+
MySQL如何实现避免幻读:
- 在快照读情况下,MySQL通过mvcc来避免幻读。
- 在当前读情况下,MySQL通过next-key来避免幻读(加行锁和间隙锁来实现的)。
-
+
next-key包括两部分:行锁和间隙锁。行锁是加在索引上的锁,间隙锁是加在索引之间的。
@@ -531,7 +531,7 @@ MySQL主要分为 Server 层和存储引擎层:
- **Server 层**:主要包括连接器、查询缓存、分析器、优化器、执行器等,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图,函数等,还有一个通用的日志模块 binglog 日志模块。
- **存储引擎**: 主要负责数据的存储和读取。server 层通过api与存储引擎进行通信。
-
+
### Server 层基本组件
diff --git "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
index 6f12608..123d7cc 100644
--- "a/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
@@ -132,7 +132,7 @@ utf8 就像是阉割版的utf8mb4,只支持部分字符。比如`emoji`表情
通过`SHOW COLLATION WHERE Charset = 'utf8mb4';`可以查看到`utf8mb4`下支持什么比较规则。
-
+
如果`collation = utf8mb4_general_ci`,是指使用utf8mb4字符集的前提下,**挨个字符进行比较**(`general`),并且不区分大小写(`_ci,case insensitice`)。
@@ -201,7 +201,7 @@ B+ 树是基于B 树和叶子节点顺序访问指针进行实现,它具有B
在 B+ 树中,节点中的 `key` 从左到右递增排列,如果某个指针的左右相邻 `key` 分别是 keyi 和 keyi+1,则该指针指向节点的所有 `key` 大于等于 keyi 且小于等于 keyi+1。
-
+
进行查找操作时,首先在根节点进行二分查找,找到`key`所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出`key`所对应的数据项。
@@ -273,7 +273,7 @@ ADD CONSTRAINT constraint_name UNIQUE KEY(column_1,column_2,...);
如下图,对(a, b) 建立索引,a 在索引树中是全局有序的,而 b 是全局无序,局部有序(当a相等时,会根据b进行排序)。直接执行`b = 2`这种查询条件无法使用索引。
-
+
当a的值确定的时候,b是有序的。例如`a = 1`时,b值为1,2是有序的状态。当`a = 2`时候,b的值为1,4也是有序状态。 当执行`a = 1 and b = 2`时a和b字段能用到索引。而执行`a > 1 and b = 2`时,a字段能用到索引,b字段用不到索引。因为a的值此时是一个范围,不是固定的,在这个范围内b值不是有序的,因此b字段无法使用索引。
@@ -307,7 +307,7 @@ explain select user_id from user_like where blog_id = 1;
`explain`结果的`Extra`列为`Using where; Using index`, 查询的列被索引覆盖,where筛选条件不符合最左前缀原则,无法通过索引查找找到符合条件的数据,但可以通过**索引扫描**找到符合条件的数据,也不需要回表查询数据。
-
+
### 索引的设计原则?
@@ -417,7 +417,7 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
每条表记录大概是这样的:
-
+
使用事务更新行记录的时候,就会生成版本链,执行过程如下:
@@ -429,15 +429,15 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
1、初始数据如下,其中`DB_ROW_ID`和`DB_ROLL_PTR`为空。
-
+
2、事务A对该行数据做了修改,将`age`修改为12,效果如下:
-
+
3、之后事务B也对该行记录做了修改,将`age`修改为8,效果如下:
-
+
4、此时undo log有两行记录,并且通过回滚指针连在一起。
@@ -457,7 +457,7 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
**前提**:`DATA_TRX_ID` 表示每个数据行的最新的事务ID;`up_limit_id`表示当前快照中的最先开始的事务;`low_limit_id`表示当前快照中的最慢开始的事务,即最后一个事务。
-
+
- 如果`DATA_TRX_ID` < `up_limit_id`:说明在创建`read view`时,修改该数据行的事务已提交,该版本的记录可被当前事务读取到。
- 如果`DATA_TRX_ID` >= `low_limit_id`:说明当前版本的记录的事务是在创建`read view`之后生成的,该版本的数据行不可以被当前事务访问。此时需要通过版本链找到上一个版本,然后重新判断该版本的记录对当前事务的可见性。
@@ -482,7 +482,7 @@ MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实
1、首先,user表只有两条记录,具体如下:
-
+
2、事务a和事务b同时开启事务`start transaction`;
@@ -500,7 +500,7 @@ update user set user_name = 'a';
5、事务b然后执行查询,查到了事务a中插入的数据。(下图左边是事务b,右边是事务a。事务开始之前只有两条记录,事务a插入一条数据之后,事务b查询出来是三条数据)
-
+
以上就是当前读出现的幻读现象。
@@ -579,7 +579,7 @@ MySQL主要分为 Server 层和存储引擎层:
垂直划分数据库是根据业务进行划分,例如购物场景,可以将库中涉及商品、订单、用户的表分别划分出成一个库,通过降低单库的大小来提高性能。同样的,分表的情况就是将一个大表根据业务功能拆分成一个个子表,例如商品基本信息和商品描述,商品基本信息一般会展示在商品列表,商品描述在商品详情页,可以将商品基本信息和商品描述拆分成两张表。
-
+
**优点**:行记录变小,数据页可以存放更多记录,在查询时减少I/O次数。
@@ -593,7 +593,7 @@ MySQL主要分为 Server 层和存储引擎层:
水平划分是根据一定规则,例如时间或id序列值等进行数据的拆分。比如根据年份来拆分不同的数据库。每个数据库结构一致,但是数据得以拆分,从而提升性能。
-
+
**优点**:单库(表)的数据量得以减少,提高性能;切分出的表结构相同,程序改动较少。
@@ -925,4 +925,4 @@ B+树中**非叶子节点存的是key + 指针**;**叶子节点存的是数据
-
+
diff --git "a/\346\241\206\346\236\266/Mybatis\351\235\242\350\257\225\351\242\230.md" "b/\346\241\206\346\236\266/Mybatis\351\235\242\350\257\225\351\242\230.md"
index 49f4bc0..5dddfd8 100644
--- "a/\346\241\206\346\236\266/Mybatis\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\241\206\346\236\266/Mybatis\351\235\242\350\257\225\351\242\230.md"
@@ -152,4 +152,4 @@ mybatis底层使用`PreparedStatement`,默认情况下,将对所有的 sql
-
+
diff --git "a/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" "b/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
index 6eba065..7e70a4c 100644
--- "a/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
+++ "b/\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
@@ -28,7 +28,7 @@ SpringBoot实现自动配置原理图解:
> 公众号【程序员大彬】,回复【自动配置】下载高清图片
-
+
在 application.properties 中设置属性 debug=true,可以在控制台查看已启用和未启用的自动配置。
@@ -297,4 +297,4 @@ hello.msg=大彬
-
+
diff --git "a/\346\241\206\346\236\266/SpringCloud\345\276\256\346\234\215\345\212\241\345\256\236\346\210\230.md" "b/\346\241\206\346\236\266/SpringCloud\345\276\256\346\234\215\345\212\241\345\256\236\346\210\230.md"
index 7a75bb4..aedbd7f 100644
--- "a/\346\241\206\346\236\266/SpringCloud\345\276\256\346\234\215\345\212\241\345\256\236\346\210\230.md"
+++ "b/\346\241\206\346\236\266/SpringCloud\345\276\256\346\234\215\345\212\241\345\256\236\346\210\230.md"
@@ -1485,7 +1485,7 @@ OAuth在"客户端"(云冲印)与"服务提供商"(谷歌)之间,设
"客户端"登录授权层以后,"服务提供商"根据令牌的权限范围和有效期,向"客户端"开放用户储存的资料。
-
+
(A)用户打开客户端以后,客户端要求用户给予授权。
diff --git "a/\346\241\206\346\236\266/SpringMVC\351\235\242\350\257\225\351\242\230.md" "b/\346\241\206\346\236\266/SpringMVC\351\235\242\350\257\225\351\242\230.md"
index 82aca97..0892077 100644
--- "a/\346\241\206\346\236\266/SpringMVC\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\241\206\346\236\266/SpringMVC\351\235\242\350\257\225\351\242\230.md"
@@ -86,4 +86,4 @@ Spring MVC的工作原理如下:
-
+
diff --git "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md" "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
index 697a649..82c11b5 100644
--- "a/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
+++ "b/\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md"
@@ -184,7 +184,7 @@ finishBeanFactoryInitialization(beanFactory);
## Bean的生命周期
-
+
1.调用bean的构造方法创建Bean
@@ -272,29 +272,124 @@ Spring 将会在应用启动时创建 `SqlSessionFactory`,并使用 `sqlSessio
## Bean注入容器有哪些方式?
-将普通类交给Spring容器管理,通常有以下方法:
+1、@Configuration + @Bean
-1、使用`@Configuration`与`@Bean`注解
+@Configuration用来声明一个配置类,然后使用 @Bean 注解,用于声明一个bean,将其加入到Spring容器中。
-2、使用`@Controller`、`@Service`、`@Repository`、`@Component` 注解标注该类,然后启用`@ComponentScan`自动扫描
+```java
+@Configuration
+public class MyConfiguration {
+ @Bean
+ public Person person() {
+ Person person = new Person();
+ person.setName("大彬");
+ return person;
+ }
+}
+```
+
+2、通过包扫描特定注解的方式
+
+@ComponentScan放置在我们的配置类上,然后可以指定一个路径,进行扫描带有特定注解的bean,然后加至容器中。
+
+特定注解包括@Controller、@Service、@Repository、@Component
+
+```java
+@Component
+public class Person {
+ //...
+}
+
+@ComponentScan(basePackages = "com.dabin.test.*")
+public class Demo1 {
+ public static void main(String[] args) {
+ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
+ Person bean = applicationContext.getBean(Person.class);
+ System.out.println(bean);
+ }
+}
+```
+
+3、@Import注解导入
-3、使用`@Import` 方法。使用@Import注解把bean导入到当前容器中,代码如下:
+@Import注解平时开发用的不多,但是也是非常重要的,在进行Spring扩展时经常会用到,它经常搭配自定义注解进行使用,然后往容器中导入一个配置文件。
```java
-//@SpringBootApplication
@ComponentScan
/*把用到的资源导入到当前容器中*/
-@Import({Dog.class, Cat.class})
+@Import({Person.class})
public class App {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
- System.out.println(context.getBean(Dog.class));
- System.out.println(context.getBean(Cat.class));
+ System.out.println(context.getBean(Person.class));
context.close();
}
}
```
+4、实现BeanDefinitionRegistryPostProcessor进行后置处理。
+
+在Spring容器启动的时候会执行 BeanDefinitionRegistryPostProcessor 的 postProcessBeanDefinitionRegistry 方法,就是等beanDefinition加载完毕之后,对beanDefinition进行后置处理,可以在此进行调整IOC容器中的beanDefinition,从而干扰到后面进行初始化bean。
+
+在下面的代码中,我们手动向beanDefinitionRegistry中注册了person的BeanDefinition。最终成功将person加入到applicationContext中。
+
+```java
+public class Demo1 {
+ public static void main(String[] args) {
+ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
+ MyBeanDefinitionRegistryPostProcessor beanDefinitionRegistryPostProcessor = new MyBeanDefinitionRegistryPostProcessor();
+ applicationContext.addBeanFactoryPostProcessor(beanDefinitionRegistryPostProcessor);
+ applicationContext.refresh();
+ Person bean = applicationContext.getBean(Person.class);
+ System.out.println(bean);
+ }
+}
+
+class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
+ @Override
+ public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
+ AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();
+ registry.registerBeanDefinition("person", beanDefinition);
+ }
+
+ @Override
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+ }
+}
+```
+
+5、使用FactoryBean接口
+
+如下图代码,使用@Configuration + @Bean的方式将 PersonFactoryBean 加入到容器中,这里没有向容器中直接注入 Person,而是注入 PersonFactoryBean,然后从容器中拿Person这个类型的bean。
+
+```java
+@Configuration
+public class Demo1 {
+ @Bean
+ public PersonFactoryBean personFactoryBean() {
+ return new PersonFactoryBean();
+ }
+
+ public static void main(String[] args) {
+ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
+ Person bean = applicationContext.getBean(Person.class);
+ System.out.println(bean);
+ }
+}
+
+class PersonFactoryBean implements FactoryBean {
+ @Override
+ public Person getObject() throws Exception {
+ return new Person();
+ }
+
+ @Override
+ public Class> getObjectType() {
+ return Person.class;
+ }
+}
+```
+
## Bean的作用域
@@ -347,7 +442,7 @@ Spring的自动装配有三种模式:**byType**(根据类型),**byName**(根
## @Autowired和@Resource的区别?
-默认情况下@Autowired是按类型匹配的(byType)。如果需要按名称(byName)匹配的话,可以使用@Qualifier注解与@Autowired结合。@Autowired 可以传递一个`required=false`的属性,false指明当userDao实例存在就注入不存就忽略,如果为true,就必须注入,若userDao实例不存在,就抛出异常。
+Autowire是spring的注解。默认情况下@Autowired是按类型匹配的(byType)。如果需要按名称(byName)匹配的话,可以使用@Qualifier注解与@Autowired结合。@Autowired 可以传递一个`required=false`的属性,false指明当userDao实例存在就注入不存就忽略,如果为true,就必须注入,若userDao实例不存在,就抛出异常。
```java
public class UserServiceImpl implements UserService {
@@ -358,7 +453,7 @@ public class UserServiceImpl implements UserService {
}
```
-@Resource,默认按 byName模式自动注入。@Resource有两个中重要的属性:name和type。Spring容器对于@Resource注解的name属性解析为bean的名字,type属性则解析为bean的类型。因此使用name属性,则按byName模式的自动注入策略,如果使用type属性则按 byType模式自动注入策略。倘若既不指定name也不指定type属性,Spring容器将通过反射技术默认按byName模式注入。
+Resource是j2ee的注解,默认按 byName模式自动注入。@Resource有两个中重要的属性:name和type。name属性指定bean的名字,type属性则指定bean的类型。因此使用name属性,则按byName模式的自动注入策略,如果使用type属性,则按 byType模式自动注入策略。倘若既不指定name也不指定type属性,Spring容器将通过反射技术默认按byName模式注入。
```java
@Resource(name="userDao")
@@ -371,9 +466,11 @@ public void setUserDao(UserDao userDao) {
}
```
-上述两种自动装配的依赖注入并不适合简单值类型,如int、boolean、long、String以及Enum等,对于这些类型,Spring容器也提供了@Value注入的方式。@Value接收一个String的值,该值指定了将要被注入到内置的java类型属性值,Spring 容器会做好类型转换。一般情况下@Value会与properties文件结合使用。
+上述两种自动装配的依赖注入并不适合简单值类型,如int、boolean、long、String以及Enum等,对于这些类型,Spring容器也提供了@Value注入的方式。
+
+@Value和@Autowired、@Resource类似,也是用来对属性进行注入的,只不过@Value是用来从Properties文件中来获取值的,并且@Value可以解析SpEL(Spring表达式)。
-jdbc.properties文件如下:
+比如,jdbc.properties文件如下:
```properties
jdbc.driver=com.mysql.jdbc.Driver
@@ -478,7 +575,97 @@ public class WebSocketConfig {
使用`PROPAGATION_NESTED`时,外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。
+## Spring事务在什么情况下会失效?
+
+**1.访问权限问题**
+
+java的访问权限主要有四种:private、default、protected、public,它们的权限从左到右,依次变大。
+
+如果事务方法的访问权限不是定义成public,这样会导致事务失效,因为spring要求被代理方法必须是`public`的。
+翻开源码,可以看到,在`AbstractFallbackTransactionAttributeSource`类的`computeTransactionAttribute`方法中有个判断,如果目标方法不是public,则返回null,即不支持事务。
+
+```java
+protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class> targetClass) {
+ // Don't allow no-public methods as required.
+ if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
+ return null;
+ }
+ ...
+}
+```
+
+**2. 方法用final修饰**
+
+如果事务方法用final修饰,将会导致事务失效。因为spring事务底层使用了aop,也就是通过jdk动态代理或者cglib,帮我们生成了代理类,在代理类中实现的事务功能。
+
+但如果某个方法用final修饰了,那么在它的代理类中,就无法重写该方法,而添加事务功能。
+
+> 同理,如果某个方法是static的,同样无法通过动态代理,变成事务方法。
+
+**3.对象没有被spring管理**
+
+使用spring事务的前提是:对象要被spring管理,需要创建bean实例。如果类没有加@Controller、@Service、@Component、@Repository等注解,即该类没有交给spring去管理,那么它的方法也不会生成事务。
+
+**4.表不支持事务**
+
+如果MySQL使用的存储引擎是myisam,这样的话是不支持事务的。因为myisam存储引擎不支持事务。
+
+**5.方法内部调用**
+
+如下代码所示,update方法上面没有加 `@Transactional` 注解,调用有 `@Transactional` 注解的 updateOrder 方法,updateOrder 方法上的事务会失效。
+
+因为发生了自身调用,调用该类自己的方法,而没有经过 Spring 的代理类,只有在外部调用事务才会生效。
+
+```java
+@Service
+public class OrderServiceImpl implements OrderService {
+
+ public void update(Order order) {
+ this.updateOrder(order);
+ }
+
+ @Transactional
+ public void updateOrder(Order order) {
+ // update order
+ }
+}
+```
+
+解决方法:
+
+1、再声明一个service,将内部调用改为外部调用
+
+2、使用编程式事务
+
+3、使用AopContext.currentProxy()获取代理对象
+
+```java
+@Servcie
+public class OrderServiceImpl implements OrderService {
+
+ public void update(Order order) {
+ ((OrderService)AopContext.currentProxy()).updateOrder(order);
+ }
+
+ @Transactional
+ public void updateOrder(Order order) {
+ // update order
+ }
+ }
+```
+
+**6.未开启事务**
+
+如果是spring项目,则需要在配置文件中手动配置事务相关参数。如果忘了配置,事务肯定是不会生效的。
+
+如果是springboot项目,那么不需要手动配置。因为springboot已经在`DataSourceTransactionManagerAutoConfiguration`类中帮我们开启了事务。
+
+**7.吞了异常**
+
+有时候事务不会回滚,有可能是在代码中手动catch了异常。因为开发者自己捕获了异常,又没有手动抛出,把异常吞掉了,这种情况下spring事务不会回滚。
+
+如果想要spring事务能够正常回滚,必须抛出它能够处理的异常。如果没有抛异常,则spring认为程序是正常的。
## Spring怎么解决循环依赖的问题?
@@ -583,6 +770,147 @@ Spring的Bean默认都是单例的,某些情况下,单例是并发不安全
如果还要进一步考虑到微服务或分布式服务的影响,方式3便不合适了。这种情况下可以借助于可以共享某些信息的分布式缓存中间件,如Redis等。这样即可保证同一种服务的不同服务实例都拥有同一份共享信息了。
+## @Async注解的原理
+
+当我们调用第三方接口或者方法的时候,我们不需要等待方法返回才去执行其它逻辑,这时如果响应时间过长,就会极大的影响程序的执行效率。所以这时就需要使用异步方法来并行执行我们的逻辑。在springboot中可以使用@Async注解实现异步操作。
+
+使用@Async注解实现异步操作的步骤:
+
+1.首先在启动类上添加 @EnableAsync 注解。
+
+```java
+@Configuration
+@EnableAsync
+public class App {
+ public static void main(String[] args) {
+ ApplicationContext ctx = new
+ AnnotationConfigApplicationContext(App.class);
+ MyAsync service = ctx.getBean(MyAsync.class);
+ System.out.println(service.getClass());
+ service.async1();
+ System.out.println("main thread finish...");
+ }
+}
+```
+
+2.在对应的方法上添加@Async注解。
+
+```java
+@Component
+public class MyAsync {
+ @Async
+ public void asyncTest() {
+ try {
+ TimeUnit.SECONDS.sleep(20);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ System.out.println("asyncTest...");
+ }
+}
+```
+
+运行代码,控制台输出:
+
+```java
+main thread finish...
+asyncTest...
+```
+
+证明asyncTest方法异步执行了。
+
+原理:
+
+我们在主启动类上贴了一个@EnableAsync注解,才能使用@Async生效。@EnableAsync的作用是通过@import导入了AsyncConfigurationSelector。在AsyncConfigurationSelector的selectImports方法将ProxyAsyncConfiguration定义为Bean注入容器。在ProxyAsyncConfiguration中通过@Bean的方式注入AsyncAnnotationBeanPostProcessor类。
+
+
+
+代码如下:
+
+```java
+@Import(AsyncConfigurationSelector.class)
+public @interface EnableAsync {
+}
+
+public class AsyncConfigurationSelector extends AdviceModeImportSelector {
+ public String[] selectImports(AdviceMode adviceMode) {
+ switch (adviceMode) {
+ case PROXY:
+ return new String[] { ProxyAsyncConfiguration.class.getName() };
+ //...
+ }
+ }
+}
+
+public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
+ @Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
+ public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
+ //创建postProcessor
+ AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
+ //...
+ }
+}
+```
+
+AsyncAnnotationBeanPostProcessor往往期创建了一个增强器AsyncAnnotationAdvisor。在AsyncAnnotationAdvisor的buildAdvice方法中,创建了AnnotationAsyncExecutionInterceptor。
+
+```java
+public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) {
+ super.setBeanFactory(beanFactory);
+ //创建一个增强器
+ AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
+ //...
+ advisor.setBeanFactory(beanFactory);
+ this.advisor = advisor;
+ }
+}
+
+
+public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
+ public AsyncAnnotationAdvisor(
+ @Nullable Supplier executor, @Nullable Supplier exceptionHandler) {
+ //增强方法
+ this.advice = buildAdvice(executor, exceptionHandler);
+ this.pointcut = buildPointcut(asyncAnnotationTypes);
+ }
+
+ // 委托给AnnotationAsyncExecutionInterceptor拦截器
+ protected Advice buildAdvice(
+ @Nullable Supplier executor, @Nullable Supplier exceptionHandler) {
+ //拦截器
+ AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null);
+ interceptor.configure(executor, exceptionHandler);
+ return interceptor;
+ }
+}
+```
+
+AnnotationAsyncExecutionInterceptor继承自AsyncExecutionInterceptor,间接实现了MethodInterceptor。该拦截器的实现的invoke方法把原来方法的调用提交到新的线程池执行,从而实现了方法的异步。
+
+```java
+public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport implements MethodInterceptor, Ordered {
+ public Object invoke(final MethodInvocation invocation) throws Throwable {
+ //...
+ //构建放到AsyncTaskExecutor执行Callable Task
+ Callable
+
# 赞赏
如果觉得**本仓库**对您有帮助的话,可以请大彬**喝一杯咖啡**(小伙伴们赞赏的时候可以备注下哦~)
-| 微信 | 支付宝 |
-| ----------------------------------------------------------- | ------------------------------------------------------------ |
+| 微信 | 支付宝 |
+| ------------------------------------------------- | ----------------------------------------------------- |
|  |  |
每笔赞赏我会在下面记录下来,感谢你们,我会更加努力,砥砺前行~
-| 日期 | 来源 | **用户** | **金额** | 备注 |
-| ---------- | ------------ | -------- | -------- | ------ |
-| 2021.11.19 | 微信收款码 | *张 | 6.66元 | 支持! |
-| 2021.11.25 | 支付宝收款码 | *海 | 1元 | |
-| 2021.12.10 | 微信收款码 | 浩*y | 10元 | |
-| 2021.12.15 | 微信收款码 | biubiu* | 6.66元 | 好 |
-| 2022.02.17 | 微信收款码 | *齐 | 8元 | |
-| 2022.05.03 | 微信收款码 | *哈 | 2元 | |
+| 日期 | 来源 | **用户** | **金额** | 备注 |
+| ---------- | ------------ | -------- | -------- | ------------------------ |
+| 2021.11.19 | 微信收款码 | *张 | 6.66元 | 支持! |
+| 2021.11.25 | 支付宝收款码 | *海 | 1元 | |
+| 2021.12.10 | 微信收款码 | 浩*y | 10元 | |
+| 2021.12.15 | 微信收款码 | biubiu* | 6.66元 | 好 |
+| 2022.02.17 | 微信收款码 | *齐 | 8元 | |
+| 2022.05.03 | 微信收款码 | *哈 | 2元 | |
+| 2022.06.12 | 微信收款码 | *可 | 8.8元 | |
+| 2022.10.19 | 微信收款码 | *斌 | 10元 | 支持一下,希望能持续更新 |
diff --git "a/\345\205\266\344\273\226/\350\256\276\350\256\241\346\250\241\345\274\217.md" "b/\345\205\266\344\273\226/\350\256\276\350\256\241\346\250\241\345\274\217.md"
index 3808e3a..ebbd8e1 100644
--- "a/\345\205\266\344\273\226/\350\256\276\350\256\241\346\250\241\345\274\217.md"
+++ "b/\345\205\266\344\273\226/\350\256\276\350\256\241\346\250\241\345\274\217.md"
@@ -1,27 +1,3 @@
-
-
-
-
-- [单例模式](#%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F)
- - [饿汉模式](#%E9%A5%BF%E6%B1%89%E6%A8%A1%E5%BC%8F)
- - [双重检查锁定](#%E5%8F%8C%E9%87%8D%E6%A3%80%E6%9F%A5%E9%94%81%E5%AE%9A)
- - [静态内部类](#%E9%9D%99%E6%80%81%E5%86%85%E9%83%A8%E7%B1%BB)
-- [装饰模式](#%E8%A3%85%E9%A5%B0%E6%A8%A1%E5%BC%8F)
-- [适配器模式](#%E9%80%82%E9%85%8D%E5%99%A8%E6%A8%A1%E5%BC%8F)
-- [观察者模式](#%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F)
-- [代理模式](#%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F)
- - [静态代理](#%E9%9D%99%E6%80%81%E4%BB%A3%E7%90%86)
- - [动态代理](#%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86)
- - [JDK动态代理](#jdk%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86)
-- [工厂模式](#%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F)
- - [简单工厂模式](#%E7%AE%80%E5%8D%95%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F)
- - [工厂方法模式](#%E5%B7%A5%E5%8E%82%E6%96%B9%E6%B3%95%E6%A8%A1%E5%BC%8F)
- - [抽象工厂模式](#%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F)
-- [模板模式](#%E6%A8%A1%E6%9D%BF%E6%A8%A1%E5%BC%8F)
-- [建造者模式](#%E5%BB%BA%E9%80%A0%E8%80%85%E6%A8%A1%E5%BC%8F)
-
-
-
## 设计模式的六大原则
- 开闭原则:对扩展开放,对修改关闭,多使用抽象类和接口。
@@ -37,6 +13,10 @@
### 饿汉模式
+JVM在类的初始化阶段,会执行类的静态方法。在执行类的初始化期间,JVM会去获取Class对象的锁。这个锁可以同步多个线程对同一个类的初始化。
+
+饿汉模式只在类加载的时候创建一次实例,没有多线程同步的问题。单例没有用到也会被创建,而且在类加载之后就被创建,内存就被浪费了。
+
```
public class Singleton {
private static Singleton instance = new Singleton();
@@ -46,12 +26,10 @@ public class Singleton {
}
}
```
-JVM在类的初始化阶段,会执行类的静态方法。在执行类的初始化期间,JVM会去获取Class对象的锁。这个锁可以同步多个线程对同一个类的初始化。
-
-饿汉模式只在类加载的时候创建一次实例,没有多线程同步的问题。单例没有用到也会被创建,而且在类加载之后就被创建,内存就被浪费了。
-
### 双重检查锁定
+双重校验锁先判断 instance 是否已经被实例化,如果没有被实例化,那么才对实例化语句进行加锁。
+
instance使用static修饰的原因:getInstance为静态方法,因为静态方法的内部不能直接使用非静态变量,只有静态成员才能在没有创建对象时进行初始化,所以返回的这个实例必须是静态的。
```
@@ -104,6 +82,12 @@ instance = memory; // 3:设置instance指向刚分配的内存地址
### 静态内部类
+它与饿汉模式一样,也是利用了类初始化机制,因此不存在多线程并发的问题。不一样的是,它是在内部类里面去创建对象实例。这样的话,只要应用中不使用内部类,JVM就不会去加载这个单例类,也就不会创建单例对象,从而实现懒汉式的延迟加载。也就是说这种方式可以同时保证延迟加载和线程安全。
+
+
+
+基于类初始化的方案的实现代码更简洁。
+
```
public class Instance {
private static class InstanceHolder {
@@ -115,62 +99,564 @@ public class Instance {
}
}
```
-它与饿汉模式一样,也是利用了类初始化机制,因此不存在多线程并发的问题。不一样的是,它是在内部类里面去创建对象实例。这样的话,只要应用中不使用内部类,JVM就不会去加载这个单例类,也就不会创建单例对象,从而实现懒汉式的延迟加载。也就是说这种方式可以同时保证延迟加载和线程安全。
+但基于volatile的双重检查锁定的方案有一个额外的优势:除了可以对静态字段实现延迟初始化外,还可以对实例字段实现延迟初始化。字段延迟初始化降低了初始化类或创建实例的开销,但增加了访问被延迟初始化的字段的开销。在大多数时候,正常的初始化要优于延迟初始化。
+
+## 工厂模式
+
+工厂模式是用来封装对象的创建。
+
+### 简单工厂模式
+
+把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化,这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。
+
+```java
+ public class ShapeFactory {
+ public static final String TAG = "ShapeFactory";
+ public static Shape getShape(String type) {
+ Shape shape = null;
+ if (type.equalsIgnoreCase("circle")) {
+ shape = new CircleShape();
+ } else if (type.equalsIgnoreCase("rect")) {
+ shape = new RectShape();
+ } else if (type.equalsIgnoreCase("triangle")) {
+ shape = new TriangleShape();
+ }
+ return shape;
+ }
+ }
+```
+
+优点:只需要一个工厂创建对象,代码量少。
+
+缺点:系统扩展困难,新增产品需要修改工厂逻辑,当产品较多时,会造成工厂逻辑过于复杂,不利于系统扩展和维护。
+
+### 工厂方法模式
+
+针对不同的对象提供不同的工厂。每个对象都有一个与之对应的工厂。
+
+```java
+public interface Reader {
+ void read();
+}
+
+public class JpgReader implements Reader {
+ @Override
+ public void read() {
+ System.out.print("read jpg");
+ }
+}
+
+public class PngReader implements Reader {
+ @Override
+ public void read() {
+ System.out.print("read png");
+ }
+}
+
+public interface ReaderFactory {
+ Reader getReader();
+}
+
+public class JpgReaderFactory implements ReaderFactory {
+ @Override
+ public Reader getReader() {
+ return new JpgReader();
+ }
+}
+
+public class PngReaderFactory implements ReaderFactory {
+ @Override
+ public Reader getReader() {
+ return new PngReader();
+ }
+}
+```
+
+客户端通过子类来指定创建对应的对象。
+
+```java
+ReaderFactory factory=new JpgReaderFactory();
+Reader reader=factory.getReader();
+reader.read();
+```
+
+优点:增加新的产品类时无须修改现有系统,只需增加新产品和对应的工厂类即可。
+
+### 抽象工厂模式
+
+抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂方法模式只是用于创建一个对象,这和抽象工厂模式有很大不同。
+
+多了一层抽象,减少了工厂的数量(HpMouseFactory和HpKeyboFactory合并为HpFactory)。
+
+
+
+
+
+## 模板模式
+
+模板模式:一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。 这种类型的设计模式属于行为型模式。定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
+
+**模板模式**主要由抽象模板(Abstract Template)角色和具体模板(Concrete Template)角色组成。
+
+- 抽象模板(Abstract Template): 定义了一个或多个抽象操作,以便让子类实现。这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤;定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。
+- 具体模板(Concrete Template): 实现父类所定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤;每一个抽象模板角色都可以有任意多个具体模板角色与之对应,而每一个具体模板角色都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。
+
+示例图如下:
+
+
+
+以游戏为例。创建一个抽象类,它的模板方法被设置为 final,这样它就不会被重写。
+
+```java
+public abstract class Game {
+ abstract void initialize();
+ abstract void startPlay();
+ abstract void endPlay();
+
+ //模板
+ public final void play(){
+ //初始化游戏
+ initialize();
+
+ //开始游戏
+ startPlay();
+
+ //结束游戏
+ endPlay();
+ }
+}
+```
+
+Football类:
+
+```java
+public class Football extends Game {
+
+ @Override
+ void endPlay() {
+ System.out.println("Football Game Finished!");
+ }
+
+ @Override
+ void initialize() {
+ System.out.println("Football Game Initialized! Start playing.");
+ }
+
+ @Override
+ void startPlay() {
+ System.out.println("Football Game Started. Enjoy the game!");
+ }
+}
+```
+
+使用Game的模板方法 play() 来演示游戏的定义方式。
+
+```java
+public class TemplatePatternDemo {
+ public static void main(String[] args) {
+
+ Game game = new Cricket();
+ game.play();
+ System.out.println();
+ game = new Football();
+ game.play();
+ }
+}
+```
+
+**模板模式优点** :
+
+1. 封装不变部分,扩展可变部分。
+2. 提取公共代码,便于维护。
+3. 行为由父类控制,子类实现。
+
+**模板模式缺点**:
+
+- 每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
+
+## 策略模式
+
+策略模式(Strategy Pattern)属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。
+
+其主要目的是通过定义相似的算法,替换if else 语句写法,并且可以随时相互替换。
+
+**策略模式**主要由这三个角色组成,环境角色(Context)、抽象策略角色(Strategy)和具体策略角色(ConcreteStrategy)。
+
+- 环境角色(Context):持有一个策略类的引用,提供给客户端使用。
+- 抽象策略角色(Strategy):这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
+- 具体策略角色(ConcreteStrategy):包装了相关的算法或行为。
+
+示例图如下:
+
+
+
+以计算器为例,如果我们想得到两个数字相加的和,我们需要用到“+”符号,得到相减的差,需要用到“-”符号等等。虽然我们可以通过字符串比较使用if/else写成通用方法,但是计算的符号每次增加,我们就不得不加在原先的方法中进行增加相应的代码,如果后续计算方法增加、修改或删除,那么会使后续的维护变得困难。
+
+但是在这些方法中,我们发现其基本方法是固定的,这时我们就可以通过策略模式来进行开发,可以有效避免通过if/else来进行判断,即使后续增加其他的计算规则也可灵活进行调整。
+
+首先定义一个抽象策略角色,并拥有一个计算的方法。
+
+```java
+interface CalculateStrategy {
+ int doOperation(int num1, int num2);
+}
+```
+
+然后再定义加减乘除这些具体策略角色并实现方法。代码如下:
+
+```java
+class OperationAdd implements CalculateStrategy {
+ @Override
+ public int doOperation(int num1, int num2) {
+ return num1 + num2;
+ }
+}
+
+class OperationSub implements CalculateStrategy {
+ @Override
+ public int doOperation(int num1, int num2) {
+ return num1 - num2;
+ }
+}
+
+class OperationMul implements CalculateStrategy {
+ @Override
+ public int doOperation(int num1, int num2) {
+ return num1 * num2;
+ }
+}
+
+class OperationDiv implements CalculateStrategy {
+ @Override
+ public int doOperation(int num1, int num2) {
+ return num1 / num2;
+ }
+}
+```
+
+最后在定义一个环境角色,提供一个计算的接口供客户端使用。代码如下:
+
+```java
+class CalculatorContext {
+ private CalculateStrategy strategy;
+
+ public CalculatorContext(CalculateStrategy strategy) {
+ this.strategy = strategy;
+ }
+
+ public int executeStrategy(int num1, int num2) {
+ return strategy.doOperation(num1, num2);
+ }
+}
+```
+
+测试代码如下:
+
+```java
+public static void main(String[] args) {
+ int a=4,b=2;
+ CalculatorContext context = new CalculatorContext(new OperationAdd());
+ System.out.println("a + b = "+context.executeStrategy(a, b));
+
+ CalculatorContext context2 = new CalculatorContext(new OperationSub());
+ System.out.println("a - b = "+context2.executeStrategy(a, b));
+
+ CalculatorContext context3 = new CalculatorContext(new OperationMul());
+ System.out.println("a * b = "+context3.executeStrategy(a, b));
+
+ CalculatorContext context4 = new CalculatorContext(new OperationDiv());
+ System.out.println("a / b = "+context4.executeStrategy(a, b));
+}
+```
+
+**策略模式优点:**
+
+- 扩展性好,可以在不修改对象结构的情况下,为新的算法进行添加新的类进行实现;
+- 灵活性好,可以对算法进行自由切换;
+
+**策略模式缺点:**
+
+- 使用策略类变多,会增加系统的复杂度。;
+- 客户端必须知道所有的策略类才能进行调用;
+
+**使用场景:**
+
+- 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为;
+ 一个系统需要动态地在几种算法中选择一种;
+- 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现;
+
+## 责任链模式
+
+为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
+
+在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,请求会自动进行传递。所以责任链将请求的发送者和请求的处理者解耦了。
+
+**责任链模式是一种对象行为型模式,其主要优点如下。**
+
+1. 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
+2. 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
+3. 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
+4. 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
+5. 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
+
+代码实现如下:
+
+请假条类
+
+```java
+public class LeaveRequest {
+ //姓名
+ private String name;
+ // 请假天数
+ private int num;
+ // 请假内容
+ private String content;
+
+ public LeaveRequest(String name, int num, String content) {
+ this.name = name;
+ this.num = num;
+ this.content = content;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getNum() {
+ return num;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+}
+```
+
+处理类
+
+```java
+public abstract class Handler {
+ protected final static int NUM_ONE = 1;
+ protected final static int NUM_THREE = 3;
+ protected final static int NUM_SEVEN = 7;
+
+ //该领导处理的请假天数区间
+ private int numStart;
+ private int numEnd;
+
+
+ //领导上还有领导
+ private Handler nextHandler;
+
+ //设置请假天数范围
+ public Handler(int numStart) {
+ this.numStart = numStart;
+ }
+
+ //设置请假天数范围
+ public Handler(int numStart, int numEnd) {
+ this.numStart = numStart;
+ this.numEnd = numEnd;
+ }
+
+ //设置上级领导
+ public void setNextHandler(Handler nextHandler) {
+ this.nextHandler = nextHandler;
+ }
+
+ //提交请假条
+ public final void submit(LeaveRequest leaveRequest) {
+ if (this.numStart == 0) {
+ return;
+ }
+ //请假天数达到领导处理要求
+ if (leaveRequest.getNum() >= this.numStart) {
+ this.handleLeave(leaveRequest);
+
+ //如果还有上级 并且请假天数超过当前领导的处理范围
+ if (this.nextHandler != null && leaveRequest.getNum() > numEnd) {
+ //继续提交
+ this.nextHandler.submit(leaveRequest);
+ } else {
+ System.out.println("流程结束!!!");
+ }
+ }
+ }
+
+ //各级领导处理请假条方法
+ protected abstract void handleLeave(LeaveRequest leave);
+
+}
+```
+
+小组长类
+
+```java
+public class GroupLeader extends Handler {
+ //1-3天的假
+ public GroupLeader() {
+ super(Handler.NUM_ONE, Handler.NUM_THREE);
+ }
+
+ @Override
+ protected void handleLeave(LeaveRequest leave) {
+ System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "!");
+ System.out.println("小组长审批通过:同意!");
+ }
+}
+```
+
+部门经理类
+
+```java
+public class Manager extends Handler {
+ //3-7天的假
+ public Manager() {
+ super(Handler.NUM_THREE, Handler.NUM_SEVEN);
+ }
+
+ @Override
+ protected void handleLeave(LeaveRequest leave) {
+ System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "!");
+ System.out.println("部门经理审批通过:同意!");
+ }
+}
+```
+
+总经理类
+
+```java
+public class GeneralManager extends Handler{
+ //7天以上的假
+ public GeneralManager() {
+ super(Handler.NUM_THREE, Handler.NUM_SEVEN);
+ }
+
+ @Override
+ protected void handleLeave(LeaveRequest leave) {
+ System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "!");
+ System.out.println("总经理审批通过:同意!");
+ }
+}
+```
+
+测试类
+
+```java
+public class Client {
+ public static void main(String[] args) {
+ //请假条
+ LeaveRequest leave = new LeaveRequest("小庄", 3, "出去旅游");
+
+ //各位领导
+ Manager manager = new Manager();
+ GroupLeader groupLeader = new GroupLeader();
+ GeneralManager generalManager = new GeneralManager();
+
+ /*
+ * 小组长上司是经理 经理上司是总经理
+ */
+ groupLeader.setNextHandler(manager);
+ manager.setNextHandler(generalManager);
+
+ //提交
+ groupLeader.submit(leave);
+
+ }
+}
+```
+
+应用场景:
+
+1. 多个对象可以处理一个请求,但具体由哪个对象处理该请求在运行时自动确定。
+2. 可动态指定一组对象处理请求,或添加新的处理者。
+3. 需要在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。
+
+[参考链接](https://segmentfault.com/a/1190000040450513)
+
+## 迭代器模式
+
+提供一种方法顺序访问一个聚合对象中的各个元素, 而又不暴露其内部的表示。
+
+把在元素之间游走的责任交给迭代器,而不是聚合对象。
-
+**应用实例:**JAVA 中的 iterator。
-基于类初始化的方案的实现代码更简洁。但基于volatile的双重检查锁定的方案有一个额外的优势:除了可以对静态字段实现延迟初始化外,还可以对实例字段实现延迟初始化。字段延迟初始化降低了初始化类或创建实例的开销,但增加了访问被延迟初始化的字段的开销。在大多数时候,正常的初始化要优于延迟初始化。
+**优点:** 1、它支持以不同的方式遍历一个聚合对象。 2、迭代器简化了聚合类。 3、在同一个聚合上可以有多个遍历。 4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
-[参考:单例模式](https://blog.csdn.net/goodlixueyong/article/details/51935526)
+**缺点:**由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
+
+**使用场景:** 1、访问一个聚合对象的内容而无须暴露它的内部表示。 2、需要为聚合对象提供多种遍历方式。 3、为遍历不同的聚合结构提供一个统一的接口。
+
+**迭代器模式在JDK中的应用**
+
+ArrayList的遍历:
+
+```java
+Iterator
iter = null;
+
+System.out.println("ArrayList:");
+iter = arrayList.iterator();
+while (iter.hasNext()) {
+ System.out.print(iter.next() + "\t");
+}
+```
## 装饰模式
+
+装饰者模式(decorator pattern):动态地将责任附加到对象上, 若要扩展功能, 装饰者提供了比继承更有弹性的替代方案。
+
装饰模式以对客户端透明的方式拓展对象的功能,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不创造更多子类的情况下,将对象的功能加以扩展。
+比如设置FileInputStream,先用BufferedInputStream装饰它,再用自己写的LowerCaseInputStream过滤器去装饰它。
+
```
InputStream in = new LowerCaseInputStream(
new BufferedInputStream(
new FileInputStream("test.txt")));
```
-设置FileInputStream,先用BufferedInputStream装饰它,再用自己写的LowerCaseInputStream过滤器去装饰它。
-
在装饰模式中的角色有:
-抽象组件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
-具体组件(ConcreteComponent)角色:定义一个将要接收附加责任的类。
-装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
-具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。
-
+
+- 抽象组件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
+- 具体组件(ConcreteComponent)角色:定义一个将要接收附加责任的类。
+- 装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
+- 具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。
## 适配器模式
-将现成的对象通过适配变成我们需要的接口。
+适配器模式将现成的对象通过适配变成我们需要的接口。 适配器让原本接口不兼容的类可以合作。
+
适配器模式有类的适配器模式和对象的适配器模式两种不同的形式。
对象适配器模式通过组合对象进行适配。
-
+
类适配器通过继承来完成适配。
-
+
-适配器模式的优点
+适配器模式的**优点**:
1. 更好的复用性。系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
2. 更好的扩展性。在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。
## 观察者模式
+
+定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会收到通知并且自动更新状态。
+
+主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。
+
多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。其作用是让主题对象和观察者松耦合。
观察者模式所涉及的角色有:
-抽象主题(Subject)角色:抽象主题角色把所有对观察者对象的引用保存在一个聚集(比如ArrayList对象)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫做抽象被观察者(Observable)角色。
-具体主题(ConcreteSubject)角色:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者(Concrete Observable)角色。
-抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口。
-具体观察者(ConcreteObserver)角色:存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。
-Observer接口 Observable类
-被观察者类都是java.util.Observable类的子类。
-
-[观察者模式](https://www.cnblogs.com/luohanguo/p/7825656.html)
+
+- 抽象主题(Subject)角色:抽象主题角色把所有对观察者对象的引用保存在一个聚集(比如ArrayList对象)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫做抽象被观察者(Observable)角色。
+- 具体主题(ConcreteSubject)角色:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者(Concrete Observable)角色。
+- 抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口。
+- 具体观察者(ConcreteObserver)角色:存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。
@@ -188,7 +674,7 @@ Observer接口 Observable类
动态代理:代理类在程序运行时创建,在内存中临时生成一个代理对象,在运行期间对业务方法进行增强。
-#### JDK动态代理
+**JDK动态代理**
JDK实现代理只需要使用newProxyInstance方法:
@@ -196,9 +682,13 @@ JDK实现代理只需要使用newProxyInstance方法:
static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h )
```
-ClassLoader loader:指定当前目标对象使用的类加载器
-Class>[] interfaces:目标对象实现的接口的类型
-InvocationHandler h:当代理对象调用目标对象的方法时,会触发事件处理器的invoke方法()
+三个入参:
+
+- ClassLoader loader:指定当前目标对象使用的类加载器
+- Class>[] interfaces:目标对象实现的接口的类型
+- InvocationHandler:当代理对象调用目标对象的方法时,会触发事件处理器的invoke方法()
+
+示例代码:
```
public class DynamicProxyDemo {
@@ -247,119 +737,11 @@ class MyInvacationHandler implements InvocationHandler {
}
```
+## 建造者模式
+建造者模式:封装一个对象的构造过程,并允许按步骤构造。
-## 工厂模式
-工厂模式是用来封装对象的创建。
-
-### 简单工厂模式
-
-只有一个工厂类。适用于需要创建的对象较少的场景。
-
-```java
- public class ShapeFactory {
- public static final String TAG = "ShapeFactory";
- public static Shape getShape(String type) {
- Shape shape = null;
- if (type.equalsIgnoreCase("circle")) {
- shape = new CircleShape();
- } else if (type.equalsIgnoreCase("rect")) {
- shape = new RectShape();
- } else if (type.equalsIgnoreCase("triangle")) {
- shape = new TriangleShape();
- }
- return shape;
- }
- }
-```
-
-优点:只需要一个工厂创建对象,代码量少。
-
-缺点:系统扩展困难,新增产品需要修改工厂逻辑,当产品较多时,会造成工厂逻辑过于复杂,不利于系统扩展和维护。
-
-### 工厂方法模式
-
-针对不同的对象提供不同的工厂。每个对象都有一个与之对应的工厂。
-
-```java
-public interface Reader {
- void read();
-}
-
-public class JpgReader implements Reader {
- @Override
- public void read() {
- System.out.print("read jpg");
- }
-}
-
-public class PngReader implements Reader {
- @Override
- public void read() {
- System.out.print("read png");
- }
-}
-
-public interface ReaderFactory {
- Reader getReader();
-}
-
-public class JpgReaderFactory implements ReaderFactory {
- @Override
- public Reader getReader() {
- return new JpgReader();
- }
-}
-
-public class PngReaderFactory implements ReaderFactory {
- @Override
- public Reader getReader() {
- return new PngReader();
- }
-}
-```
-
-客户端通过子类来指定创建对应的对象。
-
-```java
-ReaderFactory factory=new JpgReaderFactory();
-Reader reader=factory.getReader();
-reader.read();
-```
-
-优点:增加新的产品类时无须修改现有系统,只需增加新产品和对应的工厂类即可。
-
-### 抽象工厂模式
-
-多了一层抽象,减少了工厂的数量(HpMouseFactory和HpKeyboFactory合并为HpFactory)。
-
-
-
-
-## 模板模式
-
-在抽象类中定义一个操作中算法的骨架,子类按照需要重写方法实现。
-
-```java
-public abstract class DodishTemplate {
- //模板
- protected void dodish(){
- this.preparation();
- this.doing();
- this.carriedDishes();
- }
-
- public abstract void preparation();
-
- public abstract void doing();
-
- public abstract void carriedDishes ();
-}
-```
-
-
-
-## 建造者模式
+有两种形式:传统建造者模式和传统建造者模式变种。
传统建造者模式:
diff --git "a/\345\267\245\345\205\267/docker.md" "b/\345\267\245\345\205\267/docker-overview.md"
similarity index 100%
rename from "\345\267\245\345\205\267/docker.md"
rename to "\345\267\245\345\205\267/docker-overview.md"
diff --git "a/\345\267\245\345\205\267/progit2.md" "b/\345\267\245\345\205\267/git-overview.md"
similarity index 100%
rename from "\345\267\245\345\205\267/progit2.md"
rename to "\345\267\245\345\205\267/git-overview.md"
diff --git "a/\345\267\245\345\205\267/linux-overview.md" "b/\345\267\245\345\205\267/linux-overview.md"
new file mode 100644
index 0000000..f3cd728
--- /dev/null
+++ "b/\345\267\245\345\205\267/linux-overview.md"
@@ -0,0 +1,647 @@
+# 基本操作
+
+## Linux关机,重启
+
+```
+#关机
+shutdown -h now
+
+#重启
+shutdown -r now
+```
+
+## 查看系统,CPU信息
+
+```
+#查看系统内核信息
+uname -a
+
+#查看系统内核版本
+cat /proc/version
+
+#查看当前用户环境变量
+env
+
+cat /proc/cpuinfo
+
+#查看有几个逻辑cpu, 包括cpu型号
+cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c
+
+#查看有几颗cpu,每颗分别是几核
+cat /proc/cpuinfo | grep physical | uniq -c
+
+#查看当前CPU运行在32bit还是64bit模式下, 如果是运行在32bit下也不代表CPU不支持64bit
+getconf LONG_BIT
+
+#结果大于0, 说明支持64bit计算. lm指long mode, 支持lm则是64bit
+cat /proc/cpuinfo | grep flags | grep ' lm ' | wc -l
+```
+
+## 建立软连接
+
+```
+ln -s /usr/local/jdk1.8/ jdk
+```
+
+## rpm相关
+
+```
+#查看是否通过rpm安装了该软件
+rpm -qa | grep 软件名
+```
+
+## sshkey
+
+```
+#创建sshkey
+ssh-keygen -t rsa -C your_email@example.com
+
+#id_rsa.pub 的内容拷贝到要控制的服务器的 home/username/.ssh/authorized_keys 中,如果没有则新建(.ssh权限为700, authorized_keys权限为600)
+```
+
+## 命令重命名
+
+```
+#在各个用户的.bash_profile中添加重命名配置
+alias ll='ls -alF'
+```
+
+## 同步服务器时间
+
+```
+sudo ntpdate -u ntp.api.bz
+```
+
+## 后台运行命令
+
+```
+#后台运行,并且有nohup.out输出
+nohup xxx &
+
+#后台运行, 不输出任何日志
+nohup xxx > /dev/null &
+
+#后台运行, 并将错误信息做标准输出到日志中
+nohup xxx >out.log 2>&1 &
+```
+
+## 强制活动用户退出
+
+```
+#命令来完成强制活动用户退出.其中TTY表示终端名称
+pkill -kill -t [TTY]
+```
+
+## 查看命令路径
+
+```
+which <命令>
+```
+
+## 查看进程所有打开最大fd数
+
+```
+ulimit -n
+```
+
+## 配置dns
+
+```
+vim /etc/resolv.conf
+```
+
+## nslookup,查看域名路由表
+
+```
+nslookup google.com
+```
+
+## last, 最近登录信息列表
+
+```
+#最近登录的5个账号
+last -n 5
+```
+
+## 设置固定ip
+
+```
+ifconfig em1 192.168.5.177 netmask 255.255.255.0
+```
+
+## 查看进程内加载的环境变量
+
+```
+#也可以去 cd /proc 目录下, 查看进程内存中加载的东西
+ps eww -p XXXXX(进程号)
+```
+
+## 查看进程树找到服务器进程
+
+```
+ps auwxf
+```
+
+## 查看进程启动路径
+
+```
+cd /proc/xxx(进程号)
+ls -all
+#cwd对应的是启动路径
+```
+
+## 添加用户, 配置sudo权限
+
+```
+#新增用户
+useradd 用户名
+passwd 用户名
+
+#增加sudo权限
+vim /etc/sudoers
+#修改文件里面的
+#root ALL=(ALL) ALL
+#用户名 ALL=(ALL) ALL
+```
+
+## 强制关闭进程名包含xxx的所有进程
+
+```
+ps aux|grep xxx | grep -v grep | awk '{print $2}' | xargs kill -9
+```
+
+# 磁盘,文件,目录相关操作
+
+## vim操作
+
+```
+#normal模式下 g表示全局, x表示查找的内容, y表示替换后的内容
+:%s/x/y/g
+
+#normal模式下
+0 #光标移到行首(数字0)
+$ #光标移至行尾
+shift + g #跳到文件最后
+gg #跳到文件头
+
+#显示行号
+:set nu
+
+#去除行号
+:set nonu
+
+#检索
+/xxx(检索内容) #从头检索, 按n查找下一个
+?xxx(检索内容) #从尾部检索
+```
+
+## 打开只读文件,修改后需要保存时(不用切换用户即可保存的方式)
+
+```
+#在normal模式下
+:w !sudo tee %
+```
+
+## 查看磁盘, 文件目录基本信息
+
+```
+#查看磁盘挂载情况
+mount
+
+#查看磁盘分区信息
+df
+
+#查看目录及子目录大小
+du -H -h
+
+#查看当前目录下各个文件, 文件夹占了多少空间, 不会递归
+du -sh *
+```
+
+## wc命令
+
+```
+#查看文件里有多少行
+wc -l filename
+
+#看文件里有多少个word
+wc -w filename
+
+#文件里最长的那一行是多少个字
+wc -L filename
+
+#统计字节数
+wc -c
+```
+
+## 常用压缩, 解压缩命令
+
+### 压缩命令
+
+```
+tar czvf xxx.tar 压缩目录
+
+zip -r xxx.zip 压缩目录
+```
+
+### 解压缩命令
+
+```
+tar zxvf xxx.tar
+
+#解压到指定文件夹
+tar zxvf xxx.tar -C /xxx/yyy/
+
+unzip xxx.zip
+```
+
+## 变更文件所属用户, 用户组
+
+```
+chown eagleye.eagleye xxx.log
+```
+
+## cp, scp, mkdir
+
+```
+#复制
+cp xxx.log
+
+#复制并强制覆盖同名文件
+cp -f xxx.log
+
+#复制文件夹
+cp -r xxx(源文件夹) yyy(目标文件夹)
+
+#远程复制
+scp -P ssh端口 username@10.10.10.101:/home/username/xxx /home/xxx
+
+#级联创建目录
+mkdir -p /xxx/yyy/zzz
+
+#批量创建文件夹, 会在test,main下都创建java, resources文件夹
+mkdir -p src/{test,main}/{java,resources}
+```
+
+## 比较两个文件
+
+```
+diff -u 1.txt 2.txt
+```
+
+## 日志输出的字节数,可以用作性能测试
+
+```
+#如果做性能测试, 可以每执行一次, 往日志里面输出 “.” , 这样日志中的字节数就是实际的性能测试运行的次数, 还可以看见实时速率.
+tail -f xxx.log | pv -bt
+```
+
+## 查看, 去除特殊字符
+
+```
+#查看特殊字符
+cat -v xxx.sh
+
+#去除特殊字符
+sed -i 's/^M//g’ env.sh 去除文件的特殊字符, 比如^M: 需要这样输入: ctrl+v+enter
+```
+
+## 处理因系统原因引起的文件中特殊字符的问题
+
+```
+#可以转换为该系统下的文件格式
+cat file.sh > file.sh_bak
+
+#先将file.sh中文件内容复制下来然后运行, 然后粘贴内容, 最后ctrl + d 保存退出
+cat > file1.sh
+
+#在vim中通过如下设置文件编码和文件格式
+:set fileencodings=utf-8 ,然后 w (存盘)一下即可转化为 utf8 格式,
+:set fileformat=unix
+
+#在mac下使用dos2unix进行文件格式化
+find . -name "*.sh" | xargs dos2unix
+```
+
+## tee, 重定向的同时输出到屏幕
+
+```
+awk ‘{print $0}’ xxx.log | tee test.log
+```
+
+# 检索相关
+
+## grep
+
+```
+#反向匹配, 查找不包含xxx的内容
+grep -v xxx
+
+#排除所有空行
+grep -v '^/pre>
+
+#返回结果 2,则说明第二行是空行
+grep -n “^$” 111.txt
+
+#查询以abc开头的行
+grep -n “^abc” 111.txt
+
+#同时列出该词语出现在文章的第几行
+grep 'xxx' -n xxx.log
+
+#计算一下该字串出现的次数
+grep 'xxx' -c xxx.log
+
+#比对的时候,不计较大小写的不同
+grep 'xxx' -i xxx.log
+```
+
+## awk
+
+```
+#以':' 为分隔符,如果第五域有user则输出该行
+awk -F ':' '{if ($5 ~ /user/) print $0}' /etc/passwd
+
+#统计单个文件中某个字符(串)(中文无效)出现的次数
+awk -v RS='character' 'END {print --NR}' xxx.txt
+```
+
+## find检索命令
+
+```
+#在目录下找后缀是.mysql的文件
+find /home/eagleye -name '*.mysql' -print
+
+#会从 /usr 目录开始往下找,找最近3天之内存取过的文件。
+find /usr -atime 3 –print
+
+#会从 /usr 目录开始往下找,找最近5天之内修改过的文件。
+find /usr -ctime 5 –print
+
+#会从 /doc 目录开始往下找,找jacky 的、文件名开头是 j的文件。
+find /doc -user jacky -name 'j*' –print
+
+#会从 /doc 目录开始往下找,找寻文件名是 ja 开头或者 ma开头的文件。
+find /doc \( -name 'ja*' -o- -name 'ma*' \) –print
+
+#会从 /doc 目录开始往下找,找到凡是文件名结尾为 bak的文件,把它删除掉。-exec 选项是执行的意思,rm 是删除命令,{ } 表示文件名,“\;”是规定的命令结尾。
+find /doc -name '*bak' -exec rm {} \;
+```
+
+# 网络相关
+
+## 查看什么进程使用了该端口
+
+```
+lsof -i:port
+```
+
+## 获取本机ip地址
+
+```
+/sbin/ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print $2}'|tr -d "addr:"
+```
+
+## iptables
+
+```
+#查看iptables状态
+service iptables status
+
+#要封停一个ip
+iptables -I INPUT -s ***.***.***.*** -j DROP
+
+#要解封一个IP,使用下面这条命令:
+iptables -D INPUT -s ***.***.***.*** -j DROP
+
+备注: 参数-I是表示Insert(添加),-D表示Delete(删除)。后面跟的是规则,INPUT表示入站,***.***.***.***表示要封停的IP,DROP表示放弃连接。
+
+#开启9090端口的访问
+/sbin/iptables -I INPUT -p tcp --dport 9090 -j ACCEPT
+
+#防火墙开启、关闭、重启
+/etc/init.d/iptables status
+/etc/init.d/iptables start
+/etc/init.d/iptables stop
+/etc/init.d/iptables restart
+```
+
+## nc命令, tcp调试利器
+
+```
+#给某一个endpoint发送TCP请求,就将data的内容发送到对端
+nc 192.168.0.11 8000 < data.txt
+
+#nc可以当做服务器,监听某个端口号,把某一次请求的内容存储到received_data里
+nc -l 8000 > received_data
+
+#上边只监听一次,如果多次可以加上-k参数
+nc -lk 8000
+```
+
+## tcpdump
+
+```
+#dump出本机12301端口的tcp包
+tcpdump -i em1 tcp port 12301 -s 1500 -w abc.pcap
+```
+
+## 跟踪网络路由路径
+
+```
+#traceroute默认使用udp方式, 如果是-I则改成icmp方式
+traceroute -I www.163.com
+
+#从ttl第3跳跟踪
+traceroute -M 3 www.163.com
+
+#加上端口跟踪
+traceroute -p 8080 192.168.10.11
+```
+
+## ss
+
+```
+#显示本地打开的所有端口
+ss -l
+
+#显示每个进程具体打开的socket
+ss -pl
+
+#显示所有tcp socket
+ss -t -a
+
+#显示所有的UDP Socekt
+ss -u -a
+
+#显示所有已建立的SMTP连接
+ss -o state established '( dport = :smtp or sport = :smtp )'
+
+#显示所有已建立的HTTP连接
+ss -o state established '( dport = :http or sport = :http )'
+
+找出所有连接X服务器的进程
+ss -x src /tmp/.X11-unix/*
+
+列出当前socket统计信息
+ss -s
+
+解释:netstat是遍历/proc下面每个PID目录,ss直接读/proc/net下面的统计信息。所以ss执行的时候消耗资源以及消耗的时间都比netstat少很多
+```
+
+## netstat
+
+```
+#输出每个ip的连接数,以及总的各个状态的连接数
+netstat -n | awk '/^tcp/ {n=split($(NF-1),array,":");if(n<=2)++S[array[(1)]];else++S[array[(4)]];++s[$NF];++N} END {for(a in S){printf("%-20s %s\n", a, S[a]);++I}printf("%-20s %s\n","TOTAL_IP",I);for(a in s) printf("%-20s %s\n",a, s[a]);printf("%-20s %s\n","TOTAL_LINK",N);}'
+
+#统计所有连接状态,
+#CLOSED:无连接是活动的或正在进行
+#LISTEN:服务器在等待进入呼叫
+#SYN_RECV:一个连接请求已经到达,等待确认
+#SYN_SENT:应用已经开始,打开一个连接
+#ESTABLISHED:正常数据传输状态
+#FIN_WAIT1:应用说它已经完成
+#FIN_WAIT2:另一边已同意释放
+#ITMED_WAIT:等待所有分组死掉
+#CLOSING:两边同时尝试关闭
+#TIME_WAIT:主动关闭连接一端还没有等到另一端反馈期间的状态
+#LAST_ACK:等待所有分组死掉
+netstat -n | awk '/^tcp/ {++state[$NF]} END {for(key in state) print key,"\t",state[key]}'
+
+#查找较多time_wait连接
+netstat -n|grep TIME_WAIT|awk '{print $5}'|sort|uniq -c|sort -rn|head -n20
+```
+
+# 监控linux性能命令
+
+## top
+
+```
+按大写的 F 或 O 键,然后按 a-z 可以将进程按照相应的列进行排序, 然后回车。而大写的 R 键可以将当前的排序倒转
+```
+
+| 列名 | 含义 |
+| :------ | :----------------------------------------------------------- |
+| PID | 进程id |
+| PPID | 父进程id |
+| RUSER | Real user name |
+| UID | 进程所有者的用户id |
+| USER | 进程所有者的用户名 |
+| GROUP | 进程所有者的组名 |
+| TTY | 启动进程的终端名。不是从终端启动的进程则显示为 ? |
+| PR | 优先级 |
+| NI | nice值。负值表示高优先级,正值表示低优先级 |
+| P | 最后使用的CPU,仅在多CPU环境下有意义 |
+| %CPU | 上次更新到现在的CPU时间占用百分比 |
+| TIME | 进程使用的CPU时间总计,单位秒 |
+| TIME+ | 进程使用的CPU时间总计,单位1/100秒 |
+| %MEM | 进程使用的物理内存百分比 |
+| VIRT | 进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES |
+| SWAP | 进程使用的虚拟内存中,被换出的大小,单位kb。 |
+| RES | 进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA |
+| CODE | 可执行代码占用的物理内存大小,单位kb |
+| DATA | 可执行代码以外的部分(数据段+栈)占用的物理内存大小,单位kb |
+| SHR | 共享内存大小,单位kb |
+| nFLT | 页面错误次数 |
+| nDRT | 最后一次写入到现在,被修改过的页面数。 |
+| S | 进程状态。D=不可中断的睡眠状态,R=运行,S=睡眠,T=跟踪/停止,Z=僵尸进程 |
+| COMMAND | 命令名/命令行 |
+| WCHAN | 若该进程在睡眠,则显示睡眠中的系统函数名 |
+| Flags | 任务标志,参考 sched.h |
+
+## dmesg,查看系统日志
+
+```
+dmesg
+```
+
+## iostat,磁盘IO情况监控
+
+```
+iostat -xz 1
+
+#r/s, w/s, rkB/s, wkB/s:分别表示每秒读写次数和每秒读写数据量(千字节)。读写量过大,可能会引起性能问题。
+#await:IO操作的平均等待时间,单位是毫秒。这是应用程序在和磁盘交互时,需要消耗的时间,包括IO等待和实际操作的耗时。如果这个数值过大,可能是硬件设备遇到了瓶颈或者出现故障。
+#avgqu-sz:向设备发出的请求平均数量。如果这个数值大于1,可能是硬件设备已经饱和(部分前端硬件设备支持并行写入)。
+#%util:设备利用率。这个数值表示设备的繁忙程度,经验值是如果超过60,可能会影响IO性能(可以参照IO操作平均等待时间)。如果到达100%,说明硬件设备已经饱和。
+#如果显示的是逻辑设备的数据,那么设备利用率不代表后端实际的硬件设备已经饱和。值得注意的是,即使IO性能不理想,也不一定意味这应用程序性能会不好,可以利用诸如预读取、写缓存等策略提升应用性能。
+```
+
+## free,内存使用情况
+
+```
+free -m
+
+eg:
+
+ total used free shared buffers cached
+Mem: 1002 769 232 0 62 421
+-/+ buffers/cache: 286 715
+Swap: 1153 0 1153
+
+第一部分Mem行:
+total 内存总数: 1002M
+used 已经使用的内存数: 769M
+free 空闲的内存数: 232M
+shared 当前已经废弃不用,总是0
+buffers Buffer 缓存内存数: 62M
+cached Page 缓存内存数:421M
+
+关系:total(1002M) = used(769M) + free(232M)
+
+第二部分(-/+ buffers/cache):
+(-buffers/cache) used内存数:286M (指的第一部分Mem行中的used – buffers – cached)
+(+buffers/cache) free内存数: 715M (指的第一部分Mem行中的free + buffers + cached)
+
+可见-buffers/cache反映的是被程序实实在在吃掉的内存,而+buffers/cache反映的是可以挪用的内存总数.
+
+第三部分是指交换分区
+```
+
+## sar,查看网络吞吐状态
+
+```
+#sar命令在这里可以查看网络设备的吞吐率。在排查性能问题时,可以通过网络设备的吞吐量,判断网络设备是否已经饱和
+sar -n DEV 1
+
+#
+#sar命令在这里用于查看TCP连接状态,其中包括:
+#active/s:每秒本地发起的TCP连接数,既通过connect调用创建的TCP连接;
+#passive/s:每秒远程发起的TCP连接数,即通过accept调用创建的TCP连接;
+#retrans/s:每秒TCP重传数量;
+#TCP连接数可以用来判断性能问题是否由于建立了过多的连接,进一步可以判断是主动发起的连接,还是被动接受的连接。TCP重传可能是因为网络环境恶劣,或者服务器压力过大导致丢包
+sar -n TCP,ETCP 1
+```
+
+## vmstat, 给定时间监控CPU使用率, 内存使用, 虚拟内存交互, IO读写
+
+```
+#2表示每2秒采集一次状态信息, 1表示只采集一次(忽略既是一直采集)
+vmstat 2 1
+
+eg:
+r b swpd free buff cache si so bi bo in cs us sy id wa
+1 0 0 3499840 315836 3819660 0 0 0 1 2 0 0 0 100 0
+0 0 0 3499584 315836 3819660 0 0 0 0 88 158 0 0 100 0
+0 0 0 3499708 315836 3819660 0 0 0 2 86 162 0 0 100 0
+0 0 0 3499708 315836 3819660 0 0 0 10 81 151 0 0 100 0
+1 0 0 3499732 315836 3819660 0 0 0 2 83 154 0 0 100 0
+```
+
+- r 表示运行队列(就是说多少个进程真的分配到CPU),我测试的服务器目前CPU比较空闲,没什么程序在跑,当这个值超过了CPU数目,就会出现CPU瓶颈了。这个也和top的负载有关系,一般负载超过了3就比较高,超过了5就高,超过了10就不正常了,服务器的状态很危险。top的负载类似每秒的运行队列。如果运行队列过大,表示你的CPU很繁忙,一般会造成CPU使用率很高。
+- b 表示阻塞的进程,这个不多说,进程阻塞,大家懂的。
+- swpd 虚拟内存已使用的大小,如果大于0,表示你的机器物理内存不足了,如果不是程序内存泄露的原因,那么你该升级内存了或者把耗内存的任务迁移到其他机器。
+- free 空闲的物理内存的大小,我的机器内存总共8G,剩余3415M。
+- buff Linux/Unix系统是用来存储,目录里面有什么内容,权限等的缓存,我本机大概占用300多M
+- cache cache直接用来记忆我们打开的文件,给文件做缓冲,我本机大概占用300多M(这里是Linux/Unix的聪明之处,把空闲的物理内存的一部分拿来做文件和目录的缓存,是为了提高 程序执行的性能,当程序使用内存时,buffer/cached会很快地被使用。)
+- si 每秒从磁盘读入虚拟内存的大小,如果这个值大于0,表示物理内存不够用或者内存泄露了,要查找耗内存进程解决掉。我的机器内存充裕,一切正常。
+- so 每秒虚拟内存写入磁盘的大小,如果这个值大于0,同上。
+- bi 块设备每秒接收的块数量,这里的块设备是指系统上所有的磁盘和其他块设备,默认块大小是1024byte,我本机上没什么IO操作,所以一直是0,但是我曾在处理拷贝大量数据(2-3T)的机器上看过可以达到140000/s,磁盘写入速度差不多140M每秒
+- bo 块设备每秒发送的块数量,例如我们读取文件,bo就要大于0。bi和bo一般都要接近0,不然就是IO过于频繁,需要调整。
+- in 每秒CPU的中断次数,包括时间中断
+- cs 每秒上下文切换次数,例如我们调用系统函数,就要进行上下文切换,线程的切换,也要进程上下文切换,这个值要越小越好,太大了,要考虑调低线程或者进程的数目,例如在apache和nginx这种web服务器中,我们一般做性能测试时会进行几千并发甚至几万并发的测试,选择web服务器的进程可以由进程或者线程的峰值一直下调,压测,直到cs到一个比较小的值,这个进程和线程数就是比较合适的值了。系统调用也是,每次调用系统函数,我们的代码就会进入内核空间,导致上下文切换,这个是很耗资源,也要尽量避免频繁调用系统函数。上下文切换次数过多表示你的CPU大部分浪费在上下文切换,导致CPU干正经事的时间少了,CPU没有充分利用,是不可取的。
+- us 用户CPU时间,我曾经在一个做加密解密很频繁的服务器上,可以看到us接近100,r运行队列达到80(机器在做压力测试,性能表现不佳)。
+- sy 系统CPU时间,如果太高,表示系统调用时间长,例如是IO操作频繁。
+- id 空闲 CPU时间,一般来说,id + us + sy = 100,一般我认为id是空闲CPU使用率,us是用户CPU使用率,sy是系统CPU使用率。
+- wt 等待IO CPU时间。
\ No newline at end of file
diff --git "a/\345\267\245\345\205\267/Maven\345\256\236\346\210\230.md" "b/\345\267\245\345\205\267/maven-overview.md"
similarity index 100%
rename from "\345\267\245\345\205\267/Maven\345\256\236\346\210\230.md"
rename to "\345\267\245\345\205\267/maven-overview.md"
diff --git "a/\346\266\210\346\201\257\351\230\237\345\210\227/RabbitMQ.md" "b/\346\266\210\346\201\257\351\230\237\345\210\227/RabbitMQ.md"
index c89865f..6fcc798 100644
--- "a/\346\266\210\346\201\257\351\230\237\345\210\227/RabbitMQ.md"
+++ "b/\346\266\210\346\201\257\351\230\237\345\210\227/RabbitMQ.md"
@@ -1,41 +1,14 @@
-
-
-
-
-- [简介](#%E7%AE%80%E4%BB%8B)
- - [基本概念](#%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5)
- - [什么时候使用MQ](#%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99%E4%BD%BF%E7%94%A8mq)
- - [优缺点](#%E4%BC%98%E7%BC%BA%E7%82%B9)
-- [Exchange 类型](#exchange-%E7%B1%BB%E5%9E%8B)
- - [direct](#direct)
- - [fanout](#fanout)
- - [topic](#topic)
- - [headers](#headers)
-- [消息丢失](#%E6%B6%88%E6%81%AF%E4%B8%A2%E5%A4%B1)
- - [生产者确认机制](#%E7%94%9F%E4%BA%A7%E8%80%85%E7%A1%AE%E8%AE%A4%E6%9C%BA%E5%88%B6)
- - [路由不可达消息](#%E8%B7%AF%E7%94%B1%E4%B8%8D%E5%8F%AF%E8%BE%BE%E6%B6%88%E6%81%AF)
- - [Return消息机制](#return%E6%B6%88%E6%81%AF%E6%9C%BA%E5%88%B6)
- - [备份交换机](#%E5%A4%87%E4%BB%BD%E4%BA%A4%E6%8D%A2%E6%9C%BA)
- - [消费者手动消息确认](#%E6%B6%88%E8%B4%B9%E8%80%85%E6%89%8B%E5%8A%A8%E6%B6%88%E6%81%AF%E7%A1%AE%E8%AE%A4)
- - [持久化](#%E6%8C%81%E4%B9%85%E5%8C%96)
- - [镜像队列](#%E9%95%9C%E5%83%8F%E9%98%9F%E5%88%97)
-- [重复消费](#%E9%87%8D%E5%A4%8D%E6%B6%88%E8%B4%B9)
-- [消费端限流](#%E6%B6%88%E8%B4%B9%E7%AB%AF%E9%99%90%E6%B5%81)
-- [死信队列](#%E6%AD%BB%E4%BF%A1%E9%98%9F%E5%88%97)
-- [其他](#%E5%85%B6%E4%BB%96)
- - [pull模式](#pull%E6%A8%A1%E5%BC%8F)
- - [消息过期时间](#%E6%B6%88%E6%81%AF%E8%BF%87%E6%9C%9F%E6%97%B6%E9%97%B4)
-- [参考链接](#%E5%8F%82%E8%80%83%E9%93%BE%E6%8E%A5)
-
-
-
-# 简介
+---
+sidebar: heading
+---
+
+## 什么是RabbitMQ?
RabbitMQ是一个由erlang开发的消息队列。消息队列用于应用间的异步协作。

-## 基本概念
+## RabbitMQ的组件
Message:由消息头和消息体组成。消息体是不透明的,而消息头则由一系列的可选属性组成,这些属性包括routing-key、priority、delivery-mode(是否持久性存储)等。
@@ -57,15 +30,23 @@ Broker:消息队列服务器实体。
以常见的订单系统为例,用户点击下单按钮之后的业务逻辑可能包括:扣减库存、生成相应单据、发短信通知。这种场景下就可以用 MQ 。将短信通知放到 MQ 异步执行,在下单的主流程(比如扣减库存、生成相应单据)完成之后发送一条消息到 MQ, 让主流程快速完结,而由另外的线程消费MQ的消息。
-## 优缺点
+## RabbitMQ的优缺点
缺点:使用erlang实现,不利于二次开发和维护;性能较kafka差,持久化消息和ACK确认的情况下生产和消费消息单机吞吐量大约在1-2万左右,kafka单机吞吐量在十万级别。
优点:有管理界面,方便使用;可靠性高;功能丰富,支持消息持久化、消息确认机制、多种消息分发机制。
+## RabbitMQ 有哪些重要的角色?
+
+RabbitMQ 中重要的角色有:生产者、消费者和代理。
+
+1. 生产者:消息的创建者,负责创建和推送数据到消息服务器;
+2. 消费者:消息的接收方,用于处理数据和确认消息;
-# Exchange 类型
+3. 代理:就是 RabbitMQ 本身,用于扮演“快递”的角色,本身不生产消息,只是扮演“快递”的角色。
+
+## Exchange 类型
Exchange分发消息时根据类型的不同分发策略不同,目前共四种类型:direct、fanout、topic、headers 。headers 模式根据消息的headers进行路由,此外 headers 交换器和 direct 交换器完全一致,但性能差很多。
@@ -78,35 +59,35 @@ Exchange规则。
| topic | 模糊匹配 |
| headers | Exchange不依赖于routing key与binding key的匹配规则来路由消息,而是根据发送的消息内容中的header属性进行匹配。 |
-## direct
+**direct**
direct交换机会将消息路由到binding key 和 routing key完全匹配的队列中。它是完全匹配、单播的模式。

-## fanout
+**fanout**
所有发到 fanout 类型交换机的消息都会路由到所有与该交换机绑定的队列上去。fanout 类型转发消息是最快的。

-## topic
+**topic**
-topic交换机使用routing key和binding key进行模糊匹配,匹配成功则将消息发送到相应的队列。routing key和binding key都是句点号“. ”分隔的字符串,binding key中可以存在两种特殊字符“*”与“#”,用于做模糊匹配,其中“\*”用于匹配一个单词,“#”用于匹配多个单词。
+topic交换机使用routing key和binding key进行模糊匹配,匹配成功则将消息发送到相应的队列。routing key和binding key都是句点号“. ”分隔的字符串,binding key中可以存在两种特殊字符“*”与“##”,用于做模糊匹配,其中“\*”用于匹配一个单词,“##”用于匹配多个单词。

-## headers
+**headers**
headers交换机是根据发送的消息内容中的headers属性进行路由的。在绑定Queue与Exchange时指定一组键值对;当消息发送到Exchange时,RabbitMQ会取到该消息的headers(也是一个键值对的形式),对比其中的键值对是否完全匹配Queue与Exchange绑定时指定的键值对;如果完全匹配则消息会路由到该Queue,否则不会路由到该Queue。
-# 消息丢失
+## 消息丢失
消息丢失场景:生产者生产消息到RabbitMQ Server消息丢失、RabbitMQ Server存储的消息丢失和RabbitMQ Server到消费者消息丢失。
消息丢失从三个方面来解决:生产者确认机制、消费者手动确认消息和持久化。
-## 生产者确认机制
+### 生产者确认机制
生产者发送消息到队列,无法确保发送的消息成功的到达server。
@@ -120,7 +101,7 @@ headers交换机是根据发送的消息内容中的headers属性进行路由的
```yaml
spring:
rabbitmq:
- #开启 confirm 确认机制
+ ##开启 confirm 确认机制
publisher-confirms: true
```
@@ -139,20 +120,20 @@ final RabbitTemplate.ConfirmCallback confirmCallback = (CorrelationData correlat
rabbitTemplate.setConfirmCallback(confirmCallback);
```
-## 路由不可达消息
+### 路由不可达消息
生产者确认机制只确保消息正确到达交换机,对于从交换机路由到Queue失败的消息,会被丢弃掉,导致消息丢失。
对于不可路由的消息,有两种处理方式:Return消息机制和备份交换机。
-### Return消息机制
+**Return消息机制**
Return消息机制提供了回调函数 ReturnCallback,当消息从交换机路由到Queue失败才会回调这个方法。需要将`mandatory` 设置为 `true` ,才能监听到路由不可达的消息。
```yaml
spring:
rabbitmq:
- #触发ReturnCallback必须设置mandatory=true, 否则Exchange没有找到Queue就会丢弃掉消息, 而不会触发ReturnCallback
+ ##触发ReturnCallback必须设置mandatory=true, 否则Exchange没有找到Queue就会丢弃掉消息, 而不会触发ReturnCallback
template.mandatory: true
```
@@ -167,11 +148,11 @@ rabbitTemplate.setReturnCallback(returnCallback);
当消息从交换机路由到Queue失败时,会返回 `return exchange: , routingKey: MAIL, replyCode: 312, replyText: NO_ROUTE`。
-### 备份交换机
+**备份交换机**
备份交换机alternate-exchange 是一个普通的exchange,当你发送消息到对应的exchange时,没有匹配到queue,就会自动转移到备份交换机对应的queue,这样消息就不会丢失。
-## 消费者手动消息确认
+### 消费者手动消息确认
有可能消费者收到消息还没来得及处理MQ服务就宕机了,导致消息丢失。因为消息者默认采用自动ack,一旦消费者收到消息后会通知MQ Server这条消息已经处理好了,MQ 就会移除这条消息。
@@ -180,7 +161,7 @@ rabbitTemplate.setReturnCallback(returnCallback);
消费者设置手动ack:
```java
-#设置消费端手动 ack
+##设置消费端手动 ack
spring.rabbitmq.listener.simple.acknowledge-mode=manual
```
@@ -204,7 +185,7 @@ spring.rabbitmq.listener.simple.acknowledge-mode=manual
当消息消费失败时,消费端给broker回复nack,如果consumer设置了requeue为false,则nack后broker会删除消息或者进入死信队列,否则消息会重新入队。
-## 持久化
+### 持久化
如果RabbitMQ服务异常导致重启,将会导致消息丢失。RabbitMQ提供了持久化的机制,将内存中的消息持久化到硬盘上,即使重启RabbitMQ,消息也不会丢失。
@@ -216,15 +197,13 @@ spring.rabbitmq.listener.simple.acknowledge-mode=manual
当发布一条消息到交换机上时,Rabbit会先把消息写入持久化日志,然后才向生产者发送响应。一旦从队列中消费了一条消息的话并且做了确认,RabbitMQ会在持久化日志中移除这条消息。在消费消息前,如果RabbitMQ重启的话,服务器会自动重建交换机和队列,加载持久化日志中的消息到相应的队列或者交换机上,保证消息不会丢失。
-## 镜像队列
+### 镜像队列
当MQ发生故障时,会导致服务不可用。引入RabbitMQ的镜像队列机制,将queue镜像到集群中其他的节点之上。如果集群中的一个节点失效了,能自动地切换到镜像中的另一个节点以保证服务的可用性。
通常每一个镜像队列都包含一个master和多个slave,分别对应于不同的节点。发送到镜像队列的所有消息总是被直接发送到master和所有的slave之上。除了publish外所有动作都只会向master发送,然后由master将命令执行的结果广播给slave,从镜像队列中的消费操作实际上是在master上执行的。
-
-
-# 重复消费
+## 消息重复消费怎么处理?
消息重复的原因有两个:1.生产时消息重复,2.消费时消息重复。
@@ -238,9 +217,7 @@ spring.rabbitmq.listener.simple.acknowledge-mode=manual
2. 如果不存在,则正常消费,消费完毕后写入redis/db
3. 如果存在,则证明消息被消费过,直接丢弃
-
-
-# 消费端限流
+## 消费端怎么进行限流?
当 RabbitMQ 服务器积压大量消息时,队列里的消息会大量涌入消费端,可能导致消费端服务器奔溃。这种情况下需要对消费端限流。
@@ -249,7 +226,7 @@ Spring RabbitMQ 提供参数 prefetch 可以设置单个请求处理的消息个
开启消费端限流:
```properties
-#在单个请求中处理的消息个数,unack的最大数量
+##在单个请求中处理的消息个数,unack的最大数量
spring.rabbitmq.listener.simple.prefetch=2
```
@@ -261,7 +238,7 @@ spring.rabbitmq.listener.simple.prefetch=2
void basicQos(int prefetchSize, int prefetchCount, boolean global) throws IOException;
```
-# 死信队列
+## 什么是死信队列?
消费失败的消息存放的队列。
@@ -383,9 +360,7 @@ public class DeadListener {
当普通队列中有死信时,RabbitMQ 就会自动的将这个消息重新发布到设置的死信交换机去,然后被路由到死信队列。可以监听死信队列中的消息做相应的处理。
-# 其他
-
-## pull模式
+## 说说pull模式
pull模式主要是通过channel.basicGet方法来获取消息,示例代码如下:
@@ -395,7 +370,7 @@ System.out.println(new String(response.getBody()));
channel.basicAck(response.getEnvelope().getDeliveryTag(),false);
```
-## 消息过期时间
+## 怎么设置消息的过期时间?
在生产端发送消息的时候可以给消息设置过期时间,单位为毫秒(ms)
@@ -406,7 +381,7 @@ msg.getMessageProperties().setExpiration("3000");
也可以在创建队列的时候指定队列的ttl,从消息入队列开始计算,超过该时间的消息将会被移除。
-# 参考链接
+## 参考链接
[RabbitMQ基础](https://www.jianshu.com/p/79ca08116d57)
diff --git "a/\346\266\210\346\201\257\351\230\237\345\210\227/kafka.md" "b/\346\266\210\346\201\257\351\230\237\345\210\227/kafka.md"
new file mode 100644
index 0000000..5dd9255
--- /dev/null
+++ "b/\346\266\210\346\201\257\351\230\237\345\210\227/kafka.md"
@@ -0,0 +1,168 @@
+---
+sidebar: heading
+---
+
+
+
+## Kafka 都有哪些特点?
+
+- 高吞吐量、低延迟:kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒,每个topic可以分多个partition, consumer group 对partition进行consume操作。
+- 可扩展性:kafka集群支持热扩展
+- 持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失
+- 容错性:允许集群中节点失败(若副本数量为n,则允许n-1个节点失败)
+- 高并发:支持数千个客户端同时读写
+
+## 请简述下你在哪些场景下会选择 Kafka?
+
+- 日志收集:一个公司可以用Kafka可以收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer,例如hadoop、HBase、Solr等。
+- 消息系统:解耦和生产者和消费者、缓存消息等。
+- 用户活动跟踪:Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到hadoop、数据仓库中做离线分析和挖掘。
+- 运营指标:Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告。
+- 流式处理:比如spark streaming和 Flink
+
+## Kafka 的设计架构你知道吗?
+
+
+Kafka 架构分为以下几个部分:
+
+- Producer :消息生产者,就是向 kafka broker 发消息的客户端。
+- Consumer :消息消费者,向 kafka broker 取消息的客户端。
+- Topic :可以理解为一个队列,一个 Topic 又分为一个或多个分区,
+- Consumer Group:这是 kafka 用来实现一个 topic 消息的广播(发给所有的 consumer)和单播(发给任意一个 consumer)的手段。一个 topic 可以有多个 Consumer Group。
+- Broker :一台 kafka 服务器就是一个 broker。一个集群由多个 broker 组成。一个 broker 可以容纳多个 topic。
+- Partition:为了实现扩展性,一个非常大的 topic 可以分布到多个 broker上,每个 partition 是一个有序的队列。partition 中的每条消息都会被分配一个有序的id(offset)。将消息发给 consumer,kafka 只保证按一个 partition 中的消息的顺序,不保证一个 topic 的整体(多个 partition 间)的顺序。
+- Offset:kafka 的存储文件都是按照 offset.kafka 来命名,用 offset 做名字的好处是方便查找。例如你想找位于 2049 的位置,只要找到 2048.kafka 的文件即可。当然 the first offset 就是 00000000000.kafka。
+
+## Kafka 分区的目的?
+
+分区对于 Kafka 集群的好处是:实现负载均衡。分区对于消费者来说,可以提高并发度,提高效率。
+
+## 你知道 Kafka 是如何做到消息的有序性?
+
+kafka 中的每个 partition 中的消息在写入时都是有序的,而且单独一个 partition 只能由一个消费者去消费,可以在里面保证消息的顺序性。但是分区之间的消息是不保证有序的。
+
+## Kafka Producer 的执行过程?
+
+1,Producer生产消息 --> 2,从Zookeeper找到Partition的Leader --> 3,推送消息 --> 4,通过ISR列表通知给Follower --> 5, Follower从Leader拉取消息,并发送ack --> 6,Leader收到所有副本的ack,更新Offset,并向Producer发送ack,表示消息写入成功。
+
+## 讲一下你使用 Kafka Consumer 消费消息时的[线程模型](https://link.segmentfault.com/?enc=sniyt20KkIXAqIjQ4fraxQ%3D%3D.cmMRO%2FsnA0vXdDv1sttq1%2BSlDgGBRoAPWJw1zI%2Fslu10a6nBqqaCr9uYIZXuLMdYNK9%2B%2B17KBqxtMLvdyqlxYQ%3D%3D),为何如此设计?
+
+Thread-Per-Consumer Model,这种多线程模型是利用Kafka的topic分多个partition的机制来实现并行:每个线程都有自己的consumer实例,负责消费若干个partition。各个线程之间是完全独立的,不涉及任何线程同步和通信,所以实现起来非常简单。
+
+## 请谈一谈 Kafka 数据一致性原理
+
+一致性就是说不论是老的 Leader 还是新选举的 Leader,Consumer 都能读到一样的数据。
+
+假设分区的副本为3,其中副本0是 Leader,副本1和副本2是 follower,并且在 ISR 列表里面。虽然副本0已经写入了 Message4,但是 Consumer 只能读取到 Message2。因为所有的 ISR 都同步了 Message2,只有 High Water Mark 以上的消息才支持 Consumer 读取,而 High Water Mark 取决于 ISR 列表里面偏移量最小的分区,对应于上图的副本2,这个很类似于木桶原理。
+
+这样做的原因是还没有被足够多副本复制的消息被认为是“不安全”的,如果 Leader 发生崩溃,另一个副本成为新 Leader,那么这些消息很可能丢失了。如果我们允许消费者读取这些消息,可能就会破坏一致性。试想,一个消费者从当前 Leader(副本0) 读取并处理了 Message4,这个时候 Leader 挂掉了,选举了副本1为新的 Leader,这时候另一个消费者再去从新的 Leader 读取消息,发现这个消息其实并不存在,这就导致了数据不一致性问题。
+
+当然,引入了 High Water Mark 机制,会导致 Broker 间的消息复制因为某些原因变慢,那么消息到达消费者的时间也会随之变长(因为我们会先等待消息复制完毕)。延迟时间可以通过参数 replica.lag.time.max.ms 参数配置,它指定了副本在复制消息时可被允许的最大延迟时间。
+
+## ISR、OSR、AR 是什么?
+
+ISR:In-Sync Replicas 副本同步队列
+
+OSR:Out-of-Sync Replicas
+
+AR:Assigned Replicas 所有副本
+
+ISR是由leader维护,follower从leader同步数据有一些延迟(具体可以参见 图文了解 Kafka 的副本复制机制),超过相应的阈值会把 follower 剔除出 ISR, 存入OSR(Out-of-Sync Replicas )列表,新加入的follower也会先存放在OSR中。AR=ISR+OSR。
+
+## LEO、HW、LSO、LW等分别代表什么
+
+LEO:是 LogEndOffset 的简称,代表当前日志文件中下一条
+
+HW:水位或水印(watermark)一词,也可称为高水位(high watermark),通常被用在流式处理领域(比如Apache Flink、Apache Spark等),以表征元素或事件在基于时间层面上的进度。在Kafka中,水位的概念反而与时间无关,而是与位置信息相关。严格来说,它表示的就是位置信息,即位移(offset)。取 partition 对应的 ISR中 最小的 LEO 作为 HW,consumer 最多只能消费到 HW 所在的位置上一条信息。
+
+LSO:是 LastStableOffset 的简称,对未完成的事务而言,LSO 的值等于事务中第一条消息的位置(firstUnstableOffset),对已完成的事务而言,它的值同 HW 相同
+
+LW:Low Watermark 低水位, 代表 AR 集合中最小的 logStartOffset 值。
+
+## 数据传输的事务有几种?
+
+数据传输的事务定义通常有以下三种级别:
+
+(1)最多一次: 消息不会被重复发送,最多被传输一次,但也有可能一次不传输
+(2)最少一次: 消息不会被漏发送,最少被传输一次,但也有可能被重复传输.
+(3)精确的一次(Exactly once): 不会漏传输也不会重复传输,每个消息都传输被
+
+## Kafka 消费者是否可以消费指定分区消息?
+
+Kafa consumer消费消息时,向broker发出fetch请求去消费特定分区的消息,consumer指定消息在日志中的偏移量(offset),就可以消费从这个位置开始的消息,customer拥有了offset的控制权,可以向后回滚去重新消费之前的消息,这是很有意义的。
+
+## Kafka消息是采用Pull模式,还是Push模式?
+
+Kafka最初考虑的问题是,customer应该从brokes拉取消息还是brokers将消息推送到consumer,也就是pull还push。在这方面,Kafka遵循了一种大部分消息系统共同的传统的设计:producer将消息推送到broker,consumer从broker拉取消息。
+
+一些消息系统比如Scribe和Apache Flume采用了push模式,将消息推送到下游的consumer。这样做有好处也有坏处:由broker决定消息推送的速率,对于不同消费速率的consumer就不太好处理了。消息系统都致力于让consumer以最大的速率最快速的消费消息,但不幸的是,push模式下,当broker推送的速率远大于consumer消费的速率时,consumer恐怕就要崩溃了。最终Kafka还是选取了传统的pull模式。
+
+Pull模式的另外一个好处是consumer可以自主决定是否批量的从broker拉取数据。Push模式必须在不知道下游consumer消费能力和消费策略的情况下决定是立即推送每条消息还是缓存之后批量推送。如果为了避免consumer崩溃而采用较低的推送速率,将可能导致一次只推送较少的消息而造成浪费。Pull模式下,consumer就可以根据自己的消费能力去决定这些策略。
+
+Pull有个缺点是,如果broker没有可供消费的消息,将导致consumer不断在循环中轮询,直到新消息到t达。为了避免这点,Kafka有个参数可以让consumer阻塞知道新消息到达(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发
+
+## Kafka 高效文件存储设计特点
+
+Kafka把topic中一个parition大文件分成多个小文件段,通过多个小文件段,就容易定期清除或删除已经消费完文件,减少磁盘占用。
+
+通过索引信息可以快速定位message和确定response的最大大小。
+
+通过index元数据全部映射到memory,可以避免segment file的IO磁盘操作。
+
+通过索引文件稀疏存储,可以大幅降低index文件元数据占用空间大小
+
+## Kafka创建Topic时如何将分区放置到不同的Broker中
+
+副本因子不能大于 Broker 的个数;
+
+第一个分区(编号为0)的第一个副本放置位置是随机从 brokerList 选择的;
+
+其他分区的第一个副本放置位置相对于第0个分区依次往后移。也就是如果我们有5个 Broker,5个分区,假设第一个分区放在第四个 Broker 上,那么第二个分区将会放在第五个 Broker 上;第三个分区将会放在第一个 Broker 上;第四个分区将会放在第二个 Broker 上,依次类推;
+
+剩余的副本相对于第一个副本放置位置其实是由 nextReplicaShift 决定的,而这个数也是随机产生的
+
+## 谈一谈 Kafka 的再均衡
+
+在Kafka中,当有新消费者加入或者订阅的topic数发生变化时,会触发Rebalance(再均衡:在同一个消费者组当中,分区的所有权从一个消费者转移到另外一个消费者)机制,Rebalance顾名思义就是重新均衡消费者消费。Rebalance的过程如下:
+
+第一步:所有成员都向coordinator发送请求,请求入组。一旦所有成员都发送了请求,coordinator会从中选择一个consumer担任leader的角色,并把组成员信息以及订阅信息发给leader。
+
+第二步:leader开始分配消费方案,指明具体哪个consumer负责消费哪些topic的哪些partition。一旦完成分配,leader会将这个方案发给coordinator。coordinator接收到分配方案之后会把方案发给各个consumer,这样组内的所有成员就都知道自己应该消费哪些分区了。
+
+所以对于Rebalance来说,Coordinator起着至关重要的作用
+
+## Kafka 是如何实现高吞吐率的?
+
+Kafka是分布式消息系统,需要处理海量的消息,Kafka的设计是把所有的消息都写入速度低容量大的硬盘,以此来换取更强的存储能力,但实际上,使用硬盘并没有带来过多的性能损失。kafka主要使用了以下几个方式实现了超高的吞吐率:
+
+- 顺序读写;
+- 零拷贝
+- 文件分段
+- 批量发送
+- 数据压缩。
+
+## Kafka 缺点?
+
+- 由于是批量发送,数据并非真正的实时;
+- 对于mqtt协议不支持;
+- 不支持物联网传感数据直接接入;
+- 仅支持统一分区内消息有序,无法实现全局消息有序;
+- 监控不完善,需要安装插件;
+- 依赖zookeeper进行元数据管理;
+
+
+## Kafka 新旧消费者的区别
+
+旧的 Kafka 消费者 API 主要包括:SimpleConsumer(简单消费者) 和 ZookeeperConsumerConnectir(高级消费者)。SimpleConsumer 名字看起来是简单消费者,但是其实用起来很不简单,可以使用它从特定的分区和偏移量开始读取消息。高级消费者和现在新的消费者有点像,有消费者群组,有分区再均衡,不过它使用 ZK 来管理消费者群组,并不具备偏移量和再均衡的可操控性。
+
+现在的消费者同时支持以上两种行为,所以为啥还用旧消费者 API 呢?
+
+## Kafka 分区数可以增加或减少吗?为什么?
+
+我们可以使用 bin/kafka-topics.sh 命令对 Kafka 增加 Kafka 的分区数据,但是 Kafka 不支持减少分区数。
+
+Kafka 分区数据不支持减少是由很多原因的,比如减少的分区其数据放到哪里去?是删除,还是保留?删除的话,那么这些没消费的消息不就丢了。如果保留这些消息如何放到其他分区里面?追加到其他分区后面的话那么就破坏了 Kafka 单个分区的有序性。如果要保证删除分区数据插入到其他分区保证有序性,那么实现起来逻辑就会非常复杂。
+
+
+
+> 参考:https://blog.51cto.com/u_15127589/2679155
\ No newline at end of file
From 93cc798a596168b82c9297d0e1ed24afcc36c272 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 30 Oct 2022 17:32:31 +0800
Subject: [PATCH 039/112] modify url
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 3ea05f6..9fc5372 100644
--- a/README.md
+++ b/README.md
@@ -183,7 +183,7 @@
# 海量数据场景题
-1. [统计不同电话号码的个数](https://topjavaer.cn/system-design/1-scan-code-login.html)
+1. [统计不同电话号码的个数](https://topjavaer.cn/mass-data/1-count-phone-num.html)
2. [出现频率最高的100个词](https://topjavaer.cn/mass-data/2-find-hign-frequency-word.html)
3. [查找两个大文件共同的URL](https://topjavaer.cn/mass-data/3-find-same-url.html)
4. [如何在100亿的数据中找到中位数](https://topjavaer.cn/mass-data/4-find-mid-num.html)
From 9afe51d51d4648b9e70b520e6f243c8683967b66 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 6 Nov 2022 15:24:32 +0800
Subject: [PATCH 040/112] interview
---
README.md | 44 ++---
interview/1-byte-and-dance.md | 316 ++++++++++++++++++++++++++++++++++
interview/10-netease.md | 115 +++++++++++++
interview/2-tencent.md | 66 +++++++
interview/3-baidu.md | 107 ++++++++++++
interview/4-ali.md | 79 +++++++++
interview/5-kuaishou.md | 79 +++++++++
interview/6-meituan.md | 74 ++++++++
interview/7-shopee.md | 73 ++++++++
interview/8-jingdong.md | 58 +++++++
interview/9-huawei.md | 56 ++++++
interview/README.md | 13 ++
12 files changed, 1055 insertions(+), 25 deletions(-)
create mode 100644 interview/1-byte-and-dance.md
create mode 100644 interview/10-netease.md
create mode 100644 interview/2-tencent.md
create mode 100644 interview/3-baidu.md
create mode 100644 interview/4-ali.md
create mode 100644 interview/5-kuaishou.md
create mode 100644 interview/6-meituan.md
create mode 100644 interview/7-shopee.md
create mode 100644 interview/8-jingdong.md
create mode 100644 interview/9-huawei.md
create mode 100644 interview/README.md
diff --git a/README.md b/README.md
index 9fc5372..88004f5 100644
--- a/README.md
+++ b/README.md
@@ -190,6 +190,25 @@
5. [找出最热门的查询串](https://topjavaer.cn/mass-data/5-find-hot-string.html)
6. [如何找出排名前500的数字](https://topjavaer.cn/mass-data/6-top-500-num.html)
+# 大厂面经汇总
+
+- [字节跳动](./interview/1-byte-and-dance.md)
+- [腾讯](./interview/2-tencent.md)
+- [百度](./interview/3-baidu.md)
+- [阿里](./interview/4-ali.md)
+- [快手](./interview/5-kuaishou.md)
+- [美团](./interview/6-meituan.md)
+- [shopee](./interview/7-shopee.md)
+- [京东](./interview/8-jingdong.md)
+- [华为](./interview/9-huawei.md)
+- [网易](./interview/10-netease.md)
+
+# 其他优质文章
+
+[优质文章汇总,持续更新中~](https://topjavaer.cn/advance/excellent-article/)
+
+
+
# 工具
[Git 超详细总结!](工具/git-overview.md)(推荐 :+1:)
@@ -200,31 +219,6 @@
[Maven 基础总结!](tools/maven-overview.md)
-# 大厂面试系列
-
-1. [字节跳动一面面经](https://mp.weixin.qq.com/s/RH-SunzjqUTTx8HWaCmCcw)
-2. [别再问我Java List八股文了!](https://mp.weixin.qq.com/s/doyy_GYGWoH_YHgyMijStA)
-3. [腾讯面试,你真的懂HTTP吗?](https://mp.weixin.qq.com/s/kC7XRBfO7Z5hZcX6Dz2viw)
-4. [美团二面面经,最后竟然有惊喜?](https://mp.weixin.qq.com/s/3HvOtTU29HGALqmeeOZNWw)
-5. [Java多线程,被面试官难倒了!](https://mp.weixin.qq.com/s/tv8pOLaS6hpwgbKOB9w0Zw)
-6. [京东二面,Redis为什么这么快?](https://mp.weixin.qq.com/s/S3vN5T9HpziRd2s5ysLaSg)
-7. [MySQL索引,给我整不会了!](https://mp.weixin.qq.com/s/Q5CrDlNInpnckJaBQSrA7w)
-8. [别再问我Java虚拟机八股文了!](https://mp.weixin.qq.com/s/npo5-VqQt5sqZiSwPv6LVw)
-9. [计算机网络,问傻了!](https://mp.weixin.qq.com/s/WXcMLa_tdxpRLhO4U8LHIQ)
-10. [Spring这几道题你会吗?](https://mp.weixin.qq.com/s/DtgYRFfOQxQdtQosCU-6aw)
-11. [面向对象编程?](https://mp.weixin.qq.com/s/M8jDnLat61YAbM1-jIhJIA)
-12. [Java内功深厚?](https://mp.weixin.qq.com/s/v_kWSHX9GMS_aoqfwUMKsg)
-13. [面试初级Java开发,面试官问我MySQL架构?](https://mp.weixin.qq.com/s/JvDZCk4IecmaEYfFsRhpjQ)
-14. [手写红黑树?](https://mp.weixin.qq.com/s/yznh_IfMg4hWqU62U-t9GQ)
-15. [面完阿里,直接入职!](https://mp.weixin.qq.com/s/49QJ1FzaGTe-_54PT8_8jA)
-16. [华为面经](https://mp.weixin.qq.com/s/KmjwoG7pNvAHiX1UNnef6g)
-
-# 其他优质文章
-
-[优质文章汇总,持续更新中~](https://topjavaer.cn/advance/excellent-article/)
-
-
-
# 交流
如果想进**技术、面试交流群**,可以扫描下方二维码加我微信,**备注加群**,我拉你进群,群里有BAT大佬,互相学习~
diff --git a/interview/1-byte-and-dance.md b/interview/1-byte-and-dance.md
new file mode 100644
index 0000000..185ae8f
--- /dev/null
+++ b/interview/1-byte-and-dance.md
@@ -0,0 +1,316 @@
+# 字节跳动
+
+## 面经1
+
+一面:
+
+ 1.IM系统用户登录怎么实现的?
+
+ 2.登录状态是怎么保存的?session是怎么获取的?sessionid是怎么识别的?整个流程是什么样的?有没有考虑分布式session?
+
+ 3.Redis的数据类型
+
+ 4.Redis数据类型的底层数据结构
+
+ 5.三次握手、四次挥手
+
+ 6.Redis持久化机制
+
+ 7.MySQL的InnoDB索引数据结构
+
+ 8.哪些SQL的关键字会让索引失效
+
+ 9.队列、栈、数组、链表
+
+ 10.算法题:leetcode 92题
+
+ 二面:
+
+1.讲讲爬虫的构成
+
+2.爬虫抓到的数据不清洗吗?不去重吗?
+
+3.对爬虫有什么更多的了解吗?
+
+4.Linux进程间通信机制
+
+5.进程和线程的区别
+
+6.线程私有的数据有哪些?(不是Java线程)
+
+7.讲一下堆排序,每次调整的时间复杂度?堆排序是稳定的吗?(一开始说错了,应该是不稳定的,后面面试官问稳定的定义是什么)
+
+8.哈希表的原理,怎么减小哈希表的冲突,怎么保证哈希表的并行读写
+
+9.Kafka用过吗?说说Kafka的原理?怎么保证Kafka的高可用?Kafka怎么保证消息有序?
+
+10.项目里的set实现点赞,zset实现关注,为什么?
+
+11.zset底层实现?说一下跳表?节点的高度是多少?怎么决定节点的高度?
+
+12.https了解吗?
+
+13.中间人攻击知道吗?怎么做https的抓包?https怎么篡改?
+
+14.虚拟地址到物理地址的映射过程
+
+15.算法题:给一个数组,建一颗最小高度的二叉树(递归和非递归)
+
+ 三面:
+
+1.介绍一下做过的项目,哪些挑战性比较大,比较有难度的
+
+2.IM项目怎么用Netty的,为什么要用Netty,长连接交互是怎样的
+
+3.消息怎么存储,怎么发送,怎么知道消息已读和未读的
+
+4.读了5条消息、又来5条消息,你是怎么去更新的,你的消息是幂等的吗?
+
+5.项目里怎么用ES的,ES怎么支持搜索的
+
+6.技术论坛网站的评论是怎么存储的
+
+7.查询评论是在DB里扫表查询吗?怎么展示所有的评论?性能如何?想要查询更快可以做哪些优化?
+
+8.结合缓存和DB的时候会出现哪些问题?要怎么解决?
+
+9.快排了解吗?介绍一下快排?时间复杂度是多少?为什么会退化成O(n^2)?单链表可以做快排吗?快排最核心的逻辑是什么?写一下单链表快排
+
+## 面经2
+
+一年经验,Java开发
+
+1. 自我介绍;
+2. 项目介绍;(延申较浅)
+3. 手撕算法:NC95 最长连续子序列
+4. 手撕算法:NC91 最长递增子序列
+5. 线程和进程的区别?怎么创建线程?有哪些状态?有什么区别?
+6. Java1.8了解哪些新特性?重点讲讲HashMap和CurrentHashMap。
+7. OSI七层协议和TCP/IP五层协议有什么区别?TCP/UDP在哪一层?对TCP了解多少?
+8. 你还有什么要问的吗?
+
+总结:开幕雷击,字节对算法还是挺看重的。
+
+## 面经3
+
+字节电商后端
+
+- 进程和线程区别
+- 为什么进程切换开销比线程切换开销大?
+- 你知道逻辑地址和物理地址么?
+- sql题(表T (id,name,salary,city)用SQL实现找到同时符合城市平均工资大于5000,单人工资大于10000的人的名字)
+- wifi属于OSI哪一层
+- 三次握手四次挥手相关(细节,以及为什么三次,为什么四次)
+- hashmap的底层原理
+- 红黑树和二叉搜索树,二叉树之间的区别
+- 为什么hashmap不直接用红黑树
+
+ 算法题
+
+1. 求从一个树的左边的叶子节点到一个树右边的叶子节点的最短路径)
+
+2. 最长回文子串
+3. 给定一个数N,求(1-n)所有的可能子序列)比如给个1 返回1 给个2 返回1,2,1 2
+
+## 面经4
+
+1. 自我介绍
+2. 项目
+3. volatile原理和作用
+4. 什么是指令重排序,举个例子
+5. AQS原理
+6. JVM内存模型
+7. 类加载过程
+8. 双亲委派机制
+9. 介绍代理模式
+10. redis数据结构
+11. HTTP状态码,header
+12. MVCC原理
+13. 算法:
+
+ - 买股票无限次
+ - 买股票只买一次
+ - 买股票有手续费
+ - 买股票最多买两次
+
+## 面经5
+
+1. 自我介绍
+2. 业务部分:
+
+ - 2.1 堆和栈的区别
+ - 2.2 调用一个函数在堆中的过程
+ - 2.3 密码学 中间人攻击、RSA、DSA密码的差别、加密过程,私钥加密公钥加密,数字签名的过程(跟本人网络空间安全背景有关)
+ - 2.4 彩虹表、 kerberos、ECC(椭圆曲线加密)
+
+3. 算法题:
+
+ 4. 一个无序数组,求其topK
+
+ 5. 时段统计。动态规划。
+
+## 面经6
+
+1. 自我介绍
+2. 实习介绍:你在腾讯干了啥
+3. 实习项目介绍:这个项目(深挖)
+4. 项目为啥立项,你做了什么模块
+5. 你的日志模块是怎么设计的,怎么开发的
+6. 你的token怎么做的,怎么保证唯一
+7. 你开发的业务模块举个例子(给他举了),你是怎么设计数据表的,具体用了哪些中间件
+8. mysql中inoodb的索引有哪些种类
+9. B+树索引具体是怎么实现的
+10. B树与B+树的区别
+11. 为什么B+树的中间节点不储存数据?
+12. 给一个索引,在有的查询过程中他没有走索引查询,说说你能想到的原因
+13. 红黑树了解过么?
+14. 用过redis么?(妈耶能别聊数据库了么)
+15. 你提到了kafka是吧,来聊一哈
+16. 聊一下你对git的理解
+17. git rebase与git merge的区别
+18. 聊一下你对工作流的理解
+
+手撕算法:
+
+给四个整数,判断是否只通过四则运算加()优先级能否运算出24
+
+## 面经7
+
+一面:
+
+1. 自我介绍
+2. JAVA SDK起到的作用
+3. 项目
+4. 数据流(项目)
+5. 排序(介绍下你知道的排序和复杂度)
+6. Arrays.sort底层的排序算法(有三种策略)
+7. 堆排序基本思路
+8. linux,操作系统的开机流程(这题我不会。)
+9. 进程和线程的区别
+10. 进程切换会发生什么
+11. 进程调度算法有哪些
+12. TCP、udp区别
+13. java锁,关键字区别
+14. 公平锁、非公平锁解释一下
+
+二面:
+
+1. 算法题:由前序遍历中序遍历重建子树;
+2. 为什么静态类中不能使用非静态类(从类加载过程回答);
+3. java类加载过程;
+ 3.1. 加载阶段中,为什么要有自定义的类加载器;
+ 3.2. 双亲委派原则的机制;
+4. HashMap数据结构;
+ 4.1. 为什么小于6是链表,大于8变成红黑树;
+ 4.2. HashMap扩容机制;
+ 4.3. HashMap是否线程安全,例子;
+ 4.4. ConcurrentHashMap和HashTable的区别;
+ 4.5. ConcurrentHashMap如何保证高效,为什么是线程安全,为什么比HashTable优秀,分段锁机制;
+ 4.6. CAS能保证线程安全吗(我回答能,面试官说不能。。估计想考ABA问题),volatile关键字能保证线程安全吗;
+5. 随机数求根,比如根号二的值。(二分查询)
+6. 有n个筐,筐固定,每个筐内有不同数目的苹果,移动苹果,使每个筐苹果平均(移动的代价:1~2算1步,1~3算2步)使步数最小;
+
+三面:
+
+1. 自我介绍
+2. 解决什么问题,做了些什么?(项目)
+3. 多个接口,有失败怎么办(项目)
+4. redis分布式锁怎么实现
+5. 时间过期怎么办
+6. ArrayList怎么扩容,时间复杂度O(n)?插尾部O(1),平均是多少,答案O(2)需要考虑扩容,小伙伴们可以自己推一下。
+7. HashMap底层原理
+8. mysql索引什么原理、B+树
+9. mysql和redis区别(讲一下各自优缺点)
+10. 为什么不用redis存数据?
+11. 算法:LRU缓存(),先讲一下再写
+
+## 面经8
+
+抖音电商
+
+1. 自我介绍
+2. 讲一下HashMap的put方法
+3. 讲一下HashMap的扩容过程
+4. 讲一下你自定义协议怎么解决粘包问题的?
+5. 算法题:LeetCode 二叉树根节点到子节点的路径和
+6. mysql的索引结构
+7. 为什么用B+树呢?
+8. having的作用
+9. 聚簇索引、非聚簇索引
+10. 聚簇索引比非聚簇索引的优点
+11. mysql的四个隔离级别,应用场景
+12. 如何在可重复读隔离级别解决幻读问题
+13. dubbo的负载均衡策略
+14. java的动态代理
+15. Spring哪里用到了动态代理?
+16. CGlib动态代理说一下
+17. MQ如何保证消息不会丢失
+
+## 面经9
+
+一面
+
+- 怎么理解微服务
+- 微服务的缺点
+- 微服务之间怎么做负载均衡
+- Oauth2基本流程、原理
+- 登录模块是怎么做的
+- cookie和session的区别
+- 购物车为什么用Redis存,是永久存储吗
+- 为什么购物车多读多写
+- Redis怎样清除过期key,有哪些策略
+- lru是怎样的过程
+- Redis字典底层怎么实现的
+- hashtable是怎样实现的
+- ziplist怎样实现的
+- 普通的哈希表怎样实现的
+- 哈希表怎么扩容
+- 使用MQ的好处
+- MQ解耦和微服务解耦的区别
+- 算法:最长回文子串
+- https建立连接的过程(SSL/TLS协商的过程)
+- 对称加密和非对称加密的优缺点
+- 为什么要区分内核态和用户态
+- 什么时候从用户态切换到内核态
+- 你编程的情况下,系统调用什么时候会发生
+ 反问:业务,开发语言,表现,对应届生的要求(重点是基础和算法)
+ 面试体验不错,但是项目挖的有点深
+
+2面
+
+- 手写单例模式
+- volatile什么作用
+- 多线程的几种实现方式
+- 四种方式的区别
+- 锁用过哪些
+- 排它锁什么意思
+- 自旋锁什么意思
+- CAS相关
+- MySQL可以不指定主键建表吗,背后的逻辑是什么
+- 聚簇索引和其他索引有什么区别
+- 建唯一索引,插入数据时是怎么处理的
+- 重复插入会报错,是怎么处理的
+- 不同事物隔离级别的实现
+- 以前没有实习过吗
+- lc40 组合总和II
+ 反问:部门怎样培养新人,刚进来做什么(学基础,语言和中间件,做demo),大概多久做需求(1周到1个月不等,看学习情况),框架和中间件以开源的为主还是以自研的为主(自研的)
+
+3面
+
+- 有在实习吗
+- 面试通过后可以实习吗
+- 做项目的过程中遇到过什么问题
+- 内存泄露具体发生在哪
+- 什么情况下会出现多线程访问
+- 缓存穿透,怎么解决 (好像一紧张说成缓存击穿了,面试复盘的时候才发现。。。)
+- 缓存雪崩,怎么解决
+- 缓存与数据库数据一致性
+- 超卖问题怎么解决的
+- 集群环境下,Redis内存里的数据怎么保证一致
+- 算法:给定一个字符数组,和一个字符串,在字符串里找到任意一个完全由字符数组组成的子串,字符顺序无所谓(滑动窗口)
+
+
+
+**最后给大家分享一份精心整理的大厂高频面试题PDF,需要的小伙伴可以自行下载:**
+
+[大厂面试手册](http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd)
\ No newline at end of file
diff --git a/interview/10-netease.md b/interview/10-netease.md
new file mode 100644
index 0000000..7267aca
--- /dev/null
+++ b/interview/10-netease.md
@@ -0,0 +1,115 @@
+# 网易面经
+
+## 面经1
+
+- 自我介绍
+- 如何创建一个Java对象
+- 在哪检查类的合法性
+- 如果这个类不存在,会怎么样
+- 类的加载机制
+- Thread的几种状态,调用什么方法切换的
+- 对象锁,2种暂停
+- Wait()和sleep()的区别
+- 用的线程池叫什么,有什么特性
+- 对springboot的理解
+- springMVC的filter和xx的区别
+- ArrayList和LinkedList的区别
+
+
+
+## 面经2
+
+- 怼项目
+- A、B、C并发执行完,D、E、F并发执行实现方式
+- 线程池
+- CountDownLatch、LinkedHashMap、AQS实现原理
+- 部分J.U.C包底层实现
+- Sychronized和Lock区别
+- Condition用法
+- 用sychronized实现Condition功能
+- Condition和sychronized实现Condition两种方式有什么区别
+- JVM分区
+- 垃圾回收
+- 类加载机制
+- 双亲委派机制
+- Redis基本数据类型
+- Redis集群方式
+- Redis哨兵
+- Redis备份
+- Redis持久化方式及区别、优缺点、实现方式
+- Redis集群作用
+- MySQL索引方式
+- MySQL组合索引
+- MySQL实现组合索引方式
+- MySQL集群方式
+- MySQL日志文件
+- MySQL集群实现原理
+- MySQL事物隔离级别,作用
+- Spring IOC
+- 怎么解决循环引用
+- 聚簇索引和非聚簇索引
+- 怎么自定义String类型
+
+## 面经3
+
+- 自我介绍。
+- 你在xxx实习前后端都写?说说vue,angular,react的区别。
+- 如果给你个新项目,你用哪个框架,为什么。
+- 听说你用过缓存?用了什么缓存?几级缓存?怎么用的?
+- 缓存怎么保证和内存的数据一致性?
+- 如果让你自己设计一个缓存系统,你怎么设计?
+- 你了解哪些 hash 方法?一致性 hash 是干嘛的?
+- 对分布式有多少了解?
+- 为什么用 springboot,好在哪?
+- 给你一个sql语句,怎么判断有没有命中索引?
+- 如果让你现在设计xx项目,你会怎么做?
+- 你觉得java程序员写代码的时候应该关注哪些地方?
+- 在xxx实习中项目的难点?
+- pb和 json 你会怎么使用?
+
+## 面经4
+
+- 说一个源码改进业务的例子。
+- websocket心跳如何实现?
+- zk/db/redis锁怎么选型。
+- 8台物理机能撑住百万的长连接,一台能撑住13w???数据感觉有问题,至少有20台吧(lf+hl)
+- 多租户隔离,为什么不用两个进程?
+- 需要下沉容器层,有插件层消除重复。
+- 表现为同一个应用,然后只注册到注册中心一次。两个应用可以使用相同端口号。
+- 另外进程就更重了,要消耗更多的资源,而且不好管控。
+- 流程编排分支条件如何实现?
+- 业务身份,这个有问题,如果大量增加分支,会拷贝多份,会有很多重复的规则文件,但是只有一两个组件不一样。
+- 目前做法时在组件中可能有业务条件判断。后续会使用表达式语言增加分支条件支持,业务身份只支持大粒度的区分。
+- 流程编排如何避免大量调用下游服务,是否做了隔离?
+- 感觉调用下游要做熔断,例如hystrix。但是这个有点重了。sdk要做薄。
+- 客服IM也区分在线消息和离线消息,都会存起来,如果掉线了,下次登录成功了主动拉取就行。
+
+
+## 面经5
+
+- 介绍一下JVM内存模型。
+- MySQL索引优化原则
+- BufferPool原理聊聊。
+- 解释一下什么是负载均衡,Dubbo的负载均衡说一下?
+- 当MySQL单表记录数过大时,数据库如何优化?
+- 一个4库的怎么拆分成8库的在表数量不变的情况下?
+- 举举例子业务中DDD的设计。
+- 函数式编程的本质是什么,为什么需要了解过吗?
+- 知道流的原理吗 Foreach函数碰到报错后面的执行吗?为什么?
+- kafka消息怎么保证不丢失的?
+- 为什么跳槽?
+- 对不是互联网电商方向,其他方向感兴趣吗?
+- 今后有什么职场规划或者学习计划。
+- 你认为你的个人性格是怎样的呢。
+
+
+## 面经6
+
+- 自我介绍
+- 项目介绍
+- 浏览器输入 youdao.com 发生了什么有多详细说多详细。一直问到数据链路层 mac地址
+- TCP 有哪些状态
+- 归并排序 思想 及复杂度
+- Mysql MVCC
+- 算法题。剑指Offer26,树的子结构
+- 反问
\ No newline at end of file
diff --git a/interview/2-tencent.md b/interview/2-tencent.md
new file mode 100644
index 0000000..c37e35f
--- /dev/null
+++ b/interview/2-tencent.md
@@ -0,0 +1,66 @@
+# 腾讯
+
+## 一面
+1. mysql索引结构?
+2. redis持久化策略?
+3. zookeeper节点类型说一下;
+4. zookeeper选举机制?
+5. zookeeper主节点故障,如何重新选举?
+6. syn机制?
+7. 线程池的核心参数;
+8. threadlocal的实现,原理,业务用来做什么?
+9. spring DI的原理;
+10. 四次挥手;
+11. gc root选择;
+12. 标记清除算法的过程,标记清楚算法如何给对象分配内存空间?
+13. cms算法的缺点;
+## 二面
+1. CorruntHashmap理解
+2. ThreadLocal原理
+3. hashmap;
+4. Java数据类型,同步机制;
+5. 讲讲贪心算法;
+6. 如果线上用户出现502错误你怎么排查?
+7. 并发量很大,服务器宕机。你会怎么做?
+## 三面
+1. syn和lock的区别,哪个更好?怎么选择?
+2. hashmap源码,为什么8个节点变成红黑树又为什么到了6个节点才恢复为链表?
+3. 缓存穿透,怎么解决?
+4. 负载均衡算法,实现;
+5. 轮询和随机的缺点;
+6. 分布式服务治理;
+7. dns迭代和递归的区别;
+8. 算法题:最长回文串
+9. 为什么连接的时候是三次握手,关闭的时候却是四次握手?
+## 四面
+1. 自我介绍 就背景进行一些提问
+2. 简单说说计算机网络
+3. 简单描述一下从浏览器输入一个地址到服务端整个交互过程
+4. 说说数据结构
+5. 操作系统用过吗
+6. 用过 linux 的哪些命令
+7. 查看一个进程监听了哪些端口
+8. 详细介绍项目(简历上的域名访问不了)
+9. 讲解之前工作经历中做的东西
+ 10.做一道算法题(判断二叉树是否对称)
+10. java 如何从源代码转换成机器码执行的
+11. java 的击穿
+12. 网络的七层结构
+13. tcp\udp 详解 区别
+14. https 协议的交互过程
+15. linux 基础命令
+16. linux 开机过程
+17. 了解现在市面上主流的 cpu 架构
+18. fpga 概念了解吗
+19. 市面上的图数据库
+20. rdf 讲解
+21. 图数据库底层存储
+22. b 树,b+树的概念和区别
+23. 红黑树平衡二叉树优缺点和应用场景
+24. 有没有了解 docker 等云技术
+
+
+
+**最后给大家分享一份精心整理的大厂高频面试题PDF,需要的小伙伴可以自行下载:**
+
+[大厂面试手册](http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd)
\ No newline at end of file
diff --git a/interview/3-baidu.md b/interview/3-baidu.md
new file mode 100644
index 0000000..18caa36
--- /dev/null
+++ b/interview/3-baidu.md
@@ -0,0 +1,107 @@
+# 百度
+
+## 面经1
+
+shiro的组件
+分布式一致性算法
+zookeeper那些能参与投票,leader能投票吗?
+netty零拷贝实现
+volatile,如何感知到变量变化的
+redis高可用
+http如何跨域?
+tcp如何长链接。
+http如何操作浏览器缓存。
+用过消息队列吗?
+怎么自己扩展validator(参数校验)
+jwt组成 header payload 签名加密算法那些。
+rsa如何运用到jwt中
+synchronized和volatile的区别
+什么是上下文切换,URL解析过程
+http有那些方法,get那些
+进程和线程的区别。
+和别人协作出现冲突怎么办
+如何学一个新语言
+怎么自学的
+
+## 面经2
+
+说说IO多路复用
+你刚刚说的多路复用针对的是各个请求(比如set,get),那返回值Redis是怎么处理的(愣住)
+MySQL B+树一般几层,怎么算的
+数据库隔离级别
+脏读、不可重复读、幻读(结合具体场景来讲)
+MySQL隔离级别分别怎么实现的
+MVCC
+redo log、undo log
+刷脏页的流程
+算法题:平方根
+
+## 面经3
+
+自我介绍
+项目是自己练手的项目吗,怎么找的
+项目是从0开始搭建的,还是有用开源的脚手架
+秒杀大概用到哪些东西,怎么实现的
+MQ幂等性和消息积压问题
+缓存与数据库数据一致性
+唯一ID
+Java里怎么保证多个线程的互斥性
+一个线程有哪些状态
+AQS怎么理解的
+Spring IOC容器创建Bean的流程
+创建的Bean是单例还是多例的
+SpringCloud config是怎么在Bean创建后更新Bean的值的
+SpringBoot自动配置原理
+SpringMVC执行流程
+使用Spring和直接使用Java语言面向对象开发,有哪些好处
+怎么理解面向对象
+了解哪些设计模式
+策略模式描述一下
+JVM由哪些模块组成
+框架里打破双亲委派机制的SPI大概怎么实现的
+那说说双亲委派
+垃圾回收主要回收哪些区域
+怎么识别哪些是垃圾
+哪些是根节点
+什么时候会出现Full GC
+不同垃圾收集器的区别
+TCP为什么要握三次手,为什么要挥四次手,大概什么流程
+实现环形队列(数组,增加和删除功能)
+反转链表(迭代)
+
+## 面经4
+专业是偏向硬件吗
+对百度了解多少
+有什么兴趣爱好
+经常打球吗
+喜欢听什么音乐
+经常听音乐吗,什么时候开始喜欢听音乐的
+你说两个具体的歌名我听听
+平时是怎样的一个人,有什么特点
+有做过什么有成就感的事吗
+后面选择百度的概率有多少
+想过自己5年后、10年后是怎样的吗
+
+## 面经5
+
+1.面试官介绍自己,然后自我介绍
+2.java中的线程池有哪些?为什么使用线程池?你在哪里使用过或是见过?
+3.Mysql底层是怎么实现的?从内存布局,磁盘布局说起?
+4.Mysql有哪些索引?B树和B+树的区别,分别解决了什么问题?
+5.try catch finally机制讲解一下?
+6.为什么要使用SpringBoot做开发?与传统的开发有什么不一样的?
+7.什么是微服务?微服务是如何实现服务的注册与发现的?
+8.java中的集合分类有哪些?知道Queue吗?她下面有哪些实现类?重点说说HashMap?
+9.在集合中哪些集合类是线程安全的?
+10.什么是数字签名,作用是什么?使用的是什么算法?
+11.常见的网络攻击有哪些?
+12.在表单提交的时候,容易发起什么样的攻击?
+13.在进行服务调用的时候如何进行身份验证,如何防止网络攻击?
+14.你见过哪些安全框架?具体怎么使用的?(shiro)
+15.两道算法题:1)普通的二分查找,问了其中的一些细节,二分查找存在的问题? 2)判断S1中是不是有S2的排列,找到返回true,否则返回false
+16.Cookie和session 的使用场景,他们之间的关系?
+17.String,StringBuilder,StringBuffer的区别,String的两种初始化的区别?
+
+**最后给大家分享一份精心整理的大厂高频面试题PDF,需要的小伙伴可以自行下载:**
+
+[大厂面试手册](http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd)
\ No newline at end of file
diff --git a/interview/4-ali.md b/interview/4-ali.md
new file mode 100644
index 0000000..2c99b2e
--- /dev/null
+++ b/interview/4-ali.md
@@ -0,0 +1,79 @@
+# 阿里
+
+## 面经1
+
+1. 简单介绍项目
+2. 知道哪些数据结构以及他们的特点
+3. 链表增删快,那如何提高其查询效率,有没有什么想法?
+4. B+树了解吗?B+树如何范围查询?B+树退化的极端情况是什么?
+5. 跳表了解吗?
+6. 大顶堆、小顶堆了解吗?
+7. 实现长地址请求到服务端,然后服务端重定向短地址给客户端,如何实现长短地址的互相映射?
+8. 那我现在有10份数据,有1000个线程来争抢,你要怎么处理?
+9. 分布式是什么?为什么要分布式?分布式又会有哪些问题?分布式系统是如何实现事物的?
+10. Redis集群了解吗?如何处理宕机的情况?Redis的同步策略?
+11. LRU算法了解吗?你会如何实现它?这个算法可以应用在哪些场景下?
+12. TCP为什么是三次握手?两次行不行?多次行不行?
+13. TCP的安全性是如何实现的?两台服务器之间可以同时建立多条TCP链接吗?怎么实现的?
+14. 客服端输入一个网址后,是如何拿到客服想要的数据的,是怎样在网络中传输的?
+15. cookie和session
+16. java有哪些锁?共享锁是什么?CAS?乐观锁和悲观锁?synchronied的底层原理?锁升级?死锁怎么形成的?如何破解死锁?
+
+## 面经2
+
+
+1. Java容器:List,Set,Map
+2. Map的遍历方式
+3. HashMap扩容为什么是扩为两倍?
+4. Java线程同步机制(信号量,闭锁,栅栏)
+5. 对volatile的理解:常用于状态标记
+6. 八种基本数据类型的大小以及他们的封装类(顺带了解自动拆箱与装箱)
+7. 线程阻塞几种情况?如何自己实现阻塞队列?
+8. Java垃圾回收。可达性分析->引用级别->二次标记(finalize方法)->垃圾收集 算法(4个)->回收策略(3个)->垃圾收集器(GMS、G1)。
+9. java内存模型
+10. TCP/IP的理解
+11. 进程和线程的区别
+12. http状态码含义
+13. ThreadLocal(线程本地变量),如何实现一个本地缓存
+14. JVM内存区哪里会出现溢出?
+15. 双亲委派模型的理解,怎样将两个全路径相同的类加载到内存中?
+16. CMS收集器和G1收集器
+17. TCP流量控制和拥塞控制
+18. 服务器处理一个http请求的过程
+19. 例举几个Mysql优化手段
+20. 数据库死锁定义,怎样避免死锁
+21. spring的aop是什么?如何实现的
+22. 面向对象的设计原则
+23. 策略模式的实现
+24. 操作系统的内存管理的页面淘汰 算法 ,介绍下LRU(最近最少使用算法 )
+25. B+树的特点与优势
+
+## 面经3
+
+- 自我介绍,说简历里没有的东西
+- 说几个你最近在看的技术(MySQL,多线程)
+- 口述了一个统计数据的场景题
+- 如果这个统计数据场景不用MySQL,而是用Java来实现,怎么做
+- 如果数据量过大,内存放不下呢
+- 用面向对象的思想解决上面提出的问题,创建出父类,子类,方法,说一下思路
+- 下一个场景,口述了一个登录场景,同学用线程池做登录校验,会有什么问题
+- 如何解决这些问题
+- 你给出的方案弊端在哪里,还有哪些方案
+
+## 面经4
+
+
+
+- 谈谈类加载机制。
+- hashmap和concurenthashmap
+- 16g机器,让你分配jvm内存怎么分配。
+- 机器慢了怎么排查。
+- 谈谈consul和zookeeper,还有服务发现机制。
+- 详细说明raft协议。
+- 谈谈consul和zookeeper区别。
+- 服务注册的时候发现没有注册成功会是什么原因。
+- 讲讲你认为的rpc和service mesh之间的关系。
+
+**最后给大家分享一份精心整理的大厂高频面试题PDF,需要的小伙伴可以自行下载:**
+
+[大厂面试手册](http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd)
\ No newline at end of file
diff --git a/interview/5-kuaishou.md b/interview/5-kuaishou.md
new file mode 100644
index 0000000..a06a8c2
--- /dev/null
+++ b/interview/5-kuaishou.md
@@ -0,0 +1,79 @@
+# 快手
+
+## 一面
+
+1. 简单介绍项目
+2. 知道哪些数据结构以及他们的特点
+3. 链表增删快,那如何提高其查询效率,有没有什么想法?
+4. B+树了解吗?B+树如何范围查询?B+树退化的极端情况是什么?
+5. 跳表了解吗?
+6. 大顶堆、小顶堆了解吗?
+7. 实现长地址请求到服务端,然后服务端重定向短地址给客户端,如何实现长短地址的互相映射?
+8. 那我现在有10份数据,有1000个线程来争抢,你要怎么处理?
+9. 分布式是什么?为什么要分布式?分布式又会有哪些问题?分布式系统是如何实现事物的?
+10. Redis集群了解吗?如何处理宕机的情况?Redis的同步策略?
+11. LRU算法了解吗?你会如何实现它?这个算法可以应用在哪些场景下?
+12. TCP为什么是三次握手?两次行不行?多次行不行?
+13. TCP的安全性是如何实现的?两台服务器之间可以同时建立多条TCP链接吗?怎么实现的?
+14. 客服端输入一个网址后,是如何拿到客服想要的数据的,是怎样在网络中传输的?
+15. cookie和session
+16. java有哪些锁?共享锁是什么?CAS?乐观锁和悲观锁?synchronied的底层原理?锁升级?死锁怎么形成的?如何破解死锁?
+
+## 二面
+
+
+1. Java容器:List,Set,Map
+2. Map的遍历方式
+3. HashMap扩容为什么是扩为两倍?
+4. Java线程同步机制(信号量,闭锁,栅栏)
+5. 对volatile的理解:常用于状态标记
+6. 八种基本数据类型的大小以及他们的封装类(顺带了解自动拆箱与装箱)
+7. 线程阻塞几种情况?如何自己实现阻塞队列?
+8. Java垃圾回收。可达性分析->引用级别->二次标记(finalize方法)->垃圾收集 算法(4个)->回收策略(3个)->垃圾收集器(GMS、G1)。
+9. java内存模型
+10. TCP/IP的理解
+11. 进程和线程的区别
+12. http状态码含义
+13. ThreadLocal(线程本地变量),如何实现一个本地缓存
+14. JVM内存区哪里会出现溢出?
+15. 双亲委派模型的理解,怎样将两个全路径相同的类加载到内存中?
+16. CMS收集器和G1收集器
+17. TCP流量控制和拥塞控制
+18. 服务器处理一个http请求的过程
+19. 例举几个Mysql优化手段
+20. 数据库死锁定义,怎样避免死锁
+21. spring的aop是什么?如何实现的
+22. 面向对象的设计原则
+23. 策略模式的实现
+24. 操作系统的内存管理的页面淘汰 算法 ,介绍下LRU(最近最少使用算法 )
+25. B+树的特点与优势
+
+## 三面
+
+- 自我介绍,说简历里没有的东西
+- 说几个你最近在看的技术(MySQL,多线程)
+- 口述了一个统计数据的场景题
+- 如果这个统计数据场景不用MySQL,而是用Java来实现,怎么做
+- 如果数据量过大,内存放不下呢
+- 用面向对象的思想解决上面提出的问题,创建出父类,子类,方法,说一下思路
+- 下一个场景,口述了一个登录场景,同学用线程池做登录校验,会有什么问题
+- 如何解决这些问题
+- 你给出的方案弊端在哪里,还有哪些方案
+
+## 四面
+
+- 谈谈类加载机制。
+- hashmap和concurenthashmap
+- 16g机器,让你分配jvm内存怎么分配。
+- 机器慢了怎么排查。
+- 谈谈consul和zookeeper,还有服务发现机制。
+- 详细说明raft协议。
+- 谈谈consul和zookeeper区别。
+- 服务注册的时候发现没有注册成功会是什么原因。
+- 讲讲你认为的rpc和service mesh之间的关系。
+
+
+
+**最后给大家分享一份精心整理的大厂高频面试题PDF,需要的小伙伴可以自行下载:**
+
+[大厂面试手册](http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd)
\ No newline at end of file
diff --git a/interview/6-meituan.md b/interview/6-meituan.md
new file mode 100644
index 0000000..5b183c0
--- /dev/null
+++ b/interview/6-meituan.md
@@ -0,0 +1,74 @@
+# 美团
+
+## 一面
+
+- 消息队列如何保证可靠性
+- 消息队列如何保证消息幂等性
+- 消息队列的优缺点
+- 为什么用b+树
+- 聚集索引和主键区别,其他引擎怎么做的
+- 平时数据库编码
+- explain参数
+- http报文参数有哪些吗?
+- 做题,链表奇偶有序输出
+
+## 二面
+
+- 自我介绍
+- 有哪些排序算法?
+- 介绍下快排/堆排/归并排序。
+- 数据库中的索引应该如何设计?
+- 有哪些索引失效的情况?
+- 你们用到的HTTP接口用到了什么提交方式?
+- GET/POST的区别?
+- 除了GET/POST还有哪些?
+- 面向对象的基本原则?再详细说下依赖倒转。
+- 介绍下策略模式和观察者模式?
+- 如何保证用户请求的等幂性?等幂性指的是用户可能连点提交三次支付请求,返回同样的结果(支付成功),但实际后台只执行一次,保持一致性。
+- 介绍下TCP四次挥手?
+- 第四次挥手后客户端是立刻就关闭了吗?是什么状态?
+- 两个大文件,分别每行都存一个url,查找两个文件中重复的url。
+- 一个大文件中,每一行有一个整数,怎么找第100大的数?
+- 一个大文件中,每一行有一个整数,怎么找中位数?
+- redis的基本数据结构?
+- zset是怎么实现的?有哪些命令?
+- 算法题 力扣221. 最大正方形
+
+## 三面
+
+- 项目相关(模块划分,项目需求,技术方案,数据库设计,表的结构及关系,担任角色)
+- http协议的关键字段,比如request和response头部信息有哪些关键字段,有什么含义
+- http状态码:100,200,502,504
+- http和https的区别,https是为了解决什么问题
+- 三次握手、四次挥手(详细过程+状态变化)
+- 出现大量的close_wait可能是什么原因,解决方案,通过什么工具看出来网络有问题等等
+- Java中常见的集合有哪些,List、Set、Map初始容量加载因子了解吗
+- Java中线程通信的方式有哪些,大概的原理
+- MySQL如果遇到性能不好的问题,比如说慢查询,怎么做
+- 数据库优化方案(索引 | 分库分表)
+- 有哪些索引,数据结构,建立索引的原则
+- 分库分表的原则,说说场景(水平 | 垂直、热数据 | 冷数据 blabla)
+- 算法题:两数之和
+
+## 四面
+
+- 自我介绍、项目介绍,问了数据量
+- 了解微服务吗?(有没有自己在做项目时进行调研,了解企业目前常用的工具、方法)
+- 了解springcloud吗?
+- 一台机器无法满足运载需求,怎么办呢?答:多搞几台机器,问:多台机器如何协同工作?
+- 解释一下mapreduce
+- 如果有一个很大的文件,TB级别,文件里是乱序的数字,如何排序?mapreduce如何实现?
+- 排序过程中的归并排序,请描述一下其过程?时间复杂度
+- 进程、线程区别,问使用Java时,里面多线程的概念和os里的线程进程的区别是什么?真正使用时,Java里的线程和进程是如何调度?
+- 多线程的同步互斥的方法?答了信号量,问具体怎么实现,答pv操作,给了具体的场景,问变量如何初始化(等同于口述代码)
+- 有哪些索引?(mysql为例)
+- b树、b+树是什么样的树结构,查询复杂度?是平衡二叉树吗?
+- 使用过redis吗?具体做什么?
+- 手撕代码:LRU算法;正反序层序遍历二叉树
+
+
+
+**最后给大家分享一份精心整理的大厂高频面试题PDF,需要的小伙伴可以自行下载:**
+
+[大厂面试手册](http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd)
+
diff --git a/interview/7-shopee.md b/interview/7-shopee.md
new file mode 100644
index 0000000..f548f56
--- /dev/null
+++ b/interview/7-shopee.md
@@ -0,0 +1,73 @@
+# shopee
+
+## 面经1
+
+消息队列如何保证可靠性
+消息队列如何保证消息幂等性
+消息队列的优缺点
+为什么用b+树
+聚集索引和主键区别,其他引擎怎么做的
+平时数据库编码
+explain参数
+http报文参数有哪些吗?
+做题,链表奇偶有序输出
+
+## 面经2
+
+1. 自我介绍
+2. 有哪些排序算法?
+3. 介绍下快排/堆排/归并排序。
+4. 数据库中的索引应该如何设计?
+5. 有哪些索引失效的情况?
+6. 你们用到的HTTP接口用到了什么提交方式?
+7. GET/POST的区别?
+8. 除了GET/POST还有哪些?
+9. 面向对象的基本原则?再详细说下依赖倒转。
+10. 介绍下策略模式和观察者模式?
+11. 如何保证用户请求的等幂性?等幂性指的是用户可能连点提交三次支付请求,返回同样的结果(支付成功),但实际后台只执行一次,保持一致性。
+12. 介绍下TCP四次挥手?
+13. 第四次挥手后客户端是立刻就关闭了吗?是什么状态?
+14. 两个大文件,分别每行都存一个url,查找两个文件中重复的url。
+15. 一个大文件中,每一行有一个整数,怎么找第100大的数?
+16. 一个大文件中,每一行有一个整数,怎么找中位数?
+17. redis的基本数据结构?
+18. zset是怎么实现的?有哪些命令?
+19. 算法题 力扣221. 最大正方形
+
+## 面经3
+
+自我介绍、项目介绍,问了数据量
+
+了解微服务吗?(有没有自己在做项目时进行调研,了解企业目前常用的工具、方法)
+
+了解springcloud吗?
+
+一台机器无法满足运载需求,怎么办呢?答:多搞几台机器,问:多台机器如何协同工作?
+
+开始瞎答:mapreduce
+
+解释一下mapreduce
+
+如果有一个很大的文件,TB级别,文件里是乱序的数字,如何排序?mapreduce如何实现?
+
+排序过程中的归并排序,请描述一下其过程?时间复杂度
+
+进程、线程区别,问使用Java时,里面多线程的概念和os里的线程进程的区别是什么?真正使用时,Java里的线程和进程是如何调度?
+
+多线程的同步互斥的方法?答了信号量,问具体怎么实现,答pv操作,给了具体的场景,问变量如何初始化(等同于口述代码)
+
+有哪些索引?(mysql为例)
+
+b树、b+树是什么样的树结构,查询复杂度?是平衡二叉树吗?
+
+使用过redis吗?具体做什么?
+
+手撕代码:LRU算法;正反序层序遍历二叉树
+
+
+
+
+
+**最后给大家分享一份精心整理的大厂高频面试题PDF,需要的小伙伴可以自行下载:**
+
+[大厂面试手册](http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd)
\ No newline at end of file
diff --git a/interview/8-jingdong.md b/interview/8-jingdong.md
new file mode 100644
index 0000000..aa06fb9
--- /dev/null
+++ b/interview/8-jingdong.md
@@ -0,0 +1,58 @@
+# 京东
+
+## 一面
+
+- kafka在应用场景以及 项目 里的实现
+- bitmap底层
+- object里有哪些方法
+- hashmap相关
+- sychronized和reentrantlock相关问题以及锁升级
+- cas和volatile
+- 线程几种状态以及转化
+- jvm内存模型
+- mybatis相关问题
+- Redis数据结构,问了下跳表的底层
+- RDB和AOF
+- MySQL索引有哪些
+- b+树底层实现
+- 最左前缀原理
+
+## 二面
+
+- 线程的状态
+- cms
+- 增量更新法
+- GcRoots是哪些
+- java基础
+- mysql索引
+- 项目具体实现
+
+## 三面
+
+- 索引
+- 谈谈多线程
+- jvm如何调优
+- mq在项目中的用法
+- 遇到的多线程问题,如何解决
+- 最长无重复字串
+- 找到A^2+B^2 = C
+
+## 四面
+
+- 数据库乐观锁、悲观锁
+- 为啥用Redis
+- sql语句执行顺序
+- SpringMVC优点,原理
+- aop优点,原理
+- ioc优点,原理
+- 面向对象概念
+- 封装
+- 项目中封装如何体现
+- 高内聚,低耦合啥意思,如何去设计
+- 设计一个电梯场景,实现面向对象,高内聚,低耦合的情况
+- 统计学校内共享单车数量,你有啥想法(开放题)
+
+**最后分享一个BAT大佬总结的高频面试题PDF,需要的小伙伴可以自行下载(复制链接到浏览器打开):**
+
+链接:https://pan.baidu.com/s/16GnVoALA1r6BhumuUrXIRg
+提取码:6666
diff --git a/interview/9-huawei.md b/interview/9-huawei.md
new file mode 100644
index 0000000..ffb1c83
--- /dev/null
+++ b/interview/9-huawei.md
@@ -0,0 +1,56 @@
+# 华为
+
+最近越来越多公司校招进入面试流程了,为了帮助大家更好的应对面试,大彬整理了往年华为校招面试的题目,供大家参考~
+
+## 面经1
+
+### 技术一面
+
+1. 自我介绍
+2. 说下项目中的难点
+3. volatile和synchronized的区别, 问的比较细
+4. 大顶堆小顶堆怎么删除根节点
+5. CSRF攻击是什么,怎么预防
+6. 线程通信方式。
+7. Volitate关键字。
+8. Java 高效拷贝数组。
+9. 算法题 跳跃游戏 leetcode 55。
+
+
+### 技术二面
+1. 上来就手撕代码 ,奇偶链表,leetcode原题,先说思路,然后打开ide共享屏幕撕代码
+2. 手写单例模式,并说为什么这样写,会不会有什么问题,涉及到volatile原理
+3. mysql常用的数据类型
+4. Java集合框架的主类是什么,HashSet有没有继承Collection软件工程学过哪些课程
+5. 软件工程学过哪些课程
+6. 进程和线程的区别
+7. 知道哪些排序算法,快排的时间复杂度是多少,是稳定的排序算法吗
+8. 编程题/算法
+题目大概:请输出两个字符串a和b相减的结果(a>b,a和b的字符串长度介于1~50之间)。
+例:输入a:“99999”,b=“99998”
+输出:“1”
+
+
+## 面经2
+### 华为一面
+1. 项目、论文。
+2. String能否被继承。
+3. Java内存泄露和排查。
+4. Hash方式和Hash冲突解决。
+5. 静态代理和动态代理。
+6. spring boot常用的注解有哪些
+7. spring boot的配置文件
+8. redis集群的几种方式详细说一下
+9. redis缓存雪崩,缓存击穿,缓存穿透是什么,怎么解决
+10. mysql索引相关,为什么用B+树
+11. 手撕代码,链表求和,leetcode原题:https://leetcode-cn.com/problems/sum-lists-lcci/
+
+### 华为二面
+- 是否用过Java、Python做系统的项目
+- 平时熟练使用哪种语言
+- HashMap、HashSet、HashTable、StringBuffer、StringBuilder哪些是线程安全,哪些是线程不安全
+- HashSet数据结构,跟HashMap有什么区别
+- char和varchar的区别
+- mysql建索引的原则,索引是不是越多越好,为什么
+- spring boot用到了哪些设计模式,从源码层面说说你熟悉的以及实现
+- jvm调优你用什么工具,具体怎么做的,怎么调优
diff --git a/interview/README.md b/interview/README.md
new file mode 100644
index 0000000..4ebcf45
--- /dev/null
+++ b/interview/README.md
@@ -0,0 +1,13 @@
+
+## 大厂面经汇总
+
+- [字节跳动](./1-byte-and-dance.md)
+- [腾讯](./2-tencent.md)
+- [百度](./3-baidu.md)
+- [阿里](./4-ali.md)
+- [快手](./5-kuaishou.md)
+- [美团](./6-meituan.md)
+- [shopee](./7-shopee.md)
+- [京东](./8-jingdong.md)
+- [华为](./9-huawei.md)
+- [网易](./10-netease.md)
From f6221f6632d461880411d1996780f5a4511cb15a Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 6 Nov 2022 15:33:16 +0800
Subject: [PATCH 041/112] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=B8=B8=E8=A7=81?=
=?UTF-8?q?=E7=9A=84Exception=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
index 04f0e86..0992793 100644
--- "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
@@ -921,7 +921,7 @@ class BMWCar extends Car implements Alarm {
5. `NumberFormatException` //数字格式化异常
6. `ArithmeticException` //数学运算异常
-unchecked Exception:
+checked Exception:
1. `NoSuchFieldException` //反射异常,没有对应的字段
2. `ClassNotFoundException` //类没有找到异常
From 24a1369a74ca68d2d8049afcaf1258452b3dc793 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 6 Nov 2022 15:36:46 +0800
Subject: [PATCH 042/112] =?UTF-8?q?Java=E5=9F=BA=E7=A1=80=E9=9D=A2?=
=?UTF-8?q?=E8=AF=95=E9=A2=98=E8=A1=A5=E5=85=85?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...00\351\235\242\350\257\225\351\242\230.md" | 414 +++++++++++++++---
1 file changed, 361 insertions(+), 53 deletions(-)
diff --git "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
index 0992793..a3a25bb 100644
--- "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
@@ -1,12 +1,19 @@
+---
+sidebar: heading
+
+---
+
+
+
## Java的特点
-**Java是一门面向对象的编程语言。**面向对象和面向过程的区别参考下一个问题。
+**Java是一门面向对象的编程语言**。面向对象和面向过程的区别参考下一个问题。
-**Java具有平台独立性和移植性。**
+**Java具有平台独立性和移植性**。
- Java有一句口号:`Write once, run anywhere`,一次编写、到处运行。这也是Java的魅力所在。而实现这种特性的正是Java虚拟机JVM。已编译的Java程序可以在任何带有JVM的平台上运行。你可以在windows平台编写代码,然后拿到linux上运行。只要你在编写完代码后,将代码编译成.class文件,再把class文件打成Java包,这个jar包就可以在不同的平台上运行了。
-**Java具有稳健性。**
+**Java具有稳健性**。
- Java是一个强类型语言,它允许扩展编译时检查潜在类型不匹配问题的功能。Java要求显式的方法声明,它不支持C风格的隐式声明。这些严格的要求保证编译程序能捕捉调用错误,这就导致更可靠的程序。
- 异常处理是Java中使得程序更稳健的另一个特征。异常是某种类似于错误的异常条件出现的信号。使用`try/catch/finally`语句,程序员可以找到出错的处理代码,这就简化了出错处理和恢复的任务。
@@ -19,32 +26,11 @@
- Java 支持自动垃圾回收,而 C++ 需要手动回收。
- Java 不支持多重继承,只能通过实现多个接口来达到相同目的,而 C++ 支持多重继承。
-## 面向对象和面向过程的区别?
+## JDK/JRE/JVM三者的关系
-面向对象和面向过程是一种软件开发思想。
-
-- 面向过程就是分析出解决问题所需要的步骤,然后用函数按这些步骤实现,使用的时候依次调用就可以了。
+**JVM**
-- 面向对象是把构成问题事务分解成各个对象,分别设计这些对象,然后将他们组装成有完整功能的系统。面向过程只用函数实现,面向对象是用类实现各个功能模块。
-
-以五子棋为例,面向过程的设计思路就是首先分析问题的步骤:
-
-1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤2,9、输出最后结果。
-把上面每个步骤用分别的函数来实现,问题就解决了。
-
-而面向对象的设计则是从另外的思路来解决问题。整个五子棋可以分为:
-
-1. 黑白双方
-2. 棋盘系统,负责绘制画面
-3. 规则系统,负责判定诸如犯规、输赢等。
-
-黑白双方负责接受用户的输入,并告知棋盘系统棋子布局发生变化,棋盘系统接收到了棋子的变化的信息就负责在屏幕上面显示出这种变化,同时利用规则系统来对棋局进行判定。
-
-## JKD/JRE/JVM三者的关系
-
-### JVM
-
-**JVM** :英文名称(Java Virtual Machine),就是我们耳熟能详的 Java 虚拟机。Java 能够跨平台运行的核心在于 JVM 。
+英文名称(Java Virtual Machine),就是我们耳熟能详的 Java 虚拟机。Java 能够跨平台运行的核心在于 JVM 。

@@ -52,7 +38,7 @@
针对不同的系统有不同的 jvm 实现,有 Linux 版本的 jvm 实现,也有Windows 版本的 jvm 实现,但是同一段代码在编译后的字节码是一样的。这就是Java能够跨平台,实现一次编写,多处运行的原因所在。
-### JRE
+**JRE**
英文名称(Java Runtime Environment),就是Java 运行时环境。我们编写的Java程序必须要在JRE才能运行。它主要包含两个部分,JVM 和 Java 核心类库。
@@ -62,31 +48,81 @@ JRE是Java的运行环境,并不是一个开发环境,所以没有包含任
如果你只是想运行Java程序,而不是开发Java程序的话,那么你只需要安装JRE即可。
-### JDK
+**JDK**
英文名称(Java Development Kit),就是 Java 开发工具包
学过Java的同学,都应该安装过JDK。当我们安装完JDK之后,目录结构是这样的
-
+
可以看到,JDK目录下有个JRE,也就是JDK中已经集成了 JRE,不用单独安装JRE。
另外,JDK中还有一些好用的工具,如jinfo,jps,jstack等。
-
+
+最后,总结一下JDK/JRE/JVM,他们三者的关系
+**JRE = JVM + Java 核心类库**
-### 总结
+**JDK = JRE + Java工具 + 编译器 + 调试器**
-最后,总结一下JDK/JRE/JVM,他们三者的关系
+
-JRE = JVM + Java 核心类库
+## Java程序是编译执行还是解释执行?
-JDK = JRE + Java工具 + 编译器 + 调试器
+先看看什么是编译型语言和解释型语言。
-
+**编译型语言**
+
+在程序运行之前,通过编译器将源程序编译成机器码可运行的二进制,以后执行这个程序时,就不用再进行编译了。
+
+优点:编译器一般会有预编译的过程对代码进行优化。因为编译只做一次,运行时不需要编译,所以编译型语言的程序执行效率高,可以脱离语言环境独立运行。
+
+缺点:编译之后如果需要修改就需要整个模块重新编译。编译的时候根据对应的运行环境生成机器码,不同的操作系统之间移植就会有问题,需要根据运行的操作系统环境编译不同的可执行文件。
+
+**总结**:执行速度快、效率高;依靠编译器、跨平台性差些。
+
+**代表语言**:C、C++、Pascal、Object-C以及Swift。
+
+**解释型语言**
+
+定义:解释型语言的源代码不是直接翻译成机器码,而是先翻译成中间代码,再由解释器对中间代码进行解释运行。在运行的时候才将源程序翻译成机器码,翻译一句,然后执行一句,直至结束。
+
+优点:
+
+1. 有良好的平台兼容性,在任何环境中都可以运行,前提是安装了解释器(如虚拟机)。
+2. 灵活,修改代码的时候直接修改就可以,可以快速部署,不用停机维护。
+
+缺点:每次运行的时候都要解释一遍,性能上不如编译型语言。
+
+总结:解释型语言执行速度慢、效率低;依靠解释器、跨平台性好。
+
+代表语言:JavaScript、Python、Erlang、PHP、Perl、Ruby。
+
+对于Java这种语言,它的**源代码**会先通过javac编译成**字节码**,再通过jvm将字节码转换成**机器码**执行,即解释运行 和编译运行配合使用,所以可以称为混合型或者半编译型。
+
+## 面向对象和面向过程的区别?
+
+面向对象和面向过程是一种软件开发思想。
+
+- 面向过程就是分析出解决问题所需要的步骤,然后用函数按这些步骤实现,使用的时候依次调用就可以了。
+
+- 面向对象是把构成问题事务分解成各个对象,分别设计这些对象,然后将他们组装成有完整功能的系统。面向过程只用函数实现,面向对象是用类实现各个功能模块。
+
+以五子棋为例,面向过程的设计思路就是首先分析问题的步骤:
+
+1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤2,9、输出最后结果。
+把上面每个步骤用分别的函数来实现,问题就解决了。
+
+而面向对象的设计则是从另外的思路来解决问题。整个五子棋可以分为:
+
+1. 黑白双方
+2. 棋盘系统,负责绘制画面
+3. 规则系统,负责判定诸如犯规、输赢等。
+
+黑白双方负责接受用户的输入,并告知棋盘系统棋子布局发生变化,棋盘系统接收到了棋子的变化的信息就负责在屏幕上面显示出这种变化,同时利用规则系统来对棋局进行判定。
## 面向对象有哪些特性?
@@ -103,6 +139,27 @@ JDK = JRE + Java工具 + 编译器 + 调试器
4、抽象。把客观事物用代码抽象出来。
+## 数组到底是不是对象?
+
+先说说对象的概念。对象是根据某个类创建出来的一个实例,表示某类事物中一个具体的个体。
+
+对象具有各种属性,并且具有一些特定的行为。站在计算机的角度,对象就是内存中的一个内存块,在这个内存块封装了一些数据,也就是类中定义的各个属性。
+
+所以,对象是用来封装数据的。
+
+java中的数组具有java中其他对象的一些基本特点。比如封装了一些数据,可以访问属性,也可以调用方法。
+
+因此,可以说,数组是对象。
+
+也可以通过代码验证数组是对象的事实。比如以下的代码,输出结果为java.lang.Object。
+
+```java
+Class clz = int[].class;
+System.out.println(clz.getSuperclass().getName());
+```
+
+由此,可以看出,数组类的父类就是Object类,那么可以推断出数组就是对象。
+
## Java的基本数据类型有哪些?
- byte,8bit
@@ -119,6 +176,8 @@ JDK = JRE + Java工具 + 编译器 + 调试器
| 二进制位数 | 1 | 8 | 16 | 16 | 32 | 64 | 32 | 64 |
| 包装类 | Boolean | Byte | Character | Short | Integer | Long | Float | Double |
+在Java规范中,没有明确指出boolean的大小。在《Java虚拟机规范》给出了单个boolean占4个字节,和boolean数组1个字节的定义,具体 **还要看虚拟机实现是否按照规范来**,因此boolean占用1个字节或者4个字节都是有可能的。
+
## 为什么不能用浮点型表示金额?
由于计算机中保存的小数其实是十进制的小数的近似值,并不是准确值,所以,千万不要在代码中使用浮点数来表示金额等重要的指标。
@@ -127,8 +186,10 @@ JDK = JRE + Java工具 + 编译器 + 调试器
## 什么是值传递和引用传递?
-- 值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量。
-- 引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。所以对引用对象进行操作会同时改变原对象。
+- 值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量。
+- 引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本,并不是原对象本身,两者指向同一片内存空间。所以对引用对象进行操作会同时改变原对象。
+
+**java中不存在引用传递,只有值传递**。即不存在变量a指向变量b,变量b指向对象的这种情况。
## 了解Java的包装类型吗?为什么需要包装类?
@@ -228,7 +289,7 @@ private static class IntegerCache {
}
```
-这是IntegerCache静态代码块中的一段,默认Integer cache 的下限是-128,上限默认127。当赋值100给Integer时,刚好在这个范围内,所以从cache中取对应的Integer并返回,所以a和b返回的是同一个对象,所以==比较是相等的,当赋值200给Integer时,不在cache 的范围内,所以会new Integer并返回,当然==比较的结果是不相等的。
+这是IntegerCache静态代码块中的一段,默认Integer cache 的下限是-128,上限默认127。当赋值100给Integer时,刚好在这个范围内,所以从cache中取对应的Integer并返回,所以a和b返回的是同一个对象,所以`==`比较是相等的,当赋值200给Integer时,不在cache 的范围内,所以会new Integer并返回,当然`==`比较的结果是不相等的。
## String 为什么不可变?
@@ -262,14 +323,26 @@ String类内部所有的字段都是私有的,也就是被private修饰。而
主要有以下几点原因:
1. **线程安全**。同一个字符串实例可以被多个线程共享,因为字符串不可变,本身就是线程安全的。
-2. **支持hash映射和缓存。**因为String的hash值经常会使用到,比如作为 Map 的键,不可变的特性使得 hash 值也不会变,不需要重新计算。
+2. **支持hash映射和缓存**。因为String的hash值经常会使用到,比如作为 Map 的键,不可变的特性使得 hash 值也不会变,不需要重新计算。
3. **出于安全考虑**。网络地址URL、文件路径path、密码通常情况下都是以String类型保存,假若String不是固定不变的,将会引起各种安全隐患。比如将密码用String的类型保存,那么它将一直留在内存中,直到垃圾收集器把它清除。假如String类不是固定不变的,那么这个密码可能会被改变,导致出现安全隐患。
-3. **字符串常量池优化**。String对象创建之后,会缓存到字符串常量池中,下次需要创建同样的对象时,可以直接返回缓存的引用。
+4. **字符串常量池优化**。String对象创建之后,会缓存到字符串常量池中,下次需要创建同样的对象时,可以直接返回缓存的引用。
既然我们的String是不可变的,它内部还有很多substring, replace, replaceAll这些操作的方法。这些方法好像会改变String对象?怎么解释呢?
其实不是的,我们每次调用replace等方法,其实会在堆内存中创建了一个新的对象。然后其value数组引用指向不同的对象。
+## 为何JDK9要将String的底层实现由char[]改成byte[]?
+
+主要是为了**节约String占用的内存**。
+
+在大部分Java程序的堆内存中,String占用的空间最大,并且绝大多数String只有Latin-1字符,这些Latin-1字符只需要1个字节就够了。
+
+而在JDK9之前,JVM因为String使用char数组存储,每个char占2个字节,所以即使字符串只需要1字节,它也要按照2字节进行分配,浪费了一半的内存空间。
+
+到了JDK9之后,对于每个字符串,会先判断它是不是只有Latin-1字符,如果是,就按照1字节的规格进行分配内存,如果不是,就按照2字节的规格进行分配,这样便提高了内存使用率,同时GC次数也会减少,提升效率。
+
+不过Latin-1编码集支持的字符有限,比如不支持中文字符,因此对于中文字符串,用的是UTF16编码(两个字节),所以用byte[]和char[]实现没什么区别。
+
## String, StringBuffer 和 StringBuilder区别
**1. 可变性**
@@ -283,6 +356,62 @@ String类内部所有的字段都是私有的,也就是被private修饰。而
- StringBuilder 不是线程安全的
- StringBuffer 是线程安全的,内部使用 synchronized 进行同步
+## 什么是StringJoiner?
+
+StringJoiner是 Java 8 新增的一个 API,它基于 StringBuilder 实现,用于实现对字符串之间通过分隔符拼接的场景。
+
+StringJoiner 有两个构造方法,第一个构造要求依次传入分隔符、前缀和后缀。第二个构造则只要求传入分隔符即可(前缀和后缀默认为空字符串)。
+
+```java
+StringJoiner(CharSequence delimiter, CharSequence prefix, CharSequence suffix)
+StringJoiner(CharSequence delimiter)
+```
+
+有些字符串拼接场景,使用 StringBuffer 或 StringBuilder 则显得比较繁琐。
+
+比如下面的例子:
+
+```java
+List values = Arrays.asList(1, 3, 5);
+StringBuilder sb = new StringBuilder("(");
+
+for (int i = 0; i < values.size(); i++) {
+ sb.append(values.get(i));
+ if (i != values.size() -1) {
+ sb.append(",");
+ }
+}
+
+sb.append(")");
+```
+
+而通过StringJoiner来实现拼接List的各个元素,代码看起来更加简洁。
+
+```java
+List values = Arrays.asList(1, 3, 5);
+StringJoiner sj = new StringJoiner(",", "(", ")");
+
+for (Integer value : values) {
+ sj.add(value.toString());
+}
+```
+
+另外,像平时经常使用的Collectors.joining(","),底层就是通过StringJoiner实现的。
+
+源码如下:
+
+```java
+public static Collector joining(
+ CharSequence delimiter,CharSequence prefix,CharSequence suffix) {
+ return new CollectorImpl<>(
+ () -> new StringJoiner(delimiter, prefix, suffix),
+ StringJoiner::add, StringJoiner::merge,
+ StringJoiner::toString, CH_NOID);
+}
+```
+
+
+
## String 类的常用方法有哪些?
- indexOf():返回指定字符的索引。
@@ -308,6 +437,49 @@ String类内部所有的字段都是私有的,也就是被private修饰。而
字符串常量池(String Pool)保存着所有字符串字面量,这些字面量在编译时期就确定。字符串常量池位于堆内存中,专门用来存储字符串常量。在创建字符串时,JVM首先会检查字符串常量池,如果该字符串已经存在池中,则返回其引用,如果不存在,则创建此字符串并放入池中,并返回其引用。
+## String最大长度是多少?
+
+String类提供了一个length方法,返回值为int类型,而int的取值上限为2^31 -1。
+
+所以理论上String的最大长度为2^31 -1。
+
+**达到这个长度的话需要多大的内存吗**?
+
+String内部是使用一个char数组来维护字符序列的,一个char占用两个字节。如果说String最大长度是2^31 -1的话,那么最大的字符串占用内存空间约等于4GB。
+
+也就是说,我们需要有大于4GB的JVM运行内存才行。
+
+**那String一般都存储在JVM的哪块区域呢**?
+
+字符串在JVM中的存储分两种情况,一种是String对象,存储在JVM的堆栈中。一种是字符串常量,存储在常量池里面。
+
+**什么情况下字符串会存储在常量池呢**?
+
+当通过字面量进行字符串声明时,比如String s = "程序新大彬";,这个字符串在编译之后会以常量的形式进入到常量池。
+
+**那常量池中的字符串最大长度是2^31-1吗**?
+
+不是的,常量池对String的长度是有另外限制的。。Java中的UTF-8编码的Unicode字符串在常量池中以CONSTANT_Utf8类型表示。
+
+```java
+CONSTANT_Utf8_info {
+ u1 tag;
+ u2 length;
+ u1 bytes[length];
+}
+```
+
+length在这里就是代表字符串的长度,length的类型是u2,u2是无符号的16位整数,也就是说最大长度可以做到2^16-1 即 65535。
+
+不过javac编译器做了限制,需要length < 65535。所以字符串常量在常量池中的最大长度是65535 - 1 = 65534。
+
+最后总结一下:
+
+String在不同的状态下,具有不同的长度限制。
+
+- 字符串常量长度不能超过65534
+- 堆内字符串的长度不超过2^31-1
+
## Object常用方法有哪些?
Java面试经常会出现的一道题目,Object的常用方法。下面给大家整理一下。
@@ -844,7 +1016,7 @@ public class B extends A {
## 方法重载和重写的区别?
-**同个类中的多个方法可以有相同的方法名称,但是有不同的参数列表,这就称为方法重载。**参数列表又叫参数签名,包括参数的类型、参数的个数、参数的顺序,只要有一个不同就叫做参数列表不同。
+**同个类中的多个方法可以有相同的方法名称,但是有不同的参数列表,这就称为方法重载**。参数列表又叫参数签名,包括参数的类型、参数的个数、参数的顺序,只要有一个不同就叫做参数列表不同。
重载是面向对象的一个基本特性。
@@ -862,7 +1034,7 @@ public class OverrideTest {
}
```
-**方法的重写描述的是父类和子类之间的。当父类的功能无法满足子类的需求,可以在子类对方法进行重写。**方法重写时, 方法名与形参列表必须一致。
+**方法的重写描述的是父类和子类之间的。当父类的功能无法满足子类的需求,可以在子类对方法进行重写**。方法重写时, 方法名与形参列表必须一致。
如下代码,Person为父类,Student为子类,在Student中重写了dailyTask方法。
@@ -888,9 +1060,9 @@ public class Student extends Person {
1、**语法层面**上的区别
-- 抽象类可以有方法实现,而接口的方法中只能是抽象方法(Java 8 开始接口方法可以有默认实现);
+- 抽象类可以有方法实现,而接口的方法中只能是抽象方法(Java 8 之后接口方法可以有默认实现);
- 抽象类中的成员变量可以是各种类型的,接口中的成员变量只能是public static final类型;
-- 接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
+- 接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法(Java 8之后接口可以有静态方法);
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
2、**设计层面**上的区别
@@ -1071,17 +1243,60 @@ Java不支持多继承的原因:
- Optional 类 :Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
- Date Time API :加强对日期与时间的处理。
-> [Java8 新特性总结]([Java-learning/Java8.md at master · Tyson0314/Java-learning (github.com)](https://github.com/Tyson0314/Java-learning/blob/master/Java/Java8.md))
+> [Java8 新特性总结](https://github.com/Tyson0314/Java-learning/blob/master/Java/Java8新特性.md)
+
+## 序列化和反序列化
+
+- 序列化:把对象转换为字节序列的过程称为对象的序列化.
+- 反序列化:把字节序列恢复为对象的过程称为对象的反序列化.
+
+## 什么时候需要用到序列化和反序列化呢?
+
+当我们只在本地 JVM 里运行下 Java 实例,这个时候是不需要什么序列化和反序列化的,但当我们需要将内存中的对象持久化到磁盘,数据库中时,当我们需要与浏览器进行交互时,当我们需要实现 RPC 时,这个时候就需要序列化和反序列化了.
+
+前两个需要用到序列化和反序列化的场景,是不是让我们有一个很大的疑问? 我们在与浏览器交互时,还有将内存中的对象持久化到数据库中时,好像都没有去进行序列化和反序列化,因为我们都没有实现 Serializable 接口,但一直正常运行.
+
+下面先给出结论:
+
+**只要我们对内存中的对象进行持久化或网络传输,这个时候都需要序列化和反序列化.**
+
+理由:
+
+服务器与浏览器交互时真的没有用到 Serializable 接口吗? JSON 格式实际上就是将一个对象转化为字符串,所以服务器与浏览器交互时的数据格式其实是字符串,我们来看来 String 类型的源码:
+
+```
+public final class String
+ implements java.io.Serializable,Comparable,CharSequence {
+ /\*\* The value is used for character storage. \*/
+ private final char value\[\];
+
+ /\*\* Cache the hash code for the string \*/
+ private int hash; // Default to 0
+
+ /\*\* use serialVersionUID from JDK 1.0.2 for interoperability \*/
+ private static final long serialVersionUID = -6849794470754667710L;
+
+ ......
+}
+```
+
+String 类型实现了 Serializable 接口,并显示指定 serialVersionUID 的值.
-## 什么是序列化和反序列化?
+然后我们再来看对象持久化到数据库中时的情况,Mybatis 数据库映射文件里的 insert 代码:
+
+```
+
+ INSERT INTO t\_user(name,age) VALUES (#{name},#{age})
+
+```
-序列化:把内存中的对象转换为字节序列的过程。
+实际上我们并不是将整个对象持久化到数据库中,而是将对象中的属性持久化到数据库中,而这些属性(如Date/String)都实现了 Serializable 接口。
-反序列化:把字节序列恢复为Java对象的过程。
+## 实现序列化和反序列化为什么要实现 Serializable 接口?
-## 如何实现序列化
+在 Java 中实现了 Serializable 接口后, JVM 在类加载的时候就会发现我们实现了这个接口,然后在初始化实例对象的时候就会在底层帮我们实现序列化和反序列化。
-实现`Serializable`接口即可。序列化的时候(如`objectOutputStream.writeObject(user)`),会判断user是否实现了`Serializable`,如果对象没有实现`Serializable`接口,在序列化的时候会抛出`NotSerializableException`异常。源码如下:
+如果被写对象类型不是String、数组、Enum,并且没有实现Serializable接口,那么在进行序列化的时候,将抛出NotSerializableException。源码如下:
```java
// remaining cases
@@ -1103,6 +1318,20 @@ if (obj instanceof String) {
}
```
+## 实现 Serializable 接口之后,为什么还要显示指定 serialVersionUID 的值?
+
+如果不显示指定 serialVersionUID,JVM 在序列化时会根据属性自动生成一个 serialVersionUID,然后与属性一起序列化,再进行持久化或网络传输. 在反序列化时,JVM 会再根据属性自动生成一个新版 serialVersionUID,然后将这个新版 serialVersionUID 与序列化时生成的旧版 serialVersionUID 进行比较,如果相同则反序列化成功,否则报错.
+
+如果显示指定了 serialVersionUID,JVM 在序列化和反序列化时仍然都会生成一个 serialVersionUID,但值为我们显示指定的值,这样在反序列化时新旧版本的 serialVersionUID 就一致了.
+
+如果我们的类写完后不再修改,那么不指定serialVersionUID,不会有问题,但这在实际开发中是不可能的,我们的类会不断迭代,一旦类被修改了,那旧对象反序列化就会报错。 所以在实际开发中,我们都会显示指定一个 serialVersionUID。
+
+## static 属性为什么不会被序列化?
+
+因为序列化是针对对象而言的,而 static 属性优先于对象存在,随着类的加载而加载,所以不会被序列化.
+
+看到这个结论,是不是有人会问,serialVersionUID 也被 static 修饰,为什么 serialVersionUID 会被序列化? 其实 serialVersionUID 属性并没有被序列化,JVM 在序列化对象时会自动生成一个 serialVersionUID,然后将我们显示指定的 serialVersionUID 属性值赋给自动生成的 serialVersionUID.
+
## transient关键字的作用?
Java语言的关键字,变量修饰符,如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。
@@ -1128,8 +1357,87 @@ Java泛型是JDK 5中引⼊的⼀个新特性, 允许在定义类和接口的
泛型最⼤的好处是可以提⾼代码的复⽤性。以List接口为例,我们可以将String、 Integer等类型放⼊List中, 如不⽤泛型, 存放String类型要写⼀个List接口, 存放Integer要写另外⼀个List接口, 泛型可以很好的解决这个问题。
-## String为什么不可变?
+## 如何停止一个正在运行的线程?
+
+有几种方式。
+
+1、**使用线程的stop方法**。
+
+使用stop()方法可以强制终止线程。不过stop是一个被废弃掉的方法,不推荐使用。
+
+使用Stop方法,会一直向上传播ThreadDeath异常,从而使得目标线程解锁所有锁住的监视器,即释放掉所有的对象锁。使得之前被锁住的对象得不到同步的处理,因此可能会造成数据不一致的问题。
+
+2、**使用interrupt方法中断线程**,该方法只是告诉线程要终止,但最终何时终止取决于计算机。调用interrupt方法仅仅是在当前线程中打了一个停止的标记,并不是真的停止线程。
+
+接着调用 Thread.currentThread().isInterrupted()方法,可以用来判断当前线程是否被终止,通过这个判断我们可以做一些业务逻辑处理,通常如果isInterrupted返回true的话,会抛一个中断异常,然后通过try-catch捕获。
+
+3、**设置标志位**
+
+设置标志位,当标识位为某个值时,使线程正常退出。设置标志位是用到了共享变量的方式,为了保证共享变量在内存中的可见性,可以使用volatile修饰它,这样的话,变量取值始终会从主存中获取最新值。
+
+但是这种volatile标记共享变量的方式,在线程发生阻塞时是无法完成响应的。比如调用Thread.sleep() 方法之后,线程处于不可运行状态,即便是主线程修改了共享变量的值,该线程此时根本无法检查循环标志,所以也就无法实现线程中断。
+
+因此,interrupt() 加上手动抛异常的方式是目前中断一个正在运行的线程**最为正确**的方式了。
+
+## 什么是跨域?
+
+简单来讲,跨域是指从一个域名的网页去请求另一个域名的资源。由于有**同源策略**的关系,一般是不允许这么直接访问的。但是,很多场景经常会有跨域访问的需求,比如,在前后端分离的模式下,前后端的域名是不一致的,此时就会发生跨域问题。
+
+**那什么是同源策略呢**?
+
+所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。
+
+同源策略限制以下几种行为:
+
+```mipsasm
+1. Cookie、LocalStorage 和 IndexDB 无法读取
+2. DOM 和 Js对象无法获得
+3. AJAX 请求不能发送
+```
+
+**为什么要有同源策略**?
+
+举个例子,假如你刚刚在网银输入账号密码,查看了自己的余额,然后再去访问其他带颜色的网站,这个网站可以访问刚刚的网银站点,并且获取账号密码,那后果可想而知。因此,从安全的角度来讲,同源策略是有利于保护网站信息的。
+
+## 跨域问题怎么解决呢?
+
+嗯,有以下几种方法:
+
+**CORS**,跨域资源共享
+
+CORS(Cross-origin resource sharing),跨域资源共享。CORS 其实是浏览器制定的一个规范,浏览器会自动进行 CORS 通信,它的实现主要在服务端,通过一些 HTTP Header 来限制可以访问的域,例如页面 A 需要访问 B 服务器上的数据,如果 B 服务器 上声明了允许 A 的域名访问,那么从 A 到 B 的跨域请求就可以完成。
+
+**@CrossOrigin注解**
+
+如果项目使用的是Springboot,可以在Controller类上添加一个 @CrossOrigin(origins ="*") 注解就可以实现对当前controller 的跨域访问了,当然这个标签也可以加到方法上,或者直接加到入口类上对所有接口进行跨域处理。注意SpringMVC的版本要在4.2或以上版本才支持@CrossOrigin。
+
+**nginx反向代理接口跨域**
+
+nginx反向代理跨域原理如下: 首先同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。
+
+nginx反向代理接口跨域实现思路如下:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。
+
+```js
+// proxy服务器
+server {
+ listen 81;
+ server_name www.domain1.com;
+ location / {
+ proxy_pass http://www.domain2.com:8080; #反向代理
+ proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
+ index index.html index.htm;
+
+ add_header Access-Control-Allow-Origin http://www.domain1.com;
+ }
+}
+```
+
+这样我们的前端代理只要访问 http:www.domain1.com:81/*就可以了。
+
+**通过jsonp跨域**
+
+通常为了减轻web服务器的负载,我们把js、css,img等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,这是浏览器允许的操作,基于此原理,我们可以通过动态创建script,再请求一个带参网址实现跨域通信。
-
+
\ No newline at end of file
From f3e1054917b4db4d607f8a32afb45931733b47bf Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 6 Nov 2022 16:04:52 +0800
Subject: [PATCH 043/112] update
---
...7\272\347\241\200\351\235\242\350\257\225\351\242\230.md" | 5 -----
1 file changed, 5 deletions(-)
diff --git "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
index a3a25bb..bd5b11e 100644
--- "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
@@ -1,8 +1,3 @@
----
-sidebar: heading
-
----
-

## Java的特点
From 72f3eb86e253ca44a5a1a13b360f66c3500eab66 Mon Sep 17 00:00:00 2001
From: tyson
Date: Fri, 23 Dec 2022 09:22:56 +0800
Subject: [PATCH 044/112] =?UTF-8?q?=E9=87=8D=E6=9E=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitignore | 23 +-
CNAME | 1 +
Java/JVM.md | 831 ---
Java/Java web.md | 83 -
...26\347\250\213\346\200\235\346\203\263.md" | 3916 -----------
...va\345\205\263\351\224\256\345\255\227.md" | 205 -
"Java/Java\345\237\272\347\241\200.md" | 1582 -----
...66\345\217\221\347\274\226\347\250\213.md" | 392 --
...70\347\224\250\346\226\271\346\263\225.md" | 241 -
"Java/\345\271\266\345\217\221.md" | 1166 ----
"Java/\351\233\206\345\220\210.md" | 479 --
README.md | 141 +-
...23\345\255\230\345\207\273\347\251\277.md" | 60 -
docs/.vuepress/config.ts | 86 +
docs/.vuepress/enhanceApp.js | 13 +
docs/.vuepress/navbar.ts | 276 +
docs/.vuepress/sidebar.ts | 176 +
docs/.vuepress/styles/index.scss | 0
docs/.vuepress/styles/palette.scss | 0
docs/.vuepress/theme.ts | 166 +
.../vuepress-sidebar-auto.js | 92 +
docs/README.md | 82 +
docs/about/contact.md | 27 +
docs/about/introduce.md | 66 +
docs/advance/concurrent/1-current-limiting.md | 139 +
docs/advance/concurrent/2-load-balance.md | 164 +
docs/advance/concurrent/README.md | 4 +
.../advance/design-pattern-all.md | 0
docs/advance/design-pattern/1-principle.md | 9 +
docs/advance/design-pattern/10-observer.md | 17 +
docs/advance/design-pattern/11-proxy.md | 77 +
docs/advance/design-pattern/12-builder.md | 79 +
docs/advance/design-pattern/2-singleton.md | 94 +
docs/advance/design-pattern/3-factory.md | 91 +
docs/advance/design-pattern/4-template.md | 82 +
docs/advance/design-pattern/5-strategy.md | 111 +
docs/advance/design-pattern/6-chain.md | 190 +
docs/advance/design-pattern/7-iterator.md | 30 +
docs/advance/design-pattern/8-decorator.md | 20 +
docs/advance/design-pattern/9-adapter.md | 18 +
docs/advance/design-pattern/README.md | 25 +
.../advance/distributed/1-global-unique-id.md | 30 +-
.../advance/distributed/2-distributed-lock.md | 4 +-
.../advance/distributed/3-rpc.md | 4 +-
docs/advance/distributed/4-micro-service.md | 183 +
docs/advance/distributed/5-distibuted-arch.md | 3 +
.../distributed/6-distributed-transaction.md | 197 +
docs/advance/distributed/README.md | 8 +
.../excellent-article/1-redis-stock-minus.md | 317 +
.../excellent-article/10-file-upload.md | 341 +
.../11-8-architect-pattern.md | 3 +
.../12-mysql-table-max-rows.md | 212 +
.../excellent-article/13-order-by-work.md | 263 +
.../excellent-article/14-architect-forward.md | 49 +
.../excellent-article/15-http-vs-rpc.md | 265 +
.../excellent-article/16-what-is-jwt.md | 167 +
.../excellent-article/2-spring-transaction.md | 111 +
.../3-springboot-auto-assembly.md | 544 ++
.../4-remove-duplicate-code.md | 595 ++
.../excellent-article/5-jvm-optimize.md | 94 +
.../excellent-article/6-spring-three-cache.md | 119 +
.../excellent-article/7-sql-optimize.md | 421 ++
.../8-interface-idempotent.md | 88 +
.../excellent-article/9-jvm-optimize-param.md | 242 +
docs/advance/excellent-article/README.md | 19 +
.../system-design/1-scan-code-login.md | 43 +-
.../2-order-timeout-auto-cancel.md | 616 ++
docs/advance/system-design/3-file-send.md | 17 +
.../advance/system-design/3-short-url.md | 60 +-
docs/advance/system-design/4-oversold.md | 30 +
docs/advance/system-design/5-second-kill.md | 45 +
.../6-wechat-redpacket-design.md | 134 +
docs/advance/system-design/7-file-send.md | 17 +
docs/advance/system-design/8-sso-design.md | 113 +
docs/advance/system-design/README.md | 11 +
...3\346\215\267\346\226\271\345\274\217.lnk" | Bin 0 -> 705 bytes
docs/campus-recruit/README.md | 3 +
docs/campus-recruit/career-plan.md | 37 +
.../interview}/1-byte-and-dance.md | 0
.../campus-recruit/interview}/10-netease.md | 0
.../campus-recruit/interview}/2-tencent.md | 2 +-
.../campus-recruit/interview}/3-baidu.md | 0
.../campus-recruit/interview}/4-ali.md | 0
.../campus-recruit/interview}/5-kuaishou.md | 0
.../campus-recruit/interview}/6-meituan.md | 0
.../campus-recruit/interview}/7-shopee.md | 0
.../campus-recruit/interview}/8-jingdong.md | 0
.../campus-recruit/interview}/9-huawei.md | 0
.../campus-recruit/interview}/README.md | 0
docs/campus-recruit/layoffs-solution.md | 24 +
.../campus-recruit/program-language/README.md | 5 +
.../program-language/java-or-c++.md | 34 +
.../program-language/java-or-golang.md | 73 +
docs/campus-recruit/project-experience.md | 124 +
docs/campus-recruit/resume.md | 136 +
docs/campus-recruit/share/1-23-backend.md | 108 +
docs/campus-recruit/share/2-no-offer.md | 56 +
docs/campus-recruit/share/README.md | 5 +
docs/computer-basic/algorithm.md | 1440 ++++
.../computer-basic/data-structure.md | 128 +-
docs/computer-basic/network.md | 601 ++
.../computer-basic/operate-system.md | 10 +-
.../\345\233\276\350\247\243HTTP.md" | 6 +-
docs/database/es/1-es-architect.md | 53 +
.../database/mysql-basic-all.md | 224 +-
docs/database/mysql-basic/1-data-type.md | 82 +
docs/database/mysql-basic/10-view.md | 50 +
docs/database/mysql-basic/11-procedure.md | 101 +
docs/database/mysql-basic/12-cursor.md | 58 +
docs/database/mysql-basic/13-trigger.md | 51 +
docs/database/mysql-basic/14-transaction.md | 32 +
docs/database/mysql-basic/15-permission.md | 35 +
.../mysql-basic/16-performace-optimization.md | 18 +
docs/database/mysql-basic/17-index.md | 36 +
docs/database/mysql-basic/2-basic-command.md | 230 +
docs/database/mysql-basic/3-function.md | 42 +
docs/database/mysql-basic/4-sum.md | 22 +
docs/database/mysql-basic/5-group.md | 56 +
docs/database/mysql-basic/6-join.md | 92 +
.../database/mysql-basic/7-full-text-query.md | 39 +
docs/database/mysql-basic/8-table-operate.md | 105 +
docs/database/mysql-basic/9-column-operate.md | 48 +
docs/database/mysql-basic/README.md | 31 +
.../database/mysql-execution-plan.md | 38 +-
.../database/mysql.md | 221 +-
docs/distributed/1-global-unique-id.md | 489 ++
docs/distributed/2-distributed-lock.md | 259 +
docs/distributed/2.1-rpc.md | 123 +
.../distributed/3-micro-service.md | 6 +-
docs/distributed/4-distibuted-arch.md | 3 +
docs/distributed/5-distributed-transaction.md | 199 +
...PC\347\232\204\345\216\237\347\220\206.md" | 0
...15\347\275\256\344\270\255\345\277\203.md" | 0
.../framework/mybatis.md | 15 +-
.../framework/netty-overview.md | 245 +-
docs/framework/netty/1-overview.md | 16 +
docs/framework/netty/10-encoder-decoder.md | 146 +
.../netty/11-preset-channel-handler.md | 409 ++
docs/framework/netty/2-make-your-app.md | 186 +
docs/framework/netty/3-component.md | 70 +
docs/framework/netty/4-transport.md | 66 +
docs/framework/netty/6-channel-handler.md | 88 +
docs/framework/netty/7-channel-pipeline.md | 79 +
.../netty/8-eventloop-thread-model.md | 46 +
docs/framework/netty/9-guide.md | 57 +
docs/framework/netty/README.md | 24 +
.../framework/spring.md | 64 +-
.../framework/springboot.md | 65 +-
docs/framework/springcloud-interview.md | 249 +
.../framework/springcloud-overview.md | 110 +-
docs/framework/springcloud/1-basic.md | 48 +
docs/framework/springcloud/10-bus.md | 6 +
docs/framework/springcloud/11-security.md | 28 +
.../2-springboot-springcloud-diff.md | 9 +
docs/framework/springcloud/3-eureka.md | 117 +
docs/framework/springcloud/4-ribbon.md | 132 +
docs/framework/springcloud/5-hystrix.md | 82 +
docs/framework/springcloud/6-feign.md | 120 +
docs/framework/springcloud/7-zuul.md | 278 +
docs/framework/springcloud/8-gateway.md | 407 ++
docs/framework/springcloud/9-config.md | 274 +
docs/framework/springcloud/README.md | 22 +
docs/framework/springmvc.md | 188 +
.../concurrent/1-forbid-default-executor.md | 43 +
docs/interview/java/1-create-object.md | 9 +
docs/interview/java/2-3rd-interface.md | 13 +
docs/interview/java/3-design-interface.md | 11 +
docs/interview/java/4-interface-slow.md | 19 +
.../java/5-comparable-vs-comparator.md | 14 +
docs/interview/java/README.md | 7 +
.../interview/javaweb/1-interceptor-filter.md | 27 +
docs/interview/mq/1-why-to-use-mq.md | 32 +
.../network/1-input-url-return-page.md | 25 +
docs/interview/network/2-http-status-code.md | 86 +
.../java/java-basic.md | 74 +
.../java/java-collection.md | 48 +-
.../java/java-concurrent.md | 148 +-
.../java/java8-all.md | 83 +-
docs/java/java8/1-functional-program.md | 16 +
docs/java/java8/2-lambda.md | 26 +
docs/java/java8/3-functional-interface.md | 25 +
.../java8/4-inner-functional-interface.md | 149 +
docs/java/java8/5-stream.md | 272 +
docs/java/java8/6-parallel-stream.md | 31 +
docs/java/java8/7-map.md | 118 +
docs/java/java8/README.md | 11 +
.../java/jvm.md | 80 +-
docs/learn/ghelper.md | 63 +
docs/learn/leetcode.md | 25 +
docs/learn/manual.md | 22 +
docs/learning-resources/chang-gou-mall.md | 15 +
docs/learning-resources/cs-learn-guide.md | 273 +
docs/learning-resources/java-learn-guide.md | 810 +++
docs/learning-resources/leetcode-note.md | 26 +
docs/learning-resources/mysql-top.md | 213 +
docs/learning-resources/mysql45-section.md | 19 +
docs/learning-resources/sgg-java-learn.md | 33 +
docs/learning-resources/shang-chou.md | 13 +
docs/learning-resources/springboot-guide.md | 21 +
docs/leetcode/README.md | 40 +
docs/leetcode/hot120/1-two-sum.md | 49 +
...inary-tree-zigzag-level-order-traversal.md | 69 +
.../104-maximum-depth-of-binary-tree.md | 52 +
.../hot120/11-container-with-most-water.md | 52 +
.../hot120/1143-longest-common-subquence.md | 102 +
docs/leetcode/hot120/120-triangle.md | 47 +
.../121-best-time-to-buy-and-sell-stock.md | 41 +
.../122-best-time-to-buy-and-sell-stock-ii.md | 43 +
.../128-longest-consecutive-sequence.md | 54 +
.../leetcode/hot120/131-palindrome-partion.md | 69 +
docs/leetcode/hot120/133-clone-graph.md | 89 +
docs/leetcode/hot120/134-gas-station.md | 50 +
docs/leetcode/hot120/141-linked-list-cycle.md | 96 +
docs/leetcode/hot120/148-sort-list.md | 112 +
docs/leetcode/hot120/15-3sum.md | 62 +
.../hot120/152-maximum-product-subarray.md | 45 +
.../160-intersection-of-two-linked-lists.md | 39 +
docs/leetcode/hot120/169-majority-element.md | 47 +
docs/leetcode/hot120/18-4sum .md | 76 +
.../19-remove-nth-node-from-end-of-list.md | 55 +
.../hot120/199-binary-tree-right-side-view.md | 51 +
docs/leetcode/hot120/20-valid-parentheses.md | 56 +
docs/leetcode/hot120/200-number-of-islands.md | 65 +
.../hot120/206-reverse-linked-list.md | 44 +
.../hot120/21-merge-two-sorted-lists.md | 41 +
.../215-kth-largest-element-in-an-array.md | 94 +
.../hot120/22-generate-parentheses.md | 105 +
.../hot120/234-palindrome-linked-list.md | 56 +
...lowest-common-ancestor-of-a-binary-tree.md | 41 +
.../leetcode/hot120/24-swap-nodes-in-pairs.md | 42 +
.../26-remove-duplicates-from-sorted-array.md | 56 +
docs/leetcode/hot120/27-remove-element.md | 44 +
docs/leetcode/hot120/28-mirror-binary-tree.md | 44 +
.../leetcode/hot120/29-divide-two-integers.md | 65 +
...-substring-without-repeating-characters.md | 59 +
docs/leetcode/hot120/31-next-permutation.md | 53 +
...ast-position-of-element-in-sorted-array.md | 56 +
docs/leetcode/hot120/36-valid-sudoku.md | 75 +
docs/leetcode/hot120/40-combination-sum-ii.md | 68 +
docs/leetcode/hot120/415-add-strings.md | 48 +
docs/leetcode/hot120/43-multiply-strings.md | 53 +
docs/leetcode/hot120/46-permutations.md | 49 +
docs/leetcode/hot120/47-permutations-ii.md | 63 +
docs/leetcode/hot120/48-rotate-image.md | 49 +
docs/leetcode/hot120/49-group-anagrams.md | 44 +
.../hot120/5-longest-palindromic-substring.md | 68 +
docs/leetcode/hot120/50-powx-n.md | 41 +
docs/leetcode/hot120/53-maximum-subarray.md | 37 +
docs/leetcode/hot120/54-spiral-matrix.md | 71 +
.../hot120/543-diameter-of-binary-tree.md | 47 +
docs/leetcode/hot120/55-jump-game.md | 47 +
docs/leetcode/hot120/56-merge-intervals.md | 43 +
docs/leetcode/hot120/59-spiral-matrix-ii.md | 60 +
docs/leetcode/hot120/62-unique-paths.md | 120 +
docs/leetcode/hot120/7-reverse-integer.md | 58 +
docs/leetcode/hot120/71-simplify-path.md | 73 +
docs/leetcode/hot120/73-set-matrix-zeroes.md | 76 +
docs/leetcode/hot120/74-search-a-2d-matrix.md | 54 +
docs/leetcode/hot120/75-sort-colors.md | 51 +
docs/leetcode/hot120/77-combinations.md | 67 +
.../83-remove-duplicates-from-sorted-list.md | 39 +
docs/leetcode/hot120/9-palindrome-number.md | 51 +
.../hot120/92-reverse-linked-list-ii.md | 41 +
...958-check-completeness-of-a-binary-tree.md | 57 +
.../hot120/98-validate-binary-search-tree.md | 56 +
docs/leetcode/hot120/README.md | 41 +
.../mass-data/1-count-phone-num.md | 8 +-
.../mass-data/2-find-hign-frequency-word.md | 17 +-
docs/mass-data/3-find-same-url.md | 30 +
docs/mass-data/4-find-mid-num.md | 36 +
docs/mass-data/5-find-hot-string.md | 54 +
docs/mass-data/6-top-500-num.md | 116 +
docs/mass-data/7-query-frequency-sort.md | 33 +
docs/mass-data/8-topk-template.md | 166 +
docs/mass-data/README.md | 12 +
.../kafka.md" => docs/message-queue/kafka.md | 0
docs/message-queue/mq.md | 181 +
.../message-queue/rabbitmq.md | 0
docs/other/leave-a-message.md | 4 +
docs/other/site-diary.md | 109 +
.../service-performance-optimization.md | 25 +
.../redis/redis-basic-all.md | 0
docs/redis/redis-basic/1-introduce.md | 36 +
docs/redis/redis-basic/10-lua.md | 70 +
docs/redis/redis-basic/11-deletion-policy.md | 26 +
docs/redis/redis-basic/12-others.md | 81 +
docs/redis/redis-basic/2-data-type.md | 318 +
docs/redis/redis-basic/3-data-structure.md | 100 +
docs/redis/redis-basic/4-implement.md | 68 +
docs/redis/redis-basic/5-sort.md | 36 +
docs/redis/redis-basic/6-transaction.md | 61 +
docs/redis/redis-basic/7-message-queue.md | 31 +
docs/redis/redis-basic/8-persistence.md | 67 +
docs/redis/redis-basic/9-cluster.md | 121 +
docs/redis/redis-basic/README.md | 26 +
.../redis/redis.md | 78 +-
docs/resource/1-cs-books.md | 329 +
.../tools/docker-overview.md | 152 +-
docs/tools/docker/1-introduce.md | 27 +
docs/tools/docker/2-image-command.md | 209 +
docs/tools/docker/3-container-command.md | 242 +
docs/tools/docker/4-docker-compose.md | 102 +
docs/tools/docker/5-maven-build.md | 136 +
docs/tools/docker/6-other.md | 53 +
docs/tools/docker/README.md | 17 +
.../tools/git-overview.md | 143 +-
docs/tools/git/1-introduce.md | 71 +
docs/tools/git/2-basic.md | 280 +
docs/tools/git/3-remote-repo.md | 110 +
docs/tools/git/4-label.md | 101 +
docs/tools/git/5-branch.md | 305 +
docs/tools/git/README.md | 17 +
.../tools/linux-overview.md | 0
docs/tools/linux/1-basic.md | 172 +
docs/tools/linux/2-disk-file.md | 159 +
docs/tools/linux/3-search.md | 59 +
docs/tools/linux/4-net.md | 125 +
docs/tools/linux/5-monitor.md | 131 +
docs/tools/linux/README.md | 12 +
.../tools/maven-overview.md | 127 +-
docs/tools/maven/1-introduce.md | 12 +
docs/tools/maven/2-basic.md | 104 +
docs/tools/maven/3-dependency.md | 53 +
docs/tools/maven/4-repo.md | 88 +
docs/tools/maven/5-lifecycle.md | 24 +
docs/tools/maven/6-plugin.md | 63 +
docs/tools/maven/7-aggregator.md | 17 +
docs/tools/maven/8-inherit.md | 169 +
docs/tools/maven/README.md | 20 +
docs/tools/typora-overview.md | 184 +
img/1588431199776.png | Bin 63521 -> 0 bytes
img/ConcurrentHashMap-segment.jpg | Bin 230179 -> 0 bytes
img/JDK1.8-ConcurrentHashMap-Structure.jpg | Bin 12869 -> 0 bytes
img/Java-Collections.jpeg | Bin 167969 -> 0 bytes
img/all-table-search.jpg | Bin 14178 -> 0 bytes
img/aqs.png | Bin 21797 -> 0 bytes
img/bean-life-cycle.jpg | Bin 29410 -> 0 bytes
img/bloom-filter.jpg | Bin 23578 -> 0 bytes
img/cms-collector.png | Bin 86817 -> 0 bytes
img/concurrent/executors-ali.png | Bin 114331 -> 0 bytes
img/concurrent/synchronized-block.png | Bin 188866 -> 0 bytes
img/concurrent/synchronized-method.png | Bin 127575 -> 0 bytes
img/condition-await.png | Bin 49132 -> 0 bytes
img/condition-signal.png | Bin 48475 -> 0 bytes
img/condition-wait-queue.png | Bin 46923 -> 0 bytes
img/cover-index.png | Bin 25311 -> 0 bytes
img/direct-pointer.png | Bin 94396 -> 0 bytes
img/docker/docker.jpg | Bin 88608 -> 0 bytes
img/explain-all.png | Bin 8446 -> 0 bytes
img/explain-const.png | Bin 4419 -> 0 bytes
img/explain-id.png | Bin 5014 -> 0 bytes
img/explain-range.png | Bin 4762 -> 0 bytes
img/explain-ref.png | Bin 10220 -> 0 bytes
img/g1-region.jpg | Bin 19644 -> 0 bytes
img/gc-root-refer.png | Bin 14443 -> 0 bytes
img/git-status.png | Bin 109277 -> 0 bytes
img/heap-structure.png | Bin 56484 -> 0 bytes
img/image-20200520234137916.png | Bin 13219 -> 0 bytes
img/image-20200520234200868.png | Bin 26483 -> 0 bytes
img/image-20200520234231001.png | Bin 25332 -> 0 bytes
img/image-20200608232749393.png | Bin 4492 -> 0 bytes
img/image-20200608232951873.png | Bin 2591 -> 0 bytes
img/image-20200608233400458.png | Bin 2946 -> 0 bytes
img/image-20200614165333479.png | Bin 208621 -> 0 bytes
img/image-20200614165432775.png | Bin 195674 -> 0 bytes
img/index-search-range.jpg | Bin 14490 -> 0 bytes
img/index-search.jpg | Bin 12920 -> 0 bytes
img/java-basic/exception.png | Bin 37810 -> 0 bytes
img/java-basic/field-method.png | Bin 118178 -> 0 bytes
img/java-basic/io.jpg | Bin 50725 -> 0 bytes
img/java-ram-region.png | Bin 110051 -> 0 bytes
img/jmeter/http-request-param.png | Bin 30984 -> 0 bytes
img/jmeter/jmeter-aggregate-report.png | Bin 49256 -> 0 bytes
img/jmeter/jmeter-default-request.png | Bin 79751 -> 0 bytes
img/jmeter/jmeter-request-header.png | Bin 75457 -> 0 bytes
img/jmeter/jmeter-thread-group.png | Bin 34358 -> 0 bytes
img/jvm/string-equal.png | Bin 33445 -> 0 bytes
img/jvm/string-intern.png | Bin 36462 -> 0 bytes
img/jvm/string-new.png | Bin 28718 -> 0 bytes
img/left-match.jpg | Bin 31737 -> 0 bytes
img/middleware/redis-transaction.png | Bin 7291 -> 0 bytes
img/middleware/redis-usage.png | Bin 31580 -> 0 bytes
img/mysql-architecture.png | Bin 1229182 -> 0 bytes
img/mysql-clustered-index.png | Bin 35301 -> 0 bytes
img/mysql/arch.png | Bin 69339 -> 0 bytes
img/mysql/current-read.png | Bin 10277 -> 0 bytes
img/mysql/mvcc-impl.png | Bin 38026 -> 0 bytes
img/mysql/myisam-innodb-index.png | Bin 82352 -> 0 bytes
img/mysql/mysql-archpng.png | Bin 223491 -> 0 bytes
img/mysql/nested-loop.png | Bin 129701 -> 0 bytes
img/mysql/orderby.png | Bin 6252 -> 0 bytes
img/net/bio.png | Bin 6774 -> 0 bytes
img/net/cdn.png | Bin 942422 -> 0 bytes
img/net/https-certificate-chain.jpg | Bin 33273 -> 0 bytes
img/net/https-certificate.png | Bin 34566 -> 0 bytes
img/net/https-client-hello.jpg | Bin 86157 -> 0 bytes
img/net/https-server-hello.jpg | Bin 37249 -> 0 bytes
img/net/nio.png | Bin 7276 -> 0 bytes
img/oauth2.png | Bin 10836 -> 0 bytes
img/object-dead.png | Bin 49726 -> 0 bytes
img/object-handle.png | Bin 110349 -> 0 bytes
img/optimistic-lock.jpg | Bin 39394 -> 0 bytes
img/others/json-web-token.png | Bin 61702 -> 0 bytes
img/others/seckill.jpg | Bin 32958 -> 0 bytes
img/parnew-collector.png | Bin 42532 -> 0 bytes
img/rabbitmq-direct.png | Bin 8762 -> 0 bytes
img/rabbitmq-fanout.png | Bin 9472 -> 0 bytes
img/rabbitmq-return-listener.png | Bin 48332 -> 0 bytes
img/rabbitmq-topic.png | Bin 12484 -> 0 bytes
img/rabbitmq.png | Bin 6474 -> 0 bytes
img/redis/cache-consist.png | Bin 23667 -> 0 bytes
img/redis/evalsha.png | Bin 184215 -> 0 bytes
img/redis/list-api.png | Bin 178712 -> 0 bytes
img/redis/redis-replication.png | Bin 178354 -> 0 bytes
img/redis/redis-skiplist.png | Bin 11736 -> 0 bytes
img/redis/time-complexity.png | Bin 421110 -> 0 bytes
...0\344\272\222\346\226\245\351\224\201.png" | Bin 232236 -> 0 bytes
img/scheduled-task.jpg | Bin 85304 -> 0 bytes
img/serial-collector.png | Bin 34121 -> 0 bytes
img/singleton-class-init.png | Bin 49462 -> 0 bytes
...7\345\210\253\346\263\250\350\247\243.png" | Bin 57651 -> 0 bytes
...5\347\275\256\345\216\237\347\220\206.jpg" | Bin 851247 -> 0 bytes
img/thread-pool.png | Bin 17381 -> 0 bytes
img/thread-status.jpeg | Bin 88664 -> 0 bytes
img/threadlocal-oom.png | Bin 23902 -> 0 bytes
img/threadlocal.png | Bin 17096 -> 0 bytes
img/tree-visit.png | Bin 25772 -> 0 bytes
img/two-index.jpg | Bin 15986 -> 0 bytes
img/web/servlet-container.jpg | Bin 22543 -> 0 bytes
"img/\345\271\273\350\257\2731.png" | Bin 22122 -> 0 bytes
...5\345\241\236\346\216\247\345\210\266.jpg" | Bin 48625 -> 0 bytes
...5\347\275\256\346\226\271\346\263\225.png" | Bin 49050 -> 0 bytes
package-lock.json | 6195 +++++++++++++++++
package.json | 26 +
.../Elasticsearch\345\205\245\351\227\250.md" | 2438 -------
"\345\205\266\344\273\226/code.md" | 77 -
"\345\205\266\344\273\226/linux.md" | 417 --
"\345\205\266\344\273\226/note.md" | 286 -
.../\345\256\236\346\210\230\347\257\207.md" | 93 -
...03\345\274\217\344\272\213\345\212\241.md" | 77 -
...03\345\274\217\347\274\223\345\255\230.md" | 98 -
"\345\211\215\347\253\257/vue.md" | 401 --
.../GitHub\346\214\207\345\215\227.md" | 91 -
"\345\267\245\345\205\267/NPM.md" | 48 -
"\345\267\245\345\205\267/jenkins.md" | 120 -
"\345\267\245\345\205\267/jmeter.md" | 39 -
...50\347\275\262\351\241\271\347\233\256.md" | 325 -
.../MySQL\350\277\233\351\230\266.md" | 811 ---
.../SpringBoot\345\256\236\346\210\230.md" | 1028 ---
"\346\241\206\346\236\266/SpringMVC.md" | 1602 -----
...VC\351\235\242\350\257\225\351\242\230.md" | 89 -
.../Spring\345\256\236\346\210\230.md" | 3228 ---------
.../Spring\346\200\273\347\273\223.md" | 471 --
...76\350\256\241\346\250\241\345\274\217.md" | 67 -
...52\345\212\250\350\243\205\351\205\215.md" | 187 -
...06\344\270\216\345\256\236\346\210\230.md" | 2435 -------
...73\344\277\241\351\230\237\345\210\227.md" | 117 -
...27\351\235\242\350\257\225\351\242\230.md" | 72 -
...347\273\237\350\256\276\350\256\241old.md" | 24 -
.../\347\256\227\346\263\225.md" | 374 -
.../TCP IP\345\215\217\350\256\256.md" | 349 -
.../session\345\222\214cookie.md" | 23 -
.../\347\275\221\347\273\234.md" | 362 -
...21\351\235\242\350\257\225\351\242\230.md" | 315 -
464 files changed, 34561 insertions(+), 26055 deletions(-)
create mode 100644 CNAME
delete mode 100644 Java/JVM.md
delete mode 100644 Java/Java web.md
delete mode 100644 "Java/Java \347\274\226\347\250\213\346\200\235\346\203\263.md"
delete mode 100644 "Java/Java\345\205\263\351\224\256\345\255\227.md"
delete mode 100644 "Java/Java\345\237\272\347\241\200.md"
delete mode 100644 "Java/Java\345\271\266\345\217\221\347\274\226\347\250\213.md"
delete mode 100644 "Java/Object\347\261\273\345\270\270\347\224\250\346\226\271\346\263\225.md"
delete mode 100644 "Java/\345\271\266\345\217\221.md"
delete mode 100644 "Java/\351\233\206\345\220\210.md"
delete mode 100644 "Redis/\347\274\223\345\255\230\347\251\277\351\200\217\343\200\201\347\274\223\345\255\230\351\233\252\345\264\251\343\200\201\347\274\223\345\255\230\345\207\273\347\251\277.md"
create mode 100644 docs/.vuepress/config.ts
create mode 100644 docs/.vuepress/enhanceApp.js
create mode 100644 docs/.vuepress/navbar.ts
create mode 100644 docs/.vuepress/sidebar.ts
create mode 100644 docs/.vuepress/styles/index.scss
create mode 100644 docs/.vuepress/styles/palette.scss
create mode 100644 docs/.vuepress/theme.ts
create mode 100644 docs/.vuepress/vuepress-sidebar-auto/vuepress-sidebar-auto.js
create mode 100644 docs/README.md
create mode 100644 docs/about/contact.md
create mode 100644 docs/about/introduce.md
create mode 100644 docs/advance/concurrent/1-current-limiting.md
create mode 100644 docs/advance/concurrent/2-load-balance.md
create mode 100644 docs/advance/concurrent/README.md
rename "\345\205\266\344\273\226/\350\256\276\350\256\241\346\250\241\345\274\217.md" => docs/advance/design-pattern-all.md (100%)
create mode 100644 docs/advance/design-pattern/1-principle.md
create mode 100644 docs/advance/design-pattern/10-observer.md
create mode 100644 docs/advance/design-pattern/11-proxy.md
create mode 100644 docs/advance/design-pattern/12-builder.md
create mode 100644 docs/advance/design-pattern/2-singleton.md
create mode 100644 docs/advance/design-pattern/3-factory.md
create mode 100644 docs/advance/design-pattern/4-template.md
create mode 100644 docs/advance/design-pattern/5-strategy.md
create mode 100644 docs/advance/design-pattern/6-chain.md
create mode 100644 docs/advance/design-pattern/7-iterator.md
create mode 100644 docs/advance/design-pattern/8-decorator.md
create mode 100644 docs/advance/design-pattern/9-adapter.md
create mode 100644 docs/advance/design-pattern/README.md
rename "\345\210\206\345\270\203\345\274\217/\345\205\250\345\261\200\345\224\257\344\270\200ID.md" => docs/advance/distributed/1-global-unique-id.md (97%)
rename "\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\351\224\201.md" => docs/advance/distributed/2-distributed-lock.md (99%)
rename "\345\210\206\345\270\203\345\274\217/\350\277\234\347\250\213\350\260\203\347\224\250.md" => docs/advance/distributed/3-rpc.md (99%)
create mode 100644 docs/advance/distributed/4-micro-service.md
create mode 100644 docs/advance/distributed/5-distibuted-arch.md
create mode 100644 docs/advance/distributed/6-distributed-transaction.md
create mode 100644 docs/advance/distributed/README.md
create mode 100644 docs/advance/excellent-article/1-redis-stock-minus.md
create mode 100644 docs/advance/excellent-article/10-file-upload.md
create mode 100644 docs/advance/excellent-article/11-8-architect-pattern.md
create mode 100644 docs/advance/excellent-article/12-mysql-table-max-rows.md
create mode 100644 docs/advance/excellent-article/13-order-by-work.md
create mode 100644 docs/advance/excellent-article/14-architect-forward.md
create mode 100644 docs/advance/excellent-article/15-http-vs-rpc.md
create mode 100644 docs/advance/excellent-article/16-what-is-jwt.md
create mode 100644 docs/advance/excellent-article/2-spring-transaction.md
create mode 100644 docs/advance/excellent-article/3-springboot-auto-assembly.md
create mode 100644 docs/advance/excellent-article/4-remove-duplicate-code.md
create mode 100644 docs/advance/excellent-article/5-jvm-optimize.md
create mode 100644 docs/advance/excellent-article/6-spring-three-cache.md
create mode 100644 docs/advance/excellent-article/7-sql-optimize.md
create mode 100644 docs/advance/excellent-article/8-interface-idempotent.md
create mode 100644 docs/advance/excellent-article/9-jvm-optimize-param.md
create mode 100644 docs/advance/excellent-article/README.md
rename "\347\263\273\347\273\237\350\256\276\350\256\241/\346\211\253\347\240\201\347\231\273\345\275\225\345\216\237\347\220\206.md" => docs/advance/system-design/1-scan-code-login.md (51%)
create mode 100644 docs/advance/system-design/2-order-timeout-auto-cancel.md
create mode 100644 docs/advance/system-design/3-file-send.md
rename "\347\263\273\347\273\237\350\256\276\350\256\241/\347\263\273\347\273\237\350\256\276\350\256\241.md" => docs/advance/system-design/3-short-url.md (61%)
create mode 100644 docs/advance/system-design/4-oversold.md
create mode 100644 docs/advance/system-design/5-second-kill.md
create mode 100644 docs/advance/system-design/6-wechat-redpacket-design.md
create mode 100644 docs/advance/system-design/7-file-send.md
create mode 100644 docs/advance/system-design/8-sso-design.md
create mode 100644 docs/advance/system-design/README.md
create mode 100644 "docs/advance/system-design/\345\233\276\347\211\207 - \345\277\253\346\215\267\346\226\271\345\274\217.lnk"
create mode 100644 docs/campus-recruit/README.md
create mode 100644 docs/campus-recruit/career-plan.md
rename {interview => docs/campus-recruit/interview}/1-byte-and-dance.md (100%)
rename {interview => docs/campus-recruit/interview}/10-netease.md (100%)
rename {interview => docs/campus-recruit/interview}/2-tencent.md (98%)
rename {interview => docs/campus-recruit/interview}/3-baidu.md (100%)
rename {interview => docs/campus-recruit/interview}/4-ali.md (100%)
rename {interview => docs/campus-recruit/interview}/5-kuaishou.md (100%)
rename {interview => docs/campus-recruit/interview}/6-meituan.md (100%)
rename {interview => docs/campus-recruit/interview}/7-shopee.md (100%)
rename {interview => docs/campus-recruit/interview}/8-jingdong.md (100%)
rename {interview => docs/campus-recruit/interview}/9-huawei.md (100%)
rename {interview => docs/campus-recruit/interview}/README.md (100%)
create mode 100644 docs/campus-recruit/layoffs-solution.md
create mode 100644 docs/campus-recruit/program-language/README.md
create mode 100644 docs/campus-recruit/program-language/java-or-c++.md
create mode 100644 docs/campus-recruit/program-language/java-or-golang.md
create mode 100644 docs/campus-recruit/project-experience.md
create mode 100644 docs/campus-recruit/resume.md
create mode 100644 docs/campus-recruit/share/1-23-backend.md
create mode 100644 docs/campus-recruit/share/2-no-offer.md
create mode 100644 docs/campus-recruit/share/README.md
create mode 100644 docs/computer-basic/algorithm.md
rename "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\225\260\346\215\256\347\273\223\346\236\204.md" => docs/computer-basic/data-structure.md (62%)
create mode 100644 docs/computer-basic/network.md
rename "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md" => docs/computer-basic/operate-system.md (98%)
rename "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\345\233\276\350\247\243HTTP.md" => "docs/computer-basic/\345\233\276\350\247\243HTTP.md" (99%)
create mode 100644 docs/database/es/1-es-architect.md
rename "\346\225\260\346\215\256\345\272\223/MySQL\345\237\272\347\241\200.md" => docs/database/mysql-basic-all.md (94%)
create mode 100644 docs/database/mysql-basic/1-data-type.md
create mode 100644 docs/database/mysql-basic/10-view.md
create mode 100644 docs/database/mysql-basic/11-procedure.md
create mode 100644 docs/database/mysql-basic/12-cursor.md
create mode 100644 docs/database/mysql-basic/13-trigger.md
create mode 100644 docs/database/mysql-basic/14-transaction.md
create mode 100644 docs/database/mysql-basic/15-permission.md
create mode 100644 docs/database/mysql-basic/16-performace-optimization.md
create mode 100644 docs/database/mysql-basic/17-index.md
create mode 100644 docs/database/mysql-basic/2-basic-command.md
create mode 100644 docs/database/mysql-basic/3-function.md
create mode 100644 docs/database/mysql-basic/4-sum.md
create mode 100644 docs/database/mysql-basic/5-group.md
create mode 100644 docs/database/mysql-basic/6-join.md
create mode 100644 docs/database/mysql-basic/7-full-text-query.md
create mode 100644 docs/database/mysql-basic/8-table-operate.md
create mode 100644 docs/database/mysql-basic/9-column-operate.md
create mode 100644 docs/database/mysql-basic/README.md
rename "\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md" => docs/database/mysql-execution-plan.md (92%)
rename "\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" => docs/database/mysql.md (77%)
create mode 100644 docs/distributed/1-global-unique-id.md
create mode 100644 docs/distributed/2-distributed-lock.md
create mode 100644 docs/distributed/2.1-rpc.md
rename "\345\210\206\345\270\203\345\274\217/\345\276\256\346\234\215\345\212\241.md" => docs/distributed/3-micro-service.md (99%)
create mode 100644 docs/distributed/4-distibuted-arch.md
create mode 100644 docs/distributed/5-distributed-transaction.md
rename "\345\210\206\345\270\203\345\274\217/RocketMQ\345\256\236\347\216\260RPC\347\232\204\345\216\237\347\220\206.md" => "docs/distributed/RocketMQ\345\256\236\347\216\260RPC\347\232\204\345\216\237\347\220\206.md" (100%)
rename "\346\241\206\346\236\266/Apollo\351\205\215\347\275\256\344\270\255\345\277\203.md" => "docs/framework/Apollo\351\205\215\347\275\256\344\270\255\345\277\203.md" (100%)
rename "\346\241\206\346\236\266/Mybatis\351\235\242\350\257\225\351\242\230.md" => docs/framework/mybatis.md (86%)
rename "\346\241\206\346\236\266/netty\345\256\236\346\210\230.md" => docs/framework/netty-overview.md (84%)
create mode 100644 docs/framework/netty/1-overview.md
create mode 100644 docs/framework/netty/10-encoder-decoder.md
create mode 100644 docs/framework/netty/11-preset-channel-handler.md
create mode 100644 docs/framework/netty/2-make-your-app.md
create mode 100644 docs/framework/netty/3-component.md
create mode 100644 docs/framework/netty/4-transport.md
create mode 100644 docs/framework/netty/6-channel-handler.md
create mode 100644 docs/framework/netty/7-channel-pipeline.md
create mode 100644 docs/framework/netty/8-eventloop-thread-model.md
create mode 100644 docs/framework/netty/9-guide.md
create mode 100644 docs/framework/netty/README.md
rename "\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md" => docs/framework/spring.md (92%)
rename "\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" => docs/framework/springboot.md (74%)
create mode 100644 docs/framework/springcloud-interview.md
rename "\346\241\206\346\236\266/SpringCloud\345\276\256\346\234\215\345\212\241\345\256\236\346\210\230.md" => docs/framework/springcloud-overview.md (96%)
create mode 100644 docs/framework/springcloud/1-basic.md
create mode 100644 docs/framework/springcloud/10-bus.md
create mode 100644 docs/framework/springcloud/11-security.md
create mode 100644 docs/framework/springcloud/2-springboot-springcloud-diff.md
create mode 100644 docs/framework/springcloud/3-eureka.md
create mode 100644 docs/framework/springcloud/4-ribbon.md
create mode 100644 docs/framework/springcloud/5-hystrix.md
create mode 100644 docs/framework/springcloud/6-feign.md
create mode 100644 docs/framework/springcloud/7-zuul.md
create mode 100644 docs/framework/springcloud/8-gateway.md
create mode 100644 docs/framework/springcloud/9-config.md
create mode 100644 docs/framework/springcloud/README.md
create mode 100644 docs/framework/springmvc.md
create mode 100644 docs/interview/concurrent/1-forbid-default-executor.md
create mode 100644 docs/interview/java/1-create-object.md
create mode 100644 docs/interview/java/2-3rd-interface.md
create mode 100644 docs/interview/java/3-design-interface.md
create mode 100644 docs/interview/java/4-interface-slow.md
create mode 100644 docs/interview/java/5-comparable-vs-comparator.md
create mode 100644 docs/interview/java/README.md
create mode 100644 docs/interview/javaweb/1-interceptor-filter.md
create mode 100644 docs/interview/mq/1-why-to-use-mq.md
create mode 100644 docs/interview/network/1-input-url-return-page.md
create mode 100644 docs/interview/network/2-http-status-code.md
rename "Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" => docs/java/java-basic.md (91%)
rename "Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md" => docs/java/java-collection.md (91%)
rename "Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md" => docs/java/java-concurrent.md (84%)
rename "Java/Java8\346\226\260\347\211\271\346\200\247.md" => docs/java/java8-all.md (87%)
create mode 100644 docs/java/java8/1-functional-program.md
create mode 100644 docs/java/java8/2-lambda.md
create mode 100644 docs/java/java8/3-functional-interface.md
create mode 100644 docs/java/java8/4-inner-functional-interface.md
create mode 100644 docs/java/java8/5-stream.md
create mode 100644 docs/java/java8/6-parallel-stream.md
create mode 100644 docs/java/java8/7-map.md
create mode 100644 docs/java/java8/README.md
rename "Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" => docs/java/jvm.md (87%)
create mode 100644 docs/learn/ghelper.md
create mode 100644 docs/learn/leetcode.md
create mode 100644 docs/learn/manual.md
create mode 100644 docs/learning-resources/chang-gou-mall.md
create mode 100644 docs/learning-resources/cs-learn-guide.md
create mode 100644 docs/learning-resources/java-learn-guide.md
create mode 100644 docs/learning-resources/leetcode-note.md
create mode 100644 docs/learning-resources/mysql-top.md
create mode 100644 docs/learning-resources/mysql45-section.md
create mode 100644 docs/learning-resources/sgg-java-learn.md
create mode 100644 docs/learning-resources/shang-chou.md
create mode 100644 docs/learning-resources/springboot-guide.md
create mode 100644 docs/leetcode/README.md
create mode 100644 docs/leetcode/hot120/1-two-sum.md
create mode 100644 docs/leetcode/hot120/103-binary-tree-zigzag-level-order-traversal.md
create mode 100644 docs/leetcode/hot120/104-maximum-depth-of-binary-tree.md
create mode 100644 docs/leetcode/hot120/11-container-with-most-water.md
create mode 100644 docs/leetcode/hot120/1143-longest-common-subquence.md
create mode 100644 docs/leetcode/hot120/120-triangle.md
create mode 100644 docs/leetcode/hot120/121-best-time-to-buy-and-sell-stock.md
create mode 100644 docs/leetcode/hot120/122-best-time-to-buy-and-sell-stock-ii.md
create mode 100644 docs/leetcode/hot120/128-longest-consecutive-sequence.md
create mode 100644 docs/leetcode/hot120/131-palindrome-partion.md
create mode 100644 docs/leetcode/hot120/133-clone-graph.md
create mode 100644 docs/leetcode/hot120/134-gas-station.md
create mode 100644 docs/leetcode/hot120/141-linked-list-cycle.md
create mode 100644 docs/leetcode/hot120/148-sort-list.md
create mode 100644 docs/leetcode/hot120/15-3sum.md
create mode 100644 docs/leetcode/hot120/152-maximum-product-subarray.md
create mode 100644 docs/leetcode/hot120/160-intersection-of-two-linked-lists.md
create mode 100644 docs/leetcode/hot120/169-majority-element.md
create mode 100644 docs/leetcode/hot120/18-4sum .md
create mode 100644 docs/leetcode/hot120/19-remove-nth-node-from-end-of-list.md
create mode 100644 docs/leetcode/hot120/199-binary-tree-right-side-view.md
create mode 100644 docs/leetcode/hot120/20-valid-parentheses.md
create mode 100644 docs/leetcode/hot120/200-number-of-islands.md
create mode 100644 docs/leetcode/hot120/206-reverse-linked-list.md
create mode 100644 docs/leetcode/hot120/21-merge-two-sorted-lists.md
create mode 100644 docs/leetcode/hot120/215-kth-largest-element-in-an-array.md
create mode 100644 docs/leetcode/hot120/22-generate-parentheses.md
create mode 100644 docs/leetcode/hot120/234-palindrome-linked-list.md
create mode 100644 docs/leetcode/hot120/236-lowest-common-ancestor-of-a-binary-tree.md
create mode 100644 docs/leetcode/hot120/24-swap-nodes-in-pairs.md
create mode 100644 docs/leetcode/hot120/26-remove-duplicates-from-sorted-array.md
create mode 100644 docs/leetcode/hot120/27-remove-element.md
create mode 100644 docs/leetcode/hot120/28-mirror-binary-tree.md
create mode 100644 docs/leetcode/hot120/29-divide-two-integers.md
create mode 100644 docs/leetcode/hot120/3-longest-substring-without-repeating-characters.md
create mode 100644 docs/leetcode/hot120/31-next-permutation.md
create mode 100644 docs/leetcode/hot120/34-find-first-and-last-position-of-element-in-sorted-array.md
create mode 100644 docs/leetcode/hot120/36-valid-sudoku.md
create mode 100644 docs/leetcode/hot120/40-combination-sum-ii.md
create mode 100644 docs/leetcode/hot120/415-add-strings.md
create mode 100644 docs/leetcode/hot120/43-multiply-strings.md
create mode 100644 docs/leetcode/hot120/46-permutations.md
create mode 100644 docs/leetcode/hot120/47-permutations-ii.md
create mode 100644 docs/leetcode/hot120/48-rotate-image.md
create mode 100644 docs/leetcode/hot120/49-group-anagrams.md
create mode 100644 docs/leetcode/hot120/5-longest-palindromic-substring.md
create mode 100644 docs/leetcode/hot120/50-powx-n.md
create mode 100644 docs/leetcode/hot120/53-maximum-subarray.md
create mode 100644 docs/leetcode/hot120/54-spiral-matrix.md
create mode 100644 docs/leetcode/hot120/543-diameter-of-binary-tree.md
create mode 100644 docs/leetcode/hot120/55-jump-game.md
create mode 100644 docs/leetcode/hot120/56-merge-intervals.md
create mode 100644 docs/leetcode/hot120/59-spiral-matrix-ii.md
create mode 100644 docs/leetcode/hot120/62-unique-paths.md
create mode 100644 docs/leetcode/hot120/7-reverse-integer.md
create mode 100644 docs/leetcode/hot120/71-simplify-path.md
create mode 100644 docs/leetcode/hot120/73-set-matrix-zeroes.md
create mode 100644 docs/leetcode/hot120/74-search-a-2d-matrix.md
create mode 100644 docs/leetcode/hot120/75-sort-colors.md
create mode 100644 docs/leetcode/hot120/77-combinations.md
create mode 100644 docs/leetcode/hot120/83-remove-duplicates-from-sorted-list.md
create mode 100644 docs/leetcode/hot120/9-palindrome-number.md
create mode 100644 docs/leetcode/hot120/92-reverse-linked-list-ii.md
create mode 100644 docs/leetcode/hot120/958-check-completeness-of-a-binary-tree.md
create mode 100644 docs/leetcode/hot120/98-validate-binary-search-tree.md
create mode 100644 docs/leetcode/hot120/README.md
rename "\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\347\273\237\350\256\241\344\270\215\345\220\214\347\224\265\350\257\235\345\217\267\347\240\201\347\232\204\344\270\252\346\225\260.md" => docs/mass-data/1-count-phone-num.md (95%)
rename "\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\345\246\202\344\275\225\344\273\216\346\265\267\351\207\217\346\225\260\346\215\256\346\211\276\345\207\272\351\253\230\351\242\221\350\257\215.md" => docs/mass-data/2-find-hign-frequency-word.md (87%)
create mode 100644 docs/mass-data/3-find-same-url.md
create mode 100644 docs/mass-data/4-find-mid-num.md
create mode 100644 docs/mass-data/5-find-hot-string.md
create mode 100644 docs/mass-data/6-top-500-num.md
create mode 100644 docs/mass-data/7-query-frequency-sort.md
create mode 100644 docs/mass-data/8-topk-template.md
create mode 100644 docs/mass-data/README.md
rename "\346\266\210\346\201\257\351\230\237\345\210\227/kafka.md" => docs/message-queue/kafka.md (100%)
create mode 100644 docs/message-queue/mq.md
rename "\346\266\210\346\201\257\351\230\237\345\210\227/RabbitMQ.md" => docs/message-queue/rabbitmq.md (100%)
create mode 100644 docs/other/leave-a-message.md
create mode 100644 docs/other/site-diary.md
create mode 100644 docs/practice/service-performance-optimization.md
rename "Redis/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md" => docs/redis/redis-basic-all.md (100%)
create mode 100644 docs/redis/redis-basic/1-introduce.md
create mode 100644 docs/redis/redis-basic/10-lua.md
create mode 100644 docs/redis/redis-basic/11-deletion-policy.md
create mode 100644 docs/redis/redis-basic/12-others.md
create mode 100644 docs/redis/redis-basic/2-data-type.md
create mode 100644 docs/redis/redis-basic/3-data-structure.md
create mode 100644 docs/redis/redis-basic/4-implement.md
create mode 100644 docs/redis/redis-basic/5-sort.md
create mode 100644 docs/redis/redis-basic/6-transaction.md
create mode 100644 docs/redis/redis-basic/7-message-queue.md
create mode 100644 docs/redis/redis-basic/8-persistence.md
create mode 100644 docs/redis/redis-basic/9-cluster.md
create mode 100644 docs/redis/redis-basic/README.md
rename "Redis/Redis\351\235\242\350\257\225\351\242\230.md" => docs/redis/redis.md (85%)
create mode 100644 docs/resource/1-cs-books.md
rename "\345\267\245\345\205\267/docker-overview.md" => docs/tools/docker-overview.md (76%)
create mode 100644 docs/tools/docker/1-introduce.md
create mode 100644 docs/tools/docker/2-image-command.md
create mode 100644 docs/tools/docker/3-container-command.md
create mode 100644 docs/tools/docker/4-docker-compose.md
create mode 100644 docs/tools/docker/5-maven-build.md
create mode 100644 docs/tools/docker/6-other.md
create mode 100644 docs/tools/docker/README.md
rename "\345\267\245\345\205\267/git-overview.md" => docs/tools/git-overview.md (81%)
create mode 100644 docs/tools/git/1-introduce.md
create mode 100644 docs/tools/git/2-basic.md
create mode 100644 docs/tools/git/3-remote-repo.md
create mode 100644 docs/tools/git/4-label.md
create mode 100644 docs/tools/git/5-branch.md
create mode 100644 docs/tools/git/README.md
rename "\345\267\245\345\205\267/linux-overview.md" => docs/tools/linux-overview.md (100%)
create mode 100644 docs/tools/linux/1-basic.md
create mode 100644 docs/tools/linux/2-disk-file.md
create mode 100644 docs/tools/linux/3-search.md
create mode 100644 docs/tools/linux/4-net.md
create mode 100644 docs/tools/linux/5-monitor.md
create mode 100644 docs/tools/linux/README.md
rename "\345\267\245\345\205\267/maven-overview.md" => docs/tools/maven-overview.md (83%)
create mode 100644 docs/tools/maven/1-introduce.md
create mode 100644 docs/tools/maven/2-basic.md
create mode 100644 docs/tools/maven/3-dependency.md
create mode 100644 docs/tools/maven/4-repo.md
create mode 100644 docs/tools/maven/5-lifecycle.md
create mode 100644 docs/tools/maven/6-plugin.md
create mode 100644 docs/tools/maven/7-aggregator.md
create mode 100644 docs/tools/maven/8-inherit.md
create mode 100644 docs/tools/maven/README.md
create mode 100644 docs/tools/typora-overview.md
delete mode 100644 img/1588431199776.png
delete mode 100644 img/ConcurrentHashMap-segment.jpg
delete mode 100644 img/JDK1.8-ConcurrentHashMap-Structure.jpg
delete mode 100644 img/Java-Collections.jpeg
delete mode 100644 img/all-table-search.jpg
delete mode 100644 img/aqs.png
delete mode 100644 img/bean-life-cycle.jpg
delete mode 100644 img/bloom-filter.jpg
delete mode 100644 img/cms-collector.png
delete mode 100644 img/concurrent/executors-ali.png
delete mode 100644 img/concurrent/synchronized-block.png
delete mode 100644 img/concurrent/synchronized-method.png
delete mode 100644 img/condition-await.png
delete mode 100644 img/condition-signal.png
delete mode 100644 img/condition-wait-queue.png
delete mode 100644 img/cover-index.png
delete mode 100644 img/direct-pointer.png
delete mode 100644 img/docker/docker.jpg
delete mode 100644 img/explain-all.png
delete mode 100644 img/explain-const.png
delete mode 100644 img/explain-id.png
delete mode 100644 img/explain-range.png
delete mode 100644 img/explain-ref.png
delete mode 100644 img/g1-region.jpg
delete mode 100644 img/gc-root-refer.png
delete mode 100644 img/git-status.png
delete mode 100644 img/heap-structure.png
delete mode 100644 img/image-20200520234137916.png
delete mode 100644 img/image-20200520234200868.png
delete mode 100644 img/image-20200520234231001.png
delete mode 100644 img/image-20200608232749393.png
delete mode 100644 img/image-20200608232951873.png
delete mode 100644 img/image-20200608233400458.png
delete mode 100644 img/image-20200614165333479.png
delete mode 100644 img/image-20200614165432775.png
delete mode 100644 img/index-search-range.jpg
delete mode 100644 img/index-search.jpg
delete mode 100644 img/java-basic/exception.png
delete mode 100644 img/java-basic/field-method.png
delete mode 100644 img/java-basic/io.jpg
delete mode 100644 img/java-ram-region.png
delete mode 100644 img/jmeter/http-request-param.png
delete mode 100644 img/jmeter/jmeter-aggregate-report.png
delete mode 100644 img/jmeter/jmeter-default-request.png
delete mode 100644 img/jmeter/jmeter-request-header.png
delete mode 100644 img/jmeter/jmeter-thread-group.png
delete mode 100644 img/jvm/string-equal.png
delete mode 100644 img/jvm/string-intern.png
delete mode 100644 img/jvm/string-new.png
delete mode 100644 img/left-match.jpg
delete mode 100644 img/middleware/redis-transaction.png
delete mode 100644 img/middleware/redis-usage.png
delete mode 100644 img/mysql-architecture.png
delete mode 100644 img/mysql-clustered-index.png
delete mode 100644 img/mysql/arch.png
delete mode 100644 img/mysql/current-read.png
delete mode 100644 img/mysql/mvcc-impl.png
delete mode 100644 img/mysql/myisam-innodb-index.png
delete mode 100644 img/mysql/mysql-archpng.png
delete mode 100644 img/mysql/nested-loop.png
delete mode 100644 img/mysql/orderby.png
delete mode 100644 img/net/bio.png
delete mode 100644 img/net/cdn.png
delete mode 100644 img/net/https-certificate-chain.jpg
delete mode 100644 img/net/https-certificate.png
delete mode 100644 img/net/https-client-hello.jpg
delete mode 100644 img/net/https-server-hello.jpg
delete mode 100644 img/net/nio.png
delete mode 100644 img/oauth2.png
delete mode 100644 img/object-dead.png
delete mode 100644 img/object-handle.png
delete mode 100644 img/optimistic-lock.jpg
delete mode 100644 img/others/json-web-token.png
delete mode 100644 img/others/seckill.jpg
delete mode 100644 img/parnew-collector.png
delete mode 100644 img/rabbitmq-direct.png
delete mode 100644 img/rabbitmq-fanout.png
delete mode 100644 img/rabbitmq-return-listener.png
delete mode 100644 img/rabbitmq-topic.png
delete mode 100644 img/rabbitmq.png
delete mode 100644 img/redis/cache-consist.png
delete mode 100644 img/redis/evalsha.png
delete mode 100644 img/redis/list-api.png
delete mode 100644 img/redis/redis-replication.png
delete mode 100644 img/redis/redis-skiplist.png
delete mode 100644 img/redis/time-complexity.png
delete mode 100644 "img/redis/\347\274\223\345\255\230\345\207\273\347\251\277\345\212\240\344\272\222\346\226\245\351\224\201.png"
delete mode 100644 img/scheduled-task.jpg
delete mode 100644 img/serial-collector.png
delete mode 100644 img/singleton-class-init.png
delete mode 100644 "img/spring/\346\226\271\346\263\225\347\272\247\345\210\253\346\263\250\350\247\243.png"
delete mode 100644 "img/springboot/SpringBoot\347\232\204\350\207\252\345\212\250\351\205\215\347\275\256\345\216\237\347\220\206.jpg"
delete mode 100644 img/thread-pool.png
delete mode 100644 img/thread-status.jpeg
delete mode 100644 img/threadlocal-oom.png
delete mode 100644 img/threadlocal.png
delete mode 100644 img/tree-visit.png
delete mode 100644 img/two-index.jpg
delete mode 100644 img/web/servlet-container.jpg
delete mode 100644 "img/\345\271\273\350\257\2731.png"
delete mode 100644 "img/\346\213\245\345\241\236\346\216\247\345\210\266.jpg"
delete mode 100644 "img/\350\257\267\346\261\202\344\277\235\346\212\244\351\205\215\347\275\256\346\226\271\346\263\225.png"
create mode 100644 package-lock.json
create mode 100644 package.json
delete mode 100644 "\344\270\255\351\227\264\344\273\266/Elasticsearch\345\205\245\351\227\250.md"
delete mode 100644 "\345\205\266\344\273\226/code.md"
delete mode 100644 "\345\205\266\344\273\226/linux.md"
delete mode 100644 "\345\205\266\344\273\226/note.md"
delete mode 100644 "\345\205\266\344\273\226/\345\256\236\346\210\230\347\257\207.md"
delete mode 100644 "\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md"
delete mode 100644 "\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\347\274\223\345\255\230.md"
delete mode 100644 "\345\211\215\347\253\257/vue.md"
delete mode 100644 "\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md"
delete mode 100644 "\345\267\245\345\205\267/NPM.md"
delete mode 100644 "\345\267\245\345\205\267/jenkins.md"
delete mode 100644 "\345\267\245\345\205\267/jmeter.md"
delete mode 100644 "\345\267\245\345\205\267/nginx\351\203\250\347\275\262\351\241\271\347\233\256.md"
delete mode 100644 "\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md"
delete mode 100644 "\346\241\206\346\236\266/SpringBoot\345\256\236\346\210\230.md"
delete mode 100644 "\346\241\206\346\236\266/SpringMVC.md"
delete mode 100644 "\346\241\206\346\236\266/SpringMVC\351\235\242\350\257\225\351\242\230.md"
delete mode 100644 "\346\241\206\346\236\266/Spring\345\256\236\346\210\230.md"
delete mode 100644 "\346\241\206\346\236\266/Spring\346\200\273\347\273\223.md"
delete mode 100644 "\346\241\206\346\236\266/Spring\347\224\250\345\210\260\345\223\252\344\272\233\350\256\276\350\256\241\346\250\241\345\274\217.md"
delete mode 100644 "\346\241\206\346\236\266/Spring\350\207\252\345\212\250\350\243\205\351\205\215.md"
delete mode 100644 "\346\241\206\346\236\266/\346\267\261\345\205\245\346\265\205\345\207\272Mybatis\346\212\200\346\234\257\345\216\237\347\220\206\344\270\216\345\256\236\346\210\230.md"
delete mode 100644 "\346\266\210\346\201\257\351\230\237\345\210\227/\346\255\273\344\277\241\351\230\237\345\210\227.md"
delete mode 100644 "\346\266\210\346\201\257\351\230\237\345\210\227/\346\266\210\346\201\257\351\230\237\345\210\227\351\235\242\350\257\225\351\242\230.md"
delete mode 100644 "\347\263\273\347\273\237\350\256\276\350\256\241/\347\263\273\347\273\237\350\256\276\350\256\241old.md"
delete mode 100644 "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\347\256\227\346\263\225.md"
delete mode 100644 "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/TCP IP\345\215\217\350\256\256.md"
delete mode 100644 "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/session\345\222\214cookie.md"
delete mode 100644 "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\347\275\221\347\273\234.md"
delete mode 100644 "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
diff --git a/.gitignore b/.gitignore
index af0556b..0182294 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,15 +1,14 @@
.DS_Store
+*.log
node_modules/
-/dist/
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-
-# Editor directories and files
-.idea
-.vscode
-*.suo
-*.ntvs*
-*.njsproj
-*.sln
+*.bat
+*.sh
+web/
+.idea/
*.py
+public/
+docs/.vuepress/.temp/
+docs/.vuepress/.cache/
+ByteDanceVerify.html
+google053c90e5a7354c40.html
+sogousiteverification.txt
\ No newline at end of file
diff --git a/CNAME b/CNAME
new file mode 100644
index 0000000..59cab98
--- /dev/null
+++ b/CNAME
@@ -0,0 +1 @@
+topjavaer.cn
diff --git a/Java/JVM.md b/Java/JVM.md
deleted file mode 100644
index b19e730..0000000
--- a/Java/JVM.md
+++ /dev/null
@@ -1,831 +0,0 @@
-
-
-> 本文已经收录到github仓库,此仓库用于分享Java相关知识总结,包括Java基础、MySQL、Springboot、mybatis、Redis、rabbitMQ等等,欢迎大家提pr和star!
->
-> github地址:https://github.com/Tyson0314/Java-learning
->
-> gitee地址:https://gitee.com/tysondai/Java-learning
-
-
-
-
-
-- [内存结构](#%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84)
- - [程序计数器](#%E7%A8%8B%E5%BA%8F%E8%AE%A1%E6%95%B0%E5%99%A8)
- - [虚拟机栈](#%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88)
- - [本地方法栈](#%E6%9C%AC%E5%9C%B0%E6%96%B9%E6%B3%95%E6%A0%88)
- - [堆](#%E5%A0%86)
- - [方法区](#%E6%96%B9%E6%B3%95%E5%8C%BA)
- - [永久代](#%E6%B0%B8%E4%B9%85%E4%BB%A3)
- - [元空间](#%E5%85%83%E7%A9%BA%E9%97%B4)
- - [运行时常量池](#%E8%BF%90%E8%A1%8C%E6%97%B6%E5%B8%B8%E9%87%8F%E6%B1%A0)
- - [直接内存](#%E7%9B%B4%E6%8E%A5%E5%86%85%E5%AD%98)
- - [对象的访问定位](#%E5%AF%B9%E8%B1%A1%E7%9A%84%E8%AE%BF%E9%97%AE%E5%AE%9A%E4%BD%8D)
-- [类文件结构](#%E7%B1%BB%E6%96%87%E4%BB%B6%E7%BB%93%E6%9E%84)
-- [类的生命周期](#%E7%B1%BB%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F)
-- [类加载的过程](#%E7%B1%BB%E5%8A%A0%E8%BD%BD%E7%9A%84%E8%BF%87%E7%A8%8B)
- - [加载](#%E5%8A%A0%E8%BD%BD)
- - [验证](#%E9%AA%8C%E8%AF%81)
- - [准备](#%E5%87%86%E5%A4%87)
- - [解析](#%E8%A7%A3%E6%9E%90)
- - [初始化](#%E5%88%9D%E5%A7%8B%E5%8C%96)
-- [双亲委派模型](#%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%A8%A1%E5%9E%8B)
- - [实现](#%E5%AE%9E%E7%8E%B0)
-- [对象死亡](#%E5%AF%B9%E8%B1%A1%E6%AD%BB%E4%BA%A1)
- - [引用计数法](#%E5%BC%95%E7%94%A8%E8%AE%A1%E6%95%B0%E6%B3%95)
- - [可达性分析](#%E5%8F%AF%E8%BE%BE%E6%80%A7%E5%88%86%E6%9E%90)
- - [可作为GC Roots的对象](#%E5%8F%AF%E4%BD%9C%E4%B8%BAgc-roots%E7%9A%84%E5%AF%B9%E8%B1%A1)
- - [引用](#%E5%BC%95%E7%94%A8)
- - [强引用](#%E5%BC%BA%E5%BC%95%E7%94%A8)
- - [软引用](#%E8%BD%AF%E5%BC%95%E7%94%A8)
- - [弱引用](#%E5%BC%B1%E5%BC%95%E7%94%A8)
- - [虚引用](#%E8%99%9A%E5%BC%95%E7%94%A8)
- - [常量回收](#%E5%B8%B8%E9%87%8F%E5%9B%9E%E6%94%B6)
- - [类的卸载](#%E7%B1%BB%E7%9A%84%E5%8D%B8%E8%BD%BD)
-- [内存分配与回收策略](#%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D%E4%B8%8E%E5%9B%9E%E6%94%B6%E7%AD%96%E7%95%A5)
- - [Minor GC 和 Full GC](#minor-gc-%E5%92%8C-full-gc)
- - [内存分配策略](#%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D%E7%AD%96%E7%95%A5)
- - [对象优先在 Eden 分配](#%E5%AF%B9%E8%B1%A1%E4%BC%98%E5%85%88%E5%9C%A8-eden-%E5%88%86%E9%85%8D)
- - [大对象直接进入老年代](#%E5%A4%A7%E5%AF%B9%E8%B1%A1%E7%9B%B4%E6%8E%A5%E8%BF%9B%E5%85%A5%E8%80%81%E5%B9%B4%E4%BB%A3)
- - [长期存活的对象进入老年代](#%E9%95%BF%E6%9C%9F%E5%AD%98%E6%B4%BB%E7%9A%84%E5%AF%B9%E8%B1%A1%E8%BF%9B%E5%85%A5%E8%80%81%E5%B9%B4%E4%BB%A3)
- - [动态对象年龄判定](#%E5%8A%A8%E6%80%81%E5%AF%B9%E8%B1%A1%E5%B9%B4%E9%BE%84%E5%88%A4%E5%AE%9A)
- - [空间分配担保](#%E7%A9%BA%E9%97%B4%E5%88%86%E9%85%8D%E6%8B%85%E4%BF%9D)
- - [Full GC 的触发条件](#full-gc-%E7%9A%84%E8%A7%A6%E5%8F%91%E6%9D%A1%E4%BB%B6)
- - [调用 System.gc()](#%E8%B0%83%E7%94%A8-systemgc)
- - [老年代空间不足](#%E8%80%81%E5%B9%B4%E4%BB%A3%E7%A9%BA%E9%97%B4%E4%B8%8D%E8%B6%B3)
- - [空间分配担保失败](#%E7%A9%BA%E9%97%B4%E5%88%86%E9%85%8D%E6%8B%85%E4%BF%9D%E5%A4%B1%E8%B4%A5)
-- [垃圾回收算法](#%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95)
- - [标记清除算法](#%E6%A0%87%E8%AE%B0%E6%B8%85%E9%99%A4%E7%AE%97%E6%B3%95)
- - [复制清除算法](#%E5%A4%8D%E5%88%B6%E6%B8%85%E9%99%A4%E7%AE%97%E6%B3%95)
- - [标记整理算法](#%E6%A0%87%E8%AE%B0%E6%95%B4%E7%90%86%E7%AE%97%E6%B3%95)
- - [分类收集算法](#%E5%88%86%E7%B1%BB%E6%94%B6%E9%9B%86%E7%AE%97%E6%B3%95)
- - [记忆集](#%E8%AE%B0%E5%BF%86%E9%9B%86)
-- [垃圾收集器](#%E5%9E%83%E5%9C%BE%E6%94%B6%E9%9B%86%E5%99%A8)
- - [Serial 收集器](#serial-%E6%94%B6%E9%9B%86%E5%99%A8)
- - [ParNew 收集器](#parnew-%E6%94%B6%E9%9B%86%E5%99%A8)
- - [Parallel Scavenge 收集器](#parallel-scavenge-%E6%94%B6%E9%9B%86%E5%99%A8)
- - [Serial Old 收集器](#serial-old-%E6%94%B6%E9%9B%86%E5%99%A8)
- - [Parallel Old 收集器](#parallel-old-%E6%94%B6%E9%9B%86%E5%99%A8)
- - [CMS 收集器](#cms-%E6%94%B6%E9%9B%86%E5%99%A8)
- - [回收过程](#%E5%9B%9E%E6%94%B6%E8%BF%87%E7%A8%8B)
- - [CMS垃圾回收特点](#cms%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%89%B9%E7%82%B9)
- - [G1收集器](#g1%E6%94%B6%E9%9B%86%E5%99%A8)
- - [回收过程](#%E5%9B%9E%E6%94%B6%E8%BF%87%E7%A8%8B-1)
-- [JVM调优工具](#jvm%E8%B0%83%E4%BC%98%E5%B7%A5%E5%85%B7)
- - [jps](#jps)
- - [jstack](#jstack)
- - [jstat](#jstat)
- - [jmap](#jmap)
-- [补充](#%E8%A1%A5%E5%85%85)
- - [对象头](#%E5%AF%B9%E8%B1%A1%E5%A4%B4)
- - [main方法执行过程](#main%E6%96%B9%E6%B3%95%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B)
- - [对象创建过程](#%E5%AF%B9%E8%B1%A1%E5%88%9B%E5%BB%BA%E8%BF%87%E7%A8%8B)
-- [参考资料](#%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99)
-
-
-
-## 内存结构
-
-Java 内存模型(JMM)是基于共享内存的多线程通信机制。
-
-JVM内存结构 = 类加载器 + 执行引擎 + 运行时数据区域 。
-
-
-
-> 图片来源:深入理解Java虚拟机-周志明
-
-### 程序计数器
-
-程序计数器主要有两个作用:
-
-1. 当前线程所执行的字节码的行号指示器,通过改变它实现代码的流程控制,如:顺序执行、选择、循环、异常处理。
-2. 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。
-
-程序计数器是唯一一个不会出现 `OutOfMemoryError` 的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。
-
-### 虚拟机栈
-
-Java 虚拟机栈是由一个个栈帧组成,而每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法出口信息。每一次函数调用都会有一个对应的栈帧被压入虚拟机栈,每一个函数调用结束后,都会有一个栈帧被弹出。
-
-局部变量表是用于存放方法参数和方法内的局部变量。
-
-每个栈帧都包含一个指向运行时常量池中该栈所属方法的符号引用,在方法调用过程中,会进行动态链接,将这个符号引用转化为直接引用。
-
-- 部分符号引用在类加载阶段的时候就转化为直接引用,这种转化就是静态链接
-- 部分符号引用在运行期间转化为直接引用,这种转化就是动态链接
-
-Java 虚拟机栈也是线程私有的,每个线程都有各自的 Java 虚拟机栈,而且随着线程的创建而创建,随着线程的死亡而死亡。Java 虚拟机栈会出现两种错误:`StackOverFlowError` 和 `OutOfMemoryError`。
-
-可以通过 -Xss 参数来指定每个线程的 Java 虚拟机栈内存大小,在 JDK 1.4 中默认为 256K,而在 JDK 1.5+ 默认为 1M:
-
-```java
-java -Xss2M
-```
-
-### 本地方法栈
-
-虚拟机栈为虚拟机执行 Java 方法服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。Native 方法一般是用其它语言(C、C++ 或汇编语言等)编写的,并且被编译为基于本机硬件和操作系统的程序,对待这些方法需要特别处理。
-
-本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。
-
-### 堆
-
-此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。Java 堆是垃圾收集器管理的主要区域,因此也被称作GC堆。
-
-Java 堆可以细分为:新生代(Eden 空间、From Survivor、To Survivor 空间)和老年代。进一步划分的目的是更好地回收内存,或者更快地分配内存。
-
-通过 -Xms设定程序启动时占用内存大小,通过 -Xmx 设定程序运行期间最大可占用的内存大小。如果程序运行需要占用更多的内存,超出了这个设置值,就会抛出OutOfMemory异常。
-
-```java
-java -Xms1M -Xmx2M
-```
-
-通过 -Xss 设定每个线程的堆栈大小。设置这个参数,需要评估一个线程大约需要占用多少内存,可能会有多少线程同时运行等。
-
-> 在这里也给大家分享一个github仓库,上面放了**200多本经典的计算机书籍**,包括C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生等,可以star一下,下次找书直接在上面搜索,仓库持续更新中~
->
-> github地址:https://github.com/Tyson0314/java-books
->
-> 如果github访问不了,可以访问gitee仓库。
->
-> gitee地址:https://gitee.com/tysondai/java-books
-
-### 方法区
-
-方法区与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区逻辑上属于堆的一部分。
-
-对方法区进行垃圾回收的主要目标是对常量池的回收和对类的卸载。
-
-#### 永久代
-
-方法区是 JVM 的规范,而永久代(PermGen)是方法区的一种实现方式,并且只有 HotSpot 有永久代。而对于其他类型的虚拟机,如 JRockit(Oracle)、J9(IBM) 并没有永久代。由于方法区主要存储类的相关信息,所以对于动态生成类的情况比较容易出现永久代的内存溢出。最典型的场景就是,在 jsp 页面比较多的情况,容易出现永久代内存溢出。
-
-#### 元空间
-
-JDK 1.8 的时候,HotSpot 的永久代被彻底移除了,使用元空间替代。元空间的本质和永久代类似,都是对JVM规范中方法区的实现。两者最大的区别在于:元空间并不在虚拟机中,而是使用直接内存。
-
-为什么要将永久代替换为元空间呢?
-
-永久代内存受限于 JVM 可用内存,而元空间使用的是直接内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是相比永久代内存溢出的概率更小。
-
-### 运行时常量池
-
-运行时常量池是方法区的一部分,在类加载之后,会将编译器生成的各种字面量和符号引号放到运行时常量池。在运行期间动态生成的常量,如 String 类的 intern()方法,也会被放入运行时常量池。
-
-
-
-
-
-
-
-> 图片来源:https://blog.csdn.net/soonfly
-
-### 直接内存
-
-直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。而且也可能导致 OutOfMemoryError 错误出现。
-
-NIO的Buffer提供了DirectBuffer,可以直接访问系统物理内存,避免堆内内存到堆外内存的数据拷贝操作,提高效率。DirectBuffer直接分配在物理内存中,并不占用堆空间,其可申请的最大内存受操作系统限制,不受最大堆内存的限制。
-
-直接内存的读写操作比堆内存快,可以提升程序I/O操作的性能。通常在I/O通信过程中,会存在堆内内存到堆外内存的数据拷贝操作,对于需要频繁进行内存间数据拷贝且生命周期较短的暂存数据,都建议存储到直接内存。
-
-### 对象的访问定位
-
-Java 程序通过栈上的 reference 数据来操作堆上的具体对象。对象的访问方式由虚拟机实现而定,目前主流的访问方式有使用句柄和直接指针两种:
-
-- 如果使用句柄的话,那么 Java 堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。使用句柄来访问的最大好处是 reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而 reference 本身不需要修改。
-
-
-
-- 直接指针。reference 中存储的直接就是对象的地址。对象包含到对象类型数据的指针,通过这个指针可以访问对象类型数据。使用直接指针访问方式最大的好处就是访问对象速度快,它节省了一次指针定位的时间开销,虚拟机hotspot主要是使用直接指针来访问对象。
-
-
-
-
-
-## 类文件结构
-
-Class 文件结构:
-
-```java
-ClassFile {
- u4 magic; //Class 文件的标志
- u2 minor_version;//Class 的小版本号
- u2 major_version;//Class 的大版本号
- u2 constant_pool_count;//常量池的数量
- cp_info constant_pool[constant_pool_count-1];//常量池
- u2 access_flags;//Class 的访问标记
- u2 this_class;//当前类
- u2 super_class;//父类
- u2 interfaces_count;//接口
- u2 interfaces[interfaces_count];//一个类可以实现多个接口
- u2 fields_count;//Class 文件的字段属性
- field_info fields[fields_count];//一个类会可以有个字段
- u2 methods_count;//Class 文件的方法数量
- method_info methods[methods_count];//一个类可以有个多个方法
- u2 attributes_count;//此类的属性表中的属性数
- attribute_info attributes[attributes_count];//属性表集合
-}
-```
-
-魔数:class 文件标志。
-
-文件版本:高版本的 Java 虚拟机可以执行低版本编译器生成的 Class 文件,但是低版本的 Java 虚拟机不能执行高版本编译器生成的 Class 文件。
-
-常量池:存放字面量和符号引用。字面量类似于Java的常量,如字符串,声明为final的常量值等。符号引用包含三类:类和接口的全限定名,方法的名称和描述符,字段的名称和描述符。
-
-访问标志:识别一些类或者接口层次的访问信息,包括:这个 Class 是类还是接口,是否为 public 或者 abstract 类型,如果是类的话是否声明为 final 等等。
-
-当前类索引this_class:类索引用于确定这个类的全限定名。
-
-属性表集合:在 Class 文件,字段表,方法表中都可以携带自己的属性表集合,以用于描述某些场景专有的信息。与 Class 文件中其它的数据项目要求的顺序、长度和内容不同,属性表集合的限制稍微宽松一些,不再要求各个属性表具有严格的顺序,并且只要不与已有的属性名重复,任何人实现的编译器都可以向属性表中写 入自己定义的属性信息,Java 虚拟机运行时会忽略掉它不认识的属性。
-
-
-
-## 类的生命周期
-
-加载、验证、准备、解析、初始化、使用和卸载。
-
-
-
-## 类加载的过程
-
-类的加载指的是将类的class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个对象,这个对象封装了类在方法区内的数据结构,并且提供了访问方法区内的类信息的接口。
-
-### 加载
-
-类加载过程的一个阶段:通过一个类的完全限定查找此类字节码文件,并利用字节码文件创建一个Class对象。
-
-1. 通过全类名获取定义此类的二进制字节流
-2. 将字节流所代表的静态存储结构转换为方法区的运行时数据结构
-3. 在内存中生成一个代表该类的 Class 对象,作为方法区这些数据的访问入口
-
-### 验证
-
-确保Class文件的字节流中包含的信息符合虚拟机规范,保证这些信息被当作代码运行后不会危害虚拟机自身的安全。主要包括四种验证:文件格式验证,元数据验证,字节码验证,符号引用验证。
-
-### 准备
-
-为类变量分配内存并设置类变量初始值的阶段。此阶段进行内存分配的仅包括类变量,不包括实例变量和final修饰的static变量(因为final在编译的时候就会分配了),实例变量会在对象实例化时随着对象一块分配在 Java 堆中。
-
-### 解析
-
-虚拟机将常量池内的符号引用替换为直接引用的过程。符号引用用于描述目标,直接引用直接指向目标的地址。
-
-### 初始化
-
-初始化阶段就是执行类构造器\()方法的过程。\()并不是程序员在Java代码中直接编写的方法,它是Javac编译器的自动生成的,由编译器自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生的。
-
-
-
-## 双亲委派模型
-
-一个类加载器收到一个类的加载请求时,它首先不会自己尝试去加载它,而是把这个请求委派给父类加载器去完成,这样层层委派,因此所有的加载请求最终都会传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
-
-
-
-双亲委派模型的好处:可以防止内存中出现多份同样的字节码。如果没有双亲委派模型而是由各个类加载器自行加载的话,如果用户编写了一个java.lang.Object的同名类并放在ClassPath中,多个类加载器都去加载这个类到内存中,系统中将会出现多个不同的Object类,那么类之间的比较结果及类的唯一性将无法保证。
-
-### 实现
-
-双亲委派模型的具体实现代码在抽象类 java.lang.ClassLoader 中,此类的 loadClass() 方法运行过程如下:先检查类是否已经加载过,如果没有则让父类加载器去加载。当父类加载器加载失败时抛出 ClassNotFoundException,此时尝试自己去加载。
-
-```java
-public abstract class ClassLoader {
- // The parent class loader for delegation
- private final ClassLoader parent;
-
- public Class> loadClass(String name) throws ClassNotFoundException {
- return loadClass(name, false);
- }
-
- protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {
- synchronized (getClassLoadingLock(name)) {
- // First, check if the class has already been loaded
- Class> c = findLoadedClass(name);
- if (c == null) {
- try {
- if (parent != null) {
- c = parent.loadClass(name, false);
- } else {
- c = findBootstrapClassOrNull(name);
- }
- } catch (ClassNotFoundException e) {
- // ClassNotFoundException thrown if class not found
- // from the non-null parent class loader
- }
-
- if (c == null) {
- // If still not found, then invoke findClass in order
- // to find the class.
- c = findClass(name);
- }
- }
- if (resolve) {
- resolveClass(c);
- }
- return c;
- }
- }
-
- protected Class> findClass(String name) throws ClassNotFoundException {
- throw new ClassNotFoundException(name);
- }
-}
-```
-
-
-
-## 对象死亡
-
-堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断那些对象已经死亡(即不能再被任何途径使用的对象)。
-
-
-
-### 引用计数法
-
-给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加 1;当引用失效,计数器就减 1;任何时候计数器为 0 的对象就是不可能再被使用的。
-
-这种方法很难解决对象之间相互循环引用的问题。
-
-```java
-public class ReferenceCountingGc {
- Object instance = null;
- public static void main(String[] args) {
- ReferenceCountingGc objA = new ReferenceCountingGc();
- ReferenceCountingGc objB = new ReferenceCountingGc();
- objA.instance = objB;
- objB.instance = objA;
- objA = null;
- objB = null;
- }
-}
-```
-
-### 可达性分析
-
-通过GC Root对象为起点,从这些节点向下搜索,搜索所走过的路径叫引用链,当一个对象到GC Root没有任何的引用链相连时,说明这个对象是不可用的。
-
-
-
-#### 可作为GC Roots的对象
-
-1. 虚拟机栈(栈帧中的本地变量表)中引用的对象
-2. 本地方法栈中JNI(Native方法)引用的对象
-3. 方法区中类静态属性引用的对象
-4. 方法区中常量引用的对象
-5. 所有被同步锁(synchronized关键字)持有的对象。
-
-### 引用
-
-引用分为强引用、软引用、弱引用、虚引用四种。
-
-在程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为软引用可以加速 JVM 对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生。
-
-#### 强引用
-
-垃圾回收器绝不会回收它。当内存空间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。
-
-#### 软引用
-
-如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
-
-#### 弱引用
-
-在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。
-
-#### 虚引用
-
-虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。**虚引用主要用来跟踪对象被垃圾回收的活动**。
-
-### 常量回收
-
-运行时常量池主要回收的是废弃的常量。假如在常量池中存在字符串 "abc",如果当前没有任何 String 对象引用该字符串常量的话,就说明常量 "abc" 就是废弃常量,如果这时发生内存回收的话而且有必要的话,"abc" 就会被系统清理出常量池。
-
-### 类的卸载
-
-需要同时满足下面 3 个条件才能算是 “无用的类” :
-
-- 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
-- 加载该类的 ClassLoader 已经被回收。
-- 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
-
-虚拟机可以对满足上述 3 个条件的无用类进行回收,但不一定被回收。
-
-
-
-## 内存分配与回收策略
-
-### Minor GC 和 Full GC
-
-- Minor GC:回收新生代,因为新生代对象存活时间很短,因此 Minor GC 会频繁执行,执行的速度一般也会比较快。
-
-- Full GC:回收老年代和新生代,老年代对象其存活时间长,因此 Full GC 很少执行,执行速度会比 Minor GC 慢很多。
-
-### 内存分配策略
-
-#### 对象优先在 Eden 分配
-
-大多数情况下,对象在新生代 Eden 上分配,当 Eden 空间不够时,发起 Minor GC。
-
-#### 大对象直接进入老年代
-
-大对象是指需要连续内存空间的对象,最典型的大对象是那种很长的字符串以及数组。经常出现大对象会提前触发垃圾收集以获取足够的连续空间分配给大对象。
-
-可以设置JVM参数 -XX:PretenureSizeThreshold,大于此值的对象直接在老年代分配,避免在 Eden 和 Survivor 之间的大量内存复制。
-
-#### 长期存活的对象进入老年代
-
-通过参数 `-XX:MaxTenuringThreshold` 可以设置对象进入老年代的年龄阈值。对象在 Survivor 中每经过一次 MinorGC,年龄就增加 1 岁,当它的年龄增加到一定程度,就会被晋升到老年代中。
-
-#### 动态对象年龄判定
-
-虚拟机并不是永远要求对象的年龄必须达到 MaxTenuringThreshold 才能晋升老年代,如果在 Survivor 中相同年龄所有对象大小的总和大于 Survivor 空间的一半,则年龄大于或等于该年龄的对象可以直接进入老年代,无需等到 MaxTenuringThreshold 中要求的年龄。
-
-#### 空间分配担保
-
-在发生 Minor GC 之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的话,那么 Minor GC 可以确认是安全的。如果不成立的话虚拟机会查看 HandlePromotionFailure 的值是否允许担保失败。如果允许,那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次 Minor GC;如果小于,或者 HandlePromotionFailure 的值不允许冒险,那么就要进行一次 Full GC。
-
-### Full GC 的触发条件
-
-对于 Minor GC,其触发条件比较简单,当 Eden 空间满时,就将触发一次 Minor GC。而 Full GC 触发条件相对复杂,有以下条件:
-
-#### 调用 System.gc()
-
-只是建议虚拟机执行 Full GC,但是虚拟机不一定真正去执行。不建议使用这种方式,而是让虚拟机管理内存。
-
-#### 老年代空间不足
-
-老年代空间不足的常见场景为前文所讲的大对象直接进入老年代、长期存活的对象进入老年代等。为了避免以上原因引起的 Full GC,应当尽量不要创建过大的对象以及数组。除此之外,可以通过 -Xmn 参数调大新生代的大小,让对象尽量在新生代被回收掉,不进入老年代。还可以通过 -XX:MaxTenuringThreshold 调大对象进入老年代的年龄,让对象在新生代多存活一段时间。
-
-#### 空间分配担保失败
-
-使用复制算法的 Minor GC 需要老年代的内存空间作担保,如果担保失败会执行一次 Full GC。
-
-
-
-## 垃圾回收算法
-
-### 标记清除算法
-
-标记清除算法就是分为“标记”和“清除”两个阶段。标记出所有需要回收的对象,标记结束后统一回收所有被标记的对象。这种垃圾回收算法效率较低,并且会产生大量不连续的空间碎片。
-
-
-
-### 复制清除算法
-
-半区复制,用于新生代垃圾回收。将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。
-
-
-
-特点:实现简单,运行高效,但可用内存缩小为了原来的一半,浪费空间。
-
-### 标记整理算法
-
-根据老年代的特点提出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。
-
-### 分类收集算法
-
-根据各个年代的特点采用最适当的收集算法。
-
-一般将堆分为新生代和老年代。
-
-- 新生代使用:复制算法
-- 老年代使用:标记清除算法或者标记整理算法
-
-在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或者“标记-整理”算法来进行回收。
-
-由于对象之间会存在跨代引用,如果要进行一次新生代垃圾收集,除了需要遍历新生代对象,还要额外遍历整个老年代的所有对象,这会给内存回收带来很大的性能负担。
-
-跨代引用相对于同代引用来说仅占极少数。存在互相引用关系的两个对象,是应该倾向于同时生存或者同时消亡的。举个例子,如果某个新生代对象存在跨代引用,由于老年代对象难以消亡,该引用会使得新生代对象在收集时同样得以存活,进而在年龄增长之后晋升到老年代中,这时跨代引用也随即被消除了。
-
-所以没必要为了少量的跨代引用去扫描整个老年代,只需在新生代建立一个全局的数据结构 Remembered Set,这个结构把老年代划分成若干小块,标识出老年代的哪一块内存会存在跨代引用。此后当发生Minor GC时,只有包含了跨代引用的小块内存里的对象才会被加入到GC Roots进行扫描。
-
-#### 记忆集
-
-记忆集是一种用于记录从非收集区域指向收集区域的指针集合的抽象数据结构。Card Table 是最常用的一种记忆集实现形式。字节数组CARD_TABLE的每一个元素都对应着其标识的内存区域中一块特定大小的内存块,这个内存块被称作“卡页”(Card Page)。
-
-一个卡页的内存中通常包含不止一个对象,只要卡页内有一个(或更多)对象的字段存在着跨代指针,那就将对应卡表的数组元素的值标识为1,称为这个元素变脏(Dirty),没有则标识为0。在垃圾收集发生时,只要筛选出卡表中变脏的元素,就能轻易得出哪些卡页内存块中包含跨代指针,把它们加入GC Roots中一并扫描。
-
-
-
-## 垃圾收集器
-
-经典的垃圾收集器主要有三种类型:串行收集器、并行收集器和并发标记清除收集器CMS,这三种收集器分别可以是满足Java应用三种不同的需求:内存占用及并发开销最小化、应用吞吐量最大化和应用GC暂停时间最小化。
-
-JDK1.7和1.8中默认使用的是Parallel Scavenge和Parallel Old收集器组合。jdk1.9 默认垃圾收集器是G1。
-
-```java
-java -XX:+PrintCommandLineFlags -version
-```
-
-7个垃圾收集器的特点:
-
-| 收集器 | 串行、并行or并发 | 新生代/老年代 | 算法 | 目标 | 适用场景 |
-| :-------------------: | :--------------: | :-----------: | :----------------: | :----------: | :---------------------------------------: |
-| **Serial** | 串行 | 新生代 | 复制算法 | 响应速度优先 | 单CPU环境下的Client模式 |
-| **ParNew** | 并行 | 新生代 | 复制算法 | 响应速度优先 | 多CPU环境时在Server模式下与CMS配合 |
-| **Parallel Scavenge** | 并行 | 新生代 | 复制算法 | 吞吐量优先 | 在后台运算而不需要太多交互的任务 |
-| **Serial Old** | 串行 | 老年代 | 标记-整理 | 响应速度优先 | 单CPU环境下的Client模式、CMS的后备预案 |
-| **Parallel Old** | 并行 | 老年代 | 标记-整理 | 吞吐量优先 | 在后台运算而不需要太多交互的任务 |
-| **CMS** | 并发 | 老年代 | 标记-清除 | 响应速度优先 | 集中在互联网站或B/S系统服务端上的Java应用 |
-| **G1** | 并发 | both | 标记-整理+复制算法 | 响应速度优先 | 面向服务端应用,将来替换CMS |
-
-### Serial 收集器
-
-单线程收集器,使用一条垃圾收集线程去完成垃圾收集工作,在进行垃圾收集工作的时候必须暂停其他所有的工作线程( "Stop The World" ),直到它收集结束。
-
-
-
-特点:简单高效;内存消耗最小;没有线程交互的开销,单线程收集效率高;需暂停所有的工作线程,用户体验不好。
-
-### ParNew 收集器
-
-Serial 收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全一样。
-
-
-
-除了 Serial 收集器外,只有它能与 CMS 收集器配合工作。
-
-### Parallel Scavenge 收集器
-
-新生代收集器,基于复制清除算法实现的收集器。吞吐量优先收集器,也是能够并行收集的多线程收集器,允许多个垃圾回收线程同时运行,降低垃圾收集时间,提高吞吐量。所谓吞吐量就是 CPU 中用于运行用户代码的时间与 CPU 总消耗时间的比值(吞吐量 = 运行用户代码时间 /(运行用户代码时间 + 垃圾收集时间))。Parallel Scavenge 收集器关注点是吞吐量,高效率的利用 CPU 资源。CMS 垃圾收集器关注点更多的是用户线程的停顿时间。
-
-Parallel Scavenge收集器提供了两个参数用于**精确控制吞吐量**,分别是控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis参数以及直接设置吞吐量大小的-XX:GCTimeRatio参数。
-
--XX:MaxGCPauseMillis参数允许的值是一个大于0的毫秒数,收集器将尽力保证内存回收花费的时间不超过用户设定值。
-
--XX:GCTimeRatio参数的值则应当是一个大于0小于100的整数,也就是垃圾收集时间占总时间的比率,相当于吞吐量的倒数。
-
-相比ParNew收集器的优点:
-
-1. 精确控制吞吐量;
-2. 垃圾收集的自适应的调节策略。通过参数-XX:+UseAdaptiveSizePolicy 打开自适应调节策略,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整参数以提供最合适的停顿时间或者最大的吞吐量。调整的参数包括新生代的大小(-Xmn)、Eden与Survivor区的比例(-XX:SurvivorRatio)、晋升老年代对象大小(-XX:PretenureSizeThreshold)等。
-
-### Serial Old 收集器
-
-Serial 收集器的老年代版本,它同样是一个单线程收集器,使用标记整理算法。它主要有两大用途:一种用途是在 JDK1.5 以及以前的版本中与 Parallel Scavenge 收集器搭配使用,另一种用途是作为 CMS 收集器的后备方案。
-
-### Parallel Old 收集器
-
-Parallel Scavenge 收集器的老年代版本。多线程垃圾收集,使用标记-整理算法。在注重吞吐量以及 CPU 资源的场合,都可以优先考虑 Parallel Scavenge 收集器和 Parallel Old 收集器。
-
-### CMS 收集器
-
-Concurrent Mark Sweep 并发标记清除,目的是获取最短应用停顿时间。第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程基本上同时工作。在并发标记和并发清除阶段,虽然用户线程没有被暂停,但是由于垃圾收集器线程占用了一部分系统资源,应用程序的吞吐量会降低。
-
-#### 回收过程
-
-基于标记清除算法实现,垃圾收集整个过程分为四个步骤:
-
-- 初始标记: stw暂停所有的其他线程,记录直接与 gc root 直接相连的对象,速度很快 。
-- 并发标记:从GC Roots开始对堆中对象进行可达性分析,找出存活对象,耗时较长,但是不需要停顿用户线程。
-- 重新标记: 在并发标记期间对象的引用关系可能会变化,需要重新进行标记。此阶段也会stw,停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短。
-- 并发清除:清除死亡对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。
-
-
-
-由于在整个过程中耗时最长的并发标记和并发清除阶段中,垃圾收集器线程都可以与用户线程一起工作,所以从总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。
-
-优点:并发收集,低停顿。
-
-缺点:
-
-- 标记清除算法导致收集结束有大量空间碎片,往往出现老年代空间剩余,但无法找到足够大连续空间来分配当前对象,不得不提前触发一次 Full GC。
-- 会产生浮动垃圾,由于CMS并发清理阶段用户线程还在运行着,会不断有新的垃圾产生,这一部分垃圾出现在标记过程之后,CMS无法在当次收集中处理掉它们,只好等到下一次GC去处理;
-- 对处理器资源非常敏感。在并发阶段,收集器占用了一部分线程资源,导致应用程序变慢,降低总吞吐量。
-
-#### CMS垃圾回收特点
-
-1. cms只会回收老年代和永久代(1.8开始为元数据区,需要设置CMSClassUnloadingEnabled),不会收集年轻代;
-2. cms垃圾回收器开始执行回收操作,有一个触发阈值,默认是老年代或永久带达到92%,不能等到old内存用尽时回收,否则会导致并发回收失败。因为需要预留空间给用户线程运行。
-
-### G1收集器
-
-G1垃圾收集器的目标是用在多核、大内存的机器上,在不同应用场景中追求高吞吐量和低停顿之间的最佳平衡。
-
-在G1收集器出现之前的所有其他收集器,包括CMS在内,垃圾收集的目标范围要么是整个新生代(Minor GC),要么就是整个老年代(Major GC),再要么就是整个Java堆(Full GC)。而G1可以面向堆内存任何部分来组成回收集(Collection Set,一般简称CSet)进行回收,衡量标准不再是它属于哪个分代,而是哪块内存中存放的垃圾数量最多,回收收益最大,这就是G1收集器的Mixed GC模式。
-
-G1将整个堆分成相同大小的分区(Region),有四种不同类型的分区:Eden、Survivor、Old和Humongous(大对象)。分区的大小取值范围为1M到32M,都是2的幂次方。Region大小可以通过`-XX:G1HeapRegionSize`参数指定。Humongous区域用于存储大对象。G1认为只要大小超过了一个Region容量一半的对象即可判定为大对象。
-
-
-
-G1 收集器对各个Region回收所获得的空间大小和回收所需时间的经验值进行排序,得到一个优先级列表,每次根据用户设置的最大的回收停顿时间(使用参数-XX:MaxGCPauseMillis指定,默认值是200毫秒),优先处理回收价值最大的 Region。
-
-Java堆分成多个独立Region,Region里面会存在跨Region引用对象,在垃圾回收寻找GC Roots需要扫描整个堆。G1采用了Rset(Remembered Set)来避免扫描整个堆。每个Region会有一个RSet,记录了哪些Region引用本Region中对象,即谁引用了我的对象,这样的话,在做可达性分析的时候就可以避免全堆扫描。
-
-特点:可以由用户指定期望的垃圾收集停顿时间。
-
-#### 回收过程
-
-G1 收集器的运作大致分为以下几个步骤:
-
-- 初始标记:。stw暂停所有的其他线程,记录直接与 gc root 直接相连的对象,速度很快 。
-- 并发标记。从GC Root开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。
-- 最终标记。对用户线程做另一个短暂的暂停,用于处理并发阶段对象引用出现变动的区域。
-- 筛选回收。对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧的Region的全部空间。这里的操作涉及存活对象的移动,是必须暂停用户线程,由多条收集器线程并行完成的。
-
-
-
-## JVM调优工具
-
-### jps
-
-列出本机所有java进程的pid。
-
-选项
-
-- -q 仅输出VM标识符,不包括class name,jar name,arguments in main method
-- -m 输出main method的参数
-- -l 输出完全的包名,应用主类名,jar的完全路径名
-- -v 输出jvm参数
-- -V 输出通过flag文件传递到JVM中的参数(.hotspotrc文件或-XX:Flags=所指定的文件
-- -Joption 传递参数到vm,例如:-J-Xms48m
-
-```bash
-jps -lvm
-//output
-//4124 com.zzx.Application -javaagent:E:\IDEA2019\lib\idea_rt.jar=10291:E:\IDEA2019\bin -Dfile.encoding=UTF-8
-```
-
-### jstack
-
-查看某个Java进程内的线程堆栈信息。-l,long listings,打印额外的锁信息,发生死锁时可以使用`jstack -l pid`观察锁持有情况。
-
-```java
-jstack -l 4124 | more
-```
-
-output:
-
-```java
-"http-nio-8001-exec-10" #40 daemon prio=5 os_prio=0 tid=0x000000002542f000 nid=0x4028 waiting on condition [0x000000002cc9e000]
- java.lang.Thread.State: WAITING (parking)
- at sun.misc.Unsafe.park(Native Method)
- - parking to wait for <0x000000077420d7e8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
- at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
- at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
- at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
- at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:103)
- at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:31)
- at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
- at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
- at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
- at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
- at java.lang.Thread.run(Thread.java:748)
-
- Locked ownable synchronizers:
- - None
-```
-
-### jstat
-
-虚拟机各种运行状态信息(类装载、内存、垃圾收集、jit编译等运行数据)。gcuitl 查看新生代、老年代及持久代GC的情况。
-
-```java
-jstat -gcutil 4124
- S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
- 0.00 0.00 67.21 19.20 96.36 94.96 10 0.084 3 0.191 0.275
-```
-
-### jmap
-
-查看堆内存快照。查看进程中新生代、老年代、永久代的使用情况。
-
-查询进程4124的堆内存快照:
-
-```java
->jmap -heap 4124
-Attaching to process ID 4124, please wait...
-Debugger attached successfully.
-Server compiler detected.
-JVM version is 25.221-b11
-
-using thread-local object allocation.
-Parallel GC with 6 thread(s)
-
-Heap Configuration:
- MinHeapFreeRatio = 0
- MaxHeapFreeRatio = 100
- MaxHeapSize = 4238344192 (4042.0MB)
- NewSize = 88604672 (84.5MB)
- MaxNewSize = 1412431872 (1347.0MB)
- OldSize = 177733632 (169.5MB)
- NewRatio = 2
- SurvivorRatio = 8
- MetaspaceSize = 21807104 (20.796875MB)
- CompressedClassSpaceSize = 1073741824 (1024.0MB)
- MaxMetaspaceSize = 17592186044415 MB
- G1HeapRegionSize = 0 (0.0MB)
-
-Heap Usage:
-PS Young Generation
-Eden Space:
- capacity = 327155712 (312.0MB)
- used = 223702392 (213.33922576904297MB)
- free = 103453320 (98.66077423095703MB)
- 68.37795697725736% used
-From Space:
- capacity = 21495808 (20.5MB)
- used = 0 (0.0MB)
- free = 21495808 (20.5MB)
- 0.0% used
-To Space:
- capacity = 23068672 (22.0MB)
- used = 0 (0.0MB)
- free = 23068672 (22.0MB)
- 0.0% used
-PS Old Generation
- capacity = 217579520 (207.5MB)
- used = 41781472 (39.845916748046875MB)
- free = 175798048 (167.65408325195312MB)
- 19.20285144484187% used
-
-27776 interned Strings occupying 3262336 bytes.
-```
-
-查询进程pid = 41843 存活的对象占用内存前100排序: `jmap -histo:live 41843 | head -n 100`
-
-
-
-## 补充
-
-### 对象头
-
-Java对象保存在内存中时,由以下三部分组成:对象头、实例数据和对齐填充字节。
-
-java的对象头由以下三部分组成:mark word、指向类信息的指针和数组长度(数组对象才有)。
-
-mark word包含:对象的hashcode、分代年龄和锁标志位。
-
-对象的实例数据就是在java代码中对象的属性和值。
-
-对齐填充字节:因为JVM要求java的对象占的内存大小应该是8bit的倍数,所以后面有几个字节用于把对象的大小补齐至8bit的倍数。
-
-**内存对齐的主要作用是:**
-
-1. 平台原因:不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
-2. 性能原因:经过内存对齐后,CPU的内存访问速度大大提升。
-
-### main方法执行过程
-
-以下是示例代码:
-
-```java
-public class App {
- public static void main(String[] args) {
- Student s = new Student("dabin");
- s.getName();
- }
-}
-
-class Student {
- public String name;
-
- public Student(String name) {
- this.name = name;
- }
-
- public String getName() {
- return this.name;
- }
-}
-```
-
-执行main方法的步骤如下:
-
-1. 编译好 App.java 后得到 App.class 后,执行 App.class,系统会启动一个 JVM 进程,从 classpath 路径中找到一个名为 App.class 的二进制文件,将 App 的类信息加载到运行时数据区的方法区内,这个过程叫做 App 类的加载
-2. JVM 找到 App 的主程序入口,执行main方法
-3. 这个main中的第一条语句为 `Student student = new Student("dabin") `,就是让 JVM 创建一个Student对象,但是这个时候方法区中是没有 Student 类的信息的,所以 JVM 马上加载 Student 类,把 Student 类的信息放到方法区中
-4. 加载完 Student 类后,JVM 在堆中为一个新的 Student 实例分配内存,然后调用构造函数初始化 Student 实例,这个 Student 实例持有 **指向方法区中的 Student 类的类型信息** 的引用
-5. 执行student.getName()时,JVM 根据 student 的引用找到 student 对象,然后根据 student 对象持有的引用定位到方法区中 student 类的类型信息的方法表,获得 getName() 的字节码地址。
-6. 执行getName()
-
-
-
-### 对象创建过程
-
-1. 类加载检查
-
- 虚拟机遇到一条 new 指令时,首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那必须先执行相应的类加载过程。
-
-2. 分配内存
-
- 在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需的内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。
-
-3. 初始化零值
-
- 分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。
-
-4. 设置对象头
-
- Hotspot 虚拟机的对象头包括两部分信息,第一部分用于存储对象自身的运行时数据(哈希码、GC 分代年龄、锁状态标志等等),另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是那个类的实例。
-
-5. 执行init方法
-
- 按照Java代码进行初始化。
-
-
-
-## 参考资料
-
-- 周志明. 深入理解 Java 虚拟机 [M]. 机械工业出版社
diff --git a/Java/Java web.md b/Java/Java web.md
deleted file mode 100644
index b92935b..0000000
--- a/Java/Java web.md
+++ /dev/null
@@ -1,83 +0,0 @@
-
-
-
-
-- [servlet](#servlet)
-- [jsp](#jsp)
-- [Tomcat](#tomcat)
- - [tomcat和netty区别](#tomcat%E5%92%8Cnetty%E5%8C%BA%E5%88%AB)
-- [跨域](#%E8%B7%A8%E5%9F%9F)
- - [同源策略](#%E5%90%8C%E6%BA%90%E7%AD%96%E7%95%A5)
- - [CSRF攻击](#csrf%E6%94%BB%E5%87%BB)
-- [statement和prepareStatement](#statement%E5%92%8Cpreparestatement)
-
-
-
-## servlet
-
-servlet接口定义的是一套处理网络请求的规范。servlet运行在服务端,由servlet容器管理,用于生成动态的内容(早期的web技术主要用来浏览静态页面)。
-
-Servlet是什么?
-
-- 运行在Servlet容器(如Tomcat)中的Java类
-- 没有main方法,不能独立运行,必须被部署到Servlet容器中,由容器来实例化和调用Servlet的方法
-
-servlet生命周期指它从被web服务器加载到它被销毁的整个过程,分三个阶段:
-1. 初始化阶段,调用init()方法
-2. 响应客户请求阶段,调用service()方法
-3. 终止阶段,调用destroy()方法
-
-servlet容器:负责接收请求,生成servlet实例用于处理请求(调用service方法),然后将servlet生成的响应数据返回给客户端。
-
-
-
-
-
-## jsp
-
-Java server pages。当有人请求JSP时,服务器会自动帮我们把JSP中的HTML片段和java代码拼接成静态资源响应给浏览器。也就是说JSP运行在服务器端,但最终发给客户端的都已经是转换好的HTML静态页面(在响应体里)。
-
-即:**JSP = HTML + Java片段**(各种标签本质上还是Java片段)
-
-
-
-## Tomcat
-
-Tomcat 是由 Apache 开发的一个 Servlet 容器,实现了对 Servlet 和 JSP 的支持。
-
-### tomcat和netty区别
-
-Netty和Tomcat最大的区别就在于通信协议,Tomcat是基于Http协议的,他的实质是一个基于http协议的web容器,但是Netty不一样,他能通过编程自定义各种协议,因为netty能够通过codec自己来编码/解码字节流,完成类似redis访问的功能,这就是netty和tomcat最大的不同。
-
-
-
-## 跨域
-
-当发送请求时,如果浏览器发现是跨源AJAX请求,就自动在头信息之中,添加一个Origin字段。
-
-`Origin: http://api.bob.com`
-
-对于服务端,如果请求头Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP响应。浏览器收到响应后,发现响应头信息没有包含Access-Control-Allow-Origin字段,就知道出错了,从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。
-
-如果请求头Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。
-
-```java
-Access-Control-Allow-Origin: http://api.bob.com
-Access-Control-Allow-Credentials: true
-Access-Control-Expose-Headers: FooBar
-```
-
-### 同源策略
-
-同domain(或ip),同端口,同协议视为同一个域,一个域内的脚本仅仅具有本域内的权限,也就是本域脚本只能读写本域内的资源,而无法访问其它域的资源。这种安全限制称为同源策略。
-
-### CSRF攻击
-
-跨域请求有可能被黑客利用来发动 CSRF攻击。CSRF攻击(Cross-site request forgery),跨站请求伪造。攻击者盗用了你的身份,以你的名义发送请求,比如发送邮件,发消息,盗取你的账号,甚至购买商品。
-
-
-
-## statement和prepareStatement
-Statement对象每次执行sql,相关数据库都会执行sql语句的编译,prepareStatement是预编译的,支持批处理。
-PreparedStatement是预编译的,对于批量处理可以大大提高效率,也叫JDBC存储过程。
-prepareStatement对象的开销比statement对象开销大,对于一次性操作使用statement更佳。
\ No newline at end of file
diff --git "a/Java/Java \347\274\226\347\250\213\346\200\235\346\203\263.md" "b/Java/Java \347\274\226\347\250\213\346\200\235\346\203\263.md"
deleted file mode 100644
index 960b2ac..0000000
--- "a/Java/Java \347\274\226\347\250\213\346\200\235\346\203\263.md"
+++ /dev/null
@@ -1,3916 +0,0 @@
-
-
-
-- [基础知识](#%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86)
- - [数据类型](#%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B)
- - [整型](#%E6%95%B4%E5%9E%8B)
- - [浮点类型](#%E6%B5%AE%E7%82%B9%E7%B1%BB%E5%9E%8B)
- - [char 类型](#char-%E7%B1%BB%E5%9E%8B)
- - [boolean 类型](#boolean-%E7%B1%BB%E5%9E%8B)
- - [大数值](#%E5%A4%A7%E6%95%B0%E5%80%BC)
- - [操作符](#%E6%93%8D%E4%BD%9C%E7%AC%A6)
- - [注释文档](#%E6%B3%A8%E9%87%8A%E6%96%87%E6%A1%A3)
- - [代码规范](#%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83)
-- [控制执行流程](#%E6%8E%A7%E5%88%B6%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B)
- - [switch](#switch)
- - [break 和 continue 实现 goto](#break-%E5%92%8C-continue-%E5%AE%9E%E7%8E%B0-goto)
-- [初始化和清理](#%E5%88%9D%E5%A7%8B%E5%8C%96%E5%92%8C%E6%B8%85%E7%90%86)
- - [成员初始化](#%E6%88%90%E5%91%98%E5%88%9D%E5%A7%8B%E5%8C%96)
- - [可变参数列表](#%E5%8F%AF%E5%8F%98%E5%8F%82%E6%95%B0%E5%88%97%E8%A1%A8)
-- [访问权限控制](#%E8%AE%BF%E9%97%AE%E6%9D%83%E9%99%90%E6%8E%A7%E5%88%B6)
- - [访问权限修饰词](#%E8%AE%BF%E9%97%AE%E6%9D%83%E9%99%90%E4%BF%AE%E9%A5%B0%E8%AF%8D)
-- [复用类](#%E5%A4%8D%E7%94%A8%E7%B1%BB)
- - [继承语法](#%E7%BB%A7%E6%89%BF%E8%AF%AD%E6%B3%95)
- - [final 关键字](#final-%E5%85%B3%E9%94%AE%E5%AD%97)
- - [初始化及类的加载](#%E5%88%9D%E5%A7%8B%E5%8C%96%E5%8F%8A%E7%B1%BB%E7%9A%84%E5%8A%A0%E8%BD%BD)
- - [继承与初始化](#%E7%BB%A7%E6%89%BF%E4%B8%8E%E5%88%9D%E5%A7%8B%E5%8C%96)
-- [多态](#%E5%A4%9A%E6%80%81)
- - [缺陷:“覆盖”私有方法](#%E7%BC%BA%E9%99%B7%E8%A6%86%E7%9B%96%E7%A7%81%E6%9C%89%E6%96%B9%E6%B3%95)
- - [域和静态方法](#%E5%9F%9F%E5%92%8C%E9%9D%99%E6%80%81%E6%96%B9%E6%B3%95)
- - [构造器和多态](#%E6%9E%84%E9%80%A0%E5%99%A8%E5%92%8C%E5%A4%9A%E6%80%81)
- - [构造器的调用顺序](#%E6%9E%84%E9%80%A0%E5%99%A8%E7%9A%84%E8%B0%83%E7%94%A8%E9%A1%BA%E5%BA%8F)
-- [接口](#%E6%8E%A5%E5%8F%A3)
- - [抽象类](#%E6%8A%BD%E8%B1%A1%E7%B1%BB)
- - [接口的域](#%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%9F%9F)
-- [内部类](#%E5%86%85%E9%83%A8%E7%B1%BB)
- - [.this 和 .new](#this-%E5%92%8C-new)
- - [匿名内部类](#%E5%8C%BF%E5%90%8D%E5%86%85%E9%83%A8%E7%B1%BB)
- - [工厂方法](#%E5%B7%A5%E5%8E%82%E6%96%B9%E6%B3%95)
- - [嵌套类](#%E5%B5%8C%E5%A5%97%E7%B1%BB)
- - [为什么需要内部类](#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81%E5%86%85%E9%83%A8%E7%B1%BB)
- - [局部内部类](#%E5%B1%80%E9%83%A8%E5%86%85%E9%83%A8%E7%B1%BB)
- - [内部类标识符](#%E5%86%85%E9%83%A8%E7%B1%BB%E6%A0%87%E8%AF%86%E7%AC%A6)
-- [容器](#%E5%AE%B9%E5%99%A8)
- - [添加一组元素](#%E6%B7%BB%E5%8A%A0%E4%B8%80%E7%BB%84%E5%85%83%E7%B4%A0)
- - [迭代器](#%E8%BF%AD%E4%BB%A3%E5%99%A8)
- - [LinkedList](#linkedlist)
- - [Set](#set)
- - [Map](#map)
- - [Queue](#queue)
- - [PriorityQueue](#priorityqueue)
- - [foreach 和 迭代器](#foreach-%E5%92%8C-%E8%BF%AD%E4%BB%A3%E5%99%A8)
- - [适配器方法](#%E9%80%82%E9%85%8D%E5%99%A8%E6%96%B9%E6%B3%95)
-- [异常、断言和日志](#%E5%BC%82%E5%B8%B8%E6%96%AD%E8%A8%80%E5%92%8C%E6%97%A5%E5%BF%97)
- - [异常分类](#%E5%BC%82%E5%B8%B8%E5%88%86%E7%B1%BB)
- - [声明异常](#%E5%A3%B0%E6%98%8E%E5%BC%82%E5%B8%B8)
- - [捕获异常](#%E6%8D%95%E8%8E%B7%E5%BC%82%E5%B8%B8)
- - [带资源的 try 语句](#%E5%B8%A6%E8%B5%84%E6%BA%90%E7%9A%84-try-%E8%AF%AD%E5%8F%A5)
- - [断言](#%E6%96%AD%E8%A8%80)
- - [启用和禁用断言](#%E5%90%AF%E7%94%A8%E5%92%8C%E7%A6%81%E7%94%A8%E6%96%AD%E8%A8%80)
- - [日志](#%E6%97%A5%E5%BF%97)
- - [logback](#logback)
-- [字符串](#%E5%AD%97%E7%AC%A6%E4%B8%B2)
- - [格式化输出](#%E6%A0%BC%E5%BC%8F%E5%8C%96%E8%BE%93%E5%87%BA)
- - [printf() 和 format()](#printf-%E5%92%8C-format)
- - [Formatter](#formatter)
- - [正则表达式](#%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F)
- - [基础](#%E5%9F%BA%E7%A1%80)
- - [创建正则表达式](#%E5%88%9B%E5%BB%BA%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F)
- - [Pattern 和 Matcher](#pattern-%E5%92%8C-matcher)
- - [扫描输入](#%E6%89%AB%E6%8F%8F%E8%BE%93%E5%85%A5)
- - [Scanner 定界符](#scanner-%E5%AE%9A%E7%95%8C%E7%AC%A6)
- - [用正则表达式扫描](#%E7%94%A8%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%89%AB%E6%8F%8F)
-- [类型信息](#%E7%B1%BB%E5%9E%8B%E4%BF%A1%E6%81%AF)
- - [反射](#%E5%8F%8D%E5%B0%84)
-- [泛型](#%E6%B3%9B%E5%9E%8B)
- - [类型参数的好处](#%E7%B1%BB%E5%9E%8B%E5%8F%82%E6%95%B0%E7%9A%84%E5%A5%BD%E5%A4%84)
- - [泛型类](#%E6%B3%9B%E5%9E%8B%E7%B1%BB)
- - [泛型接口](#%E6%B3%9B%E5%9E%8B%E6%8E%A5%E5%8F%A3)
- - [泛型方法](#%E6%B3%9B%E5%9E%8B%E6%96%B9%E6%B3%95)
- - [可变参数和泛型方法](#%E5%8F%AF%E5%8F%98%E5%8F%82%E6%95%B0%E5%92%8C%E6%B3%9B%E5%9E%8B%E6%96%B9%E6%B3%95)
- - [匿名内部类](#%E5%8C%BF%E5%90%8D%E5%86%85%E9%83%A8%E7%B1%BB-1)
- - [泛型擦除](#%E6%B3%9B%E5%9E%8B%E6%93%A6%E9%99%A4)
- - [类型变量的限定](#%E7%B1%BB%E5%9E%8B%E5%8F%98%E9%87%8F%E7%9A%84%E9%99%90%E5%AE%9A)
- - [擦除的问题](#%E6%93%A6%E9%99%A4%E7%9A%84%E9%97%AE%E9%A2%98)
- - [边界](#%E8%BE%B9%E7%95%8C)
- - [通配符](#%E9%80%9A%E9%85%8D%E7%AC%A6)
- - [上界通配符](#%E4%B8%8A%E7%95%8C%E9%80%9A%E9%85%8D%E7%AC%A6)
- - [下界通配符](#%E4%B8%8B%E7%95%8C%E9%80%9A%E9%85%8D%E7%AC%A6)
-- [数组](#%E6%95%B0%E7%BB%84)
- - [复制数组](#%E5%A4%8D%E5%88%B6%E6%95%B0%E7%BB%84)
- - [Arrays 工具](#arrays-%E5%B7%A5%E5%85%B7)
- - [数组拷贝](#%E6%95%B0%E7%BB%84%E6%8B%B7%E8%B4%9D)
- - [数组的比较](#%E6%95%B0%E7%BB%84%E7%9A%84%E6%AF%94%E8%BE%83)
- - [数组元素的比较](#%E6%95%B0%E7%BB%84%E5%85%83%E7%B4%A0%E7%9A%84%E6%AF%94%E8%BE%83)
- - [数组排序](#%E6%95%B0%E7%BB%84%E6%8E%92%E5%BA%8F)
- - [排序数组查找](#%E6%8E%92%E5%BA%8F%E6%95%B0%E7%BB%84%E6%9F%A5%E6%89%BE)
-- [容器深入研究](#%E5%AE%B9%E5%99%A8%E6%B7%B1%E5%85%A5%E7%A0%94%E7%A9%B6)
- - [填充容器](#%E5%A1%AB%E5%85%85%E5%AE%B9%E5%99%A8)
- - [SortedSet](#sortedset)
- - [队列](#%E9%98%9F%E5%88%97)
- - [优先级队列](#%E4%BC%98%E5%85%88%E7%BA%A7%E9%98%9F%E5%88%97)
- - [双向队列](#%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97)
- - [LinkedHashMap](#linkedhashmap)
- - [Colletions 工具类](#colletions-%E5%B7%A5%E5%85%B7%E7%B1%BB)
- - [排序和查询](#%E6%8E%92%E5%BA%8F%E5%92%8C%E6%9F%A5%E8%AF%A2)
- - [只读容器](#%E5%8F%AA%E8%AF%BB%E5%AE%B9%E5%99%A8)
- - [Collection 和 Map 的同步控制](#collection-%E5%92%8C-map-%E7%9A%84%E5%90%8C%E6%AD%A5%E6%8E%A7%E5%88%B6)
- - [快速报错机制](#%E5%BF%AB%E9%80%9F%E6%8A%A5%E9%94%99%E6%9C%BA%E5%88%B6)
- - [Java 1.0/1.1 的容器](#java-1011-%E7%9A%84%E5%AE%B9%E5%99%A8)
- - [BitSet](#bitset)
-- [Java I/O 系统](#java-io-%E7%B3%BB%E7%BB%9F)
- - [输入和输出](#%E8%BE%93%E5%85%A5%E5%92%8C%E8%BE%93%E5%87%BA)
- - [InputStream 和 OutputStream](#inputstream-%E5%92%8C-outputstream)
- - [Reader 和 Writer](#reader-%E5%92%8C-writer)
- - [组合输入输出流过滤器](#%E7%BB%84%E5%90%88%E8%BE%93%E5%85%A5%E8%BE%93%E5%87%BA%E6%B5%81%E8%BF%87%E6%BB%A4%E5%99%A8)
- - [文本输入和输出](#%E6%96%87%E6%9C%AC%E8%BE%93%E5%85%A5%E5%92%8C%E8%BE%93%E5%87%BA)
- - [以文本格式存储对象](#%E4%BB%A5%E6%96%87%E6%9C%AC%E6%A0%BC%E5%BC%8F%E5%AD%98%E5%82%A8%E5%AF%B9%E8%B1%A1)
- - [字符编码方式](#%E5%AD%97%E7%AC%A6%E7%BC%96%E7%A0%81%E6%96%B9%E5%BC%8F)
- - [读写二进制数据](#%E8%AF%BB%E5%86%99%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%95%B0%E6%8D%AE)
- - [DataInput 和 DataOutput](#datainput-%E5%92%8C-dataoutput)
- - [随机访问文件](#%E9%9A%8F%E6%9C%BA%E8%AE%BF%E9%97%AE%E6%96%87%E4%BB%B6)
- - [序列化](#%E5%BA%8F%E5%88%97%E5%8C%96)
- - [操作文件](#%E6%93%8D%E4%BD%9C%E6%96%87%E4%BB%B6)
- - [目录列表器](#%E7%9B%AE%E5%BD%95%E5%88%97%E8%A1%A8%E5%99%A8)
- - [](#)
-- [枚举类型](#%E6%9E%9A%E4%B8%BE%E7%B1%BB%E5%9E%8B)
- - [基本 enum 特性](#%E5%9F%BA%E6%9C%AC-enum-%E7%89%B9%E6%80%A7)
- - [向 enum 添加新方法](#%E5%90%91-enum-%E6%B7%BB%E5%8A%A0%E6%96%B0%E6%96%B9%E6%B3%95)
- - [覆盖 enum 的方法](#%E8%A6%86%E7%9B%96-enum-%E7%9A%84%E6%96%B9%E6%B3%95)
- - [Switch 语句中的 enum](#switch-%E8%AF%AD%E5%8F%A5%E4%B8%AD%E7%9A%84-enum)
- - [EnumSet](#enumset)
- - [源码解析](#%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90)
- - [EnumMap](#enummap)
-- [注解](#%E6%B3%A8%E8%A7%A3)
- - [基本语法](#%E5%9F%BA%E6%9C%AC%E8%AF%AD%E6%B3%95)
- - [自定义注解](#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%B3%A8%E8%A7%A3)
- - [元注解](#%E5%85%83%E6%B3%A8%E8%A7%A3)
- - [编写注解处理器](#%E7%BC%96%E5%86%99%E6%B3%A8%E8%A7%A3%E5%A4%84%E7%90%86%E5%99%A8)
- - [注解综合](#%E6%B3%A8%E8%A7%A3%E7%BB%BC%E5%90%88)
-
-
-
-## 基础知识
-
-### 数据类型
-
-#### 整型
-
-| 类型 | 存储空间 |
-| ----- | -------- |
-| int | 4字节 |
-| short | 2字节 |
-| long | 8字节 |
-| byte | 1字节 |
-
-byte 的取值范围是-128~127。整型的取值范围和运行 Java 代码的机器无关。Java 没有无符号的整型。
-
-**数据类型表示:**
-
-长整型:400L
-
-十六进制:0xCA
-
-八进制:020(有前缀0)
-
-二进制:0b1001(Java 7及以上版本)
-
-从 Java 7开始,可以为数据字面量添加下划线,如1_000_000,方便易读。Java 编译器会去除这些下划线。
-
-#### 浮点类型
-
-| 类型 | 存储空间 |
-| ------ | -------- |
-| float | 4字节 |
-| double | 8字节 |
-
-float 类型的数值会有后缀 F 或者 f (2.01F),没有后缀 F 的浮点数默认为 double 类型(也可以加后缀 D)。浮点数采用二进制系统表示,有些数值会有精度损失,如`System.out.println(2.0 - 1.1)`会输出0.8999999999999999。如果数值计算不允许有任何精度误差,应该使用 BigDecimal。
-
-**特殊值**
-
-正无穷大:Double.POSITIVE_INFINITY
-
-负无穷大:Double.NEGATIVE_INFINITY
-
-NaN(不是一个数字):Double.NaN
-
-#### char 类型
-
-Unicode 字符用一个或者两个 char 值描述。Unicode 字符可以表示为十六进制,其范围从/u0000到/uffff。
-
-#### boolean 类型
-
-布尔类型有两个值:true 和 false。布尔型和整型不能相互转化。Java 语言表达式所操作的 boolean 值,在编译之后都使用Java虚拟机中的int数据类型来代替,而 boolean 数组将会被编码成 Java 虚拟机的 byte 数组,每个元素 boolean 元素占8位”。这样我们可以得出 boolean 类型占了单独使用是4个字节,在数组中又是1个字节。
-
-#### 大数值
-
-BigInteger 实现了任意精度的整数运算,BigDecimal 实现了任意精度的浮点数运算。
-
-```java
-BigInteger a = BigInteger.valueOf(100);
-BigInteger b = BigInteger.valueOf(100);
-BigInteger c = a.add(b);
-BigInteger d = c.multiply(b.add(BigInteger.valueOf(2)));
-```
-
-### 操作符
-
-**截尾和舍入**
-
-```java
-float num = 0.7f;
-//类型转化对数字进行截尾
-System.out.println((int)num); //0
-//四舍五入操作
-System.out.println(Math.round(num)); //1
-```
-
-### 注释文档
-
-javadoc 用来提取注释的工具。javadoc 只能为 public 和 protected 成员进行文档注释。
-
-### 代码规范
-
-可以采用将 public 成员放在开头,后面跟着 protect、包访问权限和 private 成员的创建类的形式,方便类的使用者阅读最为重要的部分(public成员)。
-
-```java
-public class OrganizedByAccess {
- public void method1() {}
- public void method2() {}
- protected void method3() {}
- void method4() {}
- private method5() {}
- private int i;
- //...
-}
-```
-
-
-
-## 控制执行流程
-
-### switch
-
-break 会跳出 switch 语句,没有 break 语句则会一直往下执行。
-
-```java
-int i = 1;
-switch (i) {
- case 0:
- System.out.println("i = 0");
- break;
- case 1:
- System.out.println("i = 1");
- break;
- case 2:
- System.out.println("i = 2");
- break;
- default:
- System.out.println("i >= 3");
-}
-//输出i = 1
-
-switch (i) {
- case 0:
- System.out.println("i = 0");
- //break;
- case 1:
- System.out.println("i = 1");
- //break;
- case 2:
- System.out.println("i = 2");
- //break;
- default:
- System.out.println("i >= 3");
-}
-//输出
-//i = 1
-//i = 2
-//i >= 3
-```
-
-### break 和 continue 实现 goto
-
-```java
-public class LabeledFor {
- public static void main(String[] args) {
- int i = 0;
- outer: // Can't have statements here
- for(; true ;) { // infinite loop
- inner: // Can't have statements here
- for(; i < 10; i++) {
- print("i = " + i);
- if(i == 2) {
- print("continue");
- continue;
- }
- if(i == 3) {
- print("break");
- i++;
- break;//break跳出循环,不会执行递增操作
- }
- if(i == 7) {
- print("continue outer");
- i++;
- continue outer;//continue跳到循环顶部,不会执行递增操作
- }
- if(i == 8) {
- print("break outer");
- break outer;
- }
- for(int k = 0; k < 5; k++) {
- if(k == 3) {
- print("continue inner");
- continue inner;
- }
- }
- }
- }
- // Can't break or continue to labels here
- }
-}
-```
-
-
-
-## 初始化和清理
-
-### 成员初始化
-
-局部变量未初始化就使用,会产生编译时错误。类的数据成员是基本类型,它们会有初值(如 int 类型的数据成员初值为0)。
-
-### 可变参数列表
-
-Java SE5 开始支持可变参数列表。当指定参数时,编译器会将元素列表转换为数组。
-
-```java
-public class VarArgs {
- public static void printArray(Object... args) {
- for(Object obj : args) {
- System.out.print(obj + " ");
- }
- System.out.println();
- }
-
- public static void main(String[] args) {
- printArray(new Integer(1), new Integer(3));
- printArray("tyson", "sophia", "tom");
- printArray(new Integer[]{2, 4, 6});
- printArray();//空列表
- }
-}
-```
-
-## 访问权限控制
-
-package 语句必须是文件中第一行非注释代码。
-
-### 访问权限修饰词
-
-1. 包访问权限
-
- 默认的访问权限没有任何关键字,通常是指包访问权限,即当前包的所有其他类对当前成员具有访问权限。
-
-2. protected
-
- 继承访问权限,派生类可以访问基类的 protected 元素。同时,protected 也提供包访问权限,即相同包内的其他类也可以访问 protected 元素。
-
-3. private
-
- 通过 private 隐藏了代码细节,类库设计者可以更改类内部工作方式,而不会影响客户端。
-
-4. public
-
-
-
-示例代码:
-
-```java
-package com.tyson.chapter6_access;
-//Cookie.java
-public class Cookie {
- public Cookie() {
- System.out.println("Cookie constructor");
- }
- protected void bite() {
- System.out.println("bite");
- }
-}
-
-//ChocolateChip.java
-public class ChocolateChip extends Cookie {
- //私有构造器
- private ChocolateChip() {
- System.out.println("chocolate chip constructor");
- }
- //通过静态方法创建类
- public static ChocolateChip createChocolateChip() {
- return new ChocolateChip();
- }
-
- public void chomp() {
- //访问父类的protected成员
- bite();
- }
-}
-
-class Application {
- public static void main(String[] args) {
- ChocolateChip chocolateChip = ChocolateChip.createChocolateChip();
- chocolateChip.chomp();
- }
-}
-```
-
-
-
-## 复用类
-
-### 继承语法
-
-派生类如果没有指定某个基类的构造器,则会调用基类的默认构造器,若没有默认的构造器(不带参数的构造器),编译器会报错。
-
-### final 关键字
-
-1. final 数据
-
- 基本类型变量用 final 修饰,则该变量是常量,数值不能改变;对象引用用 final 修饰,则一旦该引用被初始化指向一个对象,就不能再把它改为指向另一个对象,不过对象自身可以修改,只是引用不能修改。
-
-2. final 参数
-
- 将参数指明为 final,则无法在方法中更改参数。
-
-```java
-public class FinalArguments {
- void add(final Num num) {
- //num = new Num(1); //不能更改final参数
- num.i++;
- }
- void add(final int i) {
- //i++; //不能更改final参数的值
- }
-}
-
-class Num {
- int i;
- public Num(int i) {
- this.i = i;
- }
-}
-```
-
-3. final 方法
-
- final 方法不能被重写,使用 final 方法主要是为了防止继承类修改它的含义。过去使用 final 方法效率会高点,现在的 JVM 相比之前有了很大的优化,没必要通过设置 final 来提高效率。
-
-4. final 类
-
- final 类不能被继承。final 类的所有方法都被隐式指定为 final。JDK 的 String 类就是 final 类。
-
-### 初始化及类的加载
-
-#### 继承与初始化
-
-```java
-class Insect {
- private int i = printInit("Insect.i initialized");
- protected int j;
- Insect() {
- System.out.println("i = " + i + ", j = " + j);
- j = 39;
- }
- private static int x1 =
- printInit("static Insect.x1 initialized");
- static int printInit(String s) {
- System.out.println(s);
- return 47;
- }
-}
-
-public class Beetle extends Insect {
- private int k = printInit("Beetle.k initialized");
- public Beetle() {
- System.out.println("k = " + k + ", j = " + j);
- }
- private static int x2 =
- printInit("static Beetle.x2 initialized");
- public static void main(String[] args) {
- System.out.println("Beetle constructor");
- Beetle b = new Beetle();
- }
-}
-/*output
-static Insect.x1 initialized
-static Beetle.x2 initialized
-Beetle constructor
-Insect.i initialized
-i = 47, j = 0
-Beetle.k initialized
-k = 47, j = 39
- */
-```
-
-
-
-## 多态
-
-封装把接口和实现分离开来,多态消除类型之间的耦合关系。
-
-向上转型:将子类看作是它的基类的过程。子类转型为基类就是在继承图上向上移动。
-
-
-
-### 缺陷:“覆盖”私有方法
-
-```java
-public class PrivateOverride {
- private void f() {
- System.out.println("private f()");
- }
-
- public static void main(String[] args) {
- PrivateOverride po = new Derived();
- po.f();
- }
-}
-
-class Derived extends PrivateOverride {
- public void f() {
- System.out.println("public f()");
- }
-}
-/*
-private f()
- */
-```
-
-Derived 类的 f() 实际上是一个全新的方法。基类的 f() 方法在子类 Derived 中不可见,因此不能被重载。
-
-### 域和静态方法
-
-只有普通的方法调用具备多态性,域和静态方法没有多态性。
-
-### 构造器和多态
-
-构造器实际上是 static 方法,只不过 static 声明是隐式的,不具备多态性。
-
-#### 构造器的调用顺序
-
-```java
-class Meal {
- private int weight = get();
- Meal() {
- print("Meal()");
- }
- public int get() {
- System.out.println("Meal.get()");
- return 1;
- }
-}
-
-class Bread {
- Bread() {
- print("Bread()");
- }
-}
-
-class Cheese {
- Cheese() {
- print("Cheese()");
- }
-}
-
-class Lettuce {
- Lettuce() {
- print("Lettuce()");
- }
-}
-
-class Lunch extends Meal {
- private int price = food();
- Lunch() {
- print("Lunch()");
- }
- public int food() {
- System.out.println("Lunch.food()");
- return 1;
- }
-}
-
-class PortableLunch extends Lunch {
- PortableLunch() {
- print("PortableLunch()");
- }
-}
-
-public class Sandwich extends PortableLunch {
- private Bread b = new Bread();
- private Cheese c = new Cheese();
- private Lettuce l = new Lettuce();
-
- public Sandwich() {
- print("Sandwich()");
- }
-
- public static void main(String[] args) {
- new Sandwich();
- }
-} /* Output:
-Meal.get()
-Meal()
-Lunch.food()
-Lunch()
-PortableLunch()
-Bread()
-Cheese()
-Lettuce()
-Sandwich()
-*/
-
-```
-
-调用顺序:
-
-1. 按照声明顺序调用基类成员的初始化方法
-2. 基类构造器。从最顶层的基类到最底层的派生类
-3. 按照声明顺序调用成员的初始化方法
-4. 派生类构造器
-
-
-
-## 接口
-
-接口和内部类为我们提供了一种将接口与实现分离的方法。
-
-### 抽象类
-
-从一个抽象类继承,需要为基类中的所有方法提供方法定义。如果没有提供,则派生类也是抽象类。
-
-如果有一个类,让其包含任何 abstract 方法都没有实际意义,而我们又想阻止产生这个类的任何对象,这时候可以把它设置成一个没有任何抽象方法的抽象类。
-
-### 接口的域
-
-接口中的域都自动声明为`public static final`,故接口可以用来创建常量组。Java SE5之前没有提供 enum 实现,可以通过接口实现 enum 的功能。
-
-## 内部类
-
-内部类允许把一些逻辑相关的类组织在一起,并控制内部类的可视性。内部类可以访问外围类,有些通过编写内部类可以让代码结构更清晰。
-
-### .this 和 .new
-
-使用外部类的名字后面紧跟 .this,可以生成对外部类对象的引用。
-
-```java
-public class DotThis {
- void f() {
- System.out.println("DotThis.f()");
- }
- public class Inner {
- public DotThis outer() {
- return DotThis.this;
- }
- }
- public Inner inner() {
- return new Inner();
- }
-
- public static void main(String[] args) {
- DotThis dt = new DotThis();
- DotThis.Inner dti = dt.inner();
- dti.outer().f();
- }
-}
-//output DotThis.f()
-```
-
-使用 .new 创建内部类的对象。
-
-```java
-public class DotNew {
- public class Inner {}
-
- public static void main(String[] args) {
- DotNew dn = new DotNew();
- DotNew.Inner dni = dn.new Inner();
- }
-}
-```
-
-创建内部类的对象,必须通过外部类的对象来创建,在拥有外部类对象之前是不可能创建内部类对象的。这是因为内部类对象会隐式连接到创建它的外部类对象上。但是如果创建的是嵌套类(静态内部类),则不需要创建外部类对象。
-
-### 匿名内部类
-
-```java
-//Wrapping.java
-public class Wrapping {
- private int i;
-
- public Wrapping(int x) {
- i = x;
- }
- public int value() {
- return i;
- }
-}
-
-//Parcel.java
-public class Parcel {
- public Wrapping wrapping(int x) {
- return new Wrapping(x) {//传递构造器参数
- @Override
- public int value() {
- return super.value() * 47;
- }
- };
- }
-
- public static void main(String[] args) {
- Parcel p = new Parcel();
- Wrapping w = p.wrapping(8);
- System.out.println(w.value());
- }
-}
-//output 376
-```
-
-在匿名类定义字段时,还能够对其执行初始化操作。
-
-```java
-//Destination.java
-public interface Destination {
- String readLabel();
-}
-
-//Parcel1.java
-public class Parcel1 {
- public Destination destination(final String dest) {
- return new Destination() {
- private String label = dest;
- @Override
- public String readLabel() {
- return label;
- }
- };
- }
-
- public static void main(String[] args) {
- Parcel1 p1 = new Parcel1();
- Destination d = p1.destination("Puning");
- System.out.println(d.readLabel());
- }
-}
-```
-
-匿名内部类没有命名构造器,通过实例初始化给匿名内部类创建构造器的效果。
-
-```java
-abstract class Base {
- public Base(int i) {
- System.out.println("Base constructor, i = " + i);
- }
-
- public abstract void f();
-}
-
-public class AnonymousConstructor {
- public static Base getBase(int i) {
- return new Base(i) {
- //构造器的效果
- {
- System.out.println("Inside instance initializer");
- }
- @Override
- public void f() {
- System.out.println("In anonymous f()");
- }
- };
- }
-
- public static void main(String[] args) {
- Base base = getBase(47);
- base.f();
- }
-} /* Output:
-Base constructor, i = 47
-Inside instance initializer
-In anonymous f()
-*/
-```
-
-#### 工厂方法
-
-使用匿名内部类改进工厂方法。
-
-```java
-interface Game { boolean move();}
-interface GameFactory { Game getGame();}
-
-public class Games {
- public static void playGame(GameFactory gameFactory) {
- Game game = gameFactory.getGame();
- while(game.move()) {
- ;
- }
- }
-
- public static void main(String[] args) {
- playGame(Checkers.factory);
- playGame(Chess.factory);
- }
-}
-
-class Chess implements Game {
-
- private Chess() {}
- private int moves = 0;
- private static final int MOVES = 4;
-
- @Override
- public boolean move() {
- System.out.println("chess move " + moves);
- return ++moves != MOVES;
- }
-
- public static GameFactory factory = new GameFactory() {
- @Override
- public Game getGame() {
- return new Chess();
- }
- };
-}
-
-class Checkers implements Game {
- private Checkers() {}
- private int moves = 0;
- private static final int MOVES = 3;
- @Override
- public boolean move() {
- System.out.println("checkers move" + moves);
- return ++moves != MOVES;
- }
-
- public static GameFactory factory = new GameFactory() {
- @Override
- public Game getGame() {
- return new Checkers();
- }
- };
-}
-/*
-checkers move0
-checkers move1
-checkers move2
-chess move 0
-chess move 1
-chess move 2
-chess move 3
- */
-```
-
-### 嵌套类
-
-将内部类声明为 static,即为嵌套类。普通的内部类隐式保存了一个指向外围类对象的引用,而嵌套类没有。所以创建嵌套类的对象,不需要先创建外围类对象。并且嵌套类不能访问非静态的外围类的成员。
-
-普通的内部类不能包含 static 字段和 static 方法,也不能包含嵌套类,但嵌套类可以包含这些。
-
-```java
-public class NestingClass {
- public static class StaticInner {
- public void dynamicFuc() {
- System.out.println("StaticInner动态方法");
- }
- public static void staticFuc() {
- System.out.println("StaticInner静态方法");
- }
- }
-
- public static void main(String[] args) {
- StaticInner si = new StaticInner();
- si.dynamicFuc();
- StaticInner.staticFuc();//直接通过类名调用静态方法
- }
-}
-/*
-StaticInner动态方法
-StaticInner静态方法
- */
-```
-
-嵌套类没有.this引用。
-
-### 为什么需要内部类
-
-使用内部类可以实现多重继承。
-
-```java
-interface Selector {
- boolean end();
- Object current();
- void next();
-}
-
-public class Sequence {
- private Object[] items;
- private int next = 0;
-
- public Sequence(int size) {
- items = new Object[size];
- }
-
- public void add(Object x) {
- if (next < items.length)
- items[next++] = x;
- }
-
- private class SequenceSelector implements Selector {
- private int i = 0;
-
- public boolean end() {
- return i == items.length;
- }
- public Object current() {
- return items[i];
- }
- public void next() {
- if (i < items.length) i++;
- }
- }
-
- public Selector selector() {
- return new SequenceSelector();
- }
-
- public static void main(String[] args) {
- Sequence sequence = new Sequence(10);
- for (int i = 0; i < 10; i++)
- sequence.add(Integer.toString(i));
- Selector selector = sequence.selector();
- while (!selector.end()) {
- System.out.print(selector.current() + " ");
- selector.next();
- }
- }
-} /* Output:
-0 1 2 3 4 5 6 7 8 9
-*/
-```
-
-如果 Sequence.java 不使用内部类,就必须声明 Sequence 是一个 Selector,对于某个特定的 Sequence 只能有一个 Selector。使用内部类的话很容易就可以拥有另一个方法 reverseSelector(),用来生成一个反向遍历序列的 Selector。
-
-### 局部内部类
-
-```java
-interface Counter {int next();}
-
-public class LocalInnerClass {
- private int count = 0;
-
- Counter getCounter(final String name) {
- // A local inner class:
- class LocalCounter implements Counter {
- public LocalCounter() {
- // Local inner class can have a constructor
- print("LocalCounter()");
- }
-
- public int next() {
- printnb(name); // Access local final
- return count++;
- }
- }
- return new LocalCounter();
- }
-
- // The same thing with an anonymous inner class:
- Counter getCounter2(final String name) {
- return new Counter() {
- // Anonymous inner class cannot have a named
- // constructor, only an instance initializer:
- {
- print("Counter()");
- }
-
- public int next() {
- printnb(name); // Access local final
- return count++;
- }
- };
- }
-
- public static void main(String[] args) {
- LocalInnerClass lic = new LocalInnerClass();
- Counter c1 = lic.getCounter("Local inner "),
- c2 = lic.getCounter2("Anonymous inner ");
- print(c1.next());
- print(c2.next());
- }
-} /* Output:
-LocalCounter()
-Counter()
-Local inner 0
-Anonymous inner 1
-*/
-```
-
-局部内部类可以拥有命名的构造器或者重载构造器,而匿名内部类只能使用实例初始化。如果需要不止一个该内部类对象,那么只能使用局部内部类。
-
-### 内部类标识符
-
-内部类文件的命名格式:外围类名字加上"$",再加上内部类的名字(如果是匿名类,则是一个数字)。
-
-`LocalInnerClass$1LocalCounter.class`
-
-`LocalInnerClass$1.class`
-
-
-
-## 容器
-
-
-
-### 添加一组元素
-
-```java
-public class AddingGroups {
- public static void main(String[] args) {
- Collection nums = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
- Integer[] ints = {5, 6, 7};
- nums.addAll(Arrays.asList(ints));
- Collections.addAll(nums, 8, 9);
- Collections.addAll(nums, ints);
-
- List list = Arrays.asList(1, 2, 3);
- list.set(1, 90);
- System.out.println(list.getClass());
- //底层是数组java.util.Arrays$ArrayList,不能修改结构,java.lang.UnsupportedOperationException
- //list.add(7);
- }
-}
-```
-
-`Arrays.asList()`返回类型是`java.util.Arrays$ArrayList`,底层是数组,试图删除或增加元素会抛异常 UnsupportedOperationException。
-
-### 迭代器
-
-```java
-import typeinfo.pets.Pet;
-import typeinfo.pets.Pets;
-import java.util.Iterator;
-import java.util.List;
-
-public class SimpleIteration {
- public static void main(String[] args) {
- List pets = Pets.arrayList(8);
- Iterator it = pets.iterator();
- while(it.hasNext()) {
- Pet p = it.next();
- System.out.print(p.id() + ":" + p + " ");
- }
- System.out.println();
- for(Pet p : pets) {
- System.out.print(p.id() + ":" + p + " ");
- }
- System.out.println();
- it = pets.iterator();
- for(int i = 0; i < 4; i++) {
- it.next();
- it.remove();//删除最近遍历的元素
- }
- System.out.println(pets);
- }
-}
-/*output
-0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx
-0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx
-[Pug, Cymric, Pug, Manx]
- */
-```
-
-ListIterator 是一个更为强大的 Iterator 子类型,它只能用于各种 List 类的遍历。ListIterator 可以双向移动。它还可以产生迭代器当前位置前一个和后一个元素的索引,并且可以使用 set() 方法修改最近遍历的元素。通过 listIterator(index) 可以创建指向索引为index的元素的ListIterator。
-
-```java
-public class ListIteration {
- public static void main(String[] args) {
- List pets = Pets.arrayList(8);
- ListIterator it = pets.listIterator();
- while(it.hasNext()) {
- System.out.print(it.next() + ", " + it.nextIndex() +
- ", " + it.previousIndex() + "; ");
- }
- System.out.println();
- while(it.hasPrevious()) {
- System.out.print(it.previous().id() + " ");
- }
- System.out.println();
- System.out.println(pets);
- //创建指向索引为index元素处的ListIterator
- it = pets.listIterator(3);
- while(it.hasNext()) {
- it.next();
- it.set(Pets.randomPet());
- }
- System.out.println(pets);
- }
-}/*output
-Rat, 1, 0; Manx, 2, 1; Cymric, 3, 2; Mutt, 4, 3; Pug, 5, 4; Cymric, 6, 5; Pug, 7, 6; Manx, 8, 7;
-7 6 5 4 3 2 1 0
-[Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug, Manx]
-[Rat, Manx, Cymric, Cymric, Rat, EgyptianMau, Hamster, EgyptianMau]
- */
-```
-
-### LinkedList
-
-```java
-public class LinkedListFeatures {
- public static void main(String[] args) {
- LinkedList pets =
- new LinkedList(Pets.arrayList(5));
- print(pets);
- // Identical:
- print("pets.getFirst(): " + pets.getFirst());
- print("pets.element(): " + pets.element());
- // Only differs in empty-list behavior:
- print("pets.peek(): " + pets.peek());
- // Identical; remove and return the first element:
- print("pets.remove(): " + pets.remove());
- print("pets.removeFirst(): " + pets.removeFirst());
- // Only differs in empty-list behavior:
- print("pets.poll(): " + pets.poll());
- print(pets);
- pets.addFirst(new Rat());
- print("After addFirst(): " + pets);
- pets.offer(Pets.randomPet());
- print("After offer(): " + pets);
- pets.add(Pets.randomPet());
- print("After add(): " + pets);
- pets.addLast(new Hamster());
- print("After addLast(): " + pets);
- print("pets.removeLast(): " + pets.removeLast());
- }
-} /* Output:
-[Rat, Manx, Cymric, Mutt, Pug]
-pets.getFirst(): Rat
-pets.element(): Rat
-pets.peek(): Rat
-pets.remove(): Rat
-pets.removeFirst(): Manx
-pets.poll(): Cymric
-[Mutt, Pug]
-After addFirst(): [Rat, Mutt, Pug]
-After offer(): [Rat, Mutt, Pug, Cymric]
-After add(): [Rat, Mutt, Pug, Cymric, Pug]
-After addLast(): [Rat, Mutt, Pug, Cymric, Pug, Hamster]
-pets.removeLast(): Hamster
-*/
-```
-
-LinkedList 具有实现栈所有功能的所有方法,可以将 LinkedList 作为栈使用:
-
-```java
-public class MyStack {
- private LinkedList storage = new LinkedList<>();
- public void push(T t) {
- storage.addFirst(t);
- }
- public T pop() {
- return storage.removeFirst();
- }
- public T peek() {
- return storage.getFirst();
- }
- public boolean isEmpty() {
- return storage.isEmpty();
- }
- @Override
- public String toString() {
- return storage.toString();
- }
-}
-```
-
-### Set
-
-Set 不保存重复的元素,插入相同的元素会被忽略。HashSet 存储元素没有顺序;TreeSet 按照升序的方式存储元素;LinkedHashList 使用链表维护元素的插入顺序,并通过散列提供了快速访问能力。
-
-```java
-public class SortedSetOfInteger {
- public static void main(String[] args) {
- Random rand = new Random(47);
- SortedSet set = new TreeSet<>();
- for (int i = 0; i < 1000; i++) {
- set.add(rand.nextInt(30));
- }
- System.out.println(set);
- }
-}
-/*output
-[0, 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]
- */
-```
-
-TreeSet 对字母排序是按照字典序,大小写字母会被分到不同的组,如果想按照字母序排序,可以向 TreeSet 构造器传入 String.CASE_INSENTIVE_ORDER 比较器。
-
-```java
-public class SortedWords {
- public static void main(String[] args) {
- //往构造器传入String.CASE_INSENTIVE_ORDER 比较器
- Set words = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
- String[] strs = {"hello", "world", "tyson", "a"};
- words.addAll(Arrays.asList(strs));
- System.out.println(words);
- }
-}
-/*
-[a, hello, tyson, world]
- */
-```
-
-### Map
-
-HashMap 设计用来快速访问;TreeMap保持键始终处于升序状态;LinkedHashMap 使用链表维护元素的插入顺序,并通过散列提供了快速访问能力。
-
-```java
-public class PetMap {
- public static void main(String[] args) {
- Map petMap = new HashMap<>();
- petMap.put("Tyson", new Cat("cat"));
- petMap.put("Sophia", new Dog("dog"));
- petMap.put("Tom", new Hamster("hamster"));
-
- System.out.println(petMap.keySet());
- System.out.println(petMap.values());
- for(String person : petMap.keySet()) {
- System.out.println(person + " : " + petMap.get(person));
- }
-
- Set> entries = petMap.entrySet();
- for(Map.Entry entry : entries) {
- System.out.println(entry.getKey() + ": " + entry.getValue());
- entry.setValue(new Cat("cat"));
- System.out.println(entry.getKey() + ": " + entry.getValue());
- }
-
- }
-}
-/*output
-[Tom, Tyson, Sophia]
-[Hamster hamster, Cat cat, Dog dog]
-Tom : Hamster hamster
-Tyson : Cat cat
-Sophia : Dog dog
-Tom: Hamster hamster
-Tom: Cat cat
-Tyson: Cat cat
-Tyson: Cat cat
-Sophia: Dog dog
-Sophia: Cat cat
- */
-```
-
-### Queue
-
-LinkedList 实现了 Queue 接口,可以作为队列使用。
-
-```java
-public class QueueDemo {
- public static void printQ(Queue queue) {
- while(queue.peek() != null) {
- System.out.print(queue.remove() + " ");
- }
- System.out.println();
- }
- public static void main(String[] args) {
- Queue queue = new LinkedList<>();
- Random rand = new Random(47);
- for(int i = 0; i < 10; i++) {
- queue.offer(rand.nextInt(i + 10));
- }
- printQ(queue);
- Queue qc = new LinkedList<>();
- for(char c : "tyson".toCharArray()) {
- qc.offer(c);
- }
- printQ(qc);
- }
-}
-```
-
-peek()和 element()都将在不移除的情况下返回对头,peek()方法在队列为空时返回 null,而element()会跑 NoSuchElementExeception 异常。
-
-poll()和remove()方法将移除并返回对头,poll()在队列为空时返回 null,而remove()会跑 NoSuchElementExeception 异常。
-
-#### PriorityQueue
-
-Integer、String 和 Character 内建了自然排序,可以与 PriorityQueue 一起工作。要想在 PriorityQueue 中使用自己的类,就必须提供自己的 Comparator。
-
-```java
-public class PriorityQueueDemo {
- public static void main(String[] args) {
- PriorityQueue priorityQueue =
- new PriorityQueue();
- Random rand = new Random(47);
- for(int i = 0; i < 10; i++)
- priorityQueue.offer(rand.nextInt(i + 10));
- QueueDemo.printQ(priorityQueue);
-
- List ints = Arrays.asList(25, 22, 20,
- 18, 14, 9, 3, 1, 1, 2, 3, 9, 14, 18, 21, 23, 25);
- priorityQueue = new PriorityQueue(ints);
- QueueDemo.printQ(priorityQueue);
- priorityQueue = new PriorityQueue(
- ints.size(), Collections.reverseOrder());
- priorityQueue.addAll(ints);
- QueueDemo.printQ(priorityQueue);
-
- String fact = "EDUCATION SHOULD ESCHEW OBFUSCATION";
- List strings = Arrays.asList(fact.split(""));
- PriorityQueue stringPQ =
- new PriorityQueue(strings);
- QueueDemo.printQ(stringPQ);
- stringPQ = new PriorityQueue(
- strings.size(), Collections.reverseOrder());
- stringPQ.addAll(strings);
- QueueDemo.printQ(stringPQ);
-
- Set charSet = new HashSet();
- for(char c : fact.toCharArray())
- charSet.add(c); // Autoboxing
- PriorityQueue characterPQ =
- new PriorityQueue(charSet);
- QueueDemo.printQ(characterPQ);
- }
-} /* Output:
-0 1 1 1 1 1 3 5 8 14
-1 1 2 3 3 9 9 14 14 18 18 20 21 22 23 25 25
-25 25 23 22 21 20 18 18 14 14 9 9 3 3 2 1 1
- A A B C C C D D E E E F H H I I L N N O O O O S S S T T U U U W
-W U U U T T S S S O O O O N N L I I H H F E E E D D C C C B A A
- A B C D E F H I L N O S T U W
-*///:~
-```
-
-### foreach 和 迭代器
-
-Java SE5引入了 Iterable 接口,该接口包含了一个能够产生 Iterator 的 iterator()方法。任何实现了 Iterable 的类(如Collection)都可以应用于 foreach 语句中。
-
-```java
-public class IterableClass implements Iterable {
- protected String[] words = {"hello", "world", "program"};
- @Override
- public Iterator iterator() {
- return new Iterator() {
- private int index = 0;
- @Override
- public boolean hasNext() {
- return index < words.length;
- }
- @Override
- public String next() {
- return words[index++];
- }
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
- };
- }
-
- public static void main(String[] args) {
- for(String s : new IterableClass()) {
- System.out.print(s + " ");
- }
- }
-}
-```
-
-### 适配器方法
-
-添加反向迭代器功能。
-
-```java
-public class ReversibleArrayList extends ArrayList {
- public ReversibleArrayList(Collection c) { super(c); }
- public Iterable reversed() {
- return new Iterable() {
- @Override
- public Iterator iterator() {
- return new Iterator() {
- int current = size() - 1;
- @Override
- public boolean hasNext() { return current > -1; }
- @Override
- public T next() { return get(current--); }
- };
- }
- };
- }
- public static void main(String[] args) {
- ReversibleArrayList ral =
- new ReversibleArrayList<>(Arrays.asList(new String[]{"hello", "world", "yes"}));
- for(String s : ral.reversed()) {
- System.out.print(s + " ");
- }
- }
-}
-```
-
-
-
-## 异常、断言和日志
-
-Java 语言给出了三种处理系统错误的机制:抛出异常、日志和使用断言。
-
-### 异常分类
-
-所有异常都有 Throwable 继承而来。
-
-
-
-
-
-Error 类描述 Java 运行时系统的内部错误和资源耗尽错误,应用程序不应该抛出这种类型的对象。Error是程序无法处理的错误。
-
-RuntimeException由程序错误导致,如果发生了 Runtime Exception,那一定是你的问题,应该修正程序避免这类异常发生。常见的运行时异常有:
-
-```java
-ClassCastException
-IndexOutOfBoundsException
-NullPointerException
-ArrayStoreException
-NumberFormatException
-ArithmeticException
-```
-
-其他 Exception 由具体的环境,如读取的文件不存在、文件为空、sql异常、寻找不存在的 Class 对象等导致的异常。
-
-Java 语言规范将派生于 Error 类或 Runtime Exception 类的异常称为非检查(unchecked)异常;其他所有异常称为检查(checked)异常,编译器将会核查是否为所有的检查异常提供异常处理器。
-
-### 声明异常
-
-一个方法必须声明所有可能抛出的检查异常。任何抛出异常的方法都可能是一个死亡陷阱,如果没有异常处理器捕获这个异常,当前线程就会结束。非检查异常要么不可控,要么应当避免发生。
-
-如果在子类覆盖了父类的方法,在子类方法中可以抛出更特定的异常,或者不抛异常。如果父类方法没有抛出异常,则子类只能在方法内捕获所有检查异常,不允许在子类的 throws 说明符中出现超过超类方法所列出的异常类范围。
-
-### 捕获异常
-
-捕获多个异常可以合并 catch 子句(异常类型不存在子类关系)。
-
-```java
-try {
- //
-} catch (FileNotFindException | UnknowHostException e) {
- e.printStackTrace();
-}
-```
-
-解耦合 try/catch 和 try/finally 语句块,这种设计方式不仅代码清晰,而且还会报告 finally 子句出现的错误。
-
-```java
-InputStream in = ...;
-try {
- try {
- //
- } finally {
- in.close();
- }
-} catch (IOException ex) {
- ex.printStackTrace();
-}
-```
-
-### 带资源的 try 语句
-
-```java
-try (Scanner in = new Scanner(new FileInputStream("e:\data"), "UTF-8");
- PrintWriter out = new PrinterWriter("out.txt")) {
- while (in.hasNext()) {
- System.out.println(in.next().toUpperCase());
- }
-}
-```
-
-Java SE7 引入了带资源的 try 语句,当语句正常退出或者有异常,都会调用`in.close()`方法。如果`in.close()`方法也抛出异常,则原来的异常会重新抛出,而close方法抛出的异常会被抑制,通过调用 getSuppressed 方法可以得到从close 方法抛出并被抑制的异常列表。
-
-### 断言
-
-```java
-int x = -1;
-if(x < 0) {
- throw new IllegalArgumentException("x < 0");
-}
-double y = Math.sqrt(x);
-```
-
-这段检查代码会一直保留在程序,如果程序中含有大量这样的检查,程序运行起来将会很慢。
-
-断言允许在测试期间向代码插入一些检查语句。当代码发布时,这些检查语句将会被自动的移走。
-
-Java 语言引入了关键字 assert,assert 有两种形式:
-
-```java
-assert condition;
-assert condition: expression;//expression用于产生消息字符串
-```
-
-这两种形式都会对条件进行检测,如果结果是 false,则会抛出 AssertError 异常。第二种形式中,表达式将会被传入 AssertError 的构造器,并转化成一个消息字符串。
-
-断言 x 是一个非负数值:
-
-```java
-assert x >= 0;
-assert x >= 0 : x;//将x的值传递给AssertError异常
-```
-
-断言只应该用在测试阶段确定程序内部的错误位置。
-
-#### 启用和禁用断言
-
-默认情况下,断言被禁用,可以在运行程序时用`-eableassertions`或`-ea`选项启用。idea --> run configuration --> vm options 添加 `-ea` 开启断言。
-
-`java -enableassertions MyApp`
-
-启用和禁用断言是类加载器的功能,在启用和禁用断言时不必重新编译程序。当断言被禁用时,类加载器将跳过断言代码,因此不会降低程序的运行速度。
-
-在某个类和包使用断言:`java -ea:MyClass -ea:com.tyson.chapter12 MyApp` MyClass 类和 com.tyson.chapter12 包以及子包的所有类都会启用断言。
-
-使用选项`-dableassertions`或`-da`禁用某个类和包的断言。
-
-### 日志
-
-#### logback
-
-``的三个属性
-
-- scan:默认值为true,配置文件发生更改时会重新加载。
-- scanPeriod:设置监控配置文件的时间间隔,默认单位是毫秒。
-- debug:默认值是false,打印logback内部日志信息,实时查看logback运行状态。
-
-``的子标签
-root是根``,只有level属性,默认为DEBUG。
-logger关联包或者具体的类到appender,可以定义日志类型、级别。
-Appender主要用于指定日志输出的目的地,如控制台、文件、数据库。
-
-```
-
-
-
-
-
-
- %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
-
-
-
-
-
-
-
-
-
-
-
-
-```
-日志级别:ERROR>WARN>INFO>DEBUG>TRACE
-
-Layout用于自定义日志输出格式。
-```
-
-
-
-```
-
-
-
-## 字符串
-
-String 对象是不可变的。String 类中会修改 String 的方法都会创建一个全新的 String 对象。
-
-### 格式化输出
-
-#### printf() 和 format()
-
-```java
-public class SimpleFormat {
- public static void main(String[] args) {
- int x = 5;
- double y = 5.26;
- System.out.format("Row 1: [%d %f]\n", x, y);
- System.out.printf("Row 1: [%d %f]\n", x, y);
- }
-}
-/*output
-Row 1: [5 5.260000]
-Row 1: [5 5.260000]
- */
-```
-
-#### Formatter
-
-```java
-public class FormatterDemo {
- public static void main(String[] args) {
- Formatter f = new Formatter(System.out);
- String item = "peas";
- int quatity = 3;
- float price = 5.2f;
- //-表示右对齐
- f.format("%-10s %5s %10s\n", "item", "quatity", "price");
- //-表示右对齐,%10.10s指宽度为10,最大输出字符为10
- f.format("%-10.10s %5d %10.2f\n", item, quatity, price);
- //String.format()
- System.out.println("------string.format()------");
- System.out.println(String.format("%-10.10s %5d %10.2f", item, quatity, price));
- }
-}
-/*
-item quatity price
-peas 3 5.20
-------string.format()------
-peas 3 5.20
- */
-```
-
-String.format()接受与Formatter.format()一样的参数,它是通过创建一个 Formatter 对象实现的。
-
-### 正则表达式
-
-#### 基础
-
-可能有一个负号在最前面:`-?`
-
-`\\`在 Java 表示插入一个正则表达式的反斜线
-
-表示一个数字:`\\d`
-
-插入普通的反斜线:`\\\\`
-
-`+`表示一个或多个之前的表达式
-
-可能有一个负号,后面跟着一个或多个数字:`-?\\d+`
-
-```java
-public class IntegerMatch {
- public static void main(String[] args) {
- System.out.println("-89".matches("-?\\d+"));
- System.out.println("+98".matches("-?\\d+"));
- //竖直线|代表或操作;+是特殊符号,需转义;括号可以将表达式分组
- System.out.println("+789".matches("(-|\\+)?\\d+"));
- }
-}
-/*output
-true
-false
-true
- */
-```
-
-String 自带一个正则表达式工具 split()方法,它可以将字符串从正则表达式匹配的地方切开。特殊字符如`.`和`|`需要用反斜杠转义,即`\\.`和`\\|`。
-
-```java
-public class Splitting {
- public static String motto = "All in all, we all have a dream.";
- public static void split(String regex) {
- System.out.println(Arrays.toString(motto.split(regex)));
- }
- public static void main(String[] args) {
- //.需要转义
- split("\\.");
- split(" ");
- //\\w+表示一个或多个非单词字符
- split("\\W+");
- //表示字母n后面跟着一个或多个非单词字符
- split("n\\W+");
- }
-}
-/*output
-[All in all, we all have a dream, Thanks]
-[All, in, all,, we, all, have, a, dream., Thanks.]
-[All, in, all, we, all, have, a, dream, Thanks]
-[All i, all, we all have a dream. Thanks.]
- */
-```
-
-替换功能。
-
-```java
-public class Replacing {
- public static void main(String[] args) {
- //小写的w表示字母
- System.out.println(Splitting.motto.replaceFirst("w\\w+", "they"));
- System.out.println(Splitting.motto.replaceAll("all|dream", "apple"));
- }
-}
-/*output
-All in all, they all have a dream.
-All in apple, we apple have a apple.
- */
-```
-
-#### 创建正则表达式
-
-字符类表达式,JDK 文档中 java.util.regex.Pattern 页面有完整的表达式。
-
-[jdk8文档](https://docs.oracle.com/javase/8/docs/api/index.html)
-
-[菜鸟教程正则表达式语法](http://www.runoob.com/java/java-regular-expressions.html)
-
-**常用的字符类表达式**
-
-| 字符 | 描述 |
-| -------- | ------------------------------------------------------------ |
-| . | 匹配除"\r\n"之外的任何单个字符 |
-| + | 表示一个或多个之前的表达式,如\\\d+表示一个或多个数字 |
-| * | 零次或多次匹配前面的字符或子表达式。例如,zo* 匹配"z"和"zoo"。* 等效于 {0,} |
-| ? | 零次或一次匹配前面的字符或子表达式。如"do(es)?"匹配"do"或"does"中的"do"。? 等效于 {0,1} |
-| [abc] | 包含a/b/c的任何字符(和a\|b\|c作用相同) |
-| [^abc] | 除了a/b/c外的任何字符 |
-| [a-zA-z] | a-z/A-Z的任何字符 |
-| \s | 空白符(空格、tab、换行、换页和回车) |
-| \S | 非空白符(\[^\s]) |
-| \d | 数字0-9 |
-| \D | 非数字(\[^0-9]) |
-| \w | 字符[a-zA-Z0-9] |
-| \W | 非字符 |
-
-**边界匹配符**
-
-| 符号 | 描述 |
-| ---- | ---------- |
-| ^ | 一行的起始 |
-| $ | 一行的结束 |
-| \b | 词的边界 |
-| \B | 非词的边界 |
-
-**量词**
-
-
-
-Rudolph.java
-
-```java
-public class Rudolph {
- public static void main(String[] args) {
- for(String pattern : new String[]{ "Rudolph",
- "[rR]udolph", "[rR][aeiou][a-z]ol.*", "R.*" })
- System.out.println("Rudolph".matches(pattern));
- }
-} /* Output:
-true
-true
-true
-true
-*///:~
-```
-
-#### Pattern 和 Matcher
-
-Pattern 和 Matcher 功能较 String 更为强大。Pattern.compile(String regex) 用来编译正则表达式,生成一个。
-
-**find()**
-
-`Matcher.find()`可用来在 CharSequence 中查找多个匹配。find()可以像迭代器一样前向遍历输入的字符串。重载的 find(int index)能够接收一个整数参数,作为搜索起点。
-
-```java
-public class Finding {
- //划分为单词
- public static Pattern WORD_PATTERN = Pattern.compile("\\w+");
- public static void main(String[] args) {
- Matcher m = WORD_PATTERN.matcher("Everyone is born equally");
- //find()可用来查找多个匹配
- while(m.find()) {
- System.out.print(m.group() + " ");
- }
- System.out.println();
- int i = 0;
- //指定搜索起点
- while(m.find(i++)) {
- System.out.print(m.group() + " ");
- }
- }
-}
-/*output
-Everyone is born equally
-Everyone veryone eryone ryone yone one ne e is is s born born orn rn n equally equally qually ually ally lly ly y
- */
-```
-
-**组**
-
-组使用括号划分的正则表达式,如`A(B(C))D`,组0是ABCD,组1是BC,组2是C。Matcher 对象提供了一系列方法,用以获取与组相关的信息:
-
-`public int groupCount()`返回该匹配器的分组数目(**不包含第0组**),如上面例子分组数目为2组(不包含第0组);
-
-`public String group()`返回前一次匹配操作(如find())的第0组(整个匹配);
-
-`public String group(int i)`返回前一次匹配操作指定的组号,如果没有匹配到输入字符串的任何部分,将会返回null。
-
-```java
-public class Groups {
- public static final String POEM =
- "Twas brillig, and the slithy toves\n" +
- "Did gyre and gimble in the wabe.\n" +
- "All mimsy were the borogoves,\n" +
- "And the mome raths outgrabe.\n\n" +
- "Beware the Jabberwock, my son,\n" +
- "The jaws that bite, the claws that catch.\n" +
- "Beware the Jubjub bird, and shun\n" +
- "The frumious Bandersnatch.\n" +
- "end\n"; //匹配失败
- //捕获每行最后的三个词
- public static Pattern LAST_THREE_WORD_PATTERN = Pattern.compile("(?m)(\\S+)\\s+((\\S+)\\s+(\\S+))$");
-
- public static void main(String[] args) {
- Matcher m = LAST_THREE_WORD_PATTERN.matcher(POEM);
- while(m.find()) {
- //有4个分组,不包括分组0,分组0是整个匹配
- for(int i = 0; i <= m.groupCount(); i++) {
- System.out.print("[" + m.group(i) + "]");
- }
- System.out.println();
- }
- }
-}
-/* Output:
-[the slithy toves][the][slithy toves][slithy][toves]
-[in the wabe.][in][the wabe.][the][wabe.]
-[were the borogoves,][were][the borogoves,][the][borogoves,]
-[mome raths outgrabe.][mome][raths outgrabe.][raths][outgrabe.]
-[Jabberwock, my son,][Jabberwock,][my son,][my][son,]
-[claws that catch.][claws][that catch.][that][catch.]
-[bird, and shun][bird,][and shun][and][shun]
-[The frumious Bandersnatch.][The][frumious Bandersnatch.][frumious][Bandersnatch.]
-*/
-```
-
-默认情况下,^和$仅匹配输入的完整字符串的开始和结束。?m表示多行模式下,^和$分别匹配一行的开始和结束。
-
-**start() 与 end()**
-
-在匹配操作成功之后,start()返回匹配的起始位置的索引,end()返回所匹配字符的最后字符的索引加一的值。
-
-**Pattern 标记**
-
-Pattern 类的 compile()方法还有一个版本,可以接收一个标记参数,以调整匹配的行为:
-
-`Pattern Pattern.compile(String regex, int flag)`
-
-flag 来自以下 Pattern 类中的常量:
-
-
-
-可以通过'|'(或)操作符组合多个标记的功能:
-
-```java
-public class ReFlags {
- public static Pattern WORD_PATTERN = Pattern.compile("^java",
- Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
- public static void main(String[] args) {
- Matcher m = WORD_PATTERN.matcher("java has regex\nJava has regex\n" +
- "JAVA has pretty good regular expressions\n" +
- "Regular expressions are in Java");
- while(m.find()) {
- System.out.println(m.group());
- }
- }
-}
-/* Output:
-java
-Java
-JAVA
-*/
-```
-
-**split()**
-
-```java
-public class SplitDemo {
- public static void main(String[] args) {
- String input = "This!!unusual use!!of exclamation!!points";
- print(Arrays.toString(Pattern.compile("!!").split(input)));
- // 限制分割字符串的数量
- print(Arrays.toString(Pattern.compile("!!").split(input, 3)));
- }
-} /* Output:
-[This, unusual use, of exclamation, points]
-[This, unusual use, of exclamation!!points]
-*/
-```
-
-**替换操作**
-
-```java
-public class TheReplacements {
- public static void main(String[] args) throws Exception {
- String s = TextFile.read("src/com/tyson/chapter13/string/regex/TheReplacements.java");
- // 匹配上面的注释
- Matcher mInput = Pattern.compile("/\\*!(.*)!\\*/", Pattern.DOTALL).matcher(s);
- if (mInput.find()) {
- s = mInput.group(1); // 捕捉正则表达式括号,组是括号划分的正则表达式
- }
-
- // Replace two or more spaces with a single space:
- s = s.replaceAll(" {2,}", " ");
- // Replace one or more spaces at the beginning of each
- // line with no spaces. Must enable MULTILINE mode:
- s = s.replaceAll("(?m)^ +", "");
- print(s);
- s = s.replaceFirst("[aeiou]", "(VOWEL1)");
- StringBuffer sbuf = new StringBuffer();
- Pattern p = Pattern.compile("[aeiou]");
- Matcher m = p.matcher(s);
- // Process the find information as you
- // perform the replacements:
- while (m.find())
- //将找到的元音字母转化为大写字母
- m.appendReplacement(sbuf, m.group().toUpperCase());
- // Put in the remainder of the text:
- m.appendTail(sbuf);
- print(sbuf);
- }
-} /* Output:
-Here's a block of text to use as input to
-the regular expression matcher. Note that we'll
-first extract the block of text by looking for
-the special delimiters, then process the
-extracted block.
-H(VOWEL1)rE's A blOck Of tExt tO UsE As InpUt tO
-thE rEgUlAr ExprEssIOn mAtchEr. NOtE thAt wE'll
-fIrst ExtrAct thE blOck Of tExt by lOOkIng fOr
-thE spEcIAl dElImItErs, thEn prOcEss thE
-ExtrActEd blOck.
-*/
-```
-
-**reset()**
-
-通过 reset() 方法,可以将 Matcher 对象应用于一个新的字符序列。
-
-```java
-public class Resetting {
- public static void main(String[] args) throws Exception {
- Matcher m = Pattern.compile("[frb][aiu][gx]").matcher("fix the rug with bags");
- while (m.find())
- System.out.print(m.group() + " ");
- System.out.println();
- m.reset("fix the rig with rags");
- while (m.find())
- System.out.print(m.group() + " ");
- }
-} /* Output:
-fix rug bag
-fix rig rag
-*/
-```
-
-### 扫描输入
-
-Java SE5新增了 Scanner 类,Scanner 的构造器可以接受任何类型的输入对象,包括 File 对象、InputStream、String 或者 Readable 对象。Scanner 所有的输入、分词以及翻译都隐藏在不用类型的 next 方法中。普通的 next()方法返回下一个 String 。
-
-#### Scanner 定界符
-
-默认情况下,Scanner 根据空白字符对输入进行分词,我们可以用正则表达式指定自己所需的定界符。
-
-```java
-public class ScannerDelimiter {
- public static void main(String[] args) {
- Scanner sc = new Scanner("12, 42, 85, 23");
- //使用逗号包括逗号前后的空白字符作为定界符
- sc.useDelimiter("\\s*,\\s*");
- while(sc.hasNextInt()) {
- System.out.println(sc.nextInt());
- }
- }
-}
-```
-
-#### 用正则表达式扫描
-
-```java
-public class ThreatAnalyzer {
- static String threatData =
- "58.27.82.161@02/10/2005\n" +
- "204.45.234.40@02/11/2005\n" +
- "58.27.82.161@02/11/2005\n" +
- "58.27.82.161@02/12/2005\n" +
- "58.27.82.161@02/12/2005\n" +
- "[Next log section with different data format]";
-
- public static void main(String[] args) {
- Scanner sc = new Scanner(threatData);
- String pattern = "(\\d+[.]\\d+[.]\\d+[.]\\d+)@(\\d{2}/\\d{2}/\\d{4})";
- while(sc.hasNext(pattern)) {
- sc.next(pattern);
- MatchResult match = sc.match();
- String ip = match.group(1);
- String date = match.group(2);
- System.out.format("Thread on %s from %s\n", ip, date);
- }
- }
-}
-/*output
-Thread on 58.27.82.161 from 02/10/2005
-Thread on 204.45.234.40 from 02/11/2005
-Thread on 58.27.82.161 from 02/11/2005
-Thread on 58.27.82.161 from 02/12/2005
-Thread on 58.27.82.161 from 02/12/2005
- */
-```
-
-注意:配合正则表达式进行扫描时,正则表达式不要含有定界符(Scanner 默认定界符是空白符,根据空白符对输入进行分词)。
-
-
-
-## 类型信息
-
-通过运行时类型信息可以在程序运行时发现和使用类型信息。
-
-`Class.forName("Gum")`类 Gum 没有被加载就加载它。
-
-### 反射
-
-类方法提取:
-
-```java
-public class typeinfo {
- //匹配类似包名的字符串 com.tyson.chapter
- public static Pattern p = Pattern.compile("\\w+\\.");
- public static void main(String[] args) {
- try {
- Class> c = Class.forName("com.tyson.chapter10.innerclass.LocalInnerClass");
- Method[] methods = c.getMethods();
- for(Method m : methods) {
- //去掉包名
- System.out.println(p.matcher(m.toString()).replaceAll(""));
- }
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- }
-}
-/*output
-public static void main(String[])
-public final void wait() throws InterruptedException
-public final void wait(long,int) throws InterruptedException
-public final native void wait(long) throws InterruptedException
-public boolean equals(Object)
-public String toString()
-public native int hashCode()
-public final native Class getClass()
-public final native void notify()
-public final native void notifyAll()
- */
-```
-
-
-
-## 泛型
-
-泛型实现了参数化类型的概念,使代码可以应用于多种类型。使用泛型编写的程序比使用 Object 变量再进行强制类型转化的程序具有更好的安全性和可读性。
-
-### 类型参数的好处
-
-Java SE5泛型出现之前,ArrayList 是通过维护一个 Object 数组实现的。
-
-```java
-public class MyArrayList {
- private Object[] elementData;
- private int index = 0;
- public MyArrayList() { elementData = new Object[8]; }
- public Object get(int i) { return elementData[i]; }
- public void add(Object o) { elementData[index++] = o; }
-
- public static void main(String[] args) {
- MyArrayList list = new MyArrayList();
- list.add("tyson");
- System.out.println((String)list.get(0));
- list.add(new Cat());
- System.out.println((String)list.get(1));
- }
-}
-/*output
-tyson
-Exception in thread "main" java.lang.ClassCastException: typeinfo.pets.Cat cannot be cast to java.lang.String
- at com.tyson.chapter15.generics.MyArrayList.main(MyArrayList.java:29)
- */
-```
-
-当获取值的时候需要进行强制类型转化,并且 add 操作可以放进任何类型的对象,编译和运行都不报错,当获取值进行强制类型转换时,可能会抛异常。
-
-### 泛型类
-
-```java
-public class Holder {
- private T a;
- public Holder(T a) { this.a = a; }
- public void set(T a) { this.a = a; }
- public T get() { return a; }
-
- public static void main(String[] args) {
- Holder h = new Holder<>("Tyson");
- System.out.println(h.get());
- }
-}
-//output Tyson
-```
-
-### 泛型接口
-
-泛型也可以用于接口。
-
-```java
-//public interface Generator { T next(); }
-
-public class Fibonacci implements Generator {
- private int count = 0;
- @Override
- public Integer next() {
- return fib(count++);
- }
- public int fib(int n) {
- if(n < 2) { return 1; }
- return fib(n - 2) + fib(n - 1);
- }
-
- public static void main(String[] args) {
- Fibonacci fib = new Fibonacci();
- for(int i = 0; i < 10; i++) {
- System.out.print(fib.next() + " ");
- }
- }
-}
-//output:1 1 2 3 5 8 13 21 34 55
-```
-
-### 泛型方法
-
-如果使用泛型方法可以取代将整个类泛型化,那么就应该只使用泛型方法。对于一个 static 方法而言,无法访问泛型类的类型参数,所以如果 static 方法需要使用泛化能力,就必须使其成为泛型方法。
-
-定义泛型方法,只需将泛型参数列表放在返回值之前。
-
-```java
-public class GenericMethod {
- public void f(T t) { System.out.println(t.getClass().getName()); }
-
- public static void main(String[] args) {
- GenericMethod gm = new GenericMethod();
- //可省略,编译器自己可推断出
- gm.f("");
- gm.f(1);
- gm.f(1.0);
- gm.f(1.0f);
- gm.f(gm);
- }
-}
-/*output
-java.lang.String
-java.lang.Integer
-java.lang.Double
-java.lang.Float
-com.tyson.chapter15.generics.GenericMethod
- */
-```
-
-GenericMethod 并不是参数化的,只有 f() 拥有参数类型。
-
-#### 可变参数和泛型方法
-
-向参数个数可变的方法传递一个泛型类型的实例。
-
-```java
-public class GenericVarArgs {
- public static List makeList(T... args) {
- List result = new ArrayList<>();
- for(T item : args) {
- result.add(item);
- }
- return result;
- }
-
- public static void main(String[] args) {
- List l = makeList("a", "b");
- System.out.println(l);
- }
-}
-//output:[a, b]
-```
-
-### 匿名内部类
-
-```java
-public class Customer {
- public Customer() {}
- public static Generator generator() {
- return new Generator() {
- @Override
- public Customer next() {
- return new Customer();
- }
- };
- }
-
- public static void main(String[] args) {
- System.out.println(Customer.generator().next());
- }
-}
-```
-
-### 泛型擦除
-
-```java
-public class ErasedTypeEquvalence {
- public static void main(String[] args) {
- Class c1 = new ArrayList().getClass();
- Class c2 = new ArrayList().getClass();
- System.out.println(c1 == c2);
- }
-}
-//output:true
-```
-
-`List`的类型变量没有显式的限定,擦除类型后变成原始的 List,故`List`和`List`是相同的类型。
-
-```java
-class Manipulator {
- private T obj;
- public Manipulator(T t) { obj = t; }
- public void manipulate() { obj.f(); }
-}
-```
-
-Manipulator 类有显式限定 HasF,故擦除类型后变成:
-
-```java
-class Manipulato {
- private HasF obj;
- public Manipulator(HasF t) { obj = HasF; }
- public void manipulate() { obj.f(); }
-}
-```
-
-### 类型变量的限定
-
-给定泛型类的边界,以此告知编译器只能接受遵循这个边界的类型。
-
-```java
-//HasF.java
-public class HasF {
- public void f() {
- System.out.println("Has.f()");
- }
-}
-
-//Manipulation.java
-class Manipulator {
- private T obj;
- public Manipulator(T t) { obj = t; }
- public void manipulate() { obj.f(); }
-}
-
-public class Manipulation {
- public static void main(String[] args) {
- HasF hf = new HasF();
- Manipulator manipulator = new Manipulator<>(hf);
- manipulator.manipulate();
- }
-}
-//output: Has.f()
-```
-
-### 擦除的问题
-
-```java
-class GenericBase {
- private T element;
- public void set(T arg) { element = arg; }
- public T get() { return element; }
-
- public static void main(String[] args) {
-
- }
-}
-
-class Derived extends GenericBase {}
-
-public class ErasureAndInheritance {
- public static void main(String[] args) {
- Derived d = new Derived();
- d.set(new Object());
- System.out.println(d.get());
- }
-}
-//output: java.lang.Object@6d6f6e28
-```
-
-Derived 继承自 GenericBase,但是没有任何泛型参数,而编译器不会发出任何警告。
-
-### 边界
-
-在泛型的参数类型上设置限制条件,可以强制规定泛型可以应用的类型,从而可以按照自己的边界类型去调用方法。如果将泛型参数限制为某个类型子集,那么就可以用这些类型子集来调用方法。
-
-```java
-interface HasColor { Color getColor(); }
-
-class Colored {
- T item;
- Colored(T item) { this.item = item; }
- T getItem() { return item; }
- Color color() { return item.getColor(); }
-}
-
-class Dimension { public int x; }
-
-//编译错误,应该类在前,接口在后
-//class ColoredDimension {}
-
-class ColoredDimension {
- T item;
- ColoredDimension(T item) { this.item = item; }
- T getItem() { return item; }
- Color color() { return item.getColor(); }
- int getX() { return item.x; }
-}
-
-interface Weight { int weight(); }
-
-//边界继承只能有一个类,可以有多个接口
-class Solid {
- T item;
- Solid(T item) { this.item = item; }
- T getItem() { return item; }
- Color color() { return item.getColor(); }
- int getX() { return item.x; }
- int weight() { return item.weight(); }
-}
-
-class Bounded extends Dimension implements HasColor, Weight {
-
- @Override
- public Color getColor() { return null; }
-
- @Override
- public int weight() { return 0; }
-}
-
-public class BasicBounds {
- public static void main(String[] args) {
- Solid solid = new Solid<>(new Bounded());
- solid.color();
- solid.getX();
- solid.weight();
- }
-}
-```
-
-通过在继承的每个层次上添加边界限制,消除 BasicBounds.java 代码冗余。
-
-```java
-class HoldItem {
- T item;
- HoldItem(T item) { this.item = item; }
- T getItem() { return item; }
-}
-
-class Colored2 extends HoldItem {
- Colored2(T item) { super(item); }
- Color color() { return item.getColor(); }
-}
-
-class ColorDimension2 extends Colored2 {
- ColorDimension2(T item) { super(item); }
- int getX() { return item.x; }
-}
-
-class Solid2 extends ColorDimension2 {
- Solid2(T item) { super(item); }
- int weight() { return item.weight(); }
-}
-
-public class InheritBounds {
- public static void main(String[] args) {
- Solid2 solid2 = new Solid2<>(new Bounded());
- solid2.color();
- solid2.getX();
- solid2.weight();
- }
-}
-```
-
-### 通配符
-
-参考自:[泛型超详细解读](https://blog.csdn.net/jeffleo/article/details/52250948) | [通配符的上界通配符和下界通配符](https://blog.csdn.net/hello_worldee/article/details/77934244)
-
-#### 上界通配符
-
-上界< ? extends Class>
-
-```java
-class Fruit { }
-class Apple extends Fruit { }
-class Orange extends Fruit { }
-
-public class UpperBound {
- public static void main(String[] args) {
- List extends Fruit> list = new ArrayList<>();
- //编译错误
- //list.add(new Apple());
- //list.add(new Orange());
- //list.add(new Fruit());
- list.add(null);
-
- list.contains(new Apple());
- list.indexOf(new Apple());
-
- System.out.println(list.get(0));
- }
-}
-```
-
-可见,指定了下边界,却不能add任何类型,甚至Object都不行,除了null,因为null代表任何类型。List< ? extends Fruit>可以解读为,“具有任何从Fruit继承的类型”,但实际上,它意味着,它没有指定具体类型。对于编译器来说,当你指定了一个List< ? extends Fruit>,add的参数也变成了“? extends Fruit”。因此编译器并不能了解这里到底需要哪种Fruit的子类型,因此他不会接受任何类型的Fruit。
-
-然而,contain和indexof却能执行,这是因为,这两个方法的参数是Object,不涉及任何的通配符,所以编译器允许它调用。
-
-list.get(0)能够执行是因为,当item在此list存在时,编译器能够确定他是Apple的子类,所以能够安全获得。
-
-#### 下界通配符
-
-< ? super Class>表示,指定类的基类。
-
-```java
-class Apple1 extends Apple {}
-
-public class LowerBound{
-
- public static void main(String[] args) {
- List super Apple> list = new ArrayList<>();
- list.add(new Apple());
- list.add(new Apple1());
- //编译错误
- //list.add(new Fruit());
-
- Object apple = list.get(0);
- System.out.println(apple);
- }
-}
-//output: com.tyson.chapter15.generics.Apple@6d6f6e28
-```
-
-对于super,get返回的是Object,因为编译器不能确定列表中的是Apple的哪个子类,所以只能返回Object。
-
-List < ? super Apple>, 代表容器内存放的是Apple的所有父类,所以有多态和上转型,这个容器时可以接受所有Apple父类的子类的(多态的定义:父类可以接受子类型对象)。Apple和Apple1都直接或间接继承了Fruit,所以Apple和Apple1是能够加入List < ? super Apple>这个容器的。
-
-list.add(new Fruit())不能添加,是因为容器内存放的是Apple的**所有**父类,正是因为能存放所有,Apple的父类可能有Fruit1, Fruit2, Fruit3 , 所以编译器根本不能识别你要存放哪个Apple的父类,所以不能添加Fruit,因为这不能保证类型安全的原则。这从最后的Object apple = list.get(0)可以看出。
-
-
-
-## 数组
-
-数组的 length 是数组的大小,不是实际保存的元素个数。
-
-如果不显式初始化,基本类型数组会被自动初始化成初值,对象数组则会被初始化成 null。
-
-### 复制数组
-
-Java 标准类库提供有 static 方法 System.arraycopy(),用来复制数组比用 for 循环复制要快的多。System.arraycopy() 针对所有类型做了重载。
-
-```java
-public class CopyArrays {
- public static void main(String[] args) {
- int[] src = new int[7];
- int[] dest = new int[10];
- Arrays.fill(src, 47);
- Arrays.fill(dest, 99);
- System.out.println("src = " + Arrays.toString(src));
- System.out.println("dest = " + Arrays.toString(dest));
- System.arraycopy(src, 0, dest, 0, src.length);
- System.out.println("dest = " + Arrays.toString(dest));
- }
-}
-/*output
-src = [47, 47, 47, 47, 47, 47, 47]
-dest = [99, 99, 99, 99, 99, 99, 99, 99, 99, 99]
-dest = [47, 47, 47, 47, 47, 47, 47, 99, 99, 99]
- */
-```
-
-基本类型数组和对象数组都可以复制。复制对象只是复制对象引用,即浅复制。System.arraycopy() 不会执行自动包装和自动拆包,两个数组需要具有相同的数据类型。
-
-### Arrays 工具
-
-#### 数组拷贝
-
-```java
-public class ArrayCopy {
- //Arrays 类方法的使用
- public static void main(String[] args) {
- int[] arr = {12, 45, 2, 56, 20};
- int[] arrCopy = Arrays.copyOf(arr, arr.length);
- System.out.println("arr: " + Arrays.toString(arr));
- System.out.println("arrCopy: " + Arrays.toString(arrCopy));
- //增加数组大小
- arrCopy = Arrays.copyOf(arr, 2 * arr.length);
- System.out.println("arr: " + Arrays.toString(arr));
- System.out.println("arrCopy: " + Arrays.toString(arrCopy));
- }
-}
-/*output
-arr: [12, 45, 2, 56, 20]
-arrCopy: [12, 45, 2, 56, 20]
-arr: [12, 45, 2, 56, 20]
-arrCopy: [12, 45, 2, 56, 20, 0, 0, 0, 0, 0]
- */
-```
-
-`Array.copyOf()`底层是通过`System.arraycopy()`使用的。
-
-```java
-public static T[] copyOf(U[] original, int newLength, Class extends T[]> newType) {
- @SuppressWarnings("unchecked")
- T[] copy = ((Object)newType == (Object)Object[].class)
- ? (T[]) new Object[newLength]
- : (T[]) Array.newInstance(newType.getComponentType(), newLength);
- System.arraycopy(original, 0, copy, 0,
- Math.min(original.length, newLength));
- return copy;
-}
-```
-
-#### 数组的比较
-
-Arrays 类提供了重载后的 equals() 方法,用来比较整个数组。
-
-```java
-public class ComparingArrays {
- public static void main(String[] args) {
- int[] a1 = new int[10];
- int[] a2 = new int[10];
- Arrays.fill(a1, 47);
- Arrays.fill(a2, 47);
- System.out.println(Arrays.equals(a1, a2));
- a2[1] = 0;
- System.out.println(Arrays.equals(a1, a2));
- String[] s1 = new String[2];
- Arrays.fill(s1, "hello");
- String[] s2 = { new String("hello"), new String("hello")};
- System.out.println(Arrays.equals(s1, s2));
- }
-}
-/*output
-true
-false
-true
- */
-```
-
-#### 数组元素的比较
-
-1. 实现`java.lang.Comparable`接口,使得类本身具有比较比较能力。
-
- ```java
- public class CompType implements Comparable {
- int i;
- int j;
- public CompType(int i, int j) {
- this.i = i;
- this.j = j;
- }
-
- @Override
- public String toString() {
- return "CompType{" +
- "i=" + i +
- ", j=" + j +
- '}';
- }
-
- @Override
- public int compareTo(CompType ct) {
- return i < ct.i ? -1 : (i == ct.i ? 0 : 1);
- }
-
- private static Random r = new Random(47);
- public static Generator generator() {
- return new Generator() {
- @Override
- public CompType next() {
- return new CompType(r.nextInt(100), r.nextInt(100));
- }
- };
- }
-
- public static void main(String[] args) {
- CompType[] arr = Generated.array(new CompType[4], generator());
- System.out.println("before sorting: " + Arrays.toString(arr));
- Arrays.sort(arr);
- System.out.println("after sorting: " + Arrays.toString(arr));
- //反转自然排列顺序
- Arrays.sort(arr, Collections.reverseOrder());
- System.out.println("reverse order: " + Arrays.toString(arr));
- }
- }
- /*output
- before sorting: [CompType{i=58, j=55}, CompType{i=93, j=61}, CompType{i=61, j=29}, CompType{i=68, j=0}]
- after sorting: [CompType{i=58, j=55}, CompType{i=61, j=29}, CompType{i=68, j=0}, CompType{i=93, j=61}]
- reverse order: [CompType{i=93, j=61}, CompType{i=68, j=0}, CompType{i=61, j=29}, CompType{i=58, j=55}]
- */
- ```
-
-2. 编写自己的比较器Comparator。
-
- ```java
- class CompTypeComparator implements Comparator {
-
- @Override
- public int compare(CompType o1, CompType o2) {
- return o1.j > o2.j ? 1 : (o1.j == o2.j ? 0 : -1);
- }
- }
-
- public class ComparatorTest {
- public static void main(String[] args) {
- CompType[] arr = Generated.array(new CompType[4], CompType.generator());
- System.out.println("before sorting: " + Arrays.toString(arr));
- Arrays.sort(arr, new CompTypeComparator());
- System.out.println("after sorting: " + Arrays.toString(arr));
- }
- }
- /*output
- before sorting: [CompType{i=58, j=55}, CompType{i=93, j=61}, CompType{i=61, j=29}, CompType{i=68, j=0}]
- after sorting: [CompType{i=68, j=0}, CompType{i=61, j=29}, CompType{i=58, j=55}, CompType{i=93, j=61}]
- */
- ```
-
-#### 数组排序
-
-使用内置的排序方法,就可以对基本类型数组进行排序。也可以对对象数组进行排序,只要该对象实现了 Comparable 接口或者具有相关联的 Comparator。
-
-```java
-public class StringSorting {
- public static void main(String[] args) {
- String[] strs = Generated.array(new String[5], new RandomGenerator.String(5));
- System.out.println("before sorting: " + Arrays.toString(strs));
- Arrays.sort(strs);
- System.out.println("after sorting: " + Arrays.toString(strs));
- Arrays.sort(strs, Collections.reverseOrder());
- System.out.println("reverse order: " + Arrays.toString(strs));
- Arrays.sort(strs, String.CASE_INSENSITIVE_ORDER);
- System.out.println("case insensive: " + Arrays.toString(strs));
- }
-}
-/*output
-before sorting: [YNzbr, nyGcF, OWZnT, cQrGs, eGZMm]
-after sorting: [OWZnT, YNzbr, cQrGs, eGZMm, nyGcF]
-reverse order: [nyGcF, eGZMm, cQrGs, YNzbr, OWZnT]
-case insensive: [cQrGs, eGZMm, nyGcF, OWZnT, YNzbr]
- */
-```
-
-String 排序算法依据词典编排顺序排序。大写字母在前,小写字母在后。Java 标准类库中的排序算法针对正排序做了优化。针对基础类型设计了快速排序,针对对象设计了稳定归并排序。
-
-#### 排序数组查找
-
-若数组已经排序,既可以使用`Arrays.binarySearch()`执行快速查找。
-
-```java
-public class ArraySearching {
- public static void main(String[] args) {
- Generator gen = new RandomGenerator.Integer(1000);
- int[] arr = ConvertTo.primitive(Generated.array(new Integer[5], gen));
- Arrays.sort(arr);
- System.out.println("after sorting: " + Arrays.toString(arr));
- while(true) {
- int r = gen.next();
- int location = Arrays.binarySearch(arr, r);
- if(location >= 0) {
- System.out.println("location: " + location + "-->" + arr[location]);
- break;
- }
- }
- }
-}
-/*output
-after sorting: [258, 555, 693, 861, 961]
-location: 3-->861
- */
-```
-
-找到目标,`Arrays.binarySearch()`返回值大于或等于0。否则返回负值:`-(插入点)- 1`;
-
-对象数组使用`Arrays.binarySearch()`需要提供Comparator。
-
-```java
-public class StringSearch {
- public static void main(String[] args) {
- String[] strs = Generated.array(new String[6], new RandomGenerator.String(5));
- Arrays.sort(strs, String.CASE_INSENSITIVE_ORDER);
- System.out.println(Arrays.toString(strs));
- int index = Arrays.binarySearch(strs, strs[3], String.CASE_INSENSITIVE_ORDER);
- System.out.println("index: " + index + "-->" + strs[index]);
- }
-}
-/*output
-[cQrGs, eGZMm, JMRoE, nyGcF, OWZnT, YNzbr]
-index: 3-->nyGcF
- */
-```
-
-
-
-## 容器深入研究
-
-### 填充容器
-
-```java
-public class FillingList {
- public static void main(String[] args) {
- List list = new ArrayList<>(Collections.nCopies(3, "Tyson"));
- System.out.println(list);
- Collections.fill(list, "Sophia");
- System.out.println(list);
- }
-}
-/*output
-[Tyson, Tyson, Tyson]
-[Sophia, Sophia, Sophia]
- */
-```
-
-### SortedSet
-
-按对象的比较函数对元素进行排序。用 TreeSet 迭代通常比用 HashSet 要快。
-
-```java
-public class SortedSetDemo {
- public static void main(String[] args) {
- SortedSet sortedSet = new TreeSet<>();
- Collections.addAll(sortedSet, "one two three four".split(" "));
- System.out.println(sortedSet);
- System.out.println("first: " + sortedSet.first());
- System.out.println("last: " + sortedSet.last());
- Iterator itr = sortedSet.iterator();
- while (itr.hasNext()) {
- System.out.print(itr.next() + " ");
- }
- System.out.println();
- //from包含-to不包含
- System.out.println("subSet: " + sortedSet.subSet("one", "three"));
- //小于three的元素
- System.out.println(sortedSet.headSet("three"));
- //大于等于one的元素
- System.out.println(sortedSet.tailSet("one"));
- }
-}
-/*output
-[four, one, three, two]
-first: four
-last: two
-four one three two
-subSet: [one]
-[four, one]
-[one, three, two]
- */
-```
-
-### 队列
-
-除了并发应用,Queue Java SE5中仅有两个实现 LinkedList 和 PriorityQueue,它们的差异在于排序行为不在性能。
-
-#### 优先级队列
-
-```java
-public class ToDoList extends PriorityQueue {
- static class ToDoItem implements Comparable {
- private char primary;
- private int secondary;
- private String item;
-
- public ToDoItem(String item, char pri, int sec) {
- this.item = item;
- primary = pri;
- secondary = sec;
- }
- @Override
- public int compareTo(ToDoItem o) {
- if(primary > o.primary) {
- return 1;
- }
- if(primary == o.primary) {
- if(secondary > o.secondary) {
- return 1;
- } else if (secondary == o.secondary) {
- return 0;
- } else {
- return -1;
- }
- }
- return -1;
- }
- @Override
- public String toString() {
- return "ToDoItem{" +
- "primary=" + primary +
- ", secondary=" + secondary +
- ", item='" + item + '\'' +
- '}';
- }
- }
- public void add(String item, char pri, int sec) {
- super.add(new ToDoItem(item, pri, sec));
- }
-
- public static void main(String[] args) {
- ToDoList toDoList = new ToDoList();
- toDoList.add("homework", 'b', 3);
- toDoList.add("feed dog", 'a', 4);
- toDoList.add("lunch", 'a', 1);
- while (!toDoList.isEmpty()) {
- System.out.println(toDoList.remove());
- }
- }
-}
-/*output
-ToDoItem{primary=a, secondary=1, item='lunch'}
-ToDoItem{primary=a, secondary=4, item='feed dog'}
-ToDoItem{primary=b, secondary=3, item='homework'}
- */
-```
-
-#### 双向队列
-
-可以在队列任何一端添加或移除元素。Java 标准库中没有任何显式的用于双向队列的接口。
-
-```java
-public class Deque {
- private LinkedList deque = new LinkedList<>();
- public void addFirst(T t) { deque.add(t); }
- public void addLast(T t) { deque.add(t); }
- public T getFirst() { return deque.getFirst(); }
- public T getLast() { return deque.getLast(); }
- public T removeFirst() { return deque.removeFirst(); }
- public T removeLast() { return deque.removeLast(); }
- public int size() { return deque.size(); }
- @Override
- public String toString() { return deque.toString(); }
-
- public static void main(String[] args) {
- Deque deque = new Deque<>();
- for(int i = 20; i < 27; i++) {
- deque.addFirst(i);
- }
- for(int j = 50; j < 55; j++) {
- deque.addLast(j);
- }
- System.out.println(deque);
- while(deque.size() != 0) {
- System.out.print(deque.removeFirst() + " ");
- }
- }
-}
-/*output
-[20, 21, 22, 23, 24, 25, 26, 50, 51, 52, 53, 54]
-20 21 22 23 24 25 26 50 51 52 53 54
- */
-```
-
-### LinkedHashMap
-
-可以在构造器中设定 LinkedHashMap,使之采用基于访问的最近最少使用(LRU)算法,没有访问过的元素会出现在队列的前面。可用于定期清理元素以节省空间。
-
-```java
-public class LinkedHashMapDemo {
- public static void main(String[] args) {
- LinkedHashMap linkedHashMap = new LinkedHashMap<>(new CountingMapData(5));
- System.out.println(linkedHashMap);
- linkedHashMap = new LinkedHashMap<>(16, 0.75f, true);
- linkedHashMap.putAll(new CountingMapData(5));
- for(int i = 0; i < 3; i++) {
- linkedHashMap.get(i);
- }
- System.out.println(linkedHashMap);
- linkedHashMap.get(3);
- System.out.println(linkedHashMap);
- }
-}
-/*output
-{0=A0, 1=B0, 2=C0, 3=D0, 4=E0}
-{3=D0, 4=E0, 0=A0, 1=B0, 2=C0}
-{4=E0, 0=A0, 1=B0, 2=C0, 3=D0}
- */
-```
-
-### Colletions 工具类
-
-```java
-public class Utilities {
- static List list = Arrays.asList("one two three four".split(" "));
-
- public static void main(String[] args) {
- System.out.println(list);
- System.out.println("list disjoint(four)?" +
- Collections.disjoint(list, Collections.singletonList("four"))); //不相交
- System.out.println("max: " + Collections.max(list));
- System.out.println("min: " + Collections.min(list));
- System.out.println("max with comparator: " + Collections.max(list, String.CASE_INSENSITIVE_ORDER));
- List subList = Arrays.asList("one two".split(" "));
- System.out.println("indexOfSubList: " + Collections.indexOfSubList(list, subList));
- System.out.println("lastIndexOfSubList: " + Collections.lastIndexOfSubList(list, subList));
- Collections.replaceAll(list, "one", "emm");
- System.out.println("replace all: " + list);
- Collections.reverse(list);
- System.out.println("reverse: " + list);
- Collections.rotate(list, 1);
- System.out.println("rotate: " + list);
- List source = Arrays.asList("int the matrix".split(" "));
- Collections.copy(list, source);
- System.out.println("copy: " + list);
- Collections.swap(list, 0, list.size() - 1);
- System.out.println("swap: " + list);
- Collections.shuffle(list, new Random((47)));
- System.out.println("shuffle: " + list); //打乱
- Collections.fill(list, "frequency");
- System.out.println("fill: " + list);
- System.out.println("frequency of 'frequency': " + Collections.frequency(list, "frequency"));
- List nCopies = Collections.nCopies(3, "nCopies;");
- System.out.println("nCopies: " + nCopies);
- System.out.println("list disjoint nCopies? " + Collections.disjoint(list, nCopies));
- Enumeration e = Collections.enumeration(nCopies);
- Vector v = new Vector<>();
- while(e.hasMoreElements()) {
- v.add(e.nextElement());
- }
- ArrayList arrayList = Collections.list(v.elements());
- System.out.println("arrayList: " + arrayList);
- }
-}
-/*output
-[one, two, three, four]
-list disjoint(four)?false
-max: two
-min: four
-max with comparator: two
-indexOfSubList: 0
-lastIndexOfSubList: 0
-replace all: [emm, two, three, four]
-reverse: [four, three, two, emm]
-rotate: [emm, four, three, two]
-copy: [int, the, matrix, two]
-swap: [two, the, matrix, int]
-shuffle: [two, the, int, matrix]
-fill: [frequency, frequency, frequency, frequency]
-frequency of 'frequency': 4
-nCopies: [nCopies;, nCopies;, nCopies;]
-list disjoint nCopies? true
-arrayList: [nCopies;, nCopies;, nCopies;]
- */
-```
-
-#### 排序和查询
-
-```java
-public class ListSortSearch {
- public static void main(String[] args) {
- List list =
- new ArrayList(Utilities.list);
- list.addAll(Utilities.list);
- print(list);
- Collections.shuffle(list, new Random(47));
- print("Shuffled: " + list);
- // Use a ListIterator to trim off the last elements:
- ListIterator it = list.listIterator(4);
- while (it.hasNext()) {
- it.next();
- it.remove();
- }
- print("Trimmed: " + list);
- Collections.sort(list);
- print("Sorted: " + list);
- String key = list.get(3);
- int index = Collections.binarySearch(list, key);
- print("Location of " + key + " is " + index +
- ", list.get(" + index + ") = " + list.get(index));
- Collections.sort(list, String.CASE_INSENSITIVE_ORDER);
- print("Case-insensitive sorted: " + list);
- key = list.get(3);
- index = Collections.binarySearch(list, key,
- String.CASE_INSENSITIVE_ORDER);
- print("Location of " + key + " is " + index +
- ", list.get(" + index + ") = " + list.get(index));
- }
-} /* Output:
-[one, Two, three, Four, five, six, one, one, Two, three, Four, five, six, one]
-Shuffled: [Four, five, one, one, Two, six, six, three, three, five, Four, Two, one, one]
-Trimmed: [Four, five, one, one, Two, six, six, three, three, five]
-Sorted: [Four, Two, five, five, one, one, six, six, three, three]
-Location of six is 7, list.get(7) = six
-Case-insensitive sorted: [five, five, Four, one, one, six, six, three, three, Two]
-Location of three is 7, list.get(7) = three
-*/
-```
-
-#### 只读容器
-
-```java
-public class ReadOnly {
- static Collection data =
- new ArrayList<>(Countries.names(6));
-
- public static void main(String[] args) {
- Collection c = Collections.unmodifiableCollection(
- new ArrayList<>(data));
- print(c); // Reading is OK
- //! c.add("one"); // Can't change it
-
- List a = Collections.unmodifiableList(new ArrayList<>(data));
- ListIterator lit = a.listIterator();
- print(lit.next()); // Reading is OK
- //! lit.add("one"); // Can't change it
-
- Set s = Collections.unmodifiableSet(new HashSet<>(data));
- print(s); // Reading is OK
- //! s.add("one"); // Can't change it
-
- // For a SortedSet:
- Set ss = Collections.unmodifiableSortedSet(new TreeSet<>(data));
-
- Map m = Collections.unmodifiableMap(
- new HashMap<>(Countries.capitals(6)));
- print(m); // Reading is OK
- //! m.put("Ralph", "Howdy!");
-
- // For a SortedMap:
- Map sm = Collections.unmodifiableSortedMap(
- new TreeMap<>(Countries.capitals(6)));
- }
-}
-```
-
-#### Collection 和 Map 的同步控制
-
-```java
-public class Synchronization {
- public static void main(String[] args) {
- Collection c = Collections.synchronizedCollection(new ArrayList<>());
- List list = Collections.synchronizedList(new ArrayList<>());
- Set set = Collections.synchronizedSet(new HashSet<>());
- Set ss = Collections.synchronizedSortedSet(new TreeSet<>());
- Map map = Collections.synchronizedMap(new HashMap<>());
- Map sm = Collections.synchronizedSortedMap(new TreeMap<>());
- }
-}
-```
-
-#### 快速报错机制
-
- fast-fail 是 Java 容器的一种保护机制。当多个线程对同一个集合进行操作时,就有可能会产生 fast-fail 事件。例如:当线程a正通过 iterator 遍历集合时,另一个线程b修改了集合的内容(modCount 不等于expectedModCount),那么线程a在遍历的时候会抛出 ConcurrentModificationException,产生 fast-fail 事件。
-
-```java
-public class FastFail {
- public static void main(String[] args) {
- Collection c = new ArrayList<>();
- Iterator itr = c.iterator();
- c.add("haha");
- try {
- String s = itr.next();
- } catch (ConcurrentModificationException ex) {
- ex.printStackTrace();
- }
- }
-}
-```
-
-在此例中,应该添加完所有元素后,再获取迭代器。
-
-**多线程并发修改容器的方法:**
-
-- 使用`Colletions.synchronizedList()`方法或在修改集合内容的地方加上 synchronized。这样的话,增删集合内容的同步锁会阻塞遍历操作,影响性能。
-- 使用 CopyOnWriteArrayList 来替换 ArrayList。在对 CopyOnWriteArrayList 进行修改操作的时候,会拷贝一个新的集合,对新的集合进行操作,操作完成后再把引用指向新的集合。
-
-### Java 1.0/1.1 的容器
-
-#### BitSet
-
-参考自:[JAVA中BitSet使用](https://blog.csdn.net/xv1356027897/article/details/79518647)
-
-位图,数据的存在性可以使用bit位上的1或0来表示,一个long型数字占用64位空间,那么一个long型数字就可以保存64个数字的“存在性”状态(true or false)。BitSet内部是一个long[]数组,数组的大小由 BitSet 接收的最大数字决定,这个数组将数字分段表示[0,63],[64,127],[128,191]...。即long[0]用来存储[0,63]这个范围的数字的“存在性”,long[1]用来存储[64,127],依次递推。
-
-```java
-public class BitSetDemo {
- public static void printBitSet(BitSet b) {
- System.out.println("BitSet: " + b);
- StringBuilder sb = new StringBuilder();
- for(int i = 0; i < b.size(); i++) {
- sb.append(b.get(i) ? "1" : "0");
- }
- System.out.println("bitset pattern: " + sb);
- }
- public static void main(String[] args) {
- Random rand = new Random(47);
- int num = rand.nextInt(20);
- BitSet bs = new BitSet();
- bs.set(num);
- System.out.println(bs);
- System.out.println("num exist? " + bs.get(num));
- System.out.println("size of bitset: " + bs.size());
- System.out.println("the number of bits set: " + bs.cardinality());
- bs.set(num);//重复设置
- System.out.println("set again! num exist? " + bs.get(num));
- bs.clear(num);//清除
- System.out.println("after clearing bit: " + bs);
-
- bs.set(num + 100);//自动扩充容量
- System.out.println("num+100 exist? " + bs.get(num + 100));
- System.out.println("num-1 exist? " + bs.get(num - 1));
- System.out.println("size of bitset: " + bs.size());
- }
-}
-/*output
-{18}
-num exist? true
-size of bitset: 64
-the number of bits set: 1
-set again! num exist? true
-after clearing bit: {}
-num+100 exist? true
-num-1 exist? false
-size of bitset: 128
- */
-```
-
-
-
-## Java I/O 系统
-
-### 输入和输出
-
-继承自 InputStream 或 Reader 的类都具有 read() 方法,用于读取单个字节或者字节数组;继承自 OutputStream 或 Writer 的类都含有 write() 方法,用于写单个字节或字节数组。
-
-#### InputStream 和 OutputStream
-
-InputStream 用来表示那些从不同数据源产生输入的类。这些数据源包括:1.字节数组;2.String 对象;3.文件;4.管道;5.一个由其他种类的流组成的序列。
-
-InputStream 类有一个抽象方法:`abstract int read()`,这个方法将读入并返回一个字节,或者在遇到输入源结尾时返回-1。
-
-OutputStream 决定了输出所要去的目标:字节数组、文件或管道。OutputStream 的 `abstract void write(int b)` 可以向某个输出位置写出一个字节。
-
-read() 和 write() 方法在执行时都将阻塞,等待数据被读入或者写出。
-
-#### Reader 和 Writer
-
-字符流是由通过字节流转换得到的,转化过程耗时,而且容易出现乱码问题。I/O 流提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。如果音频文件、图片等媒体文件用字节流比较好,如果涉及到字符的话使用字符流比较好。
-
-```java
-abstract int read();
-abstract void write(char c);
-```
-
-### 组合输入输出流过滤器
-
-FileInputStream 和 FileOutputStream 可以提供附在磁盘文件上的输入流和输出流。
-
-`FileInputStream fin = new FileInputStream("E:/data.txt") //E:\\data.txt`
-
-通过 java.io.File.separator 可以获得与平台相关的 文件分隔符。
-
-FileInputStream 只支持在文件的读写字节,而DataInputStream 只支持数值的读写。
-
-实现从文件读取数字,需要将 FileInputStream 和 DataInputStream 组合。
-
-```java
-FileInputStream fin = new FileInputStream("e:/data.txt");
-DataInputStream din = new DataInputStream(fin);
-double x = din.readDouble();
-```
-
-使用缓冲机制:
-
-```java
-DataInputStream din = new DataInputStream(
- new BufferedInputStream(
- new FileInputStream("E:/data.txt")
- )
-);
-```
-
-可回推的输入流:
-
-```java
-PushbackInputStream pin = new PushbackInputStream(
- new BufferedInputStream(
- new FileInputStream("E:/data.txt")
- )
-);
-
-int b = pin.read();
-if(b != '<') {
- pin.unread(b);
-}
-```
-
-从 zip 文件读入数字:
-
-```java
-DataInputStream din = new DataInputStream(
- new ZipInputStream(
- new FileInputStream("E:/data.zip")
- )
-);
-```
-
-### 文本输入和输出
-
-将字节流转化为 Unicode 字符的读入器:
-
-```java
-//Reader in = new InputStreamReader(System.in);
-Reader in = new InputStreamReader(new FileInputStream("E:/data.txt"), StandardCharsets.UTF_8);
-```
-
-将 Unicode 字符转化为字节流的写出器:
-
-```java
-PrintWriter out = new PrintWriter("H:/data.txt", "UTF-8");
-out.print("haha");
-out.flush();
-```
-
-默认情况下,自动冲刷机制是禁用的,可以使用`PrintWriter(Writer out, Boolean autoFlush)`启用或禁用自动冲刷机制:
-
-```java
-PrintWriter out = new PrintWriter(
- new OutputStreamWriter(
- new FileOutputStream("H:/data.txt"), "UTF-8"),
- true);
-out.print("tyson");
-out.flush();
-```
-
-### 以文本格式存储对象
-
-```java
-public class TextFile {
- public static void main(String[] args) throws IOException {
- Employee[] staff = new Employee[2];
- DateTimeFormatter ymd = DateTimeFormatter.ofPattern("yyyy-MM-dd");
- //字符串转换成LocalDate类型
- LocalDate ld = LocalDate.parse("2015-11-23", ymd);
- staff[0] = new Employee("tyson", 1200.00, ld);
- staff[1] = new Employee("tom", 1200.00, ld);
-
- try (PrintWriter out = new PrintWriter("H:/data.txt", "UTF-8")) {
- writeData(staff, out);
- }
- try (Scanner in = new Scanner(
- new FileInputStream("H:/data.txt"), "UTF-8")) {
- Employee[] newStaff = readData(in);
-
- for(Employee e : newStaff) {
- System.out.println(e);
- }
- }
- }
-
- private static void writeData(Employee[] employees, PrintWriter out) throws IOException {
- out.println(employees.length);
- for(Employee e : employees) {
- writeEmployee(out, e);
- }
- }
-
- private static Employee[] readData(Scanner in) {
- int n = in.nextInt();
- in.nextLine();
-
- Employee[] employees = new Employee[n];
- for(int i = 0; i < n ; i++) {
- employees[i] = readEmployee(in);
- }
-
- return employees;
- }
-
- public static void writeEmployee(PrintWriter out, Employee e) throws IOException {
- out.println(e.getName() + "|" + e.getSalary() + "|" + e.getHireDate());
- }
-
- public static Employee readEmployee(Scanner in) {
- String line = in.nextLine();
- String[] tokens = line.split("\\|");
- String name = tokens[0];
- double salary = Double.parseDouble(tokens[1]);
- LocalDate hireDate = LocalDate.parse(tokens[2]);
- return new Employee(name, salary, hireDate);
- }
-}
-/*output
-Employee{name='tyson', salary=1200.0, hireDate=2015-11-23}
-Employee{name='tom', salary=1200.0, hireDate=2015-11-23}
- */
-```
-
-### 字符编码方式
-
-输入和输出流都是用于字节序列,很多情况下,我们希望操作的是字符序列。
-
-平台使用的编码方法可以由静态方法`Charset.defaultCharset`返回。
-
-将字节数组转化成字符串:
-
-```java
-String str = new String(bytes, StandardCharsets.UTF_8);
-```
-
-### 读写二进制数据
-
-#### DataInput 和 DataOutput
-
-DataOutput 接口定义了以二进制格式写数组、字符、字符串等的方法,如 writeChars、writeByte、writeInt 和 writeDouble。
-
-writeInt 总是将一个整数写出为4字节的二进制数量值,writeDouble 总将一个 double 值写出为8字节的二进制数量值。Java 会将所有的值都按照高位在前的模式写出,这使得 Java 数据文件可以独立于平台。
-
-DataInput 接口用于二进制格式读数据,接口定义了如下方法:readInt、readShort、readChar等。
-
-DataInputStream 实现了 DataInput 接口,DataOutputStream 实现了 DataOutput 接口:
-
-```java
-DataInputStream in = new DataInputStream(new FileInputStream("e:/data.txt"));
-DataOutputStream out = new DataOutputStream(new FileOutputStream("e:/data.txt"));
-```
-
-#### 随机访问文件
-
-RandomAccessFile 类可以在文件中的任何位置查找或写入。RandomAccessFile 类同时实现了 DataInput 和 DataOutput 接口。
-
-#### 序列化
-
-**保存和加载序列化对象**
-
-保存对象:
-
-```java
-ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("e:/data.txt"));
-Employee tyson = new Employee();
-out.writeObject(tyson);
-```
-
-读回对象:
-
-```java
-ObjectInputStream in = new ObjectInputStream(new FileInputStream("e:/data.txt"));
-Employee e = (Employee)in.readObject();
-```
-
-其中,Employee 需要实现 Serializable 接口,接口没有任何方法:
-
-```java
-Employee implements Serializable {...}
-```
-
-ObjectStreamTest.java
-
-```java
-/**
- * Copyright (C), 2018-2019
- * FileName: ObjectStream
- * Author: Tyson
- * Date: 2019/4/25/0025 14:58
- * Description: 对象序列化
- */
-package com.tyson.chapter18.io;
-
-import java.io.*;
-
-/**
- * @author Tyson
- * @ClassName: ObjectStream
- * @create 2019/4/25/0025 14:58
- */
-public class ObjectStream {
- public static void main(String[] args) throws IOException, ClassNotFoundException {
- Employee tyson = new Employee("tyson", 8888.0, "2015-02-15");
- Manager sophia = new Manager("sophia", 6666.0, "2012-03-15");
- sophia.setSecretary(tyson);
- Manager tom = new Manager("tom", 5555.0, "2014-03-16");
- tom.setSecretary(tyson);
- Employee[] staff = new Employee[3];
- staff[0] = tyson;
- staff[1] = sophia;
- staff[2] = tom;
-
- try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("e:/data.txt"))) {
- out.writeObject(staff);
- out.flush();
- }
-
- try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("e:/data.txt"))) {
- Employee[] newStaff = (Employee[]) in.readObject();
- newStaff[1].setSalary(23333.0);
- for(Employee e : newStaff) {
- System.out.println(e);
- }
- //原对象sophia不变
- System.out.println(staff[1]);
- }
- }
-}
-
-class Manager extends Employee {
- private Employee secretary;
-
- public Manager(String name, Double salary, String hireDate) {
- super(name, salary, hireDate);
- }
-
- @Override
- public String toString() {
- return "Manager{" +
- "name=" +super.getName() +
- ", salary=" +super.getSalary() +
- ", hireday=" +super.getHireDate() +
- ", secretary=" + secretary +
- '}';
- }
-
- public Employee getSecretary() {
- return secretary;
- }
-
- public void setSecretary(Employee secretary) {
- this.secretary = secretary;
- }
-}
-```
-
-通过关键字 transient 可以将某些不可序列化的域设置成瞬时的域,这些域在对象被序列化时总是被跳过。
-
-**使用序列化实现克隆**
-
-将对象序列化到输出流中,然后将其读回,产生的新对象是原对象的深拷贝。
-
-### 操作文件
-
-File 类既能代表特定文件的名称,又能代表一个目录下的一组文件的名称。
-
-#### 目录列表器
-
-获取某个目录下以".java"文件后缀结尾的文件名。
-
-```java
-class DirFilter implements FilenameFilter {
- private Pattern pattern;
- public DirFilter(String regex) {
- pattern = Pattern.compile(regex);
- }
- @Override
- public boolean accept(File dir, String name) {
- return pattern.matcher(name).matches();
- }
-}
-
-public class DirList {
- public static void main(String[] args) {
- File path = new File("H:" + File.separator +
- "java-data" + File.separator + "TIJ4-code" + File.separator + "containers");
- //Java文件后缀".+\\.java$"
- String[] list = path.list(new DirFilter("A.+\\.java$"));
- Arrays.sort(list);
- System.out.println(Arrays.toString(list));
- }
-}
-/*output
-[AssociativeArray.java]
- */
-```
-
-用匿名内部类实现。
-
-```java
-public class DirList {
- public static void main(String[] args) {
- File f = new File("H:" + File.separator +
- "java-data" + File.separator + "TIJ4-code" + File.separator + "containers");
- String[] fileNames = f.list(new FilenameFilter() {
- @Override
- public boolean accept(File dir, String name) {
- return name.endsWith(".java");
- }
- });
- System.out.println(Arrays.toString(fileNames));
- }
-}
-```
-
-###
-
-## 枚举类型
-
-### 基本 enum 特性
-
-除了不能继承自一个 enum 之外,我们基本上可以将 enum 看做一个常规的类。所有的 enum 都继承自 java.lang.Enum 类,所以 enum 不能再继承其他类。在创建一个新的 enum 时,可以同时实现一个或多个接口。
-
-```java
-enum Color { RED, GREEN, BLUE }
-
-public class EnumClass {
- public static void main(String[] args) {
- for(Color c : Color.values()) {
- System.out.println(c + " ordinal: " + c.ordinal());
- System.out.print(c.compareTo(Color.RED) + " ");
- System.out.print(c.equals(Color.RED) + " ");
- System.out.println(c == Color.RED);
- System.out.println(c.getDeclaringClass());
- System.out.println(c.name());
- System.out.println("-------------------------");
- }
- for(String s : "RED GREEN BLUE".split(" ")) {
- Color c = Enum.valueOf(Color.class, s);
- System.out.println(c);
- }
- }
-}
-/*output
-RED ordinal: 0
-0 true true
-class com.tyson.chapter19.enumerated.Color
-RED
--------------------------
-GREEN ordinal: 1
-1 false false
-class com.tyson.chapter19.enumerated.Color
-GREEN
--------------------------
-BLUE ordinal: 2
-2 false false
-class com.tyson.chapter19.enumerated.Color
-BLUE
--------------------------
-RED
-GREEN
-BLUE
- */
-```
-
-values() 是编译器添加的 static 方法,Enum 类中没有 values() 方法。ordinal() 方法返回一个 int 值,这是每个 enum 实例声明时的次序,从0开始。valueOf() 根据给定的名字返回相应的 enum实例,如果不存在给定名字的实例,将会抛出异常。
-
-### 向 enum 添加新方法
-
-```java
-public enum Direction {
- WEST("Xizang"),
- NORTH("Beijing"),
- EAST("Shanghai"),
- SOUTH("Hainan");//定义自己的方法,enum实例序列的最后需添加分号
- //属性和方法需要在enum实例之后定义
- private String discription;
- private Direction(String discription) {
- this.discription = discription;
- }
- public String getDiscription() {
- return discription;
- }
-
- public static void main(String[] args) {
- for(Direction dir : Direction.values()) {
- System.out.println(dir + ": " + dir.getDiscription());
- }
- }
-}
-/*output
-WEST: Xizang
-NORTH: Beijing
-EAST: Shanghai
-SOUTH: Hainan
- */
-```
-
-### 覆盖 enum 的方法
-
-```java
-public enum Animal {
- CAT, DOG, BIRD, PIG;
- @Override
- public String toString() {
- String id = name();
- String lower = id.substring(1).toLowerCase();
- return id.charAt(0) + lower;
- }
-
- public static void main(String[] args) {
- for(Animal a : values()) {
- System.out.print(a + " ");
- }
- }
-}
-//output: Cat Dog Bird Pig
-```
-
-### Switch 语句中的 enum
-
-```java
-enum Signal {
- GREEN, YELLOW, RED,
-}
-
-public class TrafficLight {
- Signal color = Signal.RED;
-
- public void change() {
- switch (color) {
- case RED:
- color = Signal.GREEN;
- break;
- case GREEN:
- color = Signal.YELLOW;
- break;
- case YELLOW:
- color = Signal.RED;
- break;
- }
- }
- @Override
- public String toString() {
- return "the traffic light is: " + color;
- }
-
- public static void main(String[] args) {
- TrafficLight tl = new TrafficLight();
- for (int i = 0; i < 4; i++) {
- System.out.println(tl);
- tl.change();
- }
- }
-}
-/*output
-the traffic light is: RED
-the traffic light is: GREEN
-the traffic light is: YELLOW
-the traffic light is: RED
- */
-```
-
-### EnumSet
-
- 如果你想用一个数表示多种状态,那么位运算是一种很好的选择。EnumSet 是通过位运算实现的。它是一个与枚举类型一起使用的专用 Set 实现。枚举set中所有元素都必须来自单个枚举类型(即必须是同类型,且该类型是 Enum 的子类)。
-
-```java
-public enum Season {
- SPRING, SUMMER, AUTUMN, WINTER;
-
- public static void main(String[] args) {
- Set emptyEnumSet = EnumSet.noneOf(Season.class);
- System.out.println("EnumSet.noneOf(): " + emptyEnumSet);
- emptyEnumSet.add(SPRING);
- System.out.println("emptyEnumSet.add(SPRING): " + emptyEnumSet);
- Set enumSet = EnumSet.allOf(Season.class);
- System.out.println("EnumSet.allOf(): " + enumSet);
- emptyEnumSet.addAll(enumSet);
- System.out.println("emptyEnumSet.addAll(): " + emptyEnumSet);
- Season[] seasons = new Season[emptyEnumSet.size()];
- enumSet.toArray(seasons);
- System.out.println("seasons: " + Arrays.toString(seasons));
- }
-}
-/*output
-EnumSet.noneOf(): []
-emptyEnumSet.add(SPRING): [SPRING]
-EnumSet.allOf(): [SPRING, SUMMER, AUTUMN, WINTER]
-emptyEnumSet.addAll(): [SPRING, SUMMER, AUTUMN, WINTER]
-seasons: [SPRING, SUMMER, AUTUMN, WINTER]
- */
-```
-
-#### 源码解析
-
-参考自:[EnumSet源码解析](https://www.jianshu.com/p/f7035c5816b1)
-
-```java
-//EnumSet的容量小于64,创建的是RegularEnumSet,大于64,创建的是JumboEnumSet
-public boolean add(E e) {
- // 校验枚举类型
- typeCheck(e);
-
- long oldElements = elements;
- elements |= (1L << ((Enum>)e).ordinal());
- return elements != oldElements;
-}
-
-/**
- * 用于校验枚举类型,位于EnumSet中
- */
-final void typeCheck(E e) {
- Class> eClass = e.getClass();
- if (eClass != elementType && eClass.getSuperclass() != elementType)
- throw new ClassCastException(eClass + " != " + elementType);
-}
-```
-
-每一个枚举元素都有一个属性ordinal,用来表示该元素在枚举类型中的次序或者说下标。
-
-
-
-addAll 方法就是将 elements 上,从低位到枚举长度上的下标值置为1。比如某一个枚举类型共5个元素,而addAll 就是将 elements 的二进制的低5位置为1。
-
-```java
-void addAll() {
- if (universe.length != 0)
- elements = -1L >>> -universe.length;
-}
-```
-
-### EnumMap
-
-EnumMap 是一种特殊的 Map,它要求其中的键必须来自一个 enum。由于 enum 本身的限制,所以 EnumMap 在内部可由数组实现。因此 EnumMap 的速度很快。
-
-```java
-interface Command { void action(); }
-public class EnumMaps {
- public static void main(String[] args) {
- EnumMap em = new EnumMap<>(AlarmPoints.class);
- em.put(KITCHEN, new Command() {
- @Override
- public void action() {
- System.out.println("Kitchen fire!");
- }
- });
- em.put(BATHROOM, new Command() {
- @Override
- public void action() {
- System.out.println("Bathroom alert");
- }
- });
- for(Map.Entry e : em.entrySet()) {
- System.out.print(e.getKey() + ": ");
- e.getValue().action();
- }
- //获取不存在的值
- try {
- em.get(UTILITY).action();
- } catch (Exception ex) {
- System.out.println(ex);
- }
- }
-}
-/*output
-BATHROOM: Bathroom alert
-KITCHEN: Kitchen fire!
-java.lang.NullPointerException
- */
-```
-
-与 EnumSet 一样,enum 实例定义时的次序决定了其在 EnumMap 中的顺序。每个 enum 实例都会作为键存放到 EnumMap 中,如果没有为这个键调用 put() 方法来存入相应的值的话,其对应的值为 null。
-
-
-
-## 注解
-
-Java SE5内置了三种定义在 java.lang 中的注解:@Override,@Deprecated 和 @SuppressWarnings(关闭不当的编译器警告信息)。
-
-### 基本语法
-
-```java
-@Target(ElementType.METHOD)//应用于什么地方,方法、域等
-@Retention(RetentionPolicy.SOURCE)//注解在哪一个级别可用,SOURCE/CLASS/RUNTIME
-public @interface Override {
-}
-```
-
-#### 自定义注解
-
-```java
-//UserCase.java
-@Target(ElementType.METHOD)
-@Retention(RetentionPolicy.RUNTIME)
-public @interface UserCase {
- public int id();
- public String description() default "no description";
-}
-
-//PasswordUtils.java
-public class PasswordUtils {
- @UserCase(id = 47, description = "Passwords must contain at least one numeric")
- public boolean validatePassword(String password) {
- return password.matches("\\w*\\d\\w*");
- }
-}
-```
-
-注解元素(本例的是 id和description)只能是基本类型、String、Class、enum、Annotation 和这些类型的数组。
-
-@Retention注解三种取值:**RetentionPolicy.SOURCE**、**RetentionPolicy.CLASS**、**RetentionPolicy.RUNTIME**分别对应:Java源文件(.java文件)---->.class文件---->内存中的字节码。
-
-@Target元注解决定了一个注解可以标识到哪些成分上,如标识在在类身上,或者属性身上,或者方法身上等成分,@Target默认值为任何元素(成分)。
-
-**Retention注解说明**
-
-参考自:[注解](https://www.cnblogs.com/xdp-gacl/p/3622275.html)
-
-当在Java源程序上加了一个注解,这个Java源程序要由javac去编译,javac把java源文件编译成.class文件,在编译成class时可能会把Java源程序上的一些注解给去掉,java编译器在处理java源程序时,可能会认为这个注解没有用了,于是就把这个注解去掉了,那么此时在编译好的class中就找不到注解了, 这是编译器编译java源程序时对注解进行处理的第一种可能情况,假设java编译器在把java源程序编译成class时,没有把java源程序中的注解去掉,那么此时在编译好的class中就可以找到注解,当程序使用编译好的class文件时,需要用类加载器把class文件加载到内存中,class文件中的东西不是字节码,class文件里面的东西由类加载器加载到内存中去,类加载器在加载class文件时,会对class文件里面的东西进行处理,如安全检查,处理完以后得到的最终在内存中的二进制的东西才是字节码,类加载器在把class文件加载到内存中时也有转换,转换时是否把class文件中的注解保留下来,这也有说法,所以说**一个注解的生命周期有三个阶段:java源文件是一个阶段,class文件是一个阶段,内存中的字节码是一个阶段**,javac把java源文件编译成.class文件时,有可能去掉里面的注解,类加载器把.class文件加载到内存时也有可能去掉里面的注解,因此**在自定义注解时就可以使用Retention注解指明自定义注解的生命周期,自定义注解的生命周期是在RetentionPolicy.SOURCE阶段(java源文件阶段),还是在RetentionPolicy.CLASS阶段(class文件阶段),或者是在RetentionPolicy.RUNTIME阶段(内存中的字节码运行时阶段)**,根据**JDK提供的API可以知道默认是在RetentionPolicy.CLASS阶段 (JDK的API写到:the retention policy defaults to RetentionPolicy.CLASS)。**
-
-### 元注解
-
-元、注解负责注解其他的注解。
-
-| 注解 | 作用 |
-| ----------- | -------------------------------- |
-| @Target | 表示该注解可以用于什么地方 |
-| @Retention | 表示需要在什么级别保存该注解信息 |
-| @Documented | 将此注解包含在 Javadoc 中 |
-| @Inherited | 允许子类继承父类的注解 |
-
-### 编写注解处理器
-
-使用注解时,很重要的一部分就是创建和使用注解处理器。
-
-```java
-public class UseCaseTracker {
- public static void trackUseCases(List useCases, Class> c) {
- for(Method m : c.getDeclaredMethods()) {
- UseCase uc = m.getAnnotation(UseCase.class);
- if(uc != null) {
- System.out.println("found use case: " + uc.id() + " " + uc.description());
- useCases.remove(new Integer(uc.id()));
- }
- }
- for(int i : useCases) {
- System.out.println("warning: missing use case-" + i);
- }
- }
-
- public static void main(String[] args) {
- List useCases = new ArrayList<>();
- Collections.addAll(useCases, 47, 48);
- trackUseCases(useCases, PasswordUtils.class);
- }
-}
-/*output
-found use case: 47 Passwords must contain at least one numeric
-warning: missing use case-48
- */
-```
-
-### 注解综合
-
-```java
-//TrafficLight.java
-public enum TrafficLight {
- RED, GREEN, YELLOW;
-}
-
-//MetaAnnotation.java
-public @interface MetaAnnotation {
- String value();
-}
-
-//MyAnnotation.java
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.METHOD, ElementType.TYPE})
-public @interface MyAnnotation {
- String color() default "blue";
- //当只有value属性要设置时,可以省略"value="
- String value();
- int[] arrayAttr() default {1, 2, 3};
- TrafficLight lamp() default TrafficLight.GREEN;
- MetaAnnotation annotationAttr() default @MetaAnnotation("tyson");
-}
-
-//MyAnnotationTracker.java
-@MyAnnotation(
- color = "red",
- value = "MyAnnotation注解用于MyAnnotationTracker类",
- arrayAttr = {6, 6, 6},
- lamp = TrafficLight.GREEN,
- annotationAttr = @MetaAnnotation("sophia")
-)
-public class MyAnnotationTracker {
- @MyAnnotation("MyAnnotation注解用于main方法")
- public static void main(String[] args) {
- //如果指定类型的注解存在于此元素上则返回true
- if(MyAnnotationTracker.class.isAnnotationPresent(MyAnnotation.class)) {
- //获取类的注解
- MyAnnotation myAnnotation = (MyAnnotation)MyAnnotationTracker.class.getAnnotation(MyAnnotation.class);
- System.out.println(myAnnotation.color());
- System.out.println(myAnnotation.value());
- System.out.println(Arrays.toString(myAnnotation.arrayAttr()));
- System.out.println(myAnnotation.lamp());
- MetaAnnotation ma = myAnnotation.annotationAttr();
- System.out.println(ma.value());
- }
- //获取方法的注解
- for(Method m : MyAnnotationTracker.class.getDeclaredMethods()) {
- MyAnnotation myAnnotation1 = m.getAnnotation(MyAnnotation.class);
- if(myAnnotation1 != null) {
- System.out.println(myAnnotation1.value());
- }
- }
- }
-}
-/*output
-red
-MyAnnotation注解用于MyAnnotationTracker类
-[6, 6, 6]
-GREEN
-sophia
-MyAnnotation注解用于main方法
- */
-```
-
-
-
-
-
-
-
diff --git "a/Java/Java\345\205\263\351\224\256\345\255\227.md" "b/Java/Java\345\205\263\351\224\256\345\255\227.md"
deleted file mode 100644
index 3dc54e5..0000000
--- "a/Java/Java\345\205\263\351\224\256\345\255\227.md"
+++ /dev/null
@@ -1,205 +0,0 @@
-
-
-
-
-- [static](#static)
- - [静态变量](#%E9%9D%99%E6%80%81%E5%8F%98%E9%87%8F)
- - [静态方法](#%E9%9D%99%E6%80%81%E6%96%B9%E6%B3%95)
- - [静态代码块](#%E9%9D%99%E6%80%81%E4%BB%A3%E7%A0%81%E5%9D%97)
- - [静态内部类](#%E9%9D%99%E6%80%81%E5%86%85%E9%83%A8%E7%B1%BB)
-- [final](#final)
-- [this](#this)
-- [super](#super)
-
-
-
-## static
-
-static可以用来修饰类的成员方法、类的成员变量。
-
-### 静态变量
-
-static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
-
-以下例子,age为非静态变量,则p1打印结果是:`Name:zhangsan, Age:10`;若age使用static修饰,则p1打印结果是:`Name:zhangsan, Age:12`,因为static变量在内存只有一个副本。
-
-```java
-public class Person {
- String name;
- int age;
-
- public String toString() {
- return "Name:" + name + ", Age:" + age;
- }
-
- public static void main(String[] args) {
- Person p1 = new Person();
- p1.name = "zhangsan";
- p1.age = 10;
- Person p2 = new Person();
- p2.name = "lisi";
- p2.age = 12;
- System.out.println(p1);
- System.out.println(p2);
- }
- /**Output
- * Name:zhangsan, Age:10
- * Name:lisi, Age:12
- *///~
-}
-```
-
-### 静态方法
-
-static方法一般称作静态方法。静态方法不依赖于任何对象就可以进行访问,通过类名即可调用静态方法。
-
-```java
-public class Utils {
- public static void print(String s) {
- System.out.println("hello world: " + s);
- }
-
- public static void main(String[] args) {
- Utils.print("程序员大彬");
- }
-}
-```
-
-### 静态代码块
-
-静态代码块只会在类加载的时候执行一次。以下例子,startDate和endDate在类加载的时候进行赋值。
-
-```java
-class Person {
- private Date birthDate;
- private static Date startDate, endDate;
- static{
- startDate = Date.valueOf("2008");
- endDate = Date.valueOf("2021");
- }
-
- public Person(Date birthDate) {
- this.birthDate = birthDate;
- }
-}
-```
-
-### 静态内部类
-
-**在静态方法里**,使用⾮静态内部类依赖于外部类的实例,也就是说需要先创建外部类实例,才能用这个实例去创建非静态内部类。⽽静态内部类不需要。
-
-```java
-public class OuterClass {
- class InnerClass {
- }
- static class StaticInnerClass {
- }
- public static void main(String[] args) {
- // 在静态方法里,不能直接使用OuterClass.this去创建InnerClass的实例
- // 需要先创建OuterClass的实例o,然后通过o创建InnerClass的实例
- // InnerClass innerClass = new InnerClass();
- OuterClass outerClass = new OuterClass();
- InnerClass innerClass = outerClass.new InnerClass();
- StaticInnerClass staticInnerClass = new StaticInnerClass();
-
- outerClass.test();
- }
-
- public void nonStaticMethod() {
- InnerClass innerClass = new InnerClass();
- System.out.println("nonStaticMethod...");
- }
-}
-```
-
-
-
-## final
-
-1. **基本数据**类型用final修饰,则不能修改,是常量;**对象引用**用final修饰,则引用只能指向该对象,不能指向别的对象,但是对象本身可以修改。
-
-2. final修饰的方法不能被子类重写
-
-3. final修饰的类不能被继承。
-
-
-
-## this
-
- `this.属性名称`指访问类中的成员变量,可以用来区分成员变量和局部变量。如下代码所示,`this.name`访问类Person当前实例的变量。
-
-```java
-/**
- * @description:
- * @author: 程序员大彬
- * @time: 2021-08-17 00:29
- */
-public class Person {
- String name;
- int age;
-
- public Person(String name, int age) {
- this.name = name;
- this.age = age;
- }
-}
-```
-
-`this.方法名称`用来访问本类的方法。以下代码中,`this.born()`调用类 Person 的当前实例的方法。
-
-```java
-/**
- * @description:
- * @author: 程序员大彬
- * @time: 2021-08-17 00:29
- */
-public class Person {
- String name;
- int age;
-
- public Person(String name, int age) {
- this.born();
- this.name = name;
- this.age = age;
- }
-
- void born() {
- }
-}
-```
-
-
-
-## super
-
-super 关键字用于在子类中访问父类的变量和方法。
-
-```java
-class A {
- protected String name = "大彬";
-
- public void getName() {
- System.out.println("父类:" + name);
- }
-}
-
-public class B extends A {
- @Override
- public void getName() {
- System.out.println(super.name);
- super.getName();
- }
-
- public static void main(String[] args) {
- B b = new B();
- b.getName();
- }
- /**
- * 大彬
- * 父类:大彬
- */
-}
-```
-
-在子类B中,我们重写了父类的getName()方法,如果在重写的getName()方法中我们要调用父类的相同方法,必须要通过super关键字显式指出。
-
diff --git "a/Java/Java\345\237\272\347\241\200.md" "b/Java/Java\345\237\272\347\241\200.md"
deleted file mode 100644
index 3a08dc1..0000000
--- "a/Java/Java\345\237\272\347\241\200.md"
+++ /dev/null
@@ -1,1582 +0,0 @@
-
-
-
-
-- [Java概述](#java%E6%A6%82%E8%BF%B0)
- - [Java的特点](#java%E7%9A%84%E7%89%B9%E7%82%B9)
- - [JKD和JRE](#jkd%E5%92%8Cjre)
- - [JDK](#jdk)
- - [JRE](#jre)
-- [Java基础语法](#java%E5%9F%BA%E7%A1%80%E8%AF%AD%E6%B3%95)
- - [基本数据类型](#%E5%9F%BA%E6%9C%AC%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B)
- - [包装类型](#%E5%8C%85%E8%A3%85%E7%B1%BB%E5%9E%8B)
- - [包装类缓存](#%E5%8C%85%E8%A3%85%E7%B1%BB%E7%BC%93%E5%AD%98)
- - [String](#string)
- - [String拼接](#string%E6%8B%BC%E6%8E%A5)
- - [关键字](#%E5%85%B3%E9%94%AE%E5%AD%97)
- - [static](#static)
- - [静态变量](#%E9%9D%99%E6%80%81%E5%8F%98%E9%87%8F)
- - [静态方法](#%E9%9D%99%E6%80%81%E6%96%B9%E6%B3%95)
- - [静态代码块](#%E9%9D%99%E6%80%81%E4%BB%A3%E7%A0%81%E5%9D%97)
- - [静态内部类](#%E9%9D%99%E6%80%81%E5%86%85%E9%83%A8%E7%B1%BB)
- - [final](#final)
- - [this](#this)
- - [super](#super)
- - [object常用方法](#object%E5%B8%B8%E7%94%A8%E6%96%B9%E6%B3%95)
- - [toString](#tostring)
- - [equals](#equals)
- - [hashCode](#hashcode)
- - [clone](#clone)
- - [浅拷贝](#%E6%B5%85%E6%8B%B7%E8%B4%9D)
- - [深拷贝](#%E6%B7%B1%E6%8B%B7%E8%B4%9D)
- - [getClass](#getclass)
- - [wait](#wait)
- - [notity](#notity)
- - [equals()和hashcode()的关系](#equals%E5%92%8Chashcode%E7%9A%84%E5%85%B3%E7%B3%BB)
- - [==和equals区别](#%E5%92%8Cequals%E5%8C%BA%E5%88%AB)
-- [面向对象](#%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1)
- - [面向对象特性](#%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%89%B9%E6%80%A7)
- - [多态怎么实现](#%E5%A4%9A%E6%80%81%E6%80%8E%E4%B9%88%E5%AE%9E%E7%8E%B0)
- - [类与对象](#%E7%B1%BB%E4%B8%8E%E5%AF%B9%E8%B1%A1)
- - [属性](#%E5%B1%9E%E6%80%A7)
- - [方法](#%E6%96%B9%E6%B3%95)
- - [普通方法](#%E6%99%AE%E9%80%9A%E6%96%B9%E6%B3%95)
- - [构造方法](#%E6%9E%84%E9%80%A0%E6%96%B9%E6%B3%95)
- - [方法重载](#%E6%96%B9%E6%B3%95%E9%87%8D%E8%BD%BD)
- - [方法重写](#%E6%96%B9%E6%B3%95%E9%87%8D%E5%86%99)
- - [初始化顺序](#%E5%88%9D%E5%A7%8B%E5%8C%96%E9%A1%BA%E5%BA%8F)
-- [接口和抽象类](#%E6%8E%A5%E5%8F%A3%E5%92%8C%E6%8A%BD%E8%B1%A1%E7%B1%BB)
- - [抽象类](#%E6%8A%BD%E8%B1%A1%E7%B1%BB)
- - [接口](#%E6%8E%A5%E5%8F%A3)
- - [接口与抽象类区别](#%E6%8E%A5%E5%8F%A3%E4%B8%8E%E6%8A%BD%E8%B1%A1%E7%B1%BB%E5%8C%BA%E5%88%AB)
-- [反射](#%E5%8F%8D%E5%B0%84)
- - [Class类](#class%E7%B1%BB)
- - [Field类](#field%E7%B1%BB)
- - [Method类](#method%E7%B1%BB)
-- [泛型](#%E6%B3%9B%E5%9E%8B)
- - [泛型类](#%E6%B3%9B%E5%9E%8B%E7%B1%BB)
- - [泛型接口](#%E6%B3%9B%E5%9E%8B%E6%8E%A5%E5%8F%A3)
- - [泛型方法](#%E6%B3%9B%E5%9E%8B%E6%96%B9%E6%B3%95)
-- [Exception](#exception)
- - [Throwable](#throwable)
- - [常见的Exception](#%E5%B8%B8%E8%A7%81%E7%9A%84exception)
- - [关键字](#%E5%85%B3%E9%94%AE%E5%AD%97-1)
-- [IO流](#io%E6%B5%81)
- - [InputStream 和 OutputStream](#inputstream-%E5%92%8C-outputstream)
- - [Reader 和 Writer](#reader-%E5%92%8C-writer)
- - [字符流和字节流的转换](#%E5%AD%97%E7%AC%A6%E6%B5%81%E5%92%8C%E5%AD%97%E8%8A%82%E6%B5%81%E7%9A%84%E8%BD%AC%E6%8D%A2)
- - [同步异步](#%E5%90%8C%E6%AD%A5%E5%BC%82%E6%AD%A5)
- - [阻塞非阻塞](#%E9%98%BB%E5%A1%9E%E9%9D%9E%E9%98%BB%E5%A1%9E)
- - [BIO](#bio)
- - [NIO](#nio)
- - [AIO](#aio)
- - [BIO/NIO/AIO区别](#bionioaio%E5%8C%BA%E5%88%AB)
-- [ThreadLocal](#threadlocal)
- - [原理](#%E5%8E%9F%E7%90%86)
- - [内存泄漏](#%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F)
- - [使用场景](#%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF)
-- [线程安全类](#%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E7%B1%BB)
-- [常见操作](#%E5%B8%B8%E8%A7%81%E6%93%8D%E4%BD%9C)
- - [排序](#%E6%8E%92%E5%BA%8F)
- - [数组操作](#%E6%95%B0%E7%BB%84%E6%93%8D%E4%BD%9C)
- - [拷贝](#%E6%8B%B7%E8%B4%9D)
- - [数组拷贝](#%E6%95%B0%E7%BB%84%E6%8B%B7%E8%B4%9D)
- - [对象拷贝](#%E5%AF%B9%E8%B1%A1%E6%8B%B7%E8%B4%9D)
- - [序列化](#%E5%BA%8F%E5%88%97%E5%8C%96)
- - [什么情况下需要序列化?](#%E4%BB%80%E4%B9%88%E6%83%85%E5%86%B5%E4%B8%8B%E9%9C%80%E8%A6%81%E5%BA%8F%E5%88%97%E5%8C%96)
- - [如何实现序列化](#%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E5%BA%8F%E5%88%97%E5%8C%96)
- - [serialVersionUID](#serialversionuid)
- - [遍历](#%E9%81%8D%E5%8E%86)
- - [fast-fail](#fast-fail)
- - [fail-safe](#fail-safe)
- - [移除集合元素](#%E7%A7%BB%E9%99%A4%E9%9B%86%E5%90%88%E5%85%83%E7%B4%A0)
-
-
-
-# Java概述
-
-## Java的特点
-
-**Java是一门面向对象的编程语言。**面向对象和面向过程是一种软件开发思想。
-
-- 面向过程就是分析出解决问题所需要的步骤,然后用函数按这些步骤实现,使用的时候依次调用就可以了。面向对象是把构成问题事务分解成各个对象,分别设计这些对象,然后将他们组装成有完整功能的系统。面向过程只用函数实现,面向对象是用类实现各个功能模块。
-
- 例如五子棋,面向过程的设计思路就是首先分析问题的步骤:
- 1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤2,9、输出最后结果。
- 把上面每个步骤用分别的函数来实现,问题就解决了。
-
-- 而面向对象的设计则是从另外的思路来解决问题。整个五子棋可以分为:
- 1、黑白双方
- 2、棋盘系统,负责绘制画面
- 3、规则系统,负责判定诸如犯规、输赢等。
- 黑白双方负责接受用户的输入,并告知棋盘系统棋子布局发生变化,棋盘系统接收到了棋子的变化的信息就负责在屏幕上面显示出这种变化,同时利用规则系统来对棋局进行判定。
-
-**Java具有平台独立性和移植性。**
-
-- Java有一句口号:Write once, run anywhere,一次编写、到处运行。这也是Java的魅力所在。而实现这种特性的正是Java虚拟机JVM。已编译的Java程序可以在任何带有JVM的平台上运行。你可以在windows平台编写代码,然后拿到linux上运行。只要你在编写完代码后,将代码编译成.class文件,再把class文件打成Java包,这个jar包就可以在不同的平台上运行了。
-
-**Java具有稳健性。**
-
-- Java是一个强类型语言,它允许扩展编译时检查潜在类型不匹配问题的功能。Java要求显式的方法声明,它不支持C风格的隐式声明。这些严格的要求保证编译程序能捕捉调用错误,这就导致更可靠的程序。
-- 异常处理是Java中使得程序更稳健的另一个特征。异常是某种类似于错误的异常条件出现的信号。使用try/catch/finally语句,程序员可以找到出错的处理代码,这就简化了出错处理和恢复的任务。
-
-## JKD和JRE
-
-JDK和JRE是Java开发和运行工具,其中JDK包含了JRE,而JRE是可以独立安装的。
-
-### JDK
-
-Java Development Kit,JAVA语言的软件工具开发包,是整个JAVA开发的核心,它包含了JAVA的运行(JVM+JAVA类库)环境和JAVA工具。
-
-### JRE
-
-JRE(Java Runtime Environment,Java运行环境):包含JVM标准实现及Java核心类库。JRE是Java运行环境,并不是一个开发环境,所以没有包含任何开发工具(如编译器和调试器)。
-
-JRE是运行基于Java语言编写的程序所不可缺少的运行环境。也是通过它,Java的开发者才得以将自己开发的程序发布到用户手中,让用户使用。
-
-# Java基础语法
-
-## 基本数据类型
-
-- byte,8bit
-- char,16bit
-- short,16bit
-- int,32bit
-- float,32bit
-- long,64bit
-- double,64bit
-- boolean,只有两个值:true、false,可以使⽤用 1 bit 来存储,但是具体⼤小没有明确规定。
-
-## 包装类型
-
-基本类型都有对应的包装类型,基本类型与其对应的包装类型之间的赋值通过自动装箱与拆箱完成。
-
-```java
-Integer x = 1; // 装箱 调⽤ Integer.valueOf(1)
-int y = x; // 拆箱 调⽤了 X.intValue()
-```
-
-### 包装类缓存
-
-使用Integer.valueOf(i)生成Integer,如果 -128 <= i <= 127,则直接从cache取对象返回,不会创建新的对象,避免频繁创建包装类对象。
-
-```java
- public static Integer valueOf(int i) {
- if (i >= IntegerCache.low && i <= IntegerCache.high)
- return IntegerCache.cache[i + (-IntegerCache.low)];
- return new Integer(i);
- }
-```
-
-## String
-
-String是final类,不可被继承。
-
-### String拼接
-
-字符串拼接可以使用String用+做拼接,也可以使用StringBuilder和StringBuffer实现,三种方式对比:
-
-- 底层都是char数组实现的
-
-- 字符串拼接性能:StringBuilder > StringBuffer > String
-- String 是字符串常量,一旦创建之后该对象是不可更改的,用+对String做拼接操作,实际上是先通过建立StringBuilder,然后调用append()做拼接操作,所以在大量字符串拼接的时候,会频繁创建StringBuilder,性能较差。
-- StringBuilder和StringBuffer的对象是字符串变量,对变量进行操作就是直接对该对象进行修改(修改char[]数组),所以速度要比String快很多。
-- 在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的,StringBuffer中很多方法带有synchronized关键字,可以保证线程安全。
-
-## 关键字
-
-### static
-
-static可以用来修饰类的成员方法、类的成员变量。
-
-#### 静态变量
-
-static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
-
-以下例子,age为非静态变量,则p1打印结果是:`Name:zhangsan, Age:10`;若age使用static修饰,则p1打印结果是:`Name:zhangsan, Age:12`,因为static变量在内存只有一个副本。
-
-```java
-public class Person {
- String name;
- int age;
-
- public String toString() {
- return "Name:" + name + ", Age:" + age;
- }
-
- public static void main(String[] args) {
- Person p1 = new Person();
- p1.name = "zhangsan";
- p1.age = 10;
- Person p2 = new Person();
- p2.name = "lisi";
- p2.age = 12;
- System.out.println(p1);
- System.out.println(p2);
- }
- /**Output
- * Name:zhangsan, Age:10
- * Name:lisi, Age:12
- *///~
-}
-```
-
-#### 静态方法
-
-static方法一般称作静态方法。静态方法不依赖于任何对象就可以进行访问,通过类名即可调用静态方法。
-
-```java
-public class Utils {
- public static void print(String s) {
- System.out.println("hello world: " + s);
- }
-
- public static void main(String[] args) {
- Utils.print("程序员大彬");
- }
-}
-```
-
-#### 静态代码块
-
-静态代码块只会在类加载的时候执行一次。以下例子,startDate和endDate在类加载的时候进行赋值。
-
-```java
-class Person {
- private Date birthDate;
- private static Date startDate, endDate;
- static{
- startDate = Date.valueOf("2008");
- endDate = Date.valueOf("2021");
- }
-
- public Person(Date birthDate) {
- this.birthDate = birthDate;
- }
-}
-```
-
-#### 静态内部类
-
-**在静态方法里**,使用⾮静态内部类依赖于外部类的实例,也就是说需要先创建外部类实例,才能用这个实例去创建非静态内部类。⽽静态内部类不需要。
-
-```java
-public class OuterClass {
- class InnerClass {
- }
- static class StaticInnerClass {
- }
- public static void main(String[] args) {
- // 在静态方法里,不能直接使用OuterClass.this去创建InnerClass的实例
- // 需要先创建OuterClass的实例o,然后通过o创建InnerClass的实例
- // InnerClass innerClass = new InnerClass();
- OuterClass outerClass = new OuterClass();
- InnerClass innerClass = outerClass.new InnerClass();
- StaticInnerClass staticInnerClass = new StaticInnerClass();
-
- outerClass.test();
- }
-
- public void nonStaticMethod() {
- InnerClass innerClass = new InnerClass();
- System.out.println("nonStaticMethod...");
- }
-}
-```
-
-
-
-### final
-
-1. **基本数据**类型用final修饰,则不能修改,是常量;**对象引用**用final修饰,则引用只能指向该对象,不能指向别的对象,但是对象本身可以修改。
-
-2. final修饰的方法不能被子类重写
-
-3. final修饰的类不能被继承。
-
-
-
-### this
-
- `this.属性名称`指访问类中的成员变量,可以用来区分成员变量和局部变量。如下代码所示,`this.name`访问类Person当前实例的变量。
-
-```java
-/**
- * @description:
- * @author: 程序员大彬
- * @time: 2021-08-17 00:29
- */
-public class Person {
- String name;
- int age;
-
- public Person(String name, int age) {
- this.name = name;
- this.age = age;
- }
-}
-```
-
-`this.方法名称`用来访问本类的方法。以下代码中,`this.born()`调用类 Person 的当前实例的方法。
-
-```java
-/**
- * @description:
- * @author: 程序员大彬
- * @time: 2021-08-17 00:29
- */
-public class Person {
- String name;
- int age;
-
- public Person(String name, int age) {
- this.born();
- this.name = name;
- this.age = age;
- }
-
- void born() {
- }
-}
-```
-
-
-
-### super
-
-super 关键字用于在子类中访问父类的变量和方法。
-
-```java
-class A {
- protected String name = "大彬";
-
- public void getName() {
- System.out.println("父类:" + name);
- }
-}
-
-public class B extends A {
- @Override
- public void getName() {
- System.out.println(super.name);
- super.getName();
- }
-
- public static void main(String[] args) {
- B b = new B();
- b.getName();
- }
- /**
- * 大彬
- * 父类:大彬
- */
-}
-```
-
-在子类B中,我们重写了父类的getName()方法,如果在重写的getName()方法中我们要调用父类的相同方法,必须要通过super关键字显式指出。
-
-## object常用方法
-
-Java面试经常会出现的一道题目,Object的常用方法。下面给大家整理一下。
-
-object常用方法有:toString()、equals()、hashCode()、clone()等。
-
-### toString
-
-默认输出对象地址。
-
-```java
-public class Person {
- private int age;
- private String name;
-
- public Person(int age, String name) {
- this.age = age;
- this.name = name;
- }
-
- public static void main(String[] args) {
- System.out.println(new Person(18, "程序员大彬").toString());
- }
- //output
- //me.tyson.java.core.Person@4554617c
-}
-```
-
-可以重写toString方法,按照重写逻辑输出对象值。
-
-```java
-public class Person {
- private int age;
- private String name;
-
- public Person(int age, String name) {
- this.age = age;
- this.name = name;
- }
-
- @Override
- public String toString() {
- return name + ":" + age;
- }
-
- public static void main(String[] args) {
- System.out.println(new Person(18, "程序员大彬").toString());
- }
- //output
- //程序员大彬:18
-}
-```
-
-### equals
-
-默认比较两个引用变量是否指向同一个对象(内存地址)。
-
-```java
-public class Person {
- private int age;
- private String name;
-
- public Person(int age, String name) {
- this.age = age;
- this.name = name;
- }
-
- public static void main(String[] args) {
- String name = "程序员大彬";
- Person p1 = new Person(18, name);
- Person p2 = new Person(18, name);
-
- System.out.println(p1.equals(p2));
- }
- //output
- //false
-}
-```
-
-可以重写equals方法,按照age和name是否相等来判断:
-
-```java
-public class Person {
- private int age;
- private String name;
-
- public Person(int age, String name) {
- this.age = age;
- this.name = name;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof Person) {
- Person p = (Person) o;
- return age == p.age && name.equals(p.name);
- }
- return false;
- }
-
- public static void main(String[] args) {
- String name = "程序员大彬";
- Person p1 = new Person(18, name);
- Person p2 = new Person(18, name);
-
- System.out.println(p1.equals(p2));
- }
- //output
- //true
-}
-```
-
-### hashCode
-
-将与对象相关的信息映射成一个哈希值,默认的实现hashCode值是根据内存地址换算出来。
-
-```java
-public class Cat {
- public static void main(String[] args) {
- System.out.println(new Cat().hashCode());
- }
- //out
- //1349277854
-}
-```
-
-### clone
-
-java赋值是复制对象引用,如果我们想要得到一个对象的副本,使用赋值操作是无法达到目的的。Object对象有个clone()方法,实现了对
-
-象中各个属性的复制,但它的可见范围是protected的。
-
-```java
-protected native Object clone() throws CloneNotSupportedException;
-```
-
-所以实体类使用克隆的前提是:
-
-- 实现Cloneable接口,这是一个标记接口,自身没有方法,这应该是一种约定。调用clone方法时,会判断有没有实现Cloneable接口,没有实现Cloneable的话会抛异常CloneNotSupportedException。
-- 覆盖clone()方法,可见性提升为public。
-
-```java
-public class Cat implements Cloneable {
- private String name;
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
- return super.clone();
- }
-
- public static void main(String[] args) throws CloneNotSupportedException {
- Cat c = new Cat();
- c.name = "程序员大彬";
- Cat cloneCat = (Cat) c.clone();
- c.name = "大彬";
- System.out.println(cloneCat.name);
- }
- //output
- //程序员大彬
-}
-```
-
-#### 浅拷贝
-
-拷⻉对象和原始对象的引⽤类型引用同⼀个对象。
-
-以下例子,Cat对象里面有个Person对象,调用clone之后,克隆对象和原对象的Person引用的是同一个对象,这就是浅拷贝。
-
-```java
-public class Cat implements Cloneable {
- private String name;
- private Person owner;
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
- return super.clone();
- }
-
- public static void main(String[] args) throws CloneNotSupportedException {
- Cat c = new Cat();
- Person p = new Person(18, "程序员大彬");
- c.owner = p;
-
- Cat cloneCat = (Cat) c.clone();
- p.setName("大彬");
- System.out.println(cloneCat.owner.getName());
- }
- //output
- //大彬
-}
-```
-
-#### 深拷贝
-
-拷贝对象和原始对象的引用类型引用不同的对象。
-
-以下例子,在clone函数中不仅调用了super.clone,而且调用Person对象的clone方法(Person也要实现Cloneable接口并重写clone方法),从而实现了深拷贝。可以看到,拷贝对象的值不会受到原对象的影响。
-
-```java
-public class Cat implements Cloneable {
- private String name;
- private Person owner;
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
- Cat c = null;
- c = (Cat) super.clone();
- c.owner = (Person) owner.clone();//拷贝Person对象
- return c;
- }
-
- public static void main(String[] args) throws CloneNotSupportedException {
- Cat c = new Cat();
- Person p = new Person(18, "程序员大彬");
- c.owner = p;
-
- Cat cloneCat = (Cat) c.clone();
- p.setName("大彬");
- System.out.println(cloneCat.owner.getName());
- }
- //output
- //程序员大彬
-}
-```
-
-### getClass
-
-返回此 Object 的运行时类,常用于java反射机制。
-
-```java
-public class Person {
- private String name;
-
- public Person(String name) {
- this.name = name;
- }
-
- public static void main(String[] args) {
- Person p = new Person("程序员大彬");
- Class clz = p.getClass();
- System.out.println(clz);
- //获取类名
- System.out.println(clz.getName());
- }
- /**
- * class com.tyson.basic.Person
- * com.tyson.basic.Person
- */
-}
-```
-
-### wait
-
-当前线程调用对象的wait()方法之后,当前线程会释放对象锁,进入等待状态。等待其他线程调用此对象的notify()/notifyAll()唤醒或者等待超时时间wait(long timeout)自动唤醒。线程需要获取obj对象锁之后才能调用 obj.wait()。
-
-### notity
-
-obj.notify()唤醒在此对象上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象上等待的所有线程。
-
-> LockSupport.park()/LockSupport.parkNanos(long nanos)/LockSupport.parkUntil(long deadlines),用于阻塞当前线程。对比obj.wait()方法,不需要获得锁就可以让线程进入等待状态,需要通过LockSupport.unpark(Thread thread)唤醒。
-
-### equals()和hashcode()的关系
-
-equals与hashcode的关系:
-1、如果两个对象调用equals比较返回true,那么它们的hashCode值一定要相同;
-2、如果两个对象的hashCode相同,它们并不一定相同。
-
-hashcode方法主要是用来提升对象比较的效率,先进行hashcode()的比较,如果不相同,那就不必在进行equals的比较,这样就大大减少了equals比较的次数,当比较对象的数量很大的时候能提升效率。
-
-之所以重写equals()要重写hashcode(),是为了保证equals()方法返回true的情况下hashcode值也要一致,如果重写了equals()没有重写hashcode(),就会出现两个对象相等但hashcode()不相等的情况。这样,当用其中的一个对象作为键保存到hashMap、hashTable或hashSet中,再以另一个对象作为键值去查找他们的时候,则会查找不到。
-
-### ==和equals区别
-
-- 对于基本数据类型,==比较的是他们的值。基本数据类型没有equal方法;
-
-- 对于复合数据类型,==比较的是它们的存放地址(是否是同一个对象)。equals()默认比较地址值,重写的话按照重写逻辑去比较。
-
-# 面向对象
-
-## 面向对象特性
-
-面向对象四大特性:封装,继承,多态,抽象
-
-- 封装就是将类的信息隐藏在类内部,不允许外部程序直接访问,而是通过该类的方法实现对隐藏信息的操作和访问。 良好的封装能够减少耦合。
-- 继承是从已有的类中派生出新的类,新的类继承父类的属性和行为,并能扩展新的能力,大大增加程序的重用性和易维护性。在Java中是单继承的,也就是说一个子类只有一个父类。
-- 多态是同一个行为具有多个不同表现形式的能力。在不修改程序代码的情况下改变程序运行时绑定的代码。
- 实现多态的三要素:继承、重写、父类引用指向子类对象。
- 静态多态性:通过重载实现,相同的方法有不同的參数列表,可以根据参数的不同,做出不同的处理。
- 动态多态性:在子类中重写父类的方法。运行期间判断所引用对象的实际类型,根据其实际类型调用相应的方法。
-- 抽象。把客观事物用代码抽象出来。
-
-### 多态怎么实现
-
-Java提供了编译时多态和运行时多态两种多态机制。编译时多态通过重载实现,根据传入参数不同调用不同的方法。运行时多态通过重写来实现,在子类中重写父类的方法,运行期间判断所引用对象的实际类型,根据其实际类型调用相应的方法。
-
-## 类与对象
-
-**类class**对一类事物的描述,是抽象的、概念上的定义。类的结构包括属性和方法。
-
-```java
-public class Person {
- //属性
- private int age;
- private String name;
-
- //构造方法
- public Person(int age, String name) {
- this.age = age;
- this.name = name;
- }
-}
-```
-
-对象是实际存在的该类事物的个体,也称为实例。
-
-```java
-public class Person {
- private int age;
- private String name;
-
- public Person(int age, String name) {
- this.age = age;
- this.name = name;
- }
-
- public static void main(String[] args) {
- //p为对象
- Person p = new Person(18, "程序员大彬");
- }
-}
-```
-
-在Java中,万物皆对象,一切事物都可以看做对象。上述代码通过new关键字,创建了Person对象,p为对象的引用,p指向所创建的Person对象的内存地址。
-
-## 属性
-
-属性用来描述对象的状态信息,通常以变量的形式进行定义。变量分为成员变量和局部变量。
-
-在类中,方法体之外定义的变量称为成员变量:
-
-- 成员变量定义在类中,在整个类中都可以被访问
-- 成员变量分为类变量和实例变量,实例变量存在于每个对象所在的堆内存中。实例变量要创建对象后才能访问。
-- 成员变量有默认初始化值
-- 成员变量的权限修饰符可以指定
-
-定义在方法内,代码块内的变量称为局部变量:
-
-- 局部变量存在于栈内存中
-- 局部变量作用范围结束,变量的内存空间回自动释放
-- 局部变量没有默认值,每次必须显示初始化
-- 局部变量声明时不指定权限修饰符
-
-## 方法
-
-描述的是对象的动作信息,方法也称为函数。
-
-### 普通方法
-
-普通通方法的语法结构:
-
-```java
-[修饰符列表] 返回值类型 方法名(形参列表){
- //方法体
-}
-```
-
-定义方法可以将功能代码进行封装。便于该功能进行复用。方法只有被调用才会被执行。
-
-### 构造方法
-
-构造方法是一种比较特殊的方法,通过构造方法可以创建对象以及初始化实例变量。实例变量没有手动赋值的时候,系统会赋默认值。
-
-```java
-public class Person {
- //属性
- private int age;
- private String name;
-
- //构造方法
- public Person(int age, String name) {
- this.age = age;
- this.name = name;
- }
-}
-```
-
-## 方法重载
-
-同个类中的多个方法可以有相同的方法名称,但是有不同的参数列表,这就称为方法重载。参数列表又叫参数签名,包括参数的类型、参数的个数、参数的顺序,只要有一个不同就叫做参数列表不同。
-
-重载是面向对象的一个基本特性。
-
-```java
-public class OverrideTest {
- void setPerson() { }
-
- void setPerson(String name) {
- //set name
- }
-
- void setPerson(String name, int age) {
- //set name and age
- }
-}
-```
-
-## 方法重写
-
-方法的重写描述的是父类和子类之间的。当父类的功能无法满足子类的需求,可以在子类对方法进行重写。
-
-方法重写时, 方法名与形参列表必须一致。
-
-如下代码,Person为父类,Student为子类,在Student中重写了dailyTask方法。
-
-```java
-public class Person {
- private String name;
-
- public void dailyTask() {
- System.out.println("work eat sleep");
- }
-}
-
-
-public class Student extends Person {
- @Override
- public void dailyTask() {
- System.out.println("study eat sleep");
- }
-}
-```
-
-## 初始化顺序
-
-Java中类初始化顺序:
-
-1. 静态属性,静态代码块。
-2. 普通属性,普通代码块。
-3. 构造方法。
-
-```java
-public class LifeCycle {
- // 静态属性
- private static String staticField = getStaticField();
-
- // 静态代码块
- static {
- System.out.println(staticField);
- System.out.println("静态代码块初始化");
- }
-
- // 普通属性
- private String field = getField();
-
- // 普通代码块
- {
- System.out.println(field);
- System.out.println("普通代码块初始化");
- }
-
- // 构造方法
- public LifeCycle() {
- System.out.println("构造方法初始化");
- }
-
- // 静态方法
- public static String getStaticField() {
- String statiFiled = "静态属性初始化";
- return statiFiled;
- }
-
- // 普通方法
- public String getField() {
- String filed = "普通属性初始化";
- return filed;
- }
-
- public static void main(String[] argc) {
- new LifeCycle();
- }
-
- /**
- * 静态属性初始化
- * 静态代码块初始化
- * 普通属性初始化
- * 普通代码块初始化
- * 构造方法初始化
- */
-}
-```
-
-# 接口和抽象类
-
-Java中接口和抽象类的定义语法分别为interface与abstract关键字。
-
-## 抽象类
-
-首先了解一下抽象方法。抽象方法是一种特殊的方法:它只有声明,而没有具体的实现。抽象方法的格式为:
-
-```java
-abstract void method();
-```
-
-抽象方法必须用abstract关键字进行修饰。如果一个类含有抽象方法,则称这个类为抽象类,抽象类必须在类前用abstract关键字修饰。因为抽象类中含有未实现的方法,所以不能用抽象类创建对象。
-
-抽象类的特点:
-
-- 抽象类不能被实例化只能被继承;
-- 包含抽象方法的一定是抽象类,但是抽象类不一定含有抽象方法;
-- 抽象类中的抽象方法的修饰符只能为public或者protected,默认为public。
-
-## 接口
-
-接口对行为的抽象。在Java中,定一个接口的形式如下:
-
-```java
-public interface InterfaceName {
-}
-```
-
-接口中可以含有变量和方法。
-
-**接口的特点:**
-
-- 接口中的变量会被隐式指定为public static final类型,而方法会被隐式地指定为public abstract类型;
-- 接口中的方法必须是抽象方法,所有的方法不能有具体的实现;
-- 一个类可以实现多个接口。
-
-**接口的用处**
-
-接口是对行为的抽象,通过接口可以实现不相关类的相同行为。通过接口可以知道一个类具有哪些行为特性。
-
-## 接口与抽象类区别
-
-- 语法层面上
- 1)抽象类可以有方法实现,而接口的方法中只能是抽象方法;
- 2)抽象类中的成员变量可以是各种类型的,接口中的成员变量只能是public static final类型;
- 3)接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
- 4)一个类只能继承一个抽象类,而一个类却可以实现多个接口。
-
-- 设计层面上的区别
- 1)抽象层次不同。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口只是对类行为进行抽象。继承抽象类是一种"是不是"的关系,而接口实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是具备不具备的关系,比如鸟是否能飞。
- 2) 继承抽象类的是具有相似特点的类,而实现接口的却可以不同的类。
-
- 门和警报的例子:
-
- ```java
- class AlarmDoor extends Door implements Alarm {
- //code
- }
-
- class BMWCar extends Car implements Alarm {
- //code
- }
- ```
-
-# 反射
-
-Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,并且能改变它的属性。
-
-作用:可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等。需要注意的是反射使用不当会造成很高的资源消耗!
-
-## Class类
-
-在Java中,每定义一个Java class实体都会产生一个Class对象。这个Class对象用于表示这个类的类型信息。
-
-获取Class对象的三种方式:
-
-```java
-//1、通过对象调用 getClass() 方法来获取,通常应用在:比如你传过来一个 Object
-// 类型的对象,而我不知道你具体是什么类,用这种方法
- Person p1 = new Person();
- Class c1 = p1.getClass();
-
-//2、直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高
-// 这说明任何一个类都有一个隐含的静态成员变量 class
- Class c2 = Person.class;
-
-//3、通过 Class 对象的 forName() 静态方法来获取,用的最多,
-// 但可能抛出 ClassNotFoundException 异常
- Class c3 = Class.forName("com.ys.reflex.Person");
-```
-
-Class 类提供了一些方法,可以获取成员变量、成员方法、接口、超类、构造方法:
-
-```java
- getName():获得类的完整名字。
- getFields():获得类的public类型的属性。
- getDeclaredFields():获得类的所有属性。包括private 声明的和继承类
- getMethods():获得类的public类型的方法。
- getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类
- getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。
- getConstructors():获得类的public类型的构造方法。
- getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
- newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
-```
-
-## Field类
-
-Field提供了类和接口中字段的信息,通过Field类可以动态访问这些字段。下图是Field类提供的一些方法。
-
-
-
-## Method类
-
-Method类位于 java.lang.reflect 包中,主要用于在程序运行状态中,动态地获取方法信息。
-
-Method类常用的方法:
-
-1. getAnnotation(Class annotationClass):如果该方法对象存在指定类型的注解,则返回该注解,否则返回null。
-2. getName():返回方法对象名称。
-3. isAnnotationPresent(Class extends Annotation> annotationClass):如果该方法对象上有指定类型的注解,则返回true,否则为false。
-4. getDeclaringClass ():返回该方法对象表示的方法所在类的Class对象。
-5. getParameters():返回一个参数对象数组,该数组表示该方法对象的所有参数。
-6. getReturnType():返回一个Class对象,该Class对象表示该方法对象的返回对象,会擦除泛型。
-
-# 泛型
-
-泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。编译时会进行类型擦除。在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
-
-## 泛型类
-
-泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map。
-
-泛型类示例:
-
-```java
-//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
-//在实例化泛型类时,必须指定T的具体类型
-public class Generic{
- //key这个成员变量的类型为T,T的类型由外部指定
- private T key;
-
- public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
- this.key = key;
- }
-
- public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
- return key;
- }
-}
-```
-
-泛型类的使用:
-
-```java
-@Slf4j
-public class GenericTest {
- public static void main(String[] args) {
- //泛型的类型参数只能是类类型(包括自定义类),不能是简单类型
- //传入的实参类型需与泛型的类型参数类型相同,即为Integer.
- Generic genericInteger = new Generic(666);
-
- //传入的实参类型需与泛型的类型参数类型相同,即为String.
- Generic genericString = new Generic("程序员大彬");
- log.info("泛型测试: key is {}", genericInteger.getKey());
- log.info("泛型测试: key is {}", genericString.getKey());
- }
-
- /**
- * output
- * 23:51:55.519 [main] INFO com.tyson.generic.GenericTest - 泛型测试: key is 666
- * 23:51:55.526 [main] INFO com.tyson.generic.GenericTest - 泛型测试: key is 程序员大彬
- */
-}
-```
-
-## 泛型接口
-
-泛型接口与泛型类的定义及使用基本相同。
-
-```java
-//定义一个泛型接口
-public interface Generator {
- public T next();
-}
-```
-
-## 泛型方法
-
-泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型 。
-
-```java
-@Slf4j
-public class GenericMethod {
-
- /**
- * 泛型方法的基本介绍
- * @param t 传入的泛型实参
- * @return T 返回值为T类型
- * 说明:
- * 1)public 与 返回值中间非常重要,可以理解为声明此方法为泛型方法。
- * 2)只有声明了的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
- * 3)表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
- * 4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
- */
- public void genericMethod(T t) {
- log.info(t.toString());
- }
-
- public static void main(String[] args) {
- GenericMethod genericMethod = new GenericMethod();
- genericMethod.genericMethod("程序员大彬");
- genericMethod.genericMethod(666);
- }
-
- /**
- * output
- * 23:59:11.906 [main] INFO com.tyson.generic.GenericMethod - 程序员大彬
- * 23:59:11.912 [main] INFO com.tyson.generic.GenericMethod - 666
- */
-}
-```
-
-
-
-# Exception
-
-在 JAVA 语言中,将程序执行中发生的不正常情况称为异常。异常是程序经常会出现的情况,发现错误的最佳时机是在编译阶段,也就是运行程序之前。
-
-所有异常都继承了 Throwable。
-
-## Throwable
-
-Throwable类是Error和Exception的父类,只有继承于Throwable的类或者其子类才能被抛出。Throwable分为两类:
-
-
-
-- Error:JVM 无法解决的严重问题,如栈溢出(StackOverflowError)、内存溢出(OOM)等。程序无法处理的错误。
-
- 栈溢出:如下代码递归调用main,最终会抛出StackOverflowError。
-
- ```java
- public class Test {
- public static void main(String[] args) {
- main(args);
- }
- /**
- * Exception in thread "main" java.lang.StackOverflowError
- */
- }
- ```
-
- 内存溢出OOM:下面的代码中,新建了一个数组,数组长度是 1G,又因为是 Integer 包装类型,一个元素占 4 个字节,所以,这个数组占用内存 4GB。最后,堆内存空间不足,抛出了 OOM。
-
- ```java
- public class Test {
- public static void main(String[] args) {
- Integer[] arr = new Integer[1024 * 1024 * 1024];
- }
- /**
- * Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
- */
- }
- ```
-
-- Exception:其它因编程错误或偶然的外在因素导致的一般性问题。可以在代码中进行处理。如:空指针异常、数组下标越界等。
-
-unchecked exception包括RuntimeException和Error类,其他所有异常称为检查(checked)异常。
-
-运行时异常和非运行时异常(checked)的区别:
-
-1. RuntimeException由程序错误导致,应该修正程序避免这类异常发生。
-2. checked Exception由具体的环境(读取的文件不存在或文件为空或sql异常)导致的异常。必须进行处理,不然编译不通过,可以catch或者throws。
-
-## 常见的Exception
-
-常见的RuntimeException:
-
-```java
-ClassCastException //类型转换异常
-IndexOutOfBoundsException //数组越界异常
-NullPointerException //空指针
-ArrayStoreException //数组存储异常
-NumberFormatException //数字格式化异常
-ArithmeticException //数学运算异常
-```
-
-unchecked Exception:
-
-```
-NoSuchFieldException //反射异常,没有对应的字段
-ClassNotFoundException //类没有找到异常
-IllegalAccessException //安全权限异常,可能是反射时调用了private方法
-```
-
-## 关键字
-
-- **throw**:用于抛出一个具体的异常对象。
-
-- **throws**:用在方法签名中,用于声明该方法可能抛出的异常。子类方法抛出的异常范围更加小,或者根本不抛异常。
-
-- **try**:用于监听。将可能抛出异常的代码放在try语句块之中,当try语句块内发生异常时,异常就被抛出。
-- **catch**:用于捕获异常。
-- **finally**:finally语句块总是会被执行。它主要用于回收在try块里打开的资源(如数据库连接、网络连接和磁盘文件)。
-
-如下代码示例,除以0抛出异常,发生异常之后的代码不会执行,直接跳到catch语句块执行,最后执行finally语句块。
-
-```java
-public class ExceptionTest {
- public static void main(String[] args) {
- try {
- int i = 1 / 0;
- System.out.println(i + 1);
- } catch (Exception e) {
- System.out.println(e.getMessage());
- } finally {
- System.out.println("run finally...");
- }
- }
- /**
- * / by zero
- * run finally...
- */
-}
-```
-
-
-
-# IO流
-
-Java IO流的核心就是对文件的操作,对于字节 、字符类型的输入和输出流。IO流主要分为两大类,字节流和字符流。字节流可以处理任何类型的数据,如图片,视频等,字符流只能处理字符类型的数据。
-
-
-
-> 图片参考:[Java io学习整理](https://zhuanlan.zhihu.com/p/25418336)
-
-## InputStream 和 OutputStream
-
-InputStream 用来表示那些从不同数据源产生输入的类。这些数据源包括:1.字节数组;2.String 对象;3.文件;4.管道;5.一个由其他种类的流组成的序列。
-
-InputStream 类有一个抽象方法:`abstract int read()`,这个方法将读入并返回一个字节,或者在遇到输入源结尾时返回-1。
-
-OutputStream 决定了输出所要去的目标:字节数组、文件或管道。OutputStream 的 `abstract void write(int b)` 可以向某个输出位置写出一个字节。
-
-read() 和 write() 方法在执行时都将阻塞,等待数据被读入或者写出。
-
-常用的字节流有FileInputStream、FileOutputStream、ObjectInputStream、ObjectOutputStream。
-
-## Reader 和 Writer
-
-字符流是由通过字节流转换得到的,转化过程耗时,而且容易出现乱码问题。I/O 流提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。如果音频文件、图片等媒体文件用字节流比较好,如果涉及到字符的话使用字符流比较好。
-
-```java
-abstract int read();
-abstract void write(char c);
-```
-
-## 字符流和字节流的转换
-
-InputStreamReader:字节到字符的转换,可对读取到的字节数据经过指定编码转换成字符。
-
-OutputStreamWriter:字符到字节的转换,可对读取到的字符数据经过指定编码转换成字节。
-
-## 同步异步
-
-同步:发出一个调用时,在没有得到结果之前,该调用就不返回。
-
-异步:在调用发出后,被调用者返回结果之后会通知调用者,或通过回调函数处理这个调用。
-
-## 阻塞非阻塞
-
-阻塞和非阻塞关注的是线程的状态。
-
-阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会恢复运行。
-
-非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。
-
-> 举个例子,理解下同步、阻塞、异步、非阻塞的区别:
->
-> 同步就是烧开水,要自己来看开没开;异步就是水开了,然后水壶响了通知你水开了(回调通知)。阻塞是烧开水的过程中,你不能干其他事情,必须在旁边等着;非阻塞是烧开水的过程里可以干其他事情。
-
-## BIO
-
-同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。
-
-
-
-## NIO
-
-NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了 NIO 框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。
-
-
-
-NIO与IO区别:
-
-- IO是面向流的,NIO是面向缓冲区的;
-- IO流是阻塞的,NIO流是不阻塞的;
-- NIO有选择器,而IO没有。
-
-Buffer:Buffer用于和Channel交互。从Channel中读取数据到Buffer里,从Buffer把数据写入到Channel。
-
-Channel:NIO 通过Channel(通道) 进行读写。通道是双向的,可读也可写,而流的读写是单向的。无论读写,通道只能和Buffer交互。
-
-Selector:使用更少的线程来就可以来处理通道了,相比使用多个线程,避免了线程上下文切换带来的开销。
-
-## AIO
-
-异步非阻塞 IO。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。
-
-## BIO/NIO/AIO区别
-
-同步阻塞IO : 用户进程发起一个IO操作以后,必须等待IO操作的真正完成后,才能继续运行。
-
-同步非阻塞IO: 客户端与服务器通过Channel连接,采用多路复用器轮询注册的Channel。提高吞吐量和可靠性。用户进程发起一个IO操作以后,可做其它事情,但用户进程需要轮询IO操作是否完成,这样造成不必要的CPU资源浪费。
-
-异步非阻塞IO: 非阻塞异步通信模式,NIO的升级版,采用异步通道实现异步通信,其read和write方法均是异步方法。用户进程发起一个IO操作,然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知。类比Future模式。
-
-
-
-# ThreadLocal
-线程本地变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程。
-
-## 原理
-
-每个线程都有一个ThreadLocalMap(ThreadLocal内部类),Map中元素的键为ThreadLocal,而值对应线程的变量副本。
-
-
-
-调用threadLocal.set()-->调用getMap(Thread)-->返回当前线程的ThreadLocalMap-->map.set(this, value),this是ThreadLocal
-
-```java
-public void set(T value) {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null)
- map.set(this, value);
- else
- createMap(t, value);
-}
-
-ThreadLocalMap getMap(Thread t) {
- return t.threadLocals;
-}
-
-void createMap(Thread t, T firstValue) {
- t.threadLocals = new ThreadLocalMap(this, firstValue);
-}
-```
-调用get()-->调用getMap(Thread)-->返回当前线程的ThreadLocalMap-->map.getEntry(this),返回value
-
-```java
- public T get() {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null) {
- ThreadLocalMap.Entry e = map.getEntry(this);
- if (e != null) {
- @SuppressWarnings("unchecked")
- T result = (T)e.value;
- return result;
- }
- }
- return setInitialValue();
- }
-```
-
-threadLocals的类型ThreadLocalMap的键为ThreadLocal对象,因为每个线程中可有多个threadLocal变量,如longLocal和stringLocal。
-
-```
-public class ThreadLocalDemo {
- ThreadLocal