diff --git a/README.md b/README.md index ed285aa4..7a73df4f 100644 --- a/README.md +++ b/README.md @@ -1,318 +1,15 @@ -项目对应 WebSIte:https://duhouan.github.io/Java/#/ +本仓库是对 Java 的一些基础知识、数据库知识、以及框架知识进行收集、整理(持续更新中)。
+仓库对应 WebSite:https://duhouan.github.io/Java/#
+如何流畅访问 Github -# ✏️ 计算机基础 +
-## 1. 计算机网络 +
+ + + +

-- [第一节 概述](docs/Net/1_概述.md) -- [第二节 物理层](docs/Net/2_物理层.md) -- [第三节 数据链路层](docs/Net/3_数据链路层.md) -- [第四节 网络层](docs/Net/4_网络层.md) -- [第五节 运输层](docs/Net/5_运输层.md) -- [第六节 应用层](docs/Net/6_应用层.md) - -## 2. 操作系统 - -- [第一节 操作系统概述](docs/OS/1_操作系统概述.md) -- [第二节 进程管理](docs/OS/2_进程管理.md) -- [第三节 死锁](docs/OS/3_死锁.md) -- [第四节 内存管理](docs/OS/4_内存管理.md) -- [第五节 设备管理](docs/OS/4_设备管理.md) -- [第六节 链接](docs/OS/6_链接.md) - -## 3. 数据结构和算法 - -- [第一节 概述](docs/data_structure/1_概述.md) -- [第二节 线性表](docs/data_structure/2_线性表.md) -- [第三节 栈和队列](docs/data_structure/3_栈和队列.md) -- [第四节 树](docs/data_structure/4_树.md) -- [第五节 图](docs/data_structure/5_图.md) -- [第六节 集合和映射](docs/data_structure/6_集合和映射.md) -- [第七节 并查集](docs/data_structure/7_并查集.md) -- [第八节 优先队列和堆](docs/data_structure/8_优先队列和堆.md) -- [第九节 哈希表](docs/data_structure/9_哈希表.md) -- [第十节 排序](docs/data_structure/10_排序.md) -- [第十一节 线段树](docs/data_structure/11_线段树.md) -- [第十二节 Trie树](docs/data_structure/12_Trie树.md) -- [第十三节 AVL](docs/data_structure/13_AVL.md) -- [第十四节 红黑树](docs/data_structure/14_红黑树.md) - -## 4. HTTP - -- [第一节 HTTP概述](http/1_HTTP概述.md) -- [第二节 HTTP状态码](http/2_HTTP状态码.md) -- [第三节 具体应用](http/3_具体应用.md) -- [第四节 HTTPS](http/4_HTTPS.md) -- [第五节 get和post比较](http/5_get和post比较.md) - -## 5. Linux - -- [第一节 Linux概论](docs/Linux/1_Linux概论.md) -- [第二节 Linux文件系统](docs/Linux/2_Linux文件系统.md) -- [第三节 Linux常用命令](docs/Linux/3_Linux常用命令.md) -- [第四节 Liunx进程管理](docs/Linux/4_Liunx进程管理.md) -- [第五节 Linux压缩与打包](docs/Linux/5_Linux压缩与打包.md) - -# ☕️ Java - -## 1. Java 基础 - -- [第一节 数据类型](docs/JavaBasics/1_数据类型.md) -- [第二节 String](docs/JavaBasics/2_String.md) -- [第三节 运算](docs/JavaBasics/3_运算.md) -- [第四节 Object通用方法](docs/JavaBasics/4_Object通用方法.md) -- [第五节 关键字](docs/JavaBasics/5_关键字.md) -- [第六节 反射](docs/JavaBasics/6_反射.md) -- [第七节 异常](docs/JavaBasics/7_异常.md) -- [第八节 泛型](docs/JavaBasics/8_泛型.md) -- [第九节 注解](docs/JavaBasics/9_注解.md) -- [第十节 Java常见对象](docs/JavaBasics/10_Java常见对象.md) -- [第十一节 抽象类和接口](docs/JavaBasics/11_抽象类和接口.md) -- [第十二节 其他](docs/JavaBasics/12_其他.md) - -## 2. Java 容器 - -- [第一节 Java容器概览](docs/JavaContainer/1_Java容器概览.md) -- [第二节 容器中的设计模式](docs/JavaContainer/2_容器中的设计模式.md) -- [第三节 容器源码分析 - List](docs/JavaContainer/3_容器源码分析%20-%20List.md) -- [第四节 容器源码分析 - Map](docs/JavaContainer/4_容器源码分析%20-%20Map.md) -- [第五节 容器源码分析 - 并发容器](docs/JavaContainer/5_容器源码分析%20-%20并发容器.md) - -## 3. Java 虚拟机 - -- [第一节 运行时数据区域](docs/JVM/1_JVM.md) -- [第二节 HotSpot 虚拟机对象](docs/JVM/2_JVM.md) -- [第三节 String 类和常量池](docs/JVM/3_JVM.md) -- [第四节 8 种基本类型的包装类和常量池](docs/JVM/4_JVM.md) -- [第五节 垃圾收集](docs/JVM/5_JVM.md) -- [第六节 内存分配与回收策略](docs/JVM/6_JVM.md) -- [第七节 类加载机制](docs/JVM/7_JVM.md) - -## 4. Java 并发 - -- [第一节 基础知识](docs/Java_Concurrency/1_基础知识.md) -- [第二节 并发理论](docs/Java_Concurrency/2_并发理论.md) -- [第三节 并发关键字](docs/Java_Concurrency/3_并发关键字.md) -- [第四节 Lock 体系](docs/Java_Concurrency/4_Lock%20体系.md) -- [第五节 原子操作类](docs/Java_Concurrency/5_原子操作类.md) -- [第六节 并发容器](docs/Java_Concurrency/6_并发容器.md) -- [第七节 并发工具](docs/Java_Concurrency/7_并发工具.md) -- [第八节 线程池](docs/Java_Concurrency/8_线程池.md) -- [第九节 并发实践](docs/Java_Concurrency/9_并发实践.md) - -## 5. JavaIO - -- [第一节 概览](docs/JavaIO/1_概览.md) -- [第二节 磁盘操作](docs/JavaIO/2_磁盘操作.md) -- [第三节 字节操作](docs/JavaIO/3_字节操作.md) -- [第四节 字符操作](docs/JavaIO/4_字符操作.md) -- [第五节 对象操作](docs/JavaIO/5_对象操作.md) -- [第六节 网络操作](docs/JavaIO/6_网络操作.md) -- [第七节 NIO](docs/JavaIO/7_NIO.md) -- [第八节 JavaIO方式](docs/JavaIO/8_JavaIO方式.md) - -## 6. 正则表达式 - -- [第一节 概述](docs/Regex/1_概述.md) -- [第二节 应用](docs/Regex/2_应用.md) - -# 👫 面向对象 - -## 1. 设计模式 - -- [第一节 概述](docs/OO/1_概述.md) -- [第二节 创建型](docs/OO/2_创建型.md) -- [第三节 行为型](docs/OO/3_行为型.md) -- [第四节 结构型](docs/OO/4_结构型.md) - -## 2. 面向对象思想 - -- [第一节 面向对象三大特性](docs/OO/5_面向对象三大特性.md) -- [第二节 关系类图](docs/OO/6_关系类图.md) -- [第三节 面向对象设计原则](docs/OO/7_面向对象设计原则.md) - -# 📝 编程题 - -## 1. 剑指 Offer 编程题 - -> **数据结构相关** - -- [第一节 数组和矩阵](docs/AimForOffer/数据结构相关/1_数组和矩阵.md) -- [第二节 字符串](docs/AimForOffer/数据结构相关/2_字符串.md) -- [第三节 链表](docs/AimForOffer/数据结构相关/3_链表.md) -- [第四节 树](docs/AimForOffer/数据结构相关/4_树.md) -- [第五节 栈](docs/AimForOffer/数据结构相关/5_栈.md) -- [第六节 队列](docs/AimForOffer/数据结构相关/6_队列.md) -- [第七节 堆](docs/AimForOffer/数据结构相关/7_堆.md) -- [第八节 哈希.](docs/AimForOffer/数据结构相关/8_哈希.md) - -> **算法相关** - -- [第一节 查找](docs/AimForOffer/算法思想相关/1_查找.md) -- [第二节 排序](docs/AimForOffer/算法思想相关/2_排序.md) -- [第三节 动态规划](docs/AimForOffer/算法思想相关/3_动态规划.md) -- [第四节 回溯](docs/AimForOffer/算法思想相关/4_回溯.md) -- [第五节 深度优先](docs/AimForOffer/算法思想相关/5_深度优先.md) -- [第六节 贪心](docs/AimForOffer/算法思想相关/6_贪心.md) -- [第七节 数学运算](docs/AimForOffer/算法思想相关/7_数学运算.md) -- [第八节 其他](docs/AimForOffer/算法思想相关/8_其他.md) - -## 2. LeetCode 编程题 - -> **数据结构相关** - -- [第一节 数组问题](docs/LeetCode/数据结构相关/1_数组问题.md) -- [第二节 链表问题](docs/LeetCode/数据结构相关/2_链表问题.md) -- [第三节 栈和队列](docs/LeetCode/数据结构相关/3_栈和队列.md) -- [第四节 二叉树](docs/LeetCode/数据结构相关/4_二叉树.md) -- [第五节 字符串](docs/LeetCode/数据结构相关/5_字符串.md) -- [第六节 哈希](docs/LeetCode/数据结构相关/6_哈希.md) -- [第七节 图](docs/LeetCode/数据结构相关/7_图.md) -- [第八节 数据结构设计](docs/LeetCode/数据结构相关/8_数据结构设计.md) - -> **算法思想相关** - -- [第一节 排序](docs/LeetCode/算法思想相关/1_排序.md) -- [第二节 分治思想](docs/LeetCode/算法思想相关/2_分治思想.md) -- [第三节 贪心思想](docs/LeetCode/算法思想相关/3_贪心思想.md) -- [第四节 LRU](docs/LeetCode/算法思想相关/4_LRU.md) -- [第五节 DFS](docs/LeetCode/算法思想相关/5_DFS.md) -- [第六节 回溯法](docs/LeetCode/算法思想相关/6_回溯法.md) -- [第七节 动态规划](docs/LeetCode/算法思想相关/7_动态规划.md) -- [第八节 数学问题](docs/LeetCode/算法思想相关/8_数学问题.md) - -# 💾 数据库 - -## 1. DataBase - -- [第一节 数据库系统原理](docs/DataBase/1_数据库系统原理.md) -- [第二节 关系数据库设计理论](docs/DataBase/2_关系数据库设计理论.md) -- [第三节 设计关系型数据库](docs/DataBase/3_设计关系型数据库.md) -- [第四节 SQL](docs/DataBase/4_SQL.md) -- [第五节 LeetCode_Database题解](docs/DataBase/5_LeetCode_Database题解.md) - -## 2. MySQL - -- [第一节 锁机制](docs/MySQL/1_锁机制.md) -- [第二节 事务隔离级别实现](docs/MySQL/2_事务隔离级别实现.md) -- [第三节 索引](docs/MySQL/3_索引.md) -- [第四节 MySQL架构](docs/MySQL/4_MySQL架构.md) -- [第五节 MySQL优化](docs/MySQL/5_MySQL优化.md) -- [第六节 补充知识](docs/MySQL/6_补充知识.md) - -## *3. Redis - -- [第一节 Redis初探](docs/Redis/1_Redis初探.md) -- [第二节 Redis持久化](docs/Redis/2_Redis持久化.md) -- [第三节 Redis复制](docs/Redis/3_Redis复制.md) -- [第四节 处理系统故障](docs/Redis/4_处理系统故障.md) -- [第五节 Redis事务](docs/Redis/5_Redis事务.md) -- [第六节 Redis性能方面注意事项](docs/Redis/6_Redis性能方面注意事项.md) -- [第七节 降低内存占用](docs/Redis/7_降低内存占用.md) -- [第八节 简单点赞系统](docs/Redis/8_简单点赞系统) - -## 4. 海量数据处理 - -- [第一节 概述](docs/MassDataProcessing/1_概述.md) -- [第二节 哈希分治](docs/MassDataProcessing/2_哈希分治.md) -- [第三节 位图](docs/MassDataProcessing/3_位图.md) -- [第四节 布隆过滤器](docs/MassDataProcessing/4_布隆过滤器.md) -- [第五节 Trie树](docs/MassDataProcessing/5_Trie树.md) -- [第六节 数据库](docs/MassDataProcessing/6_数据库.md) -- [第七节 倒排索引](docs/MassDataProcessing/7_倒排索引.md) - -# 🔨 消息中间件 - -## 1. Kafka - -- [第一节 消息队列](docs/Kafka/1_消息队列.md) -- [第二节 Kafka的架构](docs/Kafka/2_Kafka的架构.md) -- [第三节 Kafka的高可用原理](docs/Kafka/3_Kafka的高可用原理.md) -- [第四节 Kafka中一些常见问题](docs/Kafka/4_Kafka中一些常见问题.md) -- [第五节 Kafka特点](docs/Kafka/5_Kafka特点.md) - -## 2. RabbitMQ - -- [第一节 主流消息中间件](docs/RabbitMQ/1_主流消息中间件.md) -- [第二节 RabbitMQ概述](docs/RabbitMQ/2_RabbitMQ概述.md) -- [第三节 RabbitMQ入门](docs/RabbitMQ/3_RabbitMQ入门.md) -- [第四节 RabbitMQ高级特性](docs/RabbitMQ/4_RabbitMQ高级特性.md) -- [第五节 RabbitMQ整合SpringAMQP](docs/RabbitMQ/5_RabbitMQ整合SpringAMQP.md) -- [第六节 RabbitMQ整合SpringBoot](docs/RabbitMQ/6_RabbitMQ整合SpringBoot.md) -- [RabbitMQ 官网](https://www.rabbitmq.com/) - -# 📖 系统设计 - -## 1. 常用框架 - -- [第一节 SpringMVC](docs/SSM/1_SpringMVC.md) -- [第二节 SpringIOC](docs/SSM/2_SpringIOC.md) -- [第三节 SpringAOP](docs/SSM/3_SpringAOP.md) -- [第四节 Spring事务管理](docs/SSM/4_Spring事务管理.md) -- [第五节 Spring中Bean的作用域](docs/SSM/5_Spring中Bean的作用域.md) -- [第六节 Spring中Bean的生命周期](docs/SSM/6_Spring中Bean的生命周期.md) -- [第七节 Spring中常见注解](docs/SSM/7_Spring中常见注解.md) -- [第八节 Spring中涉及到的设计模式](docs/SSM/8_Spring中涉及到的设计模式.md) -- [第九节 MyBaits](docs/SSM/9_MyBaits.md) - -## 2. Web 安全 - -- [第一节 常见安全问题](docs/Safety/1_常见安全问题.md) -- [第二节 跨站脚本攻击](docs/Safety/2_跨站脚本攻击.md) -- [第三节 跨站请求伪造](docs/Safety/3_跨站请求伪造.md) -- [第四节 Cookies问题](docs/Safety/4_Cookies问题.md) -- [第五节 点击劫持问题](docs/Safety/5_点击劫持问题.md) -- [第六节 传输安全](docs/Safety/6_传输安全.md) -- [第七节 密码安全](docs/Safety/7_密码安全.md) -- [第八节 接入层注入问题](docs/Safety/8_接入层注入问题.md) -- [第九节 接入层上传问题](docs/Safety/9_接入层上传问题.md) -- [第十节 信息泄露](docs/Safety/10_信息泄露.md) -- [第十一节 DoS攻击](docs/Safety/11_DoS攻击.md) -- [第十二节 重放攻击](docs/Safety/12_重放攻击.md) - -## 3. 分布式 - -- [第一节 分布式系统设计理念](docs/distribution/1_分布式系统设计理念.md) -- [第二节 CAP理论](docs/distribution/2_CAP理论.md) -- [第三节 BASE理论](docs/distribution/3_BASE理论.md) -- [第四节 分布式锁](docs/distribution/4_分布式锁.md) -- [第五节 分布式事务](docs/distribution/5_分布式事务.md) -- [第六节 分布式缓存的一致性哈希算法](docs/distribution/6_分布式缓存的一致性哈希算法.md) - -## *4. 微服务 - -## 5. 网站架构 - -- [网站架构](docs/web_architecture/1_网站架构.md) -- [设计秒杀系统](docs/web_architecture/2_设计秒杀系统.md) - -# 💻 工具 - -## 1. Git - -- [git - 简明指南](http://rogerdudler.github.io/git-guide/index.zh.html) -- [git - 图解](http://marklodato.github.io/visual-git-guide/index-zh-cn.html) - -## 2. Maven - -- [Maven](docs/Maven/1_Maven.md) -- [Maven 下载](http://maven.apache.org/download.cgi) -- [Maven 官网](https://maven.apache.org/) - -## *2. Nginx - -## *3. Docker - - - -# 🔧 进阶指南 - -- [后端面试进阶指南](https://xiaozhuanlan.com/CyC2018) -- [Java 面试进阶指南](https://xiaozhuanlan.com/javainterview) -- [编码规范指南](https://github.com/alibaba/p3c) - -# 🙊 参考资料 - -- [参考仓库](docs/reference/参考仓库.md) -- [参考书籍](docs/reference/参考书籍.md) -- [慕课网](docs/reference/慕课网.md) \ No newline at end of file +| ☕️ Java | 👫 面向对象 | 📝 编程题 | 💾 数据库 | 🎓 系统设计 | ☎️ 常用框架 | 📖 工具 | 📚 参考资料 | +| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | +| Java | 面向对象 | 编程题 | 数据库 | 系统设计 | 常用框架 | 工具 | 参考资料 | diff --git "a/docs/1_\345\237\272\347\241\200.md" "b/docs/1_\345\237\272\347\241\200.md" new file mode 100644 index 00000000..406b2d57 --- /dev/null +++ "b/docs/1_\345\237\272\347\241\200.md" @@ -0,0 +1,99 @@ +# 系统设计基础 + +## 安全性 + +要求系统在应对各种攻击手段时能够有可靠的应对措施。 + +## 性能 + +### 性能指标 + +#### 响应时间 + +指某个请求从发出到接收到响应消耗的时间。 + +在对响应时间进行测试时,通常采用重复请求的方式,然后计算平均响应时间。 + +#### 吞吐量 + +指系统在单位时间内可以处理的请求数量,通常使用每秒的请求数来衡量。 + +#### 并发量 + +指系统能同时处理的并发用户请求数量。 + +在没有并发存在的系统中,请求被顺序执行,此时响应时间为吞吐量的倒数。例如系统支持的吞吐量为 100 req/s,那么平均响应时间应该为 0.01s。 + +目前的大型系统都支持多线程来处理并发请求,多线程能够提高吞吐量以及缩短响应时间,主要有两个原因: + +- 多 CPU +- IO 等待时间 + +使用 IO 多路复用等方式,系统在等待一个 IO 操作完成的这段时间内不需要被阻塞,可以去处理其它请求。通过将这个等待时间利用起来,使得 CPU 利用率大大提高。 + +并发用户数不是越高越好,因为如果并发用户数太高,系统来不及处理这么多的请求,会使得过多的请求需要等待,那么响应时间就会大大提高。 + +### 性能优化 + +#### 集群 + +将多台服务器组成集群,使用负载均衡将请求转发到集群中,避免单一服务器的负载压力过大导致性能降低。 + +#### 缓存 + +缓存能够提高性能的原因如下: + +- 缓存数据通常位于内存等介质中,这种介质对于读操作特别快; +- 缓存数据可以位于靠近用户的地理位置上; +- 可以将计算结果进行缓存,从而避免重复计算。 + +#### 异步 + +某些流程可以将操作转换为消息,将消息发送到**消息队列**之后立即返回,之后这个操作会被异步处理。 + + + +## 伸缩性 + +指不断向集群中添加服务器来缓解不断上升的用户并发访问压力和不断增长的数据存储需求。 + +### 伸缩性与性能 + +如果系统存在性能问题,那么单个用户的请求总是很慢的; + +如果系统存在伸缩性问题,那么单个用户的请求可能会很快,但是在并发数很高的情况下系统会很慢。 + +### 实现伸缩性 + +应用服务器只要不具有状态,那么就可以很容易地通过负载均衡器向集群中添加新的服务器。 + +关系型数据库的伸缩性通过 Sharding 来实现,将数据按一定的规则分布到不同的节点上,从而解决单台存储服务器的存储空间限制。 + +对于非关系型数据库,它们天生就是为海量数据而诞生,对伸缩性的支持特别好。 + +## 扩展性 + +指的是添加新功能时对现有系统的其它应用无影响,这就要求不同应用具备低耦合的特点。 + +实现可扩展主要有两种方式: + +- 使用消息队列进行解耦,应用之间通过消息传递进行通信; +- 使用分布式服务将业务和可复用的服务分离开来,业务使用分布式服务框架调用可复用的服务。新增的产品可以通过调用可复用的服务来实现业务逻辑,对其它产品没有影响。 + +## 可用性 + +### 冗余 + +保证高可用的主要手段是使用冗余,当某个服务器故障时就请求其它服务器。 + +应用服务器的冗余比较容易实现,只要保证应用服务器不具有状态,那么某个应用服务器故障时,负载均衡器将该应用服务器原先的用户请求转发到另一个应用服务器上,不会对用户有任何影响。 + +存储服务器的冗余需要使用主从复制来实现,当主服务器故障时,需要提升从服务器为主服务器,这个过程称为切换。 + +### 监控 + +对 CPU、内存、磁盘、网络等系统负载信息进行监控,当某个信息达到一定阈值时通知运维人员,从而在系统发生故障之前及时发现问题。 + +### 服务降级 + +服务降级是系统为了应对大量的请求,主动关闭部分功能,从而保证核心功能可用。 \ No newline at end of file diff --git "a/docs/Kafka/1_\346\266\210\346\201\257\351\230\237\345\210\227.md" "b/docs/1_\346\266\210\346\201\257\351\230\237\345\210\227.md" similarity index 100% rename from "docs/Kafka/1_\346\266\210\346\201\257\351\230\237\345\210\227.md" rename to "docs/1_\346\266\210\346\201\257\351\230\237\345\210\227.md" diff --git "a/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/1_\346\225\260\347\273\204\345\222\214\347\237\251\351\230\265.md" "b/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/1_\346\225\260\347\273\204\345\222\214\347\237\251\351\230\265.md" index 890a88e1..6d56d3ec 100644 --- "a/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/1_\346\225\260\347\273\204\345\222\214\347\237\251\351\230\265.md" +++ "b/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/1_\346\225\260\347\273\204\345\222\214\347\237\251\351\230\265.md" @@ -1,4 +1,4 @@ -# 数组 +# 数组和矩阵 ## 1、二维数组中的查找 diff --git "a/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/2_\345\255\227\347\254\246\344\270\262.md" "b/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/2_\345\255\227\347\254\246\344\270\262.md" index 97aef1ca..619b66af 100644 --- "a/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/2_\345\255\227\347\254\246\344\270\262.md" +++ "b/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/2_\345\255\227\347\254\246\344\270\262.md" @@ -384,4 +384,111 @@ private void reverse(char[] chs,int start,int end){ } ``` +## *10、实现 strStr() + +[实现 strStr()](https://leetcode-cn.com/problems/implement-strstr/) + +思路一:朴素模式匹配算法 + +最朴素的方法就是依次从待匹配串的每一个位置开始,逐一与模版串匹配, +因为最多检查 (n - m)个位置,所以方法的复杂度为 O(m*(n-1))。 + +
+ +```java +//haystack 是待匹配串 +//needle 是模板串 +public int strStr(String haystack, String needle) { + if(needle==null || needle.length()==0){ + return 0; + } + int i=0,j=0; + //k存储的是模板串在待匹配串的位置 + int k=i; + while(i printListFromTailToHead(ListNode listNode) { -## *2、链表中环的入口结点(9) +## *2、链表中环的入口结点 [链表中环的入口结点](https://www.nowcoder.com/practice/253d2c59ec3e4bc68da16833f79a38e4?tpId=13&tqId=11208&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) @@ -128,7 +128,7 @@ public ListNode removeElements(ListNode head, int val) { -## *4、删除链表中重复的结点(10) +## *4、删除链表中重复的结点 [删除链表中重复的结点](https://www.nowcoder.com/practice/fc533c45b73a41b0b44ccba763f866ef?tpId=13&tqId=11209&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) @@ -171,7 +171,7 @@ public ListNode deleteDuplication(ListNode pHead) { -## 5、链表中倒数第 k 个结点(29) +## 5、链表中倒数第 k 个结点 [链表中倒数第 k 个结点](https://www.nowcoder.com/practice/529d3ae5a407492994ad2a246518148a?tpId=13&tqId=11167&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) @@ -214,7 +214,7 @@ public ListNode FindKthToTail(ListNode head, int k) { -## 7、反转链表(30) +## 7、反转链表 [反转链表](https://www.nowcoder.com/practice/75e878df47f24fdc9dc3e400ec6058ca?tpId=13&tqId=11168&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) @@ -262,7 +262,7 @@ public ListNode ReverseList(ListNode head) { -## 8、合并两个排序的链表(31) +## 8、合并两个排序的链表 [合并两个排序的链表](https://www.nowcoder.com/practice/d8b6b4358f774294a89de2a6ac4d9337?tpId=13&tqId=11169&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) @@ -343,7 +343,7 @@ public ListNode Merge(ListNode list1, ListNode list2) { -## 9、二叉搜索树与双向链表(41) +## 9、二叉搜索树与双向链表 [二叉搜索树与双向链表](https://www.nowcoder.com/practice/947f6eb80d944a84850b0538bf0ec3a5?tpId=13&tqId=11179&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) @@ -386,7 +386,7 @@ private void inOrder(TreeNode node){ -## 10、两个链表的第一个公共结点(51) +## 10、两个链表的第一个公共结点 [两个链表的第一个公共结点](https://www.nowcoder.com/practice/6ab1d9a29e88450685099d45c9e31e46?tpId=13&tqId=11189&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) diff --git "a/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/4_\346\240\221.md" "b/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/4_\346\240\221.md" index 148b4fb6..a49dc51d 100644 --- "a/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/4_\346\240\221.md" +++ "b/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/4_\346\240\221.md" @@ -424,7 +424,7 @@ private String buildPath(List values){ -## 13、二叉树中和为某一值的路径(39) +## 13、二叉树中和为某一值的路径 [二叉树中和为某一值的路径](https://www.nowcoder.com/practice/b736e784e3e34731af99065031301bca?tpId=13&tqId=11177&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) diff --git "a/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/5_\346\240\210.md" "b/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/5_\346\240\210.md" index cbcadc12..1bfbef76 100644 --- "a/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/5_\346\240\210.md" +++ "b/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/5_\346\240\210.md" @@ -1,6 +1,6 @@ # 栈 -## 1、用两个栈实现队列(19) +## 1、用两个栈实现队列 [用两个栈实现队列](https://www.nowcoder.com/practice/54275ddae22f475981afa2244dd448c6?tpId=13&tqId=11158&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) @@ -24,7 +24,7 @@ public int pop() { -## *2、包含 min 函数的栈(35) +## *2、包含 min 函数的栈 [包含 min 函数的栈](https://www.nowcoder.com/practice/4c776177d2c04c2494f2555c9fcc1e49?tpId=13&tqId=11173&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) @@ -58,7 +58,7 @@ public int min() { -## *3、栈的压入、弹出序列(36) +## *3、栈的压入、弹出序列 [栈的压入、弹出序列](https://www.nowcoder.com/practice/d77d11405cc7470d82554cb392585106?tpId=13&tqId=11174&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) diff --git "a/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/1_\346\237\245\346\211\276.md" "b/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/1_\346\237\245\346\211\276.md" index f5657b81..ed9329c7 100644 --- "a/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/1_\346\237\245\346\211\276.md" +++ "b/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/1_\346\237\245\346\211\276.md" @@ -1,6 +1,8 @@ # 查找 -## 1、二维数组中的查找(1) + + +## 1、二维数组中的查找 [二维数组中的查找](https://www.nowcoder.com/practice/abc3fe2ce8e146608e868a70efebf62e?tpId=13&tqId=11154&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) @@ -30,7 +32,7 @@ public boolean Find(int target, int [][] array) { -## 2、旋转数组的最小数字(21) +## 2、旋转数组的最小数字 [旋转数组的最小数字](https://www.nowcoder.com/practice/9f3231a991af4f55b95579b44b7a01ba?tpId=13&tqId=11159&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) @@ -86,7 +88,7 @@ public int minNumberInRotateArray(int [] array) { -## 3、数字在排序数组中出现的次数(52) +## 3、数字在排序数组中出现的次数 [数字在排序数组中出现的次数](https://www.nowcoder.com/practice/70610bf967994b22bb1c26f9ae901fa2?tpId=13&tqId=11190&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) @@ -192,3 +194,4 @@ private int binarySearchLast(int[] array,int k){ return res; } ``` + diff --git "a/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/3_\345\212\250\346\200\201\350\247\204\345\210\222.md" "b/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/3_\345\212\250\346\200\201\350\247\204\345\210\222.md" index eb55b6d6..d60c1f20 100644 --- "a/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/3_\345\212\250\346\200\201\350\247\204\345\210\222.md" +++ "b/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/3_\345\212\250\346\200\201\350\247\204\345\210\222.md" @@ -1,6 +1,6 @@ # 动态规划 -## 1、正则表达式匹配(5) +## 1、正则表达式匹配 [正则表达式匹配](https://www.nowcoder.com/practice/45327ae22b7b413ea21df13ee7d6429c?tpId=13&tqId=11205&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) @@ -62,7 +62,7 @@ private boolean match(char[] str,int strIndex,char[] pattern,int patternIndex){ -## 2、斐波那契数列(22) +## 2、斐波那契数列 [斐波那契数列](https://www.nowcoder.com/practice/c6c7742f5ba7442aada113136ddea0c3?tpId=13&tqId=11160&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) @@ -97,7 +97,7 @@ public int Fibonacci(int n) { -## 3、跳台阶(23) +## 3、跳台阶 [跳台阶](https://www.nowcoder.com/practice/8c82a5b80378478f9484d87d1c5f12a4?tpId=13&tqId=11161&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) @@ -119,7 +119,7 @@ public int JumpFloor(int target) { -## 4、变态跳台阶(24) +## 4、变态跳台阶 [变态跳台阶](https://www.nowcoder.com/practice/22243d016f6b47f2a6928b4313c85387?tpId=13&tqId=11162&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) @@ -164,7 +164,7 @@ public int JumpFloorII(int target) { -## 5、矩形覆盖(25) +## 5、矩形覆盖 [矩形覆盖](https://www.nowcoder.com/practice/72a5a919508a4251859fb2cfb987a0e6?tpId=13&tqId=11163&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) @@ -191,7 +191,7 @@ public int RectCover(int target) { -## 6、连续子数组的最大和(45) +## 6、连续子数组的最大和 [连续子数组的最大和](https://www.nowcoder.com/practice/459bd355da1549fa8a49e350bf3df484?tpId=13&tqId=11183&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) @@ -256,7 +256,7 @@ public int FindGreatestSumOfSubArray(int[] array) { -## *7、丑数(46) +## *7、丑数 [丑数](https://www.nowcoder.com/practice/6aa9e04fc3794f68acf8778237ba065b?tpId=13&tqId=11186&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) @@ -448,14 +448,14 @@ public int numDecodings(String s) { int n =s.length(); int[] dp = new int[n+1]; dp[0]=1; // 没有字符则只有一种解码方式 - dp[1]=s.charAt(0)=='0'?0:1; //字符 '0',不能进行解码 + dp[1]=s.charAt(0)=='0'?0:1; // 字符 '0',不能进行解码 for(int i=2;i<=n;i++){ //解码一位字符 int one = Integer.parseInt(s.substring(i-1,i)); if(one!=0){ dp[i] += dp[i-1]; } - if(s.charAt(i-2)=='0'){ ////字符 '0',不能进行解码 + if(s.charAt(i-2)=='0'){ //字符 '0',不能进行解码 continue; } int two=Integer.parseInt(s.substring(i-2,i)); @@ -522,48 +522,51 @@ public int LCS(String s,String t){ ```java private char[][] x; +private StringBuilder res; //x[i][j]=='c' 表示斜方向 //x[i][j]=='u' 表示向上 //x[i][j]=='l' 表示向左 -private StringBuilder res; -public int LCS(String s,String t){ +public String LCS(String s,String t){ + if(s==null || s==null){ + return ""; + } int m=s.length(); int n=t.length(); - if(m==0 || n==0){ - return 0; - } - - //lcs[i][j] 表示 s[0...i] 和 t[0...j] 之间的最长公共子序列长度 - int[][] lcs=new int[m+1][n+1]; x=new char[m+1][n+1]; res=new StringBuilder(); + // lcs[i][j] 表示 s[0,i-1]、t[0,j-1] 最长公共子序列长度 + // s[i-1]==t[j-1] 则 lcs[i][j]=lcs[i-1][j-1]+1; + // s[i-1]!=t[j-1] 则 lcs[i][j]=max(lcs[i-1][j],lcs[i][j-1]) + int[][] lcs=new int[m+1][n+1]; for(int i=0;i<=m;i++){ for(int j=0;j<=n;j++){ lcs[i][j]=0; } } - for(int i=1;i<=m;i++){ for(int j=1;j<=n;j++){ if(s.charAt(i-1)==t.charAt(j-1)){ - lcs[i][j]=1+lcs[i-1][j-1]; + lcs[i][j]=lcs[i-1][j-1]+1; x[i][j]='c'; - }else if(lcs[i-1][j]>=lcs[i][j-1]){ - lcs[i][j]=lcs[i-1][j]; - x[i][j]='u'; - }else if(lcs[i-1][j]lcs[i][j-1]){ + lcs[i][j]=lcs[i-1][j]; + x[i][j]='u'; + } } } } - return lcs[m][n]; + print(s,m,n); + return res.toString(); } -public void print(String s,int m,int n){ - if(m==0 || n==0){ +private void print(String s,int m,int n){ + if(s==null || s.length()==0){ return; } if(x[m][n]=='c'){ @@ -728,6 +731,26 @@ private int binarySearch(int[] tails,int l,int r,int key){ [完全平方数](https://leetcode-cn.com/problems/perfect-squares/) ```java - +// 思路:dp[i] 表示整数 i 的完全平方数的最少数量 +// 显然这些平方数开方后数值的大小范围在 [1,sqrc(n)] 之间 +// 假设 j 在 [1,sqrc(n)] 中取值,我们知道 i 和(i-j^2)之间就差了一个j^2完全平方数 +// 则有 dp[i]=1 + min{dp[i-j^2]} +public int numSquares(int n) { + if(n<=2){ + return n; + } + int N=(int)Math.sqrt(n*1.0); + int[] dp=new int[n+1]; + dp[0]=0; // 0不是正整数,显然不能使用完全平方数去表示 + for(int i=1;i<=n;i++){ + int min=dp[i-1]; + for(int j=1;i-j*j>=0 && j<=N;j++){ + if(dp[i-j*j]> FindPath(TreeNode root, int target) { - ArrayList> res = new ArrayList<>(); - ArrayList values = new ArrayList<>(); - backtrack(root,target,values,res); - return res; -} - -// values : 记录从根节点到叶子节点的所有路径 -// paths : 存储所有可能的结果 -private void backtrack(TreeNode root, int target, - ArrayList values, - ArrayList> paths) { - if (root == null) { - return; - } - values.add(root.val); - if(root.left==null && root.right==null && root.val==target){ - paths.add(new ArrayList<>(values)); - }else{ - backtrack(root.left,target-root.val,values,paths); - backtrack(root.right,target-root.val,values,paths); - } - values.remove(values.size()-1); -} -``` - - - -## 2、矩阵中的路径(65) - -[矩阵中的路径](https://www.nowcoder.com/practice/c61c6999eecb4b8f88a98f66b273a3cc?tpId=13&tqId=11218&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) - -```java -//思路:典型的回溯法思想 -private int m,n; //矩阵的长度、宽度 -private boolean[][] visted; //标记是否访问 - -private int[][] d={ - {0,1}, //向右 - {-1,0},//向上 - {0,-1},//向左 - {1,0} //向下 -}; - -private boolean inArea(int x,int y){ - return (x>=0 && x=0 && y=0 && x=0 && y - -```java -private List> res; - -private boolean[] visited; - -public List> permute(int[] nums) { - res = new ArrayList<>(); - if(nums==null || nums.length==0){ - return res; - } - visited = new boolean[nums.length]; - List p =new ArrayList<>(); - generatePermutation(nums,0,p); - return res; -} - -//p中保存一个有 index 的元素的排列 -//向这个排列的末尾添加第 (index+1) 个元素,组成有(index+1) 个元素排列 -private void generatePermutation(int[] nums,int index,List p){ - if(index==nums.length){ - res.add(new ArrayList<>(p)); - return; - } - for(int i=0;i> res; - -private boolean[] visited; - -public List> permuteUnique(int[] nums) { - res = new ArrayList<>(); - if(nums==null || nums.length==0){ - return res; - } - visited = new boolean[nums.length]; - Arrays.sort(nums); - List p = new ArrayList<>(); - findUniquePermutation(nums,0,p); - return res; -} - -//p 保存的是有 index 元素的排列 -//向这个排列的末尾添加第 (index+1) 个元素,组成有(index+1) 个元素排列 -private void findUniquePermutation(int[] nums,int index,List p){ - if(index==nums.length){ - res.add(new ArrayList<>(p)); - return; - } - for(int i=0;i0 && nums[i-1]==nums[i] && - !visited[i-1]){ //注意:相邻元素相同,并且都未被访问 - continue; - } - visited[i]=true; - p.add(nums[i]); - findUniquePermutation(nums,index+1,p); - p.remove(p.size()-1); - visited[i]=false; - } - } - return; -} -``` - - - -## *7、字符串的排列 - -[字符串的排列](https://www.nowcoder.com/practice/fe6b651b66ae47d7acce78ffdd9a96c7?tpId=13&tqId=11180&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) - -```java -private ArrayList res; -private boolean[] visited; - -public ArrayList Permutation(String str) { - res = new ArrayList<>(); - if(str==null || str.length()==0){ - return res; - } - char[] chs = str.toCharArray(); - Arrays.sort(chs); //方便后面的去重处理 - visited = new boolean[str.length()]; - permute(chs,0,new StringBuilder()); - return res; -} - -//产生排列 -//p中保存一个存在index个元素的排列 -//向这个排列的末尾添加第(index+1)个元素,获得包含(index+1)个元素的排列 -private void permute(char[] chs,int index,StringBuilder p){ - if(index==chs.length){ - res.add(p.toString()); - return; - } - for(int i=0;i0 && chs[i-1]==chs[i] && !visited[i-1]){ - continue; - } - if(!visited[i]){ - p.append(chs[i]); - visited[i] = true; - permute(chs,index+1,p); - p.deleteCharAt(p.length()-1); - visited[i] = false; - } - } -} -``` - - - -## 8、字母大小写全排列 - -[字母大小写全排列](https://leetcode-cn.com/problems/letter-case-permutation/) - -```java -private List res; - -//处理 index 位置的数据 -//如果 index 位置是字母的话,则替换 -private void replaceLetter(int index,StringBuilder p){ - if(index==p.length()){ - res.add(p.toString()); - return; - } - char ch=p.charAt(index); - if(Character.isLetter(ch)){ - p.setCharAt(index,Character.toUpperCase(ch)); - replaceLetter(index+1,p); - p.setCharAt(index,Character.toLowerCase(ch)); - replaceLetter(index+1,p); - }else{ - replaceLetter(index+1,p); - } - return; -} - -public List letterCasePermutation(String S) { - res=new ArrayList<>(); - if(S==null || S.length()==0){ - return res; - } - StringBuilder p=new StringBuilder(S); - replaceLetter(0,p); - return res; -} -``` - - - -## 9、电话号码的字母组合 - -[电话号码的字母组合](https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/) - -```java -private final String[] letterMap={ - " ",//0 - "",//1 - "abc",//2 - "def",//3 - "ghi",//4 - "jkl",//5 - "mno",//6 - "pqrs",//7 - "tuv",//8 - "wxyz"//9 -}; - -private List res; -//s 中保留了从 digits[0...index-1] 翻译得到的字符串 -//寻找和 digits[index] 匹配的字母,获取 digits[0...index] 翻译得到的解 -private void findCombination(String digits,int index,String s){ - if(index==digits.length()){ - res.add(s); - return; - } - - char c = digits.charAt(index); - String letters = letterMap[c-'0']; // 翻译字符串 - for(int i=0;i letterCombinations(String digits) { - res = new ArrayList<>(); - if(digits==null || digits.length()==0){ - return res; - } - findCombination(digits,0,""); - return res; -} -``` - - - -## 10、组合 - -[组合](https://leetcode-cn.com/problems/combinations/) - -```java -//思路一:暴力解法 - -private List> res; - -//c存储已经找到的组合 -//从start开始搜索新的元素 -private void findCombination(int n,int k,int start,List c){ - if(k==c.size()){ - res.add(new ArrayList<>(c)); - return; - } - for(int i=start;i<=n;i++){ - c.add(i); - findCombination(n,k,i+1,c); - c.remove(c.size()-1); - } - return; -} - -public List> combine(int n, int k) { - res=new ArrayList<>(); - if(n<=0 || k<=0 || n c=new ArrayList<>(); - findCombination(n,k,1,c); - return res; -} -``` - - - -```java -//思路二:优化,进行剪枝 -private List> res; - -//c存储已经找到的组合 -//从start开始搜索新的元素 -private void findCombination(int n,int k,int start,List c){ - if(k==c.size()){ - res.add(new ArrayList<>(c)); - return; - } - - //TODO:优化--剪枝 - //c存储的是已经找到的组合。 - //此时还剩下k-c.size()个空位, - //则 [i...n]之间的元素最少要有 k-c.size() 个,即 n-i+1 >= k-c.size() - //所以有 i <= n-(k-c.size())+1 - - for(int i=start;i<=n-(k-c.size())+1;i++){ - c.add(i); - findCombination(n,k,i+1,c); - c.remove(c.size()-1); - } - return; -} - -public List> combine(int n, int k) { - res=new ArrayList<>(); - if(n<=0 || k<=0 || n c=new ArrayList<>(); - findCombination(n,k,1,c); - return res; -} -``` - - - -## 11、N皇后 - -[N皇后](https://leetcode-cn.com/problems/n-queens/) - -
- -
- -- 思路: - -
- -
- -判断对角线不合法的情况: - -(1)竖向:col[i] 表示第 i 列被占用 - -(2)对角线1:dai1[i] 表示第 i 条对角线被1占用 - -
- -(3)对角线2:dai2[i]表示第i对角线被2占用 - -
- -```java -private List> res; - -//用于判断是否在同一竖线上,因为index表示行数,是变化的,所以不用判断是否在相同行 -private boolean[] cols; -//判断是否在1类对角线上,相应坐标 i+j -private boolean[] dial1; -//判断是否在2类对角线上,相应坐标 i-j+n-1 -private boolean[] dial2; - -//在 index 行放皇后 -//row 记录在 index 行能够放皇后的位置 -//比如 row.get(0) = 1,就表示在棋盘的 [0,1] 位置放上皇后 -private void putQueen(int n,int index,List row){ - if(index==n){ - res.add(generateBoard(n,row)); - return; - } - // [index,j] 放皇后 - for(int j=0;j generateBoard(int n, List row) { - List res=new ArrayList<>(); - if(n<=0){ - return res; - } - - char[][] board=new char[n][n]; - for(int i=0;i> solveNQueens(int n) { - res=new ArrayList<>(); - if(n==0){ - return res; - } - cols=new boolean[n]; - dial1=new boolean[2*n-1]; - dial2=new boolean[2*n-1]; - List row=new ArrayList<>(); - putQueen(n,0,row); - return res; -} -``` - - - -## 12、N皇后 II - -[N皇后 II](https://leetcode-cn.com/problems/n-queens-ii/) - -```java - -``` - - - -## 13、解数独 - -[解数独](https://leetcode-cn.com/problems/sudoku-solver/) - -```java - -``` - diff --git "a/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/4_\346\220\234\347\264\242.md" "b/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/4_\346\220\234\347\264\242.md" new file mode 100644 index 00000000..3bbfe622 --- /dev/null +++ "b/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/4_\346\220\234\347\264\242.md" @@ -0,0 +1,812 @@ + + + + + + +# 回溯-Backtract + +## 1、二叉树中和为某一值的路径 + +[二叉树中和为某一值的路径](https://www.nowcoder.com/practice/b736e784e3e34731af99065031301bca?tpId=13&tqId=11177&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) + +```java +public ArrayList> FindPath(TreeNode root, int target) { + ArrayList> res = new ArrayList<>(); + ArrayList values = new ArrayList<>(); + backtrack(root,target,values,res); + return res; +} + +// values : 记录从根节点到叶子节点的所有路径 +// paths : 存储所有可能的结果 +private void backtrack(TreeNode root, int target, + ArrayList values, + ArrayList> paths) { + if (root == null) { + return; + } + values.add(root.val); + if(root.left==null && root.right==null && root.val==target){ + paths.add(new ArrayList<>(values)); + }else{ + backtrack(root.left,target-root.val,values,paths); + backtrack(root.right,target-root.val,values,paths); + } + values.remove(values.size()-1); +} +``` + + + +## *2、二叉树的所有路径 + +[二叉树的所有路径](https://leetcode-cn.com/problems/binary-tree-paths/) + +```java +public List binaryTreePaths(TreeNode root) { + List paths=new ArrayList<>(); + if(root==null){ + return paths; + } + List values=new ArrayList<>(); + backtrack(root,values,paths); + return paths; +} + +//values 记录从根节点到叶子节点 +//paths 记录路径 +private void backtrack(TreeNode root, List values,List paths){ + if(root==null){ + return; + } + values.add(root.val); + if(root.left==null && root.right==null){ // 到达叶子节点 + paths.add(buildPath(values)); + }else{ + backtrack(root.left,values,paths); + backtrack(root.right,values,paths); + } + values.remove(values.size()-1); +} + +private String buildPath(List values){ + StringBuilder res=new StringBuilder(); + for(int i=0;i"); + } + } + return res.toString(); +} +``` + + + +## *3、矩阵中的路径 + +[矩阵中的路径](https://www.nowcoder.com/practice/c61c6999eecb4b8f88a98f66b273a3cc?tpId=13&tqId=11218&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) + +```java +//思路:典型的回溯法思想 +private int m,n; //矩阵的长度、宽度 +private boolean[][] visted; //标记是否访问 + +private int[][] d={ + {0,1}, //向右 + {-1,0},//向上 + {0,-1},//向左 + {1,0} //向下 +}; + +private boolean inArea(int x,int y){ + return (x>=0 && x=0 && y=0 && x=0 && y + +
+ +- 思路: + +
+ +
+ +在第 i 行放上皇后,判断是否其他地方能够放皇后的情况: + +(1)竖向:col[j] 表示第 j 列被占用 + +(2)对角线1:dai1[m] 表示第 m 条对角线1被占用,如 dia1[0]=true 表示[0,0] 位置不能放皇后, + +dia1[1] =true 就表示 [0,1]和[1,0] 位置不能放皇后。 + +对角线1总共有 (2*n-1) 条;m 与 i 、 j 的关系:m=i+j。 + +
+ +(3)对角线2:dai2[m]表示第 m 条对角线被2占用,如 dai2[0]=true 表示[0,3] 位置不能放皇后, + +dai2[2]=true 则表示 [0,2] 和 [1,3] 不能放皇后, + +对角线2总共有(2*n-1)条;m 与 i、j 关系:m=i-j+n-1。 + +
+ +```java +private List> res; + +//用于判断是否在同一竖线上,因为index表示行数,是变化的,所以不用判断是否在相同行 +private boolean[] cols; +//判断是否在1类对角线上,相应坐标 i+j +private boolean[] dial1; +//判断是否在2类对角线上,相应坐标 i-j+n-1 +private boolean[] dial2; + +//在 index 行放皇后 +//row 记录在 index 行能够放皇后的位置 +//比如 row.get(0) = 1,就表示在棋盘的 [0,1] 位置放上皇后 +private void putQueen(int n,int index,List row){ + if(index==n){ + res.add(generateBoard(n,row)); + return; + } + // [index,j] 放皇后 + for(int j=0;j generateBoard(int n, List row) { + List res=new ArrayList<>(); + if(n<=0){ + return res; + } + + char[][] board=new char[n][n]; + for(int i=0;i> solveNQueens(int n) { + res=new ArrayList<>(); + if(n==0){ + return res; + } + cols=new boolean[n]; + dial1=new boolean[2*n-1]; + dial2=new boolean[2*n-1]; + List row=new ArrayList<>(); + putQueen(n,0,row); + return res; +} +``` + + + +## 6、解数独 + +[解数独](https://leetcode-cn.com/problems/sudoku-solver/) + +```java +// 思路:回溯法 +// 数独矩阵是 9*9 的方阵,也就是有 81 个位置, +// 可以看成是一个长度为 81 的数组,使用 pos(从0开始) 记录在数组中位置 +// row[i][m] 表示第 i 行填数字 m 的占用情况,即 row[i][m]=true,表示在第i行填上数字 m 了。 +// col[i][m] 表示第 i 列填数字 m 的占用情况,即 col[i][m]=true,表示在第i列填上数字 m 了。 +// block[i][m] 表示第 i个九宫格填数字 m 的占用情况 +public void solveSudoku(char[][] board) { + if(board==null){ + return; + } + boolean[][] row=new boolean[9][10]; + boolean[][] col=new boolean[9][10]; + boolean[][] block=new boolean[9][10]; + for(int i=0;i<9;i++){ + for(int j=0;j<9;j++){ + if(board[i][j]!='.'){ // [i,j]位置已填上数字 + int num= board[i][j]-'0'; + row[i][num]=true; + col[j][num]=true; + block[i/3*3+j/3][num]=true; + } + } + } + for(int pos=0;pos<81;pos++){ + int x=pos/9; + int y=pos%9; + if(board[x][y]=='.'){ + if(!putNum(board,pos,row,col,block)){ + continue; + } + } + } +} + +// 向 pos 位置放数字 +private boolean putNum(char[][] board, int pos, + boolean[][] row, boolean[][] col, boolean[][] block){ + if(pos==81){ + return true; + } + int nextPos = pos+1; // pos 位置的下一未填数字的位置 + for(;nextPos<81;nextPos++){ + if(board[nextPos/9][nextPos%9]=='.'){ + break; + } + } + // [x,y] 表示 pos 在表格中位置 + int x=pos/9; + int y=pos%9; + + for(int num=1;num<=9;num++){ // pos 位置可以填 1-9 任意整数 + if(!row[x][num] && !col[y][num] && !block[x/3*3+y/3][num]){ + board[x][y]=(char)(num+'0'); + row[x][num]=true; + col[y][num]=true; + block[x/3*3+y/3][num]=true; + if(putNum(board,nextPos,row,col,block)){ + return true; + } + block[x/3*3+y/3][num]=false; + col[y][num]=false; + row[x][num]=false; + board[x][y]='.'; + } + } + return false; +} +``` + + + +# 广度优先-BFS + +## 1、单词接龙 + +[单词接龙](https://leetcode-cn.com/problems/word-ladder/description/) + +```java +public int ladderLength(String beginWord, String endWord, List wordList) { + wordList.add(beginWord); + int N = wordList.size(); + int start = N - 1; + int end = 0; + while (end < N && !wordList.get(end).equals(endWord)) { + end++; + } + if (end == N) { + return 0; + } + List[] graphic = buildGraphic(wordList); + return getShortestPath(graphic, start, end); +} + +private List[] buildGraphic(List wordList) { + int N = wordList.size(); + List[] graphic = new List[N]; + for (int i = 0; i < N; i++) { + graphic[i] = new ArrayList<>(); + for (int j = 0; j < N; j++) { + if (isConnect(wordList.get(i), wordList.get(j))) { + graphic[i].add(j); + } + } + } + return graphic; +} + +private boolean isConnect(String s1, String s2) { + int diffCnt = 0; + for (int i = 0; i < s1.length() && diffCnt <= 1; i++) { + if (s1.charAt(i) != s2.charAt(i)) { + diffCnt++; + } + } + return diffCnt == 1; +} + +private int getShortestPath(List[] graphic, int start, int end) { + Queue queue = new LinkedList<>(); + boolean[] marked = new boolean[graphic.length]; + queue.add(start); + marked[start] = true; + int path = 1; + while (!queue.isEmpty()) { + int size = queue.size(); + path++; + while (size-- > 0) { + int cur = queue.poll(); + for (int next : graphic[cur]) { + if (next == end) { + return path; + } + if (marked[next]) { + continue; + } + marked[next] = true; + queue.add(next); + } + } + } + return 0; +} +``` + + + +# 深度优先-DFS + +## 1、岛屿数量 + +[岛屿数量](https://leetcode-cn.com/problems/number-of-islands/) + +```java +// 思路:典型的 flood-fill 算法 +private int m,n; + +private boolean[][] visited; + +private boolean inArea(int x,int y){ + return (x>=0 && x=0 && y=0 && x=0 && y=0 && x=0 && y0){ + sum += num %10; + num/=10; + } + return sum; +} + +private boolean valid(int threshlod ,int x,int y){ + return (getNum(x)+getNum(y))<=threshlod; +} + +private int walk(int threshold,int startx,int starty){ + visited[startx][starty]=true; + int walks = 0; + if(valid(threshold,startx,starty)){ + walks=1; + } + for(int i=0;i<4;i++){ + int newX = startx+d[i][0]; + int newY = starty+d[i][1]; + if(inArea(newX,newY)){ + if(!visited[newX][newY] && valid(threshold,newX,newY)){ + walks += walk(threshold,newX,newY); + } + } + } + return walks; +} + +public int movingCount(int threshold, int rows, int cols) { + if(threshold<0){ //threshold<0,则机器人就不能走了 + return 0; + } + m = rows; + if(m==0){ + return 0; + } + n= cols; + visited = new boolean[m][n]; + return walk(threshold,0,0); +} +``` + + + +## 4、被围绕的区域 + +[被围绕的区域](https://leetcode-cn.com/problems/surrounded-regions/) + +```java +private int m,n; + +private boolean[][] visited; + +private int[][] d={ + {-1,0}, + {0,1}, + {1,0}, + {0,-1} +}; + +private boolean inArea(int x,int y){ + return (x>=0 && x=0 && y=0 && x=0 && y=matrix[startx][starty]){ + dfs(matrix,newX,newY,visited); + } + } + } +} + +public List> pacificAtlantic(int[][] matrix) { + List> res=new ArrayList<>(); + m=matrix.length; + if(m==0){ + return res; + } + n=matrix[0].length; + pacific=new boolean[m][n]; + atlantic=new boolean[m][n]; + + for(int j=0;j + +```java +private List> res; + +private boolean[] visited; + +public List> permute(int[] nums) { + res = new ArrayList<>(); + if(nums==null || nums.length==0){ + return res; + } + visited = new boolean[nums.length]; + List p =new ArrayList<>(); + generatePermutation(nums,0,p); + return res; +} + +//p中保存一个有 index 的元素的排列 +//向这个排列的末尾添加第 (index+1) 个元素,组成有(index+1) 个元素排列 +private void generatePermutation(int[] nums,int index,List p){ + if(index==nums.length){ + res.add(new ArrayList<>(p)); + return; + } + for(int i=0;i> res; + +private boolean[] visited; + +public List> permuteUnique(int[] nums) { + res = new ArrayList<>(); + if(nums==null || nums.length==0){ + return res; + } + visited = new boolean[nums.length]; + Arrays.sort(nums); + List p = new ArrayList<>(); + findUniquePermutation(nums,0,p); + return res; +} + +//p 保存的是有 index 元素的排列 +//向这个排列的末尾添加第 (index+1) 个元素,组成有(index+1) 个元素排列 +private void findUniquePermutation(int[] nums,int index,List p){ + if(index==nums.length){ + res.add(new ArrayList<>(p)); + return; + } + for(int i=0;i0 && nums[i-1]==nums[i] && + !visited[i-1]){ //注意:相邻元素相同,并且都未被访问 + continue; + } + visited[i]=true; + p.add(nums[i]); + findUniquePermutation(nums,index+1,p); + p.remove(p.size()-1); + visited[i]=false; + } + } + return; +} +``` + + + +## 3、字符串的排列 + +[字符串的排列](https://www.nowcoder.com/practice/fe6b651b66ae47d7acce78ffdd9a96c7?tpId=13&tqId=11180&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) + +```java +private ArrayList res; +private boolean[] visited; + +public ArrayList Permutation(String str) { + res = new ArrayList<>(); + if(str==null || str.length()==0){ + return res; + } + char[] chs = str.toCharArray(); + Arrays.sort(chs); //方便后面的去重处理 + visited = new boolean[str.length()]; + permute(chs,0,new StringBuilder()); + return res; +} + +//产生排列 +//p中保存一个存在index个元素的排列 +//向这个排列的末尾添加第(index+1)个元素,获得包含(index+1)个元素的排列 +private void permute(char[] chs,int index,StringBuilder p){ + if(index==chs.length){ + res.add(p.toString()); + return; + } + for(int i=0;i0 && chs[i-1]==chs[i] && !visited[i-1]){ + continue; + } + if(!visited[i]){ + p.append(chs[i]); + visited[i] = true; + permute(chs,index+1,p); + p.deleteCharAt(p.length()-1); + visited[i] = false; + } + } +} +``` + + + +## *4、字母大小写全排列 + +[字母大小写全排列](https://leetcode-cn.com/problems/letter-case-permutation/) + +```java +private List res; + +//处理 index 位置的数据 +//如果 index 位置是字母的话,则替换 +private void replaceLetter(int index,StringBuilder p){ + if(index==p.length()){ + res.add(p.toString()); + return; + } + char ch=p.charAt(index); + if(Character.isLetter(ch)){ + p.setCharAt(index,Character.toUpperCase(ch)); + replaceLetter(index+1,p); + p.setCharAt(index,Character.toLowerCase(ch)); + replaceLetter(index+1,p); + }else{ + replaceLetter(index+1,p); + } + return; +} + +public List letterCasePermutation(String S) { + res=new ArrayList<>(); + if(S==null || S.length()==0){ + return res; + } + StringBuilder p=new StringBuilder(S); + replaceLetter(0,p); + return res; +} +``` + + + +## *5、电话号码的字母组合 + +[电话号码的字母组合](https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/) + +```java +private final String[] letterMap={ + " ",//0 + "",//1 + "abc",//2 + "def",//3 + "ghi",//4 + "jkl",//5 + "mno",//6 + "pqrs",//7 + "tuv",//8 + "wxyz"//9 +}; + +private List res; + +public List letterCombinations(String digits) { + res=new ArrayList<>(); + if(digits==null || digits.length()==0){ + return res; + } + generateLetterCombinations(digits,0,new StringBuilder()); + return res; +} + +// p 保存 digits[0,index-1] 翻译得到的字符串 +private void generateLetterCombinations(String digits,int index,StringBuilder p){ + if(index==digits.length()){ + res.add(p.toString()); + return; + } + char[] chs=letterMap[digits.charAt(index)-'0'].toCharArray(); + for(int i=0;i> res; + +//c存储已经找到的组合 +//从start开始搜索新的元素 +private void findCombination(int n,int k,int start,List c){ + if(k==c.size()){ + res.add(new ArrayList<>(c)); + return; + } + for(int i=start;i<=n;i++){ + c.add(i); + findCombination(n,k,i+1,c); + c.remove(c.size()-1); + } + return; +} + +public List> combine(int n, int k) { + res=new ArrayList<>(); + if(n<=0 || k<=0 || n c=new ArrayList<>(); + findCombination(n,k,1,c); + return res; +} +``` + +```java +//思路二:剪枝优化 +private List> res; + +//c存储已经找到的组合 +//从start开始搜索新的元素 +private void findCombination(int n,int k,int start,List c){ + if(k==c.size()){ + res.add(new ArrayList<>(c)); + return; + } + + //优化--剪枝 + //c存储的是已经找到的组合。 + //此时还剩下k-c.size()个空位, + //则 [i...n]之间的元素最少要有 k-c.size() 个,即 n-i+1 >= k-c.size() + //所以有 i <= n-(k-c.size())+1 + + for(int i=start;i<=n-(k-c.size())+1;i++){ + c.add(i); + findCombination(n,k,i+1,c); + c.remove(c.size()-1); + } + return; +} + +public List> combine(int n, int k) { + res=new ArrayList<>(); + if(n<=0 || k<=0 || n c=new ArrayList<>(); + findCombination(n,k,1,c); + return res; +} +``` + diff --git "a/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/5_\346\267\261\345\272\246\344\274\230\345\205\210.md" "b/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/5_\346\267\261\345\272\246\344\274\230\345\205\210.md" deleted file mode 100644 index 60cf3bd7..00000000 --- "a/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/5_\346\267\261\345\272\246\344\274\230\345\205\210.md" +++ /dev/null @@ -1,329 +0,0 @@ -# 深度优先 - -## 1、岛屿数量 - -[岛屿数量](https://leetcode-cn.com/problems/number-of-islands/) - -```java -// 思路:典型的 flood-fill 算法 -private int m,n; - -private boolean[][] visited; - -private boolean inArea(int x,int y){ - return (x>=0 && x=0 && y=0 && x=0 && y=0 && x=0 && y0){ - sum += num %10; - num/=10; - } - return sum; -} - -private boolean valid(int threshlod ,int x,int y){ - return (getNum(x)+getNum(y))<=threshlod; -} - -private int walk(int threshold,int startx,int starty){ - visited[startx][starty]=true; - int walks = 0; - if(valid(threshold,startx,starty)){ - walks=1; - } - for(int i=0;i<4;i++){ - int newX = startx+d[i][0]; - int newY = starty+d[i][1]; - if(inArea(newX,newY)){ - if(!visited[newX][newY] && valid(threshold,newX,newY)){ - walks += walk(threshold,newX,newY); - } - } - } - return walks; -} - -public int movingCount(int threshold, int rows, int cols) { - if(threshold<0){ //threshold<0,则机器人就不能走了 - return 0; - } - m = rows; - if(m==0){ - return 0; - } - n= cols; - visited = new boolean[m][n]; - return walk(threshold,0,0); -} -``` - - - -## 4、被围绕的区域 - -[被围绕的区域](https://leetcode-cn.com/problems/surrounded-regions/) - -```java -private int m,n; - -private boolean[][] visited; - -private int[][] d={ - {-1,0}, - {0,1}, - {1,0}, - {0,-1} -}; - -private boolean inArea(int x,int y){ - return (x>=0 && x=0 && y=0 && x=0 && y=matrix[startx][starty]){ - dfs(matrix,newX,newY,visited); - } - } - } -} - -public List> pacificAtlantic(int[][] matrix) { - List> res=new ArrayList<>(); - m=matrix.length; - if(m==0){ - return res; - } - n=matrix[0].length; - pacific=new boolean[m][n]; - atlantic=new boolean[m][n]; - - for(int j=0;j list=new ArrayList<>(); - for(int i=0;i() { //按照其实位置进行排序 - @Override - public int compare(Interval o1, Interval o2) { - int num=o1.start-o2.start; - int num2=(num==0)?(o1.end-o2.end):num; - return num2; + Arrays.sort(g); + Arrays.sort(s); + int i=g.length-1; + int j=s.length-1; + int num=0; + while (i>=0 && j>=0){ + if(s[j]>=g[i]){ + num++; + j--; } - }); - - ArrayList res=new ArrayList<>(); - Interval cur=list.get(0); - for(int i=1;i=list.get(i).start){ //存在区间交叉 - //区间进行合并 - cur.end=Math.max(cur.end,list.get(i).end); - }else{ //不存在交叉 - res.add(cur); - cur=list.get(i); - } - if(i==m-1){ - res.add(cur); - } - } - - int n=res.size(); - int[][] matrix=new int[n][2]; - for(int i=0;ilimit){ // 如果只能载一个人,则载最重的 + res++; + heavy--; + }else{ // people[light]+people[heavy]<=limit,船可以搭载两个人 + res++; + light++; + heavy--; + } + } + return res; +} ``` -## 5、无重叠区间 +## *5、汇总区间 -[无重叠区间](https://leetcode-cn.com/problems/non-overlapping-intervals/) +[汇总区间](https://leetcode-cn.com/problems/summary-ranges/) ```java +public List summaryRanges(int[] nums) { + List res=new ArrayList<>(); + if(nums==null || nums.length==0){ + return res; + } + if(nums.length==1) { + res.add(nums[0] + ""); + return res; + } + int start=nums[0]; + int end=start; + for(int i=1;i"+end; +} ``` -## 6、救生艇 +## 6、无重叠区间 -[救生艇](https://leetcode-cn.com/problems/boats-to-save-people/) +[无重叠区间](https://leetcode-cn.com/problems/non-overlapping-intervals/) ```java - +// 思路: +// 首先计算最多能组成的不重叠区间个数,然后用区间总个数减去不重叠区间的个数。 +// 其中区间结尾至关重要,选择的区间结尾越小,留给后面的空间就越大,那么后面能够选择的区间个数也就越大。 +public int eraseOverlapIntervals(int[][] intervals) { + if(intervals==null || intervals.length==0){ + return 0; + } + // 按照区间结尾进行升序排序 + // 写法一:比较器 + // 这里使用 p1[1]() { + @Override + public int compare(int[] p1, int[] p2) { + return p1[1] p1[1]p[1]));*/ + int cnt=1; // 记录最大不重叠区间数 + int end=intervals[0][1]; // 记录前一个不重叠区间结尾位置 + for(int i=1;i() { + @Override + public int compare(int[] o1, int[] o2) { + return o1[1](o1[1]o[1])); + int res=1; + int end=points[0][1]; + for(int i=1;i() { + @Override + public int compare(int[] o1, int[] o2) { + return (o1[0] + o1[0]o[0]));*/ + List intervalList=new ArrayList<>(); // 保存结果区间 + int[] cur=intervals[0]; // 记录当前重叠区间 + int m=intervals.length; + for(int i=1;i=intervals[i][0]){ // cur.end >= next.start + cur[1]=Math.max(cur[1],intervals[i][1]); + }else{ + intervalList.add(cur); + cur=intervals[i]; + } + if(i==m-1){ + intervalList.add(cur); + } + } + int[][] res=new int[intervalList.size()][2]; + for(int i=0;i=0 || j>=0){ + c+=(i>=0)?num1.charAt(i)-'0':0; + c+=(j>=0)?num2.charAt(j)-'0':0; + res.append(c%10); + c/=10; + i--; + j--; } - - int l=1; - int r=x; - while(l<=r){ - int mid=(r-l)/2+l; - int sqrt=x/mid; - if(sqrt==mid){ - return mid; - }else if(sqrtmid; - l=mid+1; - } + if(c==1){ // 考虑 num1="90"、num2="90" 的情况 + res.append("1"); } - return r; + return res.reverse().toString(); } ``` -## 7、求 1+2+3+...+n +## 2、求 1+2+3+...+n [求 1+2+3+...+n](https://www.nowcoder.com/practice/7a0da8fc483247ff8800059e12d7caf1?tpId=13&tqId=11200&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) @@ -245,11 +216,46 @@ public int Sum_Solution(int n) { -## *8、从 1 到 n 整数中 1 出现的次数 +## *3、从 1 到 n 整数中 1 出现的次数 [从 1 到 n 整数中 1 出现的次数](https://www.nowcoder.com/practice/bd7f978302044eee894445e244c7eee6?tpId=13&tqId=11184&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) ```java +// 思路一:暴力法 +// 时间复杂度:O(n^2) +public int NumberOf1Between1AndN_Solution(int n) { + if(n<=0){ + return 0; + } + int res=0; + for(int i=1;i<=n;i++){ + res+=getNumberOf1(i); + } + return res; +} + +private int getNumberOf1(int n){ + int cnt=0; + while(n>0){ + if(n%10==1){ + cnt++; + } + n/=10; + } + return cnt; +} +``` + +```java +// 思路二:参考 https://blog.csdn.net/yi_Afly/article/details/52012593 +// 总结如下规律: +// base 为当前位数 base=1,10,100, +// weight 为当前位值 +// formatter 为 weight 的后一位 +// round 就是 weight 前的所有位 +// 当 weight==0 时,count=round*base +// 当 weight==1 时,count=round*base+formatter+1 +// 当 weight>1 时,count==round*base+base public int NumberOf1Between1AndN_Solution(int n) { if(n<1){ return 0; @@ -276,13 +282,11 @@ public int NumberOf1Between1AndN_Solution(int n) { } ``` -> [Leetcode : 233. Number of Digit One](https://leetcode.com/problems/number-of-digit-one/discuss/64381/4+-lines-O(log-n)-C++JavaPython) - > [参考:从 1 到 n 整数中 1 出现的次数](https://blog.csdn.net/yi_Afly/article/details/52012593) -## *9、数字序列中的某一位数字 +## *4、数字序列中的某一位数字 数字以 0123456789101112131415... 的格式序列化到一个字符串中,求这个字符串的第 index 位。 @@ -340,7 +344,85 @@ private int getDigitAtIndex(int index, int place) { -## 10、圆圈中最后剩下的数 +# 经典数学问题 + +## *1、数值的整数次方 + +[数值的整数次方](https://www.nowcoder.com/practice/1a834e5e3e1a4b7ba251417554e07c00?tpId=13&tqId=11165&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) + +```java +//思路:分治法 +// a^n 次方 +// 若 a 为奇数 a^n = a*(a*a)^((n-1)/2) +// 若 a 为偶数 a^n = (a*a)^(n/2) + +//注意: +// n<0 时,则 a^n = 1 /(a ^(-n)) +// 但是当 n == Integer.MIN_VALUE 时,-n = Integer.MAX_VALUE+1 +// 我们可以转化为 1 / (a^(Integer.MAX_VALUE)*a) + +public double Power(double base, int exponent) { + if(exponent==0){ + return 1.0; + } + if(exponent==1){ + return base; + } + if(exponent==Integer.MIN_VALUE){ + return 1/(Power(base,Integer.MAX_VALUE)*base); + } + + if(exponent<0){ + exponent = -exponent; + return 1/Power(base,exponent); + } + + if(exponent%2==1){ + return Power(base*base,(exponent-1)/2)*base; + }else{ + return Power(base*base,exponent/2); + } +} +``` + + + +## 2、x 的平方根 + +[x 的平方根](https://leetcode-cn.com/problems/sqrtx/) + +```java +// 思路:要求是非负整数,则必然 x>=0 +// 当 x<4时,可以证明 sqrt(x) > x/2 +// 当 x=4时,sqrt(4)==4/2=2 +// 当 x>4时,可以证明 sqrt(x) < x/2 +public int mySqrt(int x) { + if(x==0){ + return 0; + } + if(x<4){ // x=1,x=2,x=3,sqrt(x)=1 + return 1; + } + // 考虑 x>=4的情况 + int l=1; + int r=x/2; + while(l<=r){ + int mid=(r-l)/2+l; + if(x/midx,采用 x/midmid){ + l=mid+1; + }else{ + return mid; + } + } + return r; // 只保留整数部分,所以取r +} +``` + + + +## *3、圆圈中最后剩下的数 [圆圈中最后剩下的数](https://www.nowcoder.com/practice/f78a359491e64a50bce2d89cff857eb6?tpId=13&tqId=11199&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) @@ -386,100 +468,183 @@ public int LastRemaining_Solution(int n, int m) { > [参考:约瑟夫环问题](https://blog.csdn.net/u011500062/article/details/72855826) -## 11、计数质数 + + +## 4、计数质数 [计数质数](https://leetcode-cn.com/problems/count-primes/) ```java - +// 思路: +// 定义一个非素数数组 notPrimes,其中 notPrimes[i] 表示元素 i 是否是素数 +// notPrimes[i]=true,说明 i 是素数 +// notPrimes[i]=false,说明 i 不是素数 +public int countPrimes(int n) { + int cnt=0; + if(n<=1){ + return cnt; + } + boolean[] notPrimes=new boolean[n]; + for(int x=2;x=n 循环停止 + for(long j=(long)x*x;j +// 11!=11*(2*5)*9*(4*2)*7*(3*2)*(1*5)*(2*2)*3*(1*2)*1=39916800,有2对<2,5> +// 也就是说,有多少对<2,5>就有多少0 +// 对于含有2的因子的话是 1*2, 2*2, 3*2, 4*2 +// 对于含有5的因子的话是 1*5, 2*5 +// 含有2的因子每两个出现一次,含有5的因子每5个出现一次,所以2出现的个数远远多于 5, +// 换言之找到一个5,一定能找到一个2与之配对。 +// 所以我们只需要找有多少个 5。 +// n! = 1 * 2 * 3 * 4 * (1 * 5) * ... * (2 * 5) * ... * (3 * 5) *... * n +// 可以得出如下规律:每隔5个数会出现一个5,每隔25个数,出现2个5,每隔125个数,出现3个5 +// 所以最终 5的个数:n/5+ n/25 (有1个5在每隔5个数已经出现过一次了) + n/125 (有1个在每隔5个数出现过了,另一个则载每隔25个数出现过了) +public int trailingZeroes(int n) { + if(n<5){ + return 0; + } + int res=0; + while (n>0){ + res+=n/5; + n/=5; + } + return res; +} ``` -## 13、字符串相加 +## 6、最少移动次数使数组元素相等 II -[字符串相加](https://leetcode-cn.com/problems/add-strings/) +[ 最少移动次数使数组元素相等 II](https://leetcode-cn.com/problems/minimum-moves-to-equal-array-elements-ii/) ```java -public String addStrings(String num1, String num2) { - if(num1==null || num1.length()==0){ - return num2; - } - if(num2==null || num2.length()==0){ - return num1; - } - - int i=num1.length()-1; - int j=num2.length()-1; - int c=0; - - StringBuilder res=new StringBuilder(); - - while(i>=0 || j>=0 || c==1){ - c+=(i>=0)?num1.charAt(i)-'0':0; - c+=(j>=0)?num2.charAt(j)-'0':0; - res.append(c%10); - c/=10; - i--; - j--; +// 思路: +// 当 x 为这个 N 个数的中位数时,可以使得距离最小。 +// 具体地,若 N 为奇数,则 x 必须为这 N 个数中的唯一中位数; +// 若 N 为偶数,中间的两个数为 p 和 q,中位数为 (p + q) / 2, +// 此时 x 只要是区间 [p, q](注意是闭区间) 中的任意一个数即可。 + +// 写法一:排序求中位数 +public int minMoves2(int[] nums) { + Arrays.sort(nums); + // 获取数组中位数获取[p,q]中的数值p + int mid=nums[nums.length/2]; + int res=0; + for(int num:nums){ + res+=Math.abs(num-mid); } - - return res.reverse().toString(); + return res; } ``` - - -## 14、最少移动次数使数组元素相等 II - -[ 最少移动次数使数组元素相等 II](https://leetcode-cn.com/problems/minimum-moves-to-equal-array-elements-ii/) - ```java +// 写法二:利用快速排序求中位数 +public int minMoves2(int[] nums) { + if(nums.length==1){ + return 0; + } + int k=nums.length/2; + int mid=nums[findKth(nums,k)]; + int res=0; + for(int num:nums){ + res+=Math.abs(num-mid); + } + return res; +} +private int findKth(int[] nums,int k){ + int start=0; + int end=nums.length-1; + while (start<=end){ + int p=partition(nums,start,end); + if(p==k){ + return p; + }else if(pk + end=p-1; + } + } + return -1; +} + +private int partition(int[] nums,int start,int end){ + int pivot=nums[start]; + while(start=pivot){ + end--; + } + nums[start]=nums[end]; + // 从左向右找第一个大于 pivot 的数 + while (start n/2,但在本题目 m 即是多数元素 public int majorityElement(int[] nums) { - int cnt=0; //统计众数 - int majority=-1; - int n=nums.length; - + int m=nums[0]; + int cnt=0; for(int num:nums){ if(cnt==0){ - majority=num; - cnt++; + m=num; + cnt=1; }else{ - if(majority!=num){ - cnt--; - }else{ + if(num==m){ cnt++; + }else{ + cnt--; } } } - - cnt=0; - for(int num:nums){ - if(num==majority){ - cnt++; - } - } - return (cnt>n/2)?majority:-1; + return m; } ``` diff --git "a/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/8_\345\205\266\344\273\226.md" "b/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/8_\345\205\266\344\273\226.md" index a69d6523..cbf78eab 100644 --- "a/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/8_\345\205\266\344\273\226.md" +++ "b/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/8_\345\205\266\344\273\226.md" @@ -47,7 +47,7 @@ public class Shuffle { ## 2、抢红包算法 -线段切割法:在一条线段上找 (N-1) 个随机点,就可以将该线段随机且公平地切割成 N 段。 +线段切割法:在一条线段上找 (N-1) 个随机点,就可以将该线段随机且公平地切割成 N 段。算上首端点和尾端点的话,总共有 (N+1) 个点 ```java public class RedPacket { @@ -57,29 +57,27 @@ public class RedPacket { * 如果是小数的话先转化为整数,相应的结果除以 100 即可 * @return List 存储 n 个红包的金额 */ - public List generatePocketByLineCutting(int n,double money){ + public List generatePocketByLineCutting(int n, double money){ Random random=new Random(); //如果是小数的话先转化为整数 int newMoney=(int)money*100; - //存储线段的的 (n-1) 个随机点,线段长度为 newMoney - Set set=new TreeSet<>(); - - while(set.size() points=new ArrayList<>(); + while (points.size() res=new ArrayList<>(); - - int pre=0; - for(Integer p:set) { - res.add((p - pre) * 1.0 / 100); - pre = p; + for(int i=1;i +
### 脏读 T1 修改一个数据,T2 随后读取这个数据。如果 T1 撤销了这次修改,那么 T2 读取的数据是脏数据。 -
+
### 不可重复读 T2 读取一个数据,T1 对该数据做了修改。如果 T2 再次读取这个数据,此时读取的结果和第一次读取的结果不同。 -
+
### 幻影读 T1 读取某个**范围**的数据,T2 在这个**范围**内插入新的数据,T1 再次读取这个**范围**的数据,此时读取的结果和和第一次读取的结果不同。 -
+
## 事务隔离级别 产生并发不一致性问题主要原因是破坏了事务的**隔离性**,解决方法是通过**并发控制**来保证隔离性。并发控制可以通过封锁(加锁)来实现,但是封锁操作需要用户自己控制,相当复杂。数据库管理系统提供了事务的**隔离级别**,让用户以一种更轻松的方式处理并发一致性问题。 -事务隔离级别如下: +### 未提交读(READ UNCOMMITTED) -| 隔离级别 | 说明 | -| :--------------------------: | :------------------------------------------------: | -| 未提交读(READ UNCOMMITTED) | 事务中的修改,即使没有提交,对其它事务也是可见的。 | -| 提交读(READ COMMITTED) | 一个事务只能读取已经提交的事务所做的修改。 | -| 可重复读(REPEATABLE READ) | 保证在同一个事务中多次读取同样数据的结果是一样的。 | -| 可串行化(SERIALIZABLE) | 强制事务串行执行。 | +事务中的修改,即使没有提交,对其它事务也是可见的。 -事务并发访问引起的问题及使用哪种事务隔离级别避免: +### 提交读(READ COMMITTED) + +一个事务只能读取已经提交的事务所做的修改。换句话说,一个事务所做的修改在提交之前对其它事务是不可见的。 + +### 可重复读(REPEATABLE READ) + +保证在同一个事务中多次读取同一数据的结果是一样的。 + +### 可串行化(SERIALIZABLE) + +强制事务串行执行,这样多个事务互不干扰,不会出现并发一致性问题。 + +隔离级别能解决的并发一致性问题: | 并发访问问题 | 事务隔离级别 | | :----------: | :------------------------------------------: | @@ -88,4 +95,3 @@ T1 读取某个**范围**的数据,T2 在这个**范围**内插入新的数据 | 提交读 (READ COMMITTED) | 避免 | 避免 | 发生 | 发生 | | 可重复读 (REPEATABLE READ) | 避免 | 避免 | 避免 | 发生 | | 可串行化 (SERIALIZABLE) | 避免 | 避免 | 避免 | 避免 | - diff --git "a/docs/DataBase/2_\345\205\263\347\263\273\346\225\260\346\215\256\345\272\223\350\256\276\350\256\241\347\220\206\350\256\272.md" "b/docs/DataBase/2_\345\205\263\347\263\273\346\225\260\346\215\256\345\272\223\350\256\276\350\256\241\347\220\206\350\256\272.md" index 3846303a..244398b2 100644 --- "a/docs/DataBase/2_\345\205\263\347\263\273\346\225\260\346\215\256\345\272\223\350\256\276\350\256\241\347\220\206\350\256\272.md" +++ "b/docs/DataBase/2_\345\205\263\347\263\273\346\225\260\346\215\256\345\272\223\350\256\276\350\256\241\347\220\206\350\256\272.md" @@ -34,7 +34,7 @@ 高级别范式的依赖于低级别的范式,1NF 是最低级别的范式。 -
+
diff --git "a/docs/DataBase/3_\350\256\276\350\256\241\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223.md" "b/docs/DataBase/3_\350\256\276\350\256\241\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223.md" index f92a8792..0309567f 100644 --- "a/docs/DataBase/3_\350\256\276\350\256\241\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223.md" +++ "b/docs/DataBase/3_\350\256\276\350\256\241\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223.md" @@ -2,7 +2,7 @@ 关系数据库管理系统(RDBMS)架构图如下: -
+
## 存储 @@ -20,7 +20,7 @@ 将取出的数据放入缓存中,一次性加载多个页数据,相当一部分不是本次访问所需的行。根据“一旦数据被访问,其相邻数据极有可能下次被访问到”,优化访问效率。 - 缓存管理机制:可以使用 [LRU](https://github.com/DuHouAn/Java-Notes/blob/master/docs/LeetCode/12%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84.md#146) 对缓存进行管理。 + 缓存管理机制:可以使用 LRU 对缓存进行管理。 - **SQL 解析** diff --git a/docs/DataBase/4_SQL.md b/docs/DataBase/4_SQL.md deleted file mode 100644 index 4a44aee8..00000000 --- a/docs/DataBase/4_SQL.md +++ /dev/null @@ -1,767 +0,0 @@ - -* [一、基础](#一基础) -* [二、创建表](#二创建表) -* [三、修改表](#三修改表) -* [四、插入](#四插入) -* [五、更新](#五更新) -* [六、删除](#六删除) -* [七、查询](#七查询) -* [八、排序](#八排序) -* [九、过滤](#九过滤) -* [十、通配符](#十通配符) -* [十一、计算字段](#十一计算字段) -* [十二、函数](#十二函数) -* [十三、分组](#十三分组) -* [十四、子查询](#十四子查询) -* [十五、连接](#十五连接) -* [十六、组合查询](#十六组合查询) -* [十七、视图](#十七视图) -* [十八、存储过程](#十八存储过程) -* [十九、游标](#十九游标) -* [二十、触发器](#二十触发器) -* [二十一、事务管理](#二十一事务管理) -* [二十二、字符集](#二十二字符集) -* [二十三、权限管理](#二十三权限管理) -* [参考资料](#参考资料) - - - -# 一、基础 - -模式定义了数据如何存储、存储什么样的数据以及数据如何分解等信息,数据库和表都有模式。 - -主键的值不允许修改,也不允许复用(不能使用已经删除的主键值赋给新数据行的主键)。 - -SQL(Structured Query Language),标准 SQL 由 ANSI 标准委员会管理,从而称为 ANSI SQL。各个 DBMS 都有自己的实现,如 PL/SQL、Transact-SQL 等。 - -SQL 语句不区分大小写,但是数据库表名、列名和值是否区分依赖于具体的 DBMS 以及配置。 - -SQL 支持以下三种注释: - -```sql -# 注释 -SELECT * -FROM mytable; -- 注释 -/* 注释1 - 注释2 */ -``` - -数据库创建与使用: - -```sql -CREATE DATABASE test; -USE test; -``` - -# 二、创建表 - -```sql -CREATE TABLE mytable ( - id INT NOT NULL AUTO_INCREMENT, - col1 INT NOT NULL DEFAULT 1, - col2 VARCHAR(45) NULL, - col3 DATE NULL, - PRIMARY KEY (`id`)); -``` - -# 三、修改表 - -添加列 - -```sql -ALTER TABLE mytable -ADD col CHAR(20); -``` - -删除列 - -```sql -ALTER TABLE mytable -DROP COLUMN col; -``` - -删除表 - -```sql -DROP TABLE mytable; -``` - -# 四、插入 - -普通插入 - -```sql -INSERT INTO mytable(col1, col2) -VALUES(val1, val2); -``` - -插入检索出来的数据 - -```sql -INSERT INTO mytable1(col1, col2) -SELECT col1, col2 -FROM mytable2; -``` - -将一个表的内容插入到一个新表 - -```sql -CREATE TABLE newtable AS -SELECT * FROM mytable; -``` - -# 五、更新 - -```sql -UPDATE mytable -SET col = val -WHERE id = 1; -``` - -# 六、删除 - -```sql -DELETE FROM mytable -WHERE id = 1; -``` - -**TRUNCATE TABLE** 可以清空表,也就是删除所有行。 - -```sql -TRUNCATE TABLE mytable; -``` - -使用更新和删除操作时一定要用 WHERE 子句,不然会把整张表的数据都破坏。可以先用 SELECT 语句进行测试,防止错误删除。 - -# 七、查询 - -## DISTINCT - -相同值只会出现一次。它作用于所有列,也就是说所有列的值都相同才算相同。 - -```sql -SELECT DISTINCT col1, col2 -FROM mytable; -``` - -## LIMIT - -限制返回的行数。可以有两个参数,第一个参数为起始行,从 0 开始;第二个参数为返回的总行数。 - -返回前 5 行: - -```sql -SELECT * -FROM mytable -LIMIT 5; -``` - -```sql -SELECT * -FROM mytable -LIMIT 0, 5; -``` - -返回第 3 \~ 5 行: - -```sql -SELECT * -FROM mytable -LIMIT 2, 3; -``` - -# 八、排序 - -- **ASC** :升序(默认) -- **DESC** :降序 - -可以按多个列进行排序,并且为每个列指定不同的排序方式: - -```sql -SELECT * -FROM mytable -ORDER BY col1 DESC, col2 ASC; -``` - -# 九、过滤 - -不进行过滤的数据非常大,导致通过网络传输了多余的数据,从而浪费了网络带宽。因此尽量使用 SQL 语句来过滤不必要的数据,而不是传输所有的数据到客户端中然后由客户端进行过滤。 - -```sql -SELECT * -FROM mytable -WHERE col IS NULL; -``` - -下表显示了 WHERE 子句可用的操作符 - -| 操作符 | 说明 | -| :---: | :---: | -| = | 等于 | -| < | 小于 | -| > | 大于 | -| <> != | 不等于 | -| <= !> | 小于等于 | -| >= !< | 大于等于 | -| BETWEEN | 在两个值之间 | -| IS NULL | 为 NULL 值 | - -应该注意到,NULL 与 0、空字符串都不同。 - -**AND 和 OR** 用于连接多个过滤条件。优先处理 AND,当一个过滤表达式涉及到多个 AND 和 OR 时,可以使用 () 来决定优先级,使得优先级关系更清晰。 - -**IN** 操作符用于匹配一组值,其后也可以接一个 SELECT 子句,从而匹配子查询得到的一组值。 - -**NOT** 操作符用于否定一个条件。 - -# 十、通配符 - -通配符也是用在过滤语句中,但它只能用于文本字段。 - -- **%** 匹配 >=0 个任意字符; - -- **\_** 匹配 ==1 个任意字符; - -- **[ ]** 可以匹配集合内的字符,例如 [ab] 将匹配字符 a 或者 b。用脱字符 ^ 可以对其进行否定,也就是不匹配集合内的字符。 - -使用 Like 来进行通配符匹配。 - -```sql -SELECT * -FROM mytable -WHERE col LIKE '[^AB]%'; -- 不以 A 和 B 开头的任意文本 -``` - -不要滥用通配符,通配符位于开头处匹配会非常慢。 - -# 十一、计算字段 - -在数据库服务器上完成数据的转换和格式化的工作往往比客户端上快得多,并且转换和格式化后的数据量更少的话可以减少网络通信量。 - -计算字段通常需要使用 **AS** 来取别名,否则输出的时候字段名为计算表达式。 - -```sql -SELECT col1 * col2 AS alias -FROM mytable; -``` - -**CONCAT()** 用于连接两个字段。许多数据库会使用空格把一个值填充为列宽,因此连接的结果会出现一些不必要的空格,使用 **TRIM()** 可以去除首尾空格。 - -```sql -SELECT CONCAT(TRIM(col1), '(', TRIM(col2), ')') AS concat_col -FROM mytable; -``` - -# 十二、函数 - -各个 DBMS 的函数都是不相同的,因此不可移植,以下主要是 MySQL 的函数。 - -## 汇总 - -|函 数 |说 明| -| :---: | :---: | -| AVG() | 返回某列的平均值 | -| COUNT() | 返回某列的行数 | -| MAX() | 返回某列的最大值 | -| MIN() | 返回某列的最小值 | -| SUM() |返回某列值之和 | - -AVG() 会忽略 NULL 行。 - -使用 DISTINCT 可以让汇总函数值汇总不同的值。 - -```sql -SELECT AVG(DISTINCT col1) AS avg_col -FROM mytable; -``` - -## 文本处理 - -| 函数 | 说明 | -| :---: | :---: | -| LEFT() | 左边的字符 | -| RIGHT() | 右边的字符 | -| LOWER() | 转换为小写字符 | -| UPPER() | 转换为大写字符 | -| LTRIM() | 去除左边的空格 | -| RTRIM() | 去除右边的空格 | -| LENGTH() | 长度 | -| SOUNDEX() | 转换为语音值 | - -其中, **SOUNDEX()** 可以将一个字符串转换为描述其语音表示的字母数字模式。 - -```sql -SELECT * -FROM mytable -WHERE SOUNDEX(col1) = SOUNDEX('apple') -``` - -## 日期和时间处理 - -- 日期格式:YYYY-MM-DD -- 时间格式:HH:MM:SS - -|函 数 | 说 明| -| :---: | :---: | -| AddDate() | 增加一个日期(天、周等)| -| AddTime() | 增加一个时间(时、分等)| -| CurDate() | 返回当前日期 | -| CurTime() | 返回当前时间 | -| Date() |返回日期时间的日期部分| -| DateDiff() |计算两个日期之差| -| Date_Add() |高度灵活的日期运算函数| -| Date_Format() |返回一个格式化的日期或时间串| -| Day()| 返回一个日期的天数部分| -| DayOfWeek() |对于一个日期,返回对应的星期几| -| Hour() |返回一个时间的小时部分| -| Minute() |返回一个时间的分钟部分| -| Month() |返回一个日期的月份部分| -| Now() |返回当前日期和时间| -| Second() |返回一个时间的秒部分| -| Time() |返回一个日期时间的时间部分| -| Year() |返回一个日期的年份部分| - -```sql -mysql> SELECT NOW(); -``` - -``` -2018-4-14 20:25:11 -``` - -## 数值处理 - -| 函数 | 说明 | -| :---: | :---: | -| SIN() | 正弦 | -| COS() | 余弦 | -| TAN() | 正切 | -| ABS() | 绝对值 | -| SQRT() | 平方根 | -| MOD() | 余数 | -| EXP() | 指数 | -| PI() | 圆周率 | -| RAND() | 随机数 | - -# 十三、分组 - -分组就是把具有相同的数据值的行放在同一组中。 - -可以对同一分组数据使用汇总函数进行处理,例如求分组数据的平均值等。 - -指定的分组字段除了能按该字段进行分组,也会自动按该字段进行排序。 - -```sql -SELECT col, COUNT(*) AS num -FROM mytable -GROUP BY col; -``` - -GROUP BY 自动按分组字段进行排序,ORDER BY 也可以按汇总字段来进行排序。 - -```sql -SELECT col, COUNT(*) AS num -FROM mytable -GROUP BY col -ORDER BY num; -``` - -WHERE 过滤行,HAVING 过滤分组,行过滤应当先于分组过滤。 - -```sql -SELECT col, COUNT(*) AS num -FROM mytable -WHERE col > 2 -GROUP BY col -HAVING num >= 2; -``` - -分组规定: - -- GROUP BY 子句出现在 WHERE 子句之后,ORDER BY 子句之前; -- 除了汇总字段外,SELECT 语句中的每一字段都必须在 GROUP BY 子句中给出; -- NULL 的行会单独分为一组; -- 大多数 SQL 实现不支持 GROUP BY 列具有可变长度的数据类型。 - -# 十四、子查询 - -子查询中只能返回一个字段的数据。 - -可以将子查询的结果作为 WHRER 语句的过滤条件: - -```sql -SELECT * -FROM mytable1 -WHERE col1 IN (SELECT col2 - FROM mytable2); -``` - -下面的语句可以检索出客户的订单数量,子查询语句会对第一个查询检索出的每个客户执行一次: - -```sql -SELECT cust_name, (SELECT COUNT(*) - FROM Orders - WHERE Orders.cust_id = Customers.cust_id) - AS orders_num -FROM Customers -ORDER BY cust_name; -``` - -# 十五、连接 - -连接用于连接多个表,使用 JOIN 关键字,并且条件语句使用 ON 而不是 WHERE。 - -连接可以替换子查询,并且比子查询的效率一般会更快。 - -可以用 AS 给列名、计算字段和表名取别名,给表名取别名是为了简化 SQL 语句以及连接相同表。 - -## 内连接 - -内连接又称等值连接,使用 INNER JOIN 关键字。 - -```sql -SELECT A.value, B.value -FROM tablea AS A INNER JOIN tableb AS B -ON A.key = B.key; -``` - -可以不明确使用 INNER JOIN,而使用普通查询并在 WHERE 中将两个表中要连接的列用等值方法连接起来。 - -```sql -SELECT A.value, B.value -FROM tablea AS A, tableb AS B -WHERE A.key = B.key; -``` - -在没有条件语句的情况下返回笛卡尔积。 - -## 自连接 - -自连接可以看成内连接的一种,只是连接的表是自身而已。 - -一张员工表,包含员工姓名和员工所属部门,要找出与 Jim 处在同一部门的所有员工姓名。 - -子查询版本 - -```sql -SELECT name -FROM employee -WHERE department = ( - SELECT department - FROM employee - WHERE name = "Jim"); -``` - -自连接版本 - -```sql -SELECT e1.name -FROM employee AS e1 INNER JOIN employee AS e2 -ON e1.department = e2.department - AND e2.name = "Jim"; -``` - -## 自然连接 - -自然连接是把同名列通过等值测试连接起来的,同名列可以有多个。 - -内连接和自然连接的区别:内连接提供连接的列,而自然连接自动连接所有同名列。 - -```sql -SELECT A.value, B.value -FROM tablea AS A NATURAL JOIN tableb AS B; -``` - -## 外连接 - -外连接保留了没有关联的那些行。分为左外连接,右外连接以及全外连接,左外连接就是保留左表没有关联的行。 - -检索所有顾客的订单信息,包括还没有订单信息的顾客。 - -```sql -SELECT Customers.cust_id, Orders.order_num -FROM Customers LEFT OUTER JOIN Orders -ON Customers.cust_id = Orders.cust_id; -``` - -customers 表: - -| cust_id | cust_name | -| :---: | :---: | -| 1 | a | -| 2 | b | -| 3 | c | - -orders 表: - -| order_id | cust_id | -| :---: | :---: | -|1 | 1 | -|2 | 1 | -|3 | 3 | -|4 | 3 | - -结果: - -| cust_id | cust_name | order_id | -| :---: | :---: | :---: | -| 1 | a | 1 | -| 1 | a | 2 | -| 3 | c | 3 | -| 3 | c | 4 | -| 2 | b | Null | - -# 十六、组合查询 - -使用 **UNION** 来组合两个查询,如果第一个查询返回 M 行,第二个查询返回 N 行,那么组合查询的结果一般为 M+N 行。 - -每个查询必须包含相同的列、表达式和聚集函数。 - -默认会去除相同行,如果需要保留相同行,使用 UNION ALL。 - -只能包含一个 ORDER BY 子句,并且必须位于语句的最后。 - -```sql -SELECT col -FROM mytable -WHERE col = 1 -UNION -SELECT col -FROM mytable -WHERE col =2; -``` - -# 十七、视图 - -视图是虚拟的表,本身不包含数据,也就不能对其进行索引操作。 - -对视图的操作和对普通表的操作一样。 - -视图具有如下好处: - -- 简化复杂的 SQL 操作,比如复杂的连接; -- 只使用实际表的一部分数据; -- 通过只给用户访问视图的权限,保证数据的安全性; -- 更改数据格式和表示。 - -```sql -CREATE VIEW myview AS -SELECT Concat(col1, col2) AS concat_col, col3*col4 AS compute_col -FROM mytable -WHERE col5 = val; -``` - -# 十八、存储过程 - -存储过程可以看成是对一系列 SQL 操作的批处理。 - -使用存储过程的好处: - -- 代码封装,保证了一定的安全性; -- 代码复用; -- 由于是预先编译,因此具有很高的性能。 - -命令行中创建存储过程需要自定义分隔符,因为命令行是以 ; 为结束符,而存储过程中也包含了分号,因此会错误把这部分分号当成是结束符,造成语法错误。 - -包含 in、out 和 inout 三种参数。 - -给变量赋值都需要用 select into 语句。 - -每次只能给一个变量赋值,不支持集合的操作。 - -```sql -delimiter // - -create procedure myprocedure( out ret int ) - begin - declare y int; - select sum(col1) - from mytable - into y; - select y*y into ret; - end // - -delimiter ; -``` - -```sql -call myprocedure(@ret); -select @ret; -``` - -# 十九、游标 - -在存储过程中使用游标可以对一个结果集进行移动遍历。 - -游标主要用于交互式应用,其中用户需要对数据集中的任意行进行浏览和修改。 - -使用游标的四个步骤: - -1. 声明游标,这个过程没有实际检索出数据; -2. 打开游标; -3. 取出数据; -4. 关闭游标; - -```sql -delimiter // -create procedure myprocedure(out ret int) - begin - declare done boolean default 0; - - declare mycursor cursor for - select col1 from mytable; - # 定义了一个 continue handler,当 sqlstate '02000' 这个条件出现时,会执行 set done = 1 - declare continue handler for sqlstate '02000' set done = 1; - - open mycursor; - - repeat - fetch mycursor into ret; - select ret; - until done end repeat; - - close mycursor; - end // - delimiter ; -``` - -# 二十、触发器 - -触发器会在某个表执行以下语句时而自动执行:DELETE、INSERT、UPDATE。 - -触发器必须指定在语句执行之前还是之后自动执行,之前执行使用 BEFORE 关键字,之后执行使用 AFTER 关键字。BEFORE 用于数据验证和净化,AFTER 用于审计跟踪,将修改记录到另外一张表中。 - -INSERT 触发器包含一个名为 NEW 的虚拟表。 - -```sql -CREATE TRIGGER mytrigger AFTER INSERT ON mytable -FOR EACH ROW SELECT NEW.col into @result; - -SELECT @result; -- 获取结果 -``` - -DELETE 触发器包含一个名为 OLD 的虚拟表,并且是只读的。 - -UPDATE 触发器包含一个名为 NEW 和一个名为 OLD 的虚拟表,其中 NEW 是可以被修改的,而 OLD 是只读的。 - -MySQL 不允许在触发器中使用 CALL 语句,也就是不能调用存储过程。 - -# 二十一、事务管理 - -基本术语: - -- 事务(transaction)指一组 SQL 语句; -- 回退(rollback)指撤销指定 SQL 语句的过程; -- 提交(commit)指将未存储的 SQL 语句结果写入数据库表; -- 保留点(savepoint)指事务处理中设置的临时占位符(placeholder),你可以对它发布回退(与回退整个事务处理不同)。 - -不能回退 SELECT 语句,回退 SELECT 语句也没意义;也不能回退 CREATE 和 DROP 语句。 - -MySQL 的事务提交默认是隐式提交,每执行一条语句就把这条语句当成一个事务然后进行提交。当出现 START TRANSACTION 语句时,会关闭隐式提交;当 COMMIT 或 ROLLBACK 语句执行后,事务会自动关闭,重新恢复隐式提交。 - -通过设置 autocommit 为 0 可以取消自动提交;autocommit 标记是针对每个连接而不是针对服务器的。 - -如果没有设置保留点,ROLLBACK 会回退到 START TRANSACTION 语句处;如果设置了保留点,并且在 ROLLBACK 中指定该保留点,则会回退到该保留点。 - -```sql -START TRANSACTION -// ... -SAVEPOINT delete1 -// ... -ROLLBACK TO delete1 -// ... -COMMIT -``` - -# 二十二、字符集 - -基本术语: - -- 字符集为字母和符号的集合; -- 编码为某个字符集成员的内部表示; -- 校对字符指定如何比较,主要用于排序和分组。 - -除了给表指定字符集和校对外,也可以给列指定: - -```sql -CREATE TABLE mytable -(col VARCHAR(10) CHARACTER SET latin COLLATE latin1_general_ci ) -DEFAULT CHARACTER SET hebrew COLLATE hebrew_general_ci; -``` - -可以在排序、分组时指定校对: - -```sql -SELECT * -FROM mytable -ORDER BY col COLLATE latin1_general_ci; -``` - -# 二十三、权限管理 - -MySQL 的账户信息保存在 mysql 这个数据库中。 - -```sql -USE mysql; -SELECT user FROM user; -``` - -**创建账户** - -新创建的账户没有任何权限。 - -```sql -CREATE USER myuser IDENTIFIED BY 'mypassword'; -``` - -**修改账户名** - -```sql -RENAME myuser TO newuser; -``` - -**删除账户** - -```sql -DROP USER myuser; -``` - -**查看权限** - -```sql -SHOW GRANTS FOR myuser; -``` - -**授予权限** - -账户用 username@host 的形式定义,username@% 使用的是默认主机名。 - -```sql -GRANT SELECT, INSERT ON mydatabase.* TO myuser; -``` - -**删除权限** - -GRANT 和 REVOKE 可在几个层次上控制访问权限: - -- 整个服务器,使用 GRANT ALL 和 REVOKE ALL; -- 整个数据库,使用 ON database.\*; -- 特定的表,使用 ON database.table; -- 特定的列; -- 特定的存储过程。 - -```sql -REVOKE SELECT, INSERT ON mydatabase.* FROM myuser; -``` - -**更改密码** - -必须使用 Password() 函数 - -```sql -SET PASSWROD FOR myuser = Password('new_password'); -``` - -# 参考资料 - -- BenForta. SQL 必知必会 [M]. 人民邮电出版社, 2013. diff --git "a/docs/DataBase/5_LeetCode_Database\351\242\230\350\247\243.md" "b/docs/DataBase/5_LeetCode_Database\351\242\230\350\247\243.md" index c3d52b50..0f3123f8 100644 --- "a/docs/DataBase/5_LeetCode_Database\351\242\230\350\247\243.md" +++ "b/docs/DataBase/5_LeetCode_Database\351\242\230\350\247\243.md" @@ -422,15 +422,15 @@ ON p.PersonId=a.PersonId; * 内连接:返回两张表的交集部分。 -
+
* 左连接: -
+
* 右连接: -
+
# *8、超过经理收入的员工(181) diff --git a/docs/JVM/1_JVM.md b/docs/JVM/1_JVM.md index bb466f50..3ec65d58 100644 --- a/docs/JVM/1_JVM.md +++ b/docs/JVM/1_JVM.md @@ -164,7 +164,7 @@ Class 文件中的常量池(编译器生成的字面量和符号引用)会 除了在编译期生成的常量,还允许动态生成,例如 String 类的 intern()。 -直接内存 +## 直接内存 在 JDK 1.4 中新引入了 NIO 类,它可以使用 Native 函数库直接分配堆外内存,然后通过 Java 堆里的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在堆内存和堆外内存来回拷贝数据。 diff --git "a/docs/JVM/1_Java\345\206\205\345\255\230\345\214\272\345\237\237.md" "b/docs/JVM/1_Java\345\206\205\345\255\230\345\214\272\345\237\237.md" new file mode 100644 index 00000000..ae76568a --- /dev/null +++ "b/docs/JVM/1_Java\345\206\205\345\255\230\345\214\272\345\237\237.md" @@ -0,0 +1,458 @@ +# Java 运行数数据区域 + +Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域。 + +JDK 1.8 和之前的版本略有不同: + +- JDK 1.7 之前**运行时常量池**逻辑包含**字符串常量池**存放在方法区 +- JDK 1.7 字符串常量池被从方法区中拿到了堆,运行时常量池剩下的内容还在方法区 +- JDK1.8 HotSpot 虚拟机**移除了永久代**,采用**元空间(Metaspace)** 代替方法区,这时候**字符串常量池还在堆**,运行时常量池还在方法区,只不过方法区的实现从永久代变成了元空间。 + +
+ +## 程序计数器 + +程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的**行号指示器(逻辑上)**。主要有以下两个作用: + +- 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。 +- 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。 + +此外,程序计数器还有如下特性: + +- 通过改变计数器的值来选取下一条需要执行的字节码指令 +- 和线程一对一的关系,即“线程私有” +- 对 Java 方法计数,如果是 Native 方法则计数器值为 Undefined +- 只是计数,不会发生内存泄漏,生命周期随着线程的创建而创建,随着线程的结束而死亡。 + +## Java 虚拟机栈 + +每个 Java 方法在执行的同时会创建一个栈帧用于存储**局部变量表**、**操作数栈**、动态链接、方法出口信息等。 + +
+ +从方法调用直至执行完成的过程,就对应着**一个栈帧在 Java 虚拟机栈中入栈和出栈的过程**。Java 方法有两种返回方式: + +- return 语句 +- 抛出异常 + +不管使用哪种返回方式都会导致栈帧被弹出。 + +可以通过 -Xss 这个虚拟机参数来指定每个线程的 Java 虚拟机栈内存大小: + +```java +java -Xss512M HackTheJava +``` + +该区域可能抛出以下异常: + +- 当线程请求的栈深度超过最大值,会抛出 StackOverflowError 异常; +- 栈进行动态扩展时如果无法申请到足够内存,会抛出 OutOfMemoryError 异常。 + +注意:**HotSpot 虚拟机的栈容量不可以进行动态扩展的**,所以在 HotSpot 虚拟机是不会由于虚拟机栈无法扩展而导致 OOM 的,但是如果申请时就失败,仍然会出现 OOM 异常。 + +### 局部变量表 + +局部变量表主要存放了编译期可知的各种数据类型(boolean、byte、char、short、int、float、long、double)和对象引用。 + +### 操作数栈 + +- 局部变量表:包含方法执行过程中的所有变量 +- 操作数栈:入栈、出栈、复制、交换、产生消费变量 + +通过 javap 命令分析 Java 汇编指令,感受操作数栈和局部变量表的关系。 + +定义测试类:该类中定义了一个静态方法 add() + +```java +public class JVMTest { + public static int add(int a ,int b) { + int c = 0; + c = a + b; + return c; + } +} +``` + +使用 javap 命令(javap 分析的是字节码文件) + +```html +javap -verbose JVMTest +``` + +得到如下汇编指令: + +```html + public static int add(int, int); + descriptor: (II)I + flags: ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=3, args_size=2 + 0: iconst_0 + 1: istore_2 + 2: iload_0 + 3: iload_1 + 4: iadd + 5: istore_2 + 6: iload_2 + 7: ireturn + LineNumberTable: + line 5: 0 + line 6: 2 + line 7: 6 +``` + +解读上述指令: + +- stack = 2 说明栈的深度是 2 ; +- locals = 3 说明有 3 个本地变量 ; +- args_size = 2 说明该方法需传入 2 个参数 +- load 指令表示入操作数栈,store 表示出操作数栈 + +执行 add(1,2),说明局部变量表和操作数栈的关系: + +- 首先会将栈帧按照程序计数器指令从大到小依次入栈,栈帧按照程序计数器指令依次出栈。 +- 数据 1、2 是入参,已经存在局部变量表 0、1 位置 +- 首先执行 iconst_0,将数据 0 push 进操作数栈 +- 执行 istore_2,将数据 0 pop出操作数栈并放入局部变量表中 2 位置 +- 执行 iload_0,将 0 位置元素(数值 1) push 进操作数栈 +- 执行 iload_1,将 1 位置元素(数值 2) push 进操作数栈 +- 执行 iadd,将数值1和数值2元素 pop出操作数栈,执行加法运算后,得到结果3,将 3 push 进操作数栈 +- 执行 istore_2,将数据 3 pop出操作数栈并放入局部变量表中 2 位置 +- 执行 iload_2,将 2 位置元素(数值 3)push 进操作数栈 +- 执行 ireturn,返回操作数栈栈顶元素 + +
+
+ +## 本地方法栈 + +本地方法栈与 Java 虚拟机栈类似,它们之间的区别只不过是本地方法栈为本地方法服务。 + +本地方法一般是用其它语言(C、C++ 或汇编语言等)编写的,并且被编译为基于本机硬件和操作系统的程序,对待这些方法需要特别处理。 + +本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。 + +方法执行完毕后相应的栈帧也会出栈并释放内存空间,也会出现 StackOverFlowError 和 OutOfMemoryError 。 + +## 堆 + +几乎所有的对象实例以及数组都在这里分配内存,是垃圾收集的主要区域,所以也被称作 **GC 堆(Garbage Collected Heap)**。 + +现代的垃圾收集器基本都是**采用分代收集算法**,其主要的思想是针对不同类型的对象采取不同的垃圾回收算法。可以将堆分成两块: + +- 新生代 (Young Generation),新生代可以划分为 Eden 、From Survivor、To Survivor 空间 +- 老年代 (Old Generation) + +堆不需要连续内存,并且可以动态增加其内存,增加失败会抛出 OutOfMemoryError 异常。 + +可以通过 -Xms 和 -Xmx 这两个虚拟机参数来指定一个程序的堆内存大小,第一个参数设置初始值,第二个参数设置最大值。 + +```java +java -Xms1M -Xmx2M HackTheJava +``` + +JDK 7 版本及 JDK 7 版本之前,Hotspot 虚拟机的堆结构如下: + +- 新生代 (Young Generation) +- 老年代 (Old Generation) +- 永久代 (Permanent Generation) + +
+ +JDK 8 版本之后 HotSpot 虚拟机的永久代被彻底移除了,取而代之是元空间,元空间使用的是直接内存。 + +
+ +## 堆和栈的关系 + +Java 内存可以粗糙的区分为堆内存(Heap)和栈内存 (Stack),其中**栈就是现在说的虚拟机栈**,或者说是虚拟机栈中局部变量表部分。引用对象、数组时,栈里定义变量保存堆中目标的首地址。 + +栈和堆的区别: + +- 物理地址 + + 堆的物理内存分配是不连续的; + + 栈的物理内存分配是连续的 + +- 分配内存 + + 堆是不连续的,分配的内存是在运行期确定的,大小不固定; + + 栈是连续的,分配的内存在编译器就已经确定,大小固定 + +- 存放内容 + + 堆中存放的是对象和数组,关注的是数据的存储; + + 栈中存放局部变量,关注的是程序方法的执行 + +- 是否线程私有 + + 堆内存中的对象对所有线程可见,可被所有线程访问; + + 栈内存属于某个线程私有的 + +- 异常 + + 栈扩展失败,会抛出 StackOverflowError; + + 堆内存不足,会抛出 OutOfMemoryError + +## 方法区 + +用于存放已被加载的**类信息**、**常量**、**静态变量**、**即时编译器编译后的代码**等数据。 + +和堆一样不需要连续的内存,并且可以动态扩展,动态扩展失败一样会抛出 OutOfMemoryError 异常。 + +对这块区域进行垃圾回收的主要目标是对常量池的回收和对类的卸载,但是一般比较难实现。 + +HotSpot 虚拟机把它当成永久代来进行垃圾回收。但很难确定永久代的大小,因为它受到很多因素影响,并且每次 Full GC 之后永久代的大小都会改变,所以经常会抛出 OutOfMemoryError 异常。为了更容易管理方法区,从 JDK 1.8 开始,移除永久代,并把方法区移至元空间,它位于本地内存中,而不是虚拟机内存中。 + +### 方法区与永久代 + +**方法区只是一个 JVM 规范**,在不同的 JVM 上方法区的实现可能是不同的。 + +方法区和永久代的关系类似 Java 中接口和类的关系,类实现了接口,**永久代就是 HotSpot 虚拟机对 JVM 规范中方法区的一种实现方式**。 + +方法区是 JVM 规范中的定义,是一种规范,而永久代是一种实现,一个是标准一个是实现,其他的虚拟机实现并没有永久代这一说法。 + +### 元空间与永久代 + +**方法区只是一个 JVM 规范,永久代与元空间都是其一种实现方式**。在 JDK 1.8 之后,原来永久代的数据被分到了堆和元空间中:**元空间存储类的元信息,静态变量和常量池等则放入堆中**。 + +元空间与永久代的最大区别在于:元空间使用本地内存,而永久代使用 JVM 的内存,元空间相比永久代具有如下优势: + +- 永久代存在一个 JVM 本身设置的固定大小上限,无法进行调整,而元空间使用的是直接内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是比原来出现的几率会更小。当元空间溢出时会得到如下错误: `java.lang.OutOfMemoryError: MetaSpace` 可以使用 `-XX:MaxMetaspaceSize` 标志设置最大元空间大小,默认值为 unlimited,这意味着它只受系统内存的限制。 +- 元空间里面存放的是类的元数据,这样加载多少类的元数据就不由 `MaxPermSize` 控制了, 而由系统的实际可用空间来控制,可以加载更多的类。 + + + +## 运行时常量池 + +运行时常量池是方法区的一部分。 + +Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有常量池表(用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中)。 + +运行时常量池相对于 Class 文件常量池的另外一个重要特征是具备动态性,Java 语言并不要求常量 +一定只有编译期才能产生,运行期间也可以将新的常量放入池中,例如 String 类的 intern()。 + + + +## 字符串常量池 + +JDK 1.7 之前**运行时常量池**逻辑包含**字符串常量池**存放在方法区。 + +JDK 1.7 **字符串常量池被单独拿到堆**,运行时常量池剩下的内容还在方法区。 + +JDK1.8 HotSpot 虚拟机**移除了永久代**,采用**元空间(Metaspace)** 代替方法区,这时候**字符串常量池还在堆**,运行时常量池还在方法区,只不过方法区的实现从永久代变成了元空间。 + +### String 对象创建方式 + +创建方式 1: + +```java +String str1 = "abcd"; +``` + +创建方式 2: + +```java +String str2 = new String("abcd"); +``` + +这两种不同的创建方法是有差别的: + +方式 1 是在**常量池**中获取对象("abcd" 属于字符串字面量,因此编译时期会在常量池中创建一个字符串对象)。 + +方式 2 会创建两个字符串对象(前提是常量池中还没有 "abcd" 字符串对象): + +- "abcd" 属于字符串字面量,因此编译时期会在常量池中创建一个字符串对象,该字符串对象指向这个 "abcd" 字符串字面量; +- 使用 new 的方式会在堆中创建一个字符串对象。 + +(**字符串常量"abcd"在编译期**就已经确定放入常量池,而 Java **堆上的"abcd"是在运行期**初始化阶段才确定)。 + +**str1 指向常量池中的 “abcd” 对象,而 str2 指向堆中的字符串对象。** + +### String 的 intern() 方法 + +String 的 intern() 是一个 **Native 方法**,当调用 intern() 方法时: + +- 如果运行时常量池中已经包含一个等于该 String 对象内容的字符串,则返回常量池中该字符串的引用。 +- 如果没有等于该 String 对象的字符串,JDK1.7 之前(不包含 1.7)是在常量池中创建与此 String 内容相同的字符串,并返回常量池中创建的字符串的引用,**JDK1.7 以及之后的处理方式是对于存在堆中的对象,在常量池中直接保存对象的引用,而不会重新创建对象。** + +```java +String s3 = new String("1") + new String("1"); +s3.intern(); +String s4 = "11"; +System.out.println(s3 == s4); +``` + +JDK 6 输出结果: + +```html +false +``` + +JDK 8 输出结果: + +```html +true +``` + +补充:[String 的 intern() 方法详解](https://www.cnblogs.com/wxgblogs/p/5635099.html) + +### 字符串拼接问题 + +```java +String str1 = "hello"; +String str2 = "world"; + +String str3 = "hello" + "world";//常量池中的对象 +String str4 = str1 + str2; //在堆上创建的新的对象 +String str5 = "helloworld";//常量池中的对象 + +System.out.println(str3 == str4); +System.out.println(str3 == str5); +System.out.println(str4 == str5); +``` + +输出结果如下: + +```html +false +true +false +``` + +str1、str2 是从字符串常量池中获取的对象。 + +对于 str3,字符串 "hello" 和字符串 "world" 相加有后得到 "helloworld",在字符串常量池中创建 "helloworld" 对象。 + +对于 str4,str1+str2 则会在堆中创建新的 "helloworld" 对象。 + +对于 str5,字符串常量池已有 "helloworld" 对象,str5 直接引用该对象。 + +
+ +所以,尽量避免多个字符串拼接,因为这样会重新创建新的对象。如果需要改变字符串的话,可以使用 StringBuilder 或者 StringBuffer。 + +## 直接内存 + +在 JDK 1.4 中新引入了 NIO 类,它可以使用 Native 函数库直接分配堆外内存,然后通过 Java 堆里的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在堆内存和堆外内存来回拷贝数据。 + + + +# Hotpot 虚拟机对象 + +## 对象的内存布局 + +在 Hotspot 虚拟机中,对象在内存中的布局可以分为 3 块区域: + +- 对象头 +- 实例数据 +- 对齐填充 + +
+ +### 对象头 + +Hotspot 虚拟机的对象头包括两部分信息: + +一部分用于存储对象自身的运行时数据(哈希码、GC分代年龄、锁状态标志等等); + +另一部分是类型指针,即对象指向它的**类元数据的指针**,虚拟机通过这个指针来**确定这个对象是哪个类的实例**。 + +### 实例数据 + +实例数据部分是对象真正存储的有效信息,也是在程序中所定义的各种类型的字段内容。 + +### 对齐填充 + +对齐填充部分不是必然存在的,也没有什么特别的含义,仅仅起**占位**作用。 + +因为 Hotspot 虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说就是对象的大小必须是8字节的整数倍。而对象头部分正好是8字节的倍数(1倍或2倍),因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。 + + + +## 对象的创建 + +Java 对象的创建过程分为以下5步: + +- 类加载检查 +- 分配内存 +- 初始化零值 +- 设置对象头 +- 执行 \ 方法 + +### 1. 类加载检查 + +虚拟机遇到一条 new 指令时,首先将去检查这个指令的参数是否能在常量池中定位到这个类的**符号引用**, +并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。 +如果没有,那必须先执行相应的类加载过程。 + +### 2. 分配内存 + +在类加载检查通过后,接下来虚拟机将为新生对象分配内存。 +对象所需的内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。分配方式有 “指针碰撞” 和 “空闲列表” 两种,选择那种分配方式由 Java 堆是否规整决定。 + + Java 堆内存是否规整,则取决于 GC 收集器的算法是“标记-清除”,还是“标记-整理”(也称作“标记-压缩”),值得注意的是,“复制算法”内存也是规整的。 + +#### 两种内存分配方式 + +##### 指针碰撞 + +- 原理:用过的内存全部整合到一边,没有用过的内存放在另一边,中间有一个分界值指针,只需要向着没用过的内存方向将指针移动一段与对象大小相等的距离。 +- 适用场景:堆内存规整(即没有内存碎片)的情况 +- GC(Garbage Collection)收集器:Serial、ParNew + +##### 空闲列表 + +- 原理:虚拟机会维护一个列表,在该列表中记录哪些内存块是可用的,在分配的时候,找一块足够大的内存块划分给对象示例,然后更新列表记录 +- 适用场景:堆内存规整 +- GC(Garbage Collection)收集器:CMS + +#### 内存分配并发问题 + +在创建对象的时候有一个很重要的问题,就是线程安全,因为在实际开发过程中,创建对象是很频繁的事情, +作为虚拟机来说,必须要保证线程是安全的,通常来讲,虚拟机采用两种方式来保证线程安全: + +- CAS+失败重试: CAS 是乐观锁的一种实现方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。虚拟机采用CAS配上失败重试的方式保证更新操作的原子性。 +- TLAB:每一个线程预先在Java堆中分配一块内存,称为**本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)**。哪个线程要分配内存,就在哪个线程的 TLAB 上分配,只有 TLAB 用完并分配新的 TLAB 时,才采用上述的 CAS 进行内存分配。 + +### 3. 初始化零值 + +内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作**保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用**,程序能访问到这些字段的数据类型所对应的零值。 + +### 4. 设置对象头 + +初始化零值完成之后,虚拟机要对对象进行必要的设置,例如这个对象是那个类的实例、如何才能找到类的元数据信息、对象的哈希吗、对象的 GC 分代年龄等信息。 这些信息存放在对象头中。 +另外,根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。 + +### 5. 执行 init 方法 + +在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了,但从 Java 程序的视角来看,对象创建才刚开始,**\ 方法还没有执行,所有的字段都还为零**。所以一般来说,执行 new 指令之后会接着执行 \ 方法,把**对象按照程序员的意愿进行初始化**,这样一个真正可用的对象才算完全产生出来。 + + + +## 对象的访问定位 + +建立对象就是为了使用对象,我们的Java程序通过栈上的 reference 数据来操作堆上的具体对象。 +对象的访问方式视虚拟机的实现而定,目前主流的访问方式有两种:使用句柄、直接指针。 + +### 使用句柄 + +如果使用句柄的话,那么 Java 堆中将会划分出一块内存来作为句柄池,reference 中存储的就是**对象的句柄地址**,而句柄中包含了对象实例数据与类型数据各自的具体地址信息 。 + +
+ +### 直接指针 + +如果使用直接指针访问,那么 Java 堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而 reference 中存储的直接就是**对象的地址**。 + +
+ +这两种对象访问方式各有优势: + +- 使用句柄来访问的最大好处是 reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而 **reference 本身不需要修改**。 +- 使用直接指针访问方式最大的好处就是**速度快**,它节省了一次指针定位的时间开销。 \ No newline at end of file diff --git a/docs/JavaBasics/2_String.md b/docs/JVM/2_String.md similarity index 100% rename from docs/JavaBasics/2_String.md rename to docs/JVM/2_String.md diff --git "a/docs/JVM/2_\345\236\203\345\234\276\346\224\266\351\233\206.md" "b/docs/JVM/2_\345\236\203\345\234\276\346\224\266\351\233\206.md" new file mode 100644 index 00000000..020c1779 --- /dev/null +++ "b/docs/JVM/2_\345\236\203\345\234\276\346\224\266\351\233\206.md" @@ -0,0 +1,425 @@ +# 垃圾收集 (Garbage Collection,GC) + +垃圾收集主要是针对堆和方法区进行。程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后就会消失,因此不需要对这三个区域进行垃圾回收。 + +## 判断一个对象是否可被回收 + +堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断哪些对象已经死亡(即不能再被任何途径使用的对象)。 + +### 引用计数算法 + +为对象添加一个引用计数器,当对象增加一个引用时计数器加 1,引用失效时计数器减 1。引用计数为 0 的对象可被回收。 + +在两个对象出现循环引用的情况下,此时引用计数器永远不为 0,导致无法对它们进行回收。正是因为**循环引用**的存在,因此 Java 虚拟机不使用引用计数算法。 + +```java +public class Test { + + public Object instance = null; + + public static void main(String[] args) { + Test a = new Test(); + Test b = new Test(); + a.instance = b; + b.instance = a; + a = null; + b = null; + doSomething(); + } +} +``` + +在上述代码中,a 与 b 引用的对象实例互相持有了对象的引用,因此当我们把对 a 对象与 b 对象的引用去除之后,由于两个对象还存在互相之间的引用,导致两个 Test 对象无法被回收。 + +- 优点:执行效率高,程序执行受影响较小。 +- 缺点:无法检测出循环引用的情况,引起内存泄漏。 + +### 可达性分析算法 + +通过判断对象的引用链是否可达来决定对象是否可以被回收。 + +以 GC Roots 为起始点进行搜索,可达的对象都是存活的,不可达的对象可被回收。 + +
+ +**Java 虚拟机使用可达性分析算法来判断对象是否可被回收**,GC Roots 一般包含以下几种: + +- 虚拟机栈中局部变量表中引用的对象(栈帧中的本地方法变量表) +- 本地方法栈中 JNI(Native方法) 中引用的对象 +- 方法区中类静态属性引用的对象 +- 方法区中的常量引用的对象 +- 活跃线程的引用对象 + + + +### 方法区的回收 + +因为方法区主要存放永久代对象,而永久代对象的回收率比新生代低很多,所以在方法区上进行回收性价比不高。 + +主要是对常量池的回收和对类的卸载。 + +为了避免内存溢出,在大量使用反射和动态代理的场景都需要虚拟机具备类卸载功能。 + +类的卸载条件很多,需要满足以下三个条件,并且满足了条件也不一定会被卸载: + +- 该类所有的实例都已经被回收,此时堆中不存在该类的任何实例。 +- 加载该类的 ClassLoader 已经被回收。 +- 该类对应的 Class 对象没有在任何地方被引用,也就无法在任何地方通过反射访问该类方法。 + + + +### finalize() + +类似 C++ 的析构函数(注意:只是类似,C++ 析构函数调用确定,而 finalize() 方法是不确定的),用于关闭外部资源。但是 try-finally 等方式可以做得更好,并且该方法运行代价很高,不确定性大,无法保证各个对象的调用顺序,因此最好不要使用。 + +当垃圾回收器要宣告一个对象死亡时,至少要经历两次标记过程。 + +如果对象在进行可达性分析以后,没有与GC Root 直接相连接的引用量,就会被**第一次标记**,并且判断是否执行 finalize() 方法; + +如果这个对象覆盖了 finalize() 方法,并且未被引用,就会被放置于 F-Queue 队列,稍后由虚拟机创建的一个低优先级的 finalize() 线程去执行**触发 finalize() 方法**,在该方法中让对象重新被引用,从而实现自救。但是该线程的优先级比较低,执行过程随时可能会被终止。此外,自救只能进行一次,如果回收的对象之前调用了 finalize() 方法自救,后面回收时不会再调用该方法。 + + + +### 引用类型 + +无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象是否可达,判定对象是否可被回收都与引用有关。 + +JDK1.2 之前,Java 中引用定义:如果 reference 类型的数据存储的数值代表的是另一块内存的起始地址,就称这块内存代表一个引用。 + +JDK1.2 以后,Java 对引用的概念进行了扩充,将引用分为强引用、软引用、弱引用、虚引用四种(引用强度逐渐减弱)。 + +#### 强引用 (Strong Reference) + +被强引用关联的对象不会被回收。 + +使用 new 一个新对象的方式来创建强引用。 + +```java +Object obj = new Object(); +``` + +当内存空间不足,JVM 抛出 OOM Error 终止程序也不会回收具有强引用的对象,只有通过将对象设置为 null 来弱化引用,才能使其被回收。 + +#### 软引用 (Soft Reference) + +表示对象处在**有用但非必须**的状态。 + +被软引用关联的对象只有在内存不够的情况下才会被回收。可以用来实现内存敏感的高速缓存。 + +软引用可以和一个引用队列 ReferenceQueue 联合使用,如果软引用所引用的对象被垃圾回收,JVM 就会把这个软引用加入到与之关联的引用队列中。如果一个弱引用对象本身在引用队列中,就说明该引用对象所指向的对象被回收了。 + +使用 SoftReference 类来创建软引用。 + +```java +Object obj = new Object(); +SoftReference sf = new SoftReference(obj); +obj = null; // 使对象只被软引用关联 +``` + +#### 弱引用 (Weak Reference) + +表示非必须的对象,比软引用更弱一些。适用于偶尔被使用且不影响垃圾收集的对象。 + +被弱引用关联的对象一定会被回收,也就是说它只能存活到下一次垃圾回收发生之前。 + +弱引用可以和一个引用队列 ReferenceQueue 联合使用,如果弱引用所引用的对象被垃圾回收,JVM 就会把这个弱引用加入到与之关联的引用队列中。如果一个弱引用对象本身在引用队列中,就说明该引用对象所指向的对象被回收了。 + +使用 WeakReference 类来创建弱引用。 + +```java +Object obj = new Object(); +WeakReference wf = new WeakReference(obj); +obj = null; +``` + +#### 虚引用 (Phantom Reference) + +又称为幽灵引用或者幻影引用,一个对象是否有虚引用的存在,不会对其生存时间造成影响,也无法通过虚引用得到一个对象。 + +不会决定对象的生命周期,任何时候都可能被垃圾回收器回收。**必须和引用队列 ReferenceQueue 联合使用**。 + +为一个对象设置虚引用的唯一目的是**能在这个对象被回收时收到一个系统通知,起哨兵作用**。具体来说,就是通过判断引用队列 ReferenceQueue 是否加入虚引用来判断被引用对象是否被 GC(垃圾回收线程) 回收:当 GC 准备回收一个对象时,如果发现它还仅有虚引用指向它,就会在回收该对象之前,把这个虚引用加入到与之关联的引用队列 ReferenceQueue 中。**如果一个虚引用对象本身就在引用队列中,就说明该引用对象所指向的对象被回收了**。 + +使用 PhantomReference 来创建虚引用。 + +```java +Object obj = new Object(); +ReferenceQueue queue = new ReferenceQueue(); +// 虚引用必须和引用队列 ReferenceQueue 联合使用 +PhantomReference pf = new PhantomReference(obj, queue); +obj = null; +``` + +总结: + +| 引用类型 | 被垃圾回收的时间 | 用途 | 生存时间 | +| -------- | ---------------- | -------------- | ----------------- | +| 强引用 | 从来不会 | 对象的一般状态 | JVM停止运行时终止 | +| 软引用 | 在内存不足的时候 | 对象缓存 | 内存不足时终止 | +| 弱引用 | 在垃圾回收的时候 | 对象缓存 | GC运行后终止 | +| 虚引用 | Unknown | 标记、哨兵 | Unknown | + + + +## 垃圾收集算法 + +### “标记-清除” 算法 + +
+ +在标记阶段,从根集合进行扫描,会检查每个对象是否为活动对象,如果是活动对象,则程序会在对象头部打上标记。 + +在清除阶段,会进行对象回收并取消标志位,另外,还会判断回收后的分块与前一个空闲分块是否连续,若连续,会合并这两个分块。回收对象就是把对象作为分块,连接到被称为 “空闲链表” 的单向链表,之后进行分配时只需要遍历这个空闲链表,就可以找到分块。 + +在分配时,程序会搜索空闲链表寻找空间大于等于新对象大小 size 的块 block。如果它找到的块等于 size,会直接返回这个分块;如果找到的块大于 size,会将块分割成大小为 size 与 (block - size) 的两部分,返回大小为 size 的分块,并把大小为 (block - size) 的块返回给空闲链表。 + +不足: + +- 标记和清除过程效率都不高; +- 会产生大量不连续的内存碎片,导致无法给大对象分配内存。 + +### ”标记-整理“ 算法 + +
+ +标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。 + +优点:不会产生内存碎片 + +不足:需要移动大量对象,处理效率比较低。 + +### ”复制“ 算法 + +
+ +将内存划分为大小相等的两块,每次只使用其中一块,当这一块内存用完了就将还存活的对象复制到另一块上面,然后再把使用过的内存空间进行一次清理。 + +主要不足是只使用了内存的一半。 + +现在的商业虚拟机都采用这种收集算法回收新生代,但是并不是划分为大小相等的两块,而是一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 和其中一块 Survivor。在回收时,将 Eden 和 Survivor 中还存活着的对象全部复制到另一块 Survivor 上,最后清理 Eden 和使用过的那一块 Survivor。 + +HotSpot 虚拟机的 Eden 和 Survivor 大小比例默认为 8:1,保证了内存的利用率达到 90%。如果每次回收有多于 10% 的对象存活,那么一块 Survivor 就不够用了,此时需要依赖于老年代进行空间分配担保,也就是借用老年代的空间存储放不下的对象。 + +### 分代收集 + +现在的商业虚拟机采用分代收集算法,它根据对象存活周期将内存划分为几块,不同块采用适当的收集算法。 + +一般将堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法: + +- 新生代:新生代对象存活时间很短,所以可以选择**“复制”算法**,只需要付出少量对象的复制成本就可以完成每次垃圾收集。 +- 老年代:老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择**“标记-清除”或“标记-整理”算法**进行垃圾收集。 + +## Stop-the-World & SafePoint + +### Stop-the-World + +所谓 Stop-the-World(简称 STW),指的是 **JVM 由于要执行 GC 而停止了应用程序的执行** : + +- 可达性分析算法中 GC Roots 会导致所有 Java 执行线程停顿,原因如下: + - 分析工作必须在一一个能确保一致性的快照中进行 + - 一致性指整个分析期间整个执行系统看起来像被冻结在某个时间点上 + - 如果出现分析过程中对象引用关系还在不断变化,则分析结果的准确性无法保证 +- 被 STW 中断的应用程序线程会在完成 GC 之后恢复,频繁中断会让用户感觉像是网速不快造成电影卡带一样, 影响用户体验,所以需要减少 STW 的发生。 + +STW 事件和采用哪款垃圾收集器无关,所有的 GC 都有这个事件。哪怕是 G1 也不能完全避免 STW 情况发生,只能说垃圾回收器越来越优秀,回收效率越来越高,尽可能地缩短了暂停时间。 + +STW 是 JVM 在后台自动发起和自动完成的。在用户不可见的情况下,把用户正常的工作线程全部停掉。开发中不要用 System.gc() 这样会导致 STW 的发生。 + +目前,降低系统的停顿时间两种算法:增量收集算法和分区算法。 + +#### 增量收集算法 + +基本思想:如果一次性将所有的垃圾进行处理,需要造成系统长时间的停顿,那么就可以让垃圾收集线程和应用程序线程交替执行。**垃圾收集线程一次只收集一小片区域的内存空间,接着切换到应用程序线程**。依次反复,直到垃圾收集完成。总的来说,增量收集算法的基础仍是传统的标记-清除和复制算法。增量收集算法通过对线程间冲突的妥善处理,允许垃圾收集线程以分阶段的方式完成标记、清理或复制工作。 + +不足:由于在垃圾回收过程中,间断性地还执行了应用程序代码,所以能减少系统的停顿时间。但是因为线程切换和上下文转换的消耗,会使得垃圾回收的总体成本上升,造成系统吞吐量的下降。 + +#### 分区算法 + +基本思想:一般来说,在相同条件下,堆空间越大,一次 GC 时所需要的时间就长,有关 GC 产生的停顿也越长。为了更好地控制 GC 产生的停顿时间,**将一块大的内存区域分割成多个小块**,根据目标的停顿时间,每次合理地回收若干个小区间,而不是整个堆空间,从而减少一次 GC 所产生的停顿。 + +注意分区算法与分代收集算法是不同的:分代收集算法将按照对象的生命周期长短划分成两个部分,而分区算法将整个堆空间划分成**连续的不同小区间**,其中每个小区间都独立使用,独立回收,这样可以控制一次回收多少个小区间。 + +总结: + +- 增量收集算法是将总的收集量一部分一部分的去执行 +- 分区算法是将总的内存空间分为小分区,一次可控的去收集多少个小区间。 + +### SafePoint + +程序执行时并非可以在任何地方都能停顿下来开始 GC,只有在特定的位置才能停顿下来开始 GC,这些位置称为Safepoint 。 + +SafePoint 的选择很重要,如果太少可能导致 GC 等待的时间太长,如果太频繁可能导致运行时的性能问题。 + +大部分指令的执行时间都非常短暂,通常会根据是否具有让程序长时间执行的特征为标准。比如选择一些执行时间较长的指令作为 SafePoint,如方法调用、循环跳转和异常跳转等。 + + + +## 垃圾收集器 + +### 评估 GC 的性能指标 + +#### 吞吐量 + +运行用户代码的时间占总运行时间的比例。其中总运行时间 = 程序的运行时间 + 内存回收的时间。 + +比如:虚拟机总共运行了100分钟, 其中垃圾收集花掉1分钟,那吞吐量就是99%。 + +#### 暂停时间 + +执行垃圾收集时,程序的工作线程被暂停的时间。 + +”高吞吐量” 和 ”低暂停时间” 是矛盾的: + +- 如果选择以吞吐量优先,那么必然需要降低内存回收的执行频率,但是这样会导致 GC 需要更长的暂停时间来执行内存回收。 +- 如果选择以低延迟优先为原则,那么为了降低每次执行内存回收时的暂停时间,也只能频繁地执行内存回收,但这又引起了年轻代内存的缩减和导致程序吞吐量的下降。 + +**现在标准:在最大吞吐量优先的情况下,降低停顿时间**。 + +
+ + + +### 经典垃圾收集器 + +**如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。** + +虽然我们对各个收集器进行比较,但并非要挑选出一个最好的收集器。因为直到现在为止还没有最好的垃圾收集器出现,更加没有万能的垃圾收集器,我们能做的就是**根据具体应用场景选择适合自己的垃圾收集器**。 + +
+ +以上是 HotSpot 虚拟机中的 7 个垃圾收集器,连线表示垃圾收集器可以配合使用。 JDK 9 取消了对 Serial+CMS、 +ParNew+Serial Old 这两个组合的支持。 + +- 单线程与多线程:单线程指的是垃圾收集器只使用一个线程,而多线程使用多个线程; +- 串行与并行:串行指的是垃圾收集器与用户程序交替执行,这意味着在执行垃圾收集的时候需要停顿用户程序;并行指的是垃圾收集器和用户程序同时执行。除了 CMS 和 G1 之外,其它垃圾收集器都是以串行的方式执行。 + +#### 1. Serial 收集器 + +
+ +Serial 翻译为串行,也就是说它以串行的方式执行。 + +它是单线程的收集器,只会使用一个线程进行垃圾收集工作。 + +它的优点是简单高效,在单个 CPU 环境下,由于没有线程交互的开销,因此拥有最高的单线程收集效率。 + +它是 Client 场景下的默认新生代收集器,因为在该场景下内存一般来说不会很大。它收集一两百兆垃圾的停顿时间可以控制在一百多毫秒以内,只要不是太频繁,这点停顿时间是可以接受的。 + +#### 2. ParNew 收集器 + +
+ +它是 Serial 收集器的多线程版本。 + +它是 Server 场景下默认的新生代收集器,除了性能原因外,主要是因为除了 Serial 收集器,只有它能与 CMS 收集器配合使用。 + +#### 3. Parallel Scavenge 收集器 + +与 ParNew 一样是多线程收集器。 + +其它收集器目标是尽可能缩短垃圾收集时用户线程的停顿时间,而它的目标是达到一个可控制的吞吐量,因此**它被称为“吞吐量优先”收集器**。这里的吞吐量指 CPU 用于运行用户程序的时间占总时间的比值。 + +停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验。而高吞吐量则可以高效率地利用 CPU 时间,尽快完成程序的运算任务,适合在后台运算而不需要太多交互的任务。 + +缩短停顿时间是以牺牲吞吐量和新生代空间来换取的:新生代空间变小,垃圾回收变得频繁,导致吞吐量下降。 + +可以通过一个开关参数打开 GC 自适应的调节策略(GC Ergonomics),就不需要手工指定新生代的大小(-Xmn)、Eden 和 Survivor 区的比例、晋升老年代对象年龄等细节参数了。虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量。 + +#### 4. Serial Old 收集器 + +
+ +是 Serial 收集器的老年代版本,也是给 Client 场景下的虚拟机使用。如果用在 Server 场景下,它有两大用途: + +- 在 JDK 1.5 以及之前版本(Parallel Old 诞生以前)中与 Parallel Scavenge 收集器搭配使用。 +- 作为 CMS 收集器的后备预案,在并发收集发生 Concurrent Mode Failure 时使用。 + +#### 5. Parallel Old 收集器 + +
+ +是 Parallel Scavenge 收集器的老年代版本。 + +**在注重吞吐量以及 CPU 资源敏感的场合**,都可以优先考虑 Parallel Scavenge 加 Parallel Old 收集器。 + +#### 6. CMS 收集器 + +
+ +CMS(Concurrent Mark Sweep),Mark Sweep 指的是标记 - 清除算法。 + +分为以下六个流程: + +- 初始标记:仅仅只是标记一下 GC Roots 能直接关联到的对象,速度很快,需要停顿。 +- 并发标记:进行 GC Roots Tracing 的过程,它在整个回收过程中耗时最长,不需要停顿。 +- 并发预清理:查找执行并发标记阶段从年轻代晋升到老年代的对象 +- **重新标记**:为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,需要停顿。 +- 并发清除:清理垃圾对象,不需要停顿。 +- 并发重置:重置CMS收集器的数据结构,等待下一次垃圾回收。 + +在整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,不需要进行停顿。 + +CMS 具有以下缺点: + +- 吞吐量低:低停顿时间是以牺牲吞吐量为代价的,导致 CPU 利用率不够高。 +- 无法处理浮动垃圾,可能出现 Concurrent Mode Failure。浮动垃圾是指并发清除阶段由于用户线程继续运行而产生的垃圾,这部分垃圾只能到下一次 GC 时才能进行回收。由于浮动垃圾的存在,因此需要预留出一部分内存,意味着 CMS 收集不能像其它收集器那样等待老年代快满的时候再回收。如果预留的内存不够存放浮动垃圾,就会出现 Concurrent Mode Failure,这时虚拟机将临时启用 Serial Old 来替代 CMS。 +- 标记 - 清除算法导致的空间碎片,往往出现老年代空间剩余,但无法找到足够大连续空间来分配当前对象,不得不提前触发一次 Full GC。 + +#### 7. G1 收集器 + +G1(Garbage-First),它是一款面向服务端应用的垃圾收集器,在多 CPU 和大内存的场景下有很好的性能。HotSpot 开发团队赋予它的使命是未来可以替换掉 CMS 收集器。 + +堆被分为新生代和老年代,其它收集器进行收集的范围都是整个新生代或者老年代,而 G1 可以直接对新生代和老年代一起回收。 + +
+ +G1 把堆划分成多个大小相等的独立区域(Region),新生代和老年代不再物理隔离。 + +通过引入 Region 的概念,从而将原来的一整块内存空间划分成多个的小空间,使得每个小空间可以单独进行垃圾回收。这种划分方法带来了很大的灵活性,使得可预测的停顿时间模型成为可能。通过记录每个 Region 垃圾回收时间以及回收所获得的空间(这两个值是通过过去回收的经验获得),并维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的 Region,保证了 G1 在有限的时间内可以获取尽可能高的收集效率。这种方式的侧重点在于回收垃圾最大量的 Region),G1 (Garbage First) 因此得名。 + +每个 Region 都有一个 Remembered Set,用来记录该 Region 对象的引用对象所在的 Region。通过使用 Remembered Set,在做可达性分析的时候就可以避免全堆扫描。 + +
+ +如果不计算维护 Remembered Set 的操作,G1 收集器的运作大致可划分为以下几个步骤: + +- 初始标记 +- 并发标记 +- 最终标记:为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程的 Remembered Set Logs 里面,最终标记阶段需要把 Remembered Set Logs 的数据合并到 Remembered Set 中。这阶段需要停顿线程,但是可并行执行。 +- 筛选回收:首先对各个 Region 中的回收价值和成本进行排序,根据用户所期望的 GC 停顿时间来制定回收计划。此阶段其实也可以做到与用户程序一起并发执行,但是因为只回收一部分 Region,时间是用户可控制的,而且停顿用户线程将大幅度提高收集效率。 + +与其他垃圾收集器相比,G1 具备如下特点: + +- 并行和并发 + + 并行性:G1 在回收期间,可以有多个 GC 线程同时工作,有效利用多核计算能力。此时用户线程STW + + 并发性:G1 拥有与应用程序交替执行的能力,部分工作可以和应用程序同时执行,因此,一般来说,不会在整个回收阶段发生完全阻塞应用程序的情况 + +- 分代收集 + 将堆空间分为若干个区域(Region) ,这些区域中包含了逻辑上的年轻代和老年代。 + +- 空间整合 + + G1将内存划分为一个个的 Region,内存的回收以 Region 作为基本单位。从局部(两个 Region 之间)上来看是基于“复制”算法,整体来看是基于“标记 - 整理”算法,这两种算法都可以避免内存碎片。这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次 GC。 尤其是当 Java 堆非常大的时候,G1 的优势更加明显。 + +- 可预测的停顿 + + G1 除了追求低停顿外(G1 可以只选取部分区域进行内存回收,这样缩小了回收的范围,因此对于全局停顿情况的发生也能得到较好的控制),还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过 N 毫秒。 + +相较于CMS,G1还不具备全方位、压倒性优势: + +比如在用户程序运行过程中,G1 无论是为了垃圾收集产生的内存占用还是程序运行时的额外执行负载(overload) 都要比 CMS 要高。从使用经验上来说,对于小内存应用,CMS 的表现大概率会优于 G1,而 G1 在大内存应用上则发挥其优势。平衡点在6- 8GB之间。 + +7 种经典垃圾收集器总结如下: + +| 垃圾收集器 | 运行状态 | 作用位置 | 收集算法 | 特点 | 适用场景 | +| :----------: | :---------: | :------------: | :-----------------------: | :----------: | :--------------------------------: | +| Serial | 串行 | 新生代 | “复制”算法 | 响应速度优先 | 单 CPU 下的 Client 模式 | +| ParNew | 并行 | 新生代 | “复制”算法 | 响应速度优先 | 多 CPU 的 Server 模式配合 CMS 使用 | +| Parallel | 并行 | 新生代 | “复制”算法 | 吞吐量优先 | 后台运算且不需要太多交互的场景 | +| Serial Old | 串行 | 老年代 | “标记-整理”算法 | 响应速度优先 | 单 CPU 下的 Client 模式 | +| Parallel Old | 并行 | 老年代 | “标记-整理”算法 | 吞吐量优先 | 后台运算且不需要太多交互的场景 | +| CMS | 并发 | 老年代 | “标记-清除”算法 | 响应速度优先 | 互联网或 B/S 业务 | +| G1 | 并发 + 并行 | 新生代+ 老年代 | “标记-整理” + “复制” 算法 | 响应速度优先 | 面向服务端应用 | + diff --git a/docs/JVM/6_JVM.md "b/docs/JVM/3_\345\206\205\345\255\230\345\210\206\351\205\215\344\270\216\345\233\236\346\224\266\347\255\226\347\225\245.md" similarity index 81% rename from docs/JVM/6_JVM.md rename to "docs/JVM/3_\345\206\205\345\255\230\345\210\206\351\205\215\344\270\216\345\233\236\346\224\266\347\255\226\347\225\245.md" index 2eae28e6..e1874468 100644 --- a/docs/JVM/6_JVM.md +++ "b/docs/JVM/3_\345\206\205\345\255\230\345\210\206\351\205\215\344\270\216\345\233\236\346\224\266\347\255\226\347\225\245.md" @@ -1,11 +1,18 @@ # 内存分配与回收策略 -[内存回收脑图](http://naotu.baidu.com/file/488e2f0745f7cfff1b03eb1c3d81fe3e?token=df2309819db31dde) +## HotSpot 虚拟机 GC 分类 -## Minor GC 和 Full GC +针对 HotSpot 虚拟机的实现,GC 可以分为 2 大类: -- Minor GC:回收新生代,因为新生代对象存活时间很短,因此 Minor GC 会频繁执行,执行的速度一般也会比较快。 -- Full GC:回收老年代和新生代,老年代对象其存活时间长,因此 Full GC 很少执行,执行速度会比 Minor GC 慢很多。 +- **部分收集(Partial GC)** + + - 新生代收集(**Minor GC** / Young GC):回收新生代,因为新生代对象存活时间很短,因此 Minor GC 会频繁执行,执行的速度一般也会比较快。 + - 老年代收集(**Major GC** / Old GC):只对老年代进行垃圾收集。需要注意的是 Major GC 在有的语境中也用于指代 Full GC。只有 CMS 的并发清除存在这个模式 + - 混合收集(Mixed GC):对整个新生代和部分老年代进行垃圾收集。只有 G1 存在这个模式 + +- **整堆收集(Full GC)** + + 收集整个 Java 堆,包括新生代、老年代和永久代(如果存在的话)。老年代对象其存活时间长,因此 Full GC 很少执行,执行速度会比 Minor GC 慢很多。 ## 内存分配策略 @@ -66,11 +73,3 @@ ### 5. Concurrent Mode Failure 执行 CMS GC 的过程中同时有对象要放入老年代,而此时老年代空间不足(可能是 GC 过程中浮动垃圾过多导致暂时性的空间不足),便会报 Concurrent Mode Failure 错误,并触发 Full GC。 - -## 常见的调优参数 - -| 参数 | 解释 | -| ------------------------ | ---------------------------------------------- | -| -XX:SurvivorRatio | Eden和Survivor的比值,默认为8:1 | -| -XX:NewRatio | 老年代和年轻代内存大小的比例 | -| -XX:MaxTenuringThreshold | 对象从年轻代晋升到老年代经过的GC次数的最大阈值 | \ No newline at end of file diff --git "a/docs/JVM/4_JVM\350\260\203\344\274\230.md" "b/docs/JVM/4_JVM\350\260\203\344\274\230.md" new file mode 100644 index 00000000..6624b20a --- /dev/null +++ "b/docs/JVM/4_JVM\350\260\203\344\274\230.md" @@ -0,0 +1,3 @@ +# JVM 调优 + +待补充。 \ No newline at end of file diff --git "a/docs/JVM/5_\347\261\273\346\226\207\344\273\266\347\273\223\346\236\204.md" "b/docs/JVM/5_\347\261\273\346\226\207\344\273\266\347\273\223\346\236\204.md" new file mode 100644 index 00000000..33dbf928 --- /dev/null +++ "b/docs/JVM/5_\347\261\273\346\226\207\344\273\266\347\273\223\346\236\204.md" @@ -0,0 +1,126 @@ +# 类文件结构 + +## 类文件概述 + +JVM 可以理解的代码就叫做**字节码**(即扩展名为 .class 的文件,即类文件),它不面向任何特定的处理器,只面向虚拟机。Java 语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。 + +字节码并不针对一种特定的机器,因此 Java 程序无须重新编译便可在多种不同操作系统的计算机上运行。 + + + +## 类文件结构 + +根据 JVM 规范,Class 文件通过 ClassFile 定义: + +```c +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]; // 属性表集合 +} +``` + +通过分析 ClassFilee,得到 class 文件的组成: + +
+ +
+ + + +### 魔数 + +每个 Class 文件的头 4 个字节称为魔数(Magic Number),它的唯一作用是**确定这个文件是否为一个能被虚拟机接收的 Class 文件**。 + +### 文件版本号 + +紧接着魔数的四个字节存储的是 Class 文件的版本号:第 5 和第 6 字节位是**次版本号**(Minor Version),第 7 和第 8 字节位是**主版本号**(Major Version)。 + +每当 Java 发布大版本(比如 Java 8,Java 9)的时候,主版本号都会加 1。 + +可以使用 `javap -v` 命令来快速查看 Class 文件的版本号信息。 + +高版本的 Java 虚拟机可以执行低版本编译器生成的 Class 文件,但是低版本的 Java 虚拟机不能执行高版本编译器生成的 Class 文件。在开发时要确保开发的的 JDK 版本和生产环境的 JDK 版本保持一致。 + +### 常量池 + +紧接着主次版本号之后的是常量池,常量池的数量是 constant_pool_count-1。 + +这是因为常量池计数器是从 1 开始计数的,将第 0 项常量空出来是有特殊考虑的,索引值为 0 代表“不引用任何一个常量池项。 + +常量池主要存放两大常量:**字面量**和**符号引用**。 + +字面量比较接近于 Java 语言层面的的常量概念,如文本字符串、声明为 final 的常量值等。 + +符号引用则属于编译原理方面的概念,包括下面三类常量: + +- 类和接口的全限定名 +- 字段的名称和描述符 +- 方法的名称和描述符 + +常量池中每一项常量都是一个表:表开始的第一位是一个 u1(占 1 个字节) 类型的标志位 \- tag 来标识常量的类型,代表当前这个常量属于哪种常量类型。 + +### 访问标志 + +紧接着常量池的两个字节代表访问标志(Access Flag),这个标志用于识别一些类或者接口层次的访问信息,包括: + +- 这个 Class 是类还是接口 +- 是否为 public 或者 abstract 类型 +- 如果是类的话是否声明为 final 等 + +### 当前类 & 父类 + +类索引(This Class)用于确定这个类的全限定名,父类索引(Super Class)用于确定这个类的父类的全限定名,由于 Java 语言的单继承,所以父类索引只有一个,除了 `java.lang.Object` 之外,所有的 Java 类都有父类,因此除了 java.lang.Object 外,所有 Java 类的父类索引都不为 0。 + +### 接口索引集合 + +接口索引集合用来描述这个类实现了哪些接口,这些被实现的接口将按 implements (如果这个类本身是接口的话则是extends)后的接口顺序从左到右排列在接口索引集合中。 + +### 字段表集合 + +字段表(Fields)用于描述接口或类中声明的变量。 + +字段包括类级变量以及实例变量,但不包括在方法内部声明的局部变量。 + +```c +field_info{ + u2 access_flags; + u2 name_index; + u2 descriptor_index; + u2 attributes_count; + attribute_info attributes[attributes_count]; +} +``` + +其中: + +- access_flags:字段的作用域(public,protected,private);实例变量还是类变量(static);是否可被序列化(transient);可变性(final);可见性(volatile) +- name_index:对常量池的引用,表示字段的名称 +- descriptor_index:对常量池的引用,表示字段和方法的描述符 +- attributes_count:一个字段还会拥有一些额外的属性,表示额外属性的个数 +- attributes[attributes_count]:存放属性具体内容。 + +### 方法表集合 + +methods_count 表示方法的数量,而 method_info 表示方法表。 + +Class 文件存储格式中对方法的描述与对字段的描述几乎采用了完全一致的方式。方法表的结构如同字段表一样,依次包括了访问标志、名称索引、描述符索引、属性表集合几项。 + +因为 volatile 修饰符和 transient 修饰符不可以修饰方法,所以方法表的访问标志中没有这两个对应的标志,但是增加了 synchronized、native、abstract 等关键字修饰方法,所以也就多了这些关键字对应的标志。 + +### 属性表集合 + +在 Class 文件,字段表,方法表中都可以携带自己的属性表集合,以用于描述某些场景专有的信息。与 Class 文件中其它的数据项目要求的顺序、长度和内容不同,属性表集合的限制稍微宽松一些,不再要求各个属性表具有严格的顺序,并且只要不与已有的属性名重复,任何人实现的编译器都可以向属性表中写 入自己定义的属性信息,JVM 运行时会忽略掉它不认识的属性。 \ No newline at end of file diff --git a/docs/JVM/7_JVM.md "b/docs/JVM/6_\347\261\273\345\212\240\350\275\275\346\234\272\345\210\266.md" similarity index 54% rename from docs/JVM/7_JVM.md rename to "docs/JVM/6_\347\261\273\345\212\240\350\275\275\346\234\272\345\210\266.md" index f1326b6f..210636ca 100644 --- a/docs/JVM/7_JVM.md +++ "b/docs/JVM/6_\347\261\273\345\212\240\350\275\275\346\234\272\345\210\266.md" @@ -1,28 +1,18 @@ -# 类加载机制 +# 类的生命周期 -类是在运行期间第一次使用时动态加载的,而不是一次性加载所有类。因为如果一次性加载,那么会占用很多的内存。 - -## 类的生命周期 - -
+从类被加载到虚拟机内存中开始,到释放内存总共有 7 个阶段:加载(Loading),验证(Verification),准备(Preparation),解析(Resolution),初始化(Initialization),使用(Using),卸载(Unloading)。 -包括以下 7 个阶段: - -- **加载(Loading)** -- **验证(Verification)** -- **准备(Preparation)** -- **解析(Resolution)** -- **初始化(Initialization)** -- 使用(Using) -- 卸载(Unloading) +其中验证,准备,解析三个部分统称为**连接(Linking)**。 ## 类加载过程 -包含了加载、验证、准备、解析和初始化这 5 个阶段。 +类是在运行期间第一次使用时动态加载的,而不是一次性加载所有类。因为如果一次性加载,那么会占用很多的内存。 + +类加载过程包括加载、连接和初始化这 3 个阶段。其中连接过程又分为 3 步:验证、准备和解析,所以可以说类加载过程包含了加载、验证、准备、解析和初始化这 5 个阶段。 ### 1. 加载 -加载是类加载的一个阶段,注意不要混淆。 +**加载是类加载的一个阶段,注意不要混淆**。 加载过程完成以下三件事: @@ -39,15 +29,20 @@ ### 2. 验证 -确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。 +确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。包括如下验证: + +- 文件格式验证:验证字节流是否符合 Class 文件格式规范 +- 元数据验证:对字节码描述的信息进行语义分析,保证其描述的信息符合 Java 语言规范的要求 +- 字节码验证:通过数据流和控制流分析,确保程序语义是合法的、符合逻辑的。 +- 符号引用验证:确保解析动作能正确执行。 ### 3. 准备 -类变量是被 static 修饰的变量,准备阶段为类变量分配内存并设置初始值,使用的是方法区的内存。 +类变量是被 static 修饰的变量,**准备阶段为类变量分配内存并设置初始值,使用的是方法区的内存**。注意:在 JDK 7 及之后,HotSpot 已经把原本放在永久代的字符串常量池、**静态变量等移动到堆中**,这个时候类变量则会随着 Class 对象一起存放在 Java 堆中。 实例变量不会在这阶段分配内存,它会在对象实例化时随着对象一起被分配在堆中。应该注意到,实例化不是类加载的一个过程,类加载发生在所有实例化操作之前,并且类加载只进行一次,实例化可以进行多次。 -初始值一般为 0 值,例如下面的类变量 value 被初始化为 0 而不是 123。 +初始值一般为 0 值,例如下面的类变量 value 的初始值是 0 而不是 123(初始化阶段才会赋值)。 ```java public static int value = 123; @@ -61,16 +56,14 @@ public static final int value = 123; ### 4. 解析 -将常量池的符号引用替换为直接引用的过程。 +解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用限定符 7 类符号引用进行。 -其中解析过程在某些情况下可以在初始化阶段之后再开始,这是为了支持 Java 的动态绑定。 +其中,符号引用指的是一组符号来描述目标,可以是任何字面量。直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。在程序实际运行时,只有符号引用是不够的,举个例子:在程序执行方法时,系统需要明确知道这个方法所在的位置。Java 虚拟机为每个类都准备了一张方法表来存放类中所有的方法。当需要调用一个类的方法的时候,只要知道这个方法在方法表中的偏移量就可以直接调用该方法了。通过解析操作符号引用就可以直接转变为目标方法在类中方法表的位置,从而使得方法可以被调用。 -
+**解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,也就是得到类或字段、方法在内存中的指针或者偏移量**。 ### 5. 初始化 -
- 初始化阶段才真正开始执行类中定义的 Java 程序代码。初始化阶段是虚拟机执行类构造器 <clinit>() 方法的过程。在准备阶段,类变量已经赋过一次系统要求的初始值,而在初始化阶段,根据程序员通过程序制定的主观计划去初始化类变量和其它资源。 <clinit>() 是由编译器自动收集类中所有类变量的赋值动作和静态语句块中的语句合并产生的,编译器收集的顺序由语句在源文件中出现的顺序决定。特别注意的是,静态语句块只能访问到定义在它之前的类变量,定义在它之后的类变量只能赋值,不能访问。例如以下代码: @@ -79,13 +72,13 @@ public static final int value = 123; public class Test { static { i = 0; // 给变量赋值可以正常编译通过 - System.out.print(i); // 这句编译器会提示“非法向前引用” + System.out.print(i); // 这句编译器会提示“非法向前引用”,因为变量 i 在后面才定义的。 } static int i = 1; } ``` -由于父类的 <clinit>() 方法先执行,也就意味着父类中定义的静态语句块的执行要优先于子类。例如以下代码: +由于父类的 <clinit>() 方法先执行,也就意味着**父类中定义的静态语句块的执行要优先于子类**。例如以下代码: ```java static class Parent { @@ -108,47 +101,164 @@ public static void main(String[] args) { 虚拟机会保证一个类的 <clinit>() 方法在多线程环境下被正确的加锁和同步,如果多个线程同时初始化一个类,只会有一个线程执行这个类的 <clinit>() 方法,其它线程都会阻塞等待,直到活动线程执行 <clinit>() 方法完毕。如果在一个类的 <clinit>() 方法中有耗时的操作,就可能造成多个线程阻塞,在实际过程中此种阻塞很隐蔽。 -> loadClass 和 forName 的区别? -- loadClass:得到的是第一个阶段加载的class对象,并没有初始化,例如Spring中Bean的实例的懒加载就是通过这种方式实现的。 -- forName :得到的是第五阶段初始化的class对象,例如JDBC中的数据连接。 ## 类初始化时机 ### 1. 主动引用 -虚拟机规范中并没有强制约束何时进行加载,但是规范严格规定了有且只有下列五种情况必须对类进行初始化(加载、验证、准备都会随之发生): +只有主动去使用类才会初始化类。 + +虚拟机规范中并没有强制约束何时进行加载,但是规范严格规定了有且只有下列 6 种情况必须对类进行初始化(加载、验证、准备都会随之发生): -- 遇到 new、getstatic、putstatic、invokestatic 这四条字节码指令时,如果类没有进行过初始化,则必须先触发其初始化。最常见的生成这 4 条指令的场景是:使用 new 关键字实例化对象的时候;读取或设置一个类的静态字段(被 final 修饰、已在编译期把结果放入常量池的静态字段除外)的时候;以及调用一个类的静态方法的时候。 -- 使用 java.lang.reflect 包的方法对类进行反射调用的时候,如果类没有进行初始化,则需要先触发其初始化。 -- 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。 -- 当虚拟机启动时,用户需要指定一个要执行的主类(包含 main() 方法的那个类),虚拟机会先初始化这个主类; -- 当使用 JDK 1.7 的动态语言支持时,如果一个 java.lang.invoke.MethodHandle 实例最后的解析结果为 REF_getStatic, REF_putStatic, REF_invokeStatic 的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化; +- 情况 1:遇到 new、getstatic、putstatic 或 invokestatic 这四条字节码指令时,如果类型没有进行过初始 + 化,则需要先触发其初始化阶段。能够生成这四条指令的典型 Java 代码场景有: + - 使用 new 关键字实例化对象的时候。 + - 设置一个类型的静态字段(**被 final 修饰、已在编译期把结果放入常量池的静态字段除外**)的时候。 + - 读取一个类型的静态字段(**被 final 修饰、已在编译期把结果放入常量池的静态字段除外**)的时候。 + - 调用一个类型的静态方法的时候。( JVM 执行 invokestatic 指令时会初始化类) +- 情况 2:使用 java.lang.reflect 包的方法对类型进行**反射调用**的时候,如果类型没有进行过初始化,则需 + 要先触发其初始化。 +- 情况 3:当初始化类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。 +- 情况 4:当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先 + 初始化这个主类。 +- 情况 5:当使用 JDK 1.7 的动态语言支持时,如果一个 java.lang.invoke.MethodHandle 实例最后的解析结果为 REF_getStatic, REF_putStatic, REF_invokeStatic 的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。 +- 情况 6:当一个接口中定义了 JDK8 新加入的默认方法(被 default 关键字修饰的接口方法)时,如果有这个接口的实现类发生了初始化,那该接口要在其之前被初始化。 ### 2. 被动引用 -以上 5 种场景中的行为称为对一个类进行主动引用。除此之外,所有引用类的方式都不会触发初始化,称为被动引用。被动引用的常见例子包括: +以上 6 种场景中的行为称为对一个类进行主动引用。除此之外,所有引用类的方式都不会触发初始化,称为被动引用。被动引用的常见例子包括: - 通过子类引用父类的静态字段,不会导致子类初始化。 -```java -System.out.println(SubClass.value); // value 字段在 SuperClass 中定义 -``` + 举个例子: + + ```java + public class SuperClass { + static { + System.out.println("SuperClass init!"); + } + + public static int value=123; + } + ``` + + ```java + public class SubClass extends SuperClass{ + static { + System.out.println("SubClass init!"); + } + } + ``` + + ```java + public class NotInitialization { + public static void main(String[] args) { + System.out.println(SubClass.value); + } + } + ``` + + 输出结果为: + + ```html + SuperClass init! + 123 + ``` + + 这是因为对于静态字段,只有直接定义这个字段的类才会被初始化,因此通过其子类来引用父类中定义的静态字段,只会触发父类的初始化而不会触发子类的初始化。 + +- 通过数组定义来引用类,不会触发此类的初始化。该过程会对数组类进行初始化,**数组类是一个由虚拟机自动生成的、直接继承自 Object 的子类,其中包含了数组的属性和方法**(数组类型不通过类加载器创建,它由 JVM 直接创建)。 + + 举个例子: + + ```java + public class SuperClass { + static { + System.out.println("SuperClass init!"); + } + + public static int value=123; + } + ``` + + ```java + public class SubClass extends SuperClass{ + static { + System.out.println("SubClass init!"); + } + } + ``` + + ```java + public class NotInitialization { + public static void main(String[] args) { + SuperClass[] sca = new SuperClass[10]; + } + } + ``` + + 输出结果为: + + ```html + 没有输出 SuperClass init! + ``` -- 通过数组定义来引用类,不会触发此类的初始化。该过程会对数组类进行初始化,数组类是一个由虚拟机自动生成的、直接继承自 Object 的子类,其中包含了数组的属性和方法。 +- 常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化。 -```java -SuperClass[] sca = new SuperClass[10]; -``` + 举了例子: -- 常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化。 + ```java + public class ConstClass { + static { + System.out.println("ConstClass init!"); + } + public static final String HELLOWORLD = "hello world"; + } + ``` -```java -System.out.println(ConstClass.HELLOWORLD); -``` + ```java + public class NotInitialization { + public static void main(String[] args) { + System.out.println(ConstClass.HELLOWORLD); + } + } + ``` + + 输出结果为: + + ```html + hello world + ``` + + +## loadClass 和 forName + +loadClass 和 forName 方法都可以获取 Class 对象,但是两者是有区别的: + +- loadClass 得到的是加载阶段的 Class 对象,并没有初始化,例如 Spring 中 Bean 的实例的懒加载就是通过这种方式实现的。 +- forName 得到的是初始化阶段的 Class 对象,例如 JDBC 中的数据连接。 + +## 卸载 + +卸载类即该类的 Class 对象被 GC。 + +类的卸载条件很多,需要满足以下三个条件,并且满足了条件也不一定会被卸载: + +- 该类所有的实例都已经被回收,此时堆中不存在该类的任何实例。 +- 加载该类的 ClassLoader 已经被回收。 +- 该类对应的 Class 对象没有在任何地方被引用,也就无法在任何地方通过反射访问该类方法。 + +JDK 自带的 BootstrapClassLoader, ExtClassLoader 和 AppClassLoader 负责加载 JDK 提供的类,所以这些类加载器的实例肯定不会被回收。但是我们自定义的类加载器的实例是可以被回收的,所以使用我们自定义加载器加载的类是可以被卸载掉的。 + + + +# 类加载器 ## 类与类加载器 +所有的类都由类加载器加载,加载的作用就是将 .class 文件加载到内存中。 + 两个类相等,需要类本身相等,并且使用同一个类加载器进行加载。这是因为每一个类加载器都拥有一个独立的类名称空间。 这里的相等,包括类的 Class 对象的 equals() 方法、isAssignableFrom() 方法、isInstance() 方法的返回结果为 true,也包括使用 instanceof 关键字做对象所属关系判定结果为 true。 @@ -170,21 +280,19 @@ System.out.println(ConstClass.HELLOWORLD); ## 双亲委派模型 -应用程序是由三种类加载器互相配合从而实现类加载,除此之外还可以加入自己定义的类加载器。 +每一个类都有一个对应它的类加载器。系统中的 ClassLoader 在协同工作的时候会默认使用双亲委派模型(Parents Delegation Model)。该模型要求除了顶层的启动类加载器外,其它的类加载器都要有自己的父类加载器。这里的父子关系一般通过组合关系(Composition)来实现,而不是继承关系(Inheritance)。 -下图展示了类加载器之间的层次关系,称为双亲委派模型(Parents Delegation Model)。该模型要求除了顶层的启动类加载器外,其它的类加载器都要有自己的父类加载器。这里的父子关系一般通过组合关系(Composition)来实现,而不是继承关系(Inheritance)。 - -
+
### 1. 工作过程 -一个类加载器首先将类加载请求转发到父类加载器,只有当父类加载器无法完成时才尝试自己加载。 +在类加载的时候,系统会首先判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载。加载的时候,首先会把该请求委派给父类加载器的 loadClass() 处理,因此所有的请求最终都应该传送到顶层的启动类加载器 BootstrapClassLoader 中。当父类加载器无法处理时,才由自己来处理。当父类加载器为 null 时,会使用启动类加载器 BootstrapClassLoader 作为父类加载器。 ### 2. 好处 使得 Java 类随着它的类加载器一起具有一种带有优先级的层次关系,从而使得基础类得到统一。 -可以避免多份同样的字节码的加载 +可以避免多份同样的字节码的加载。 例如 java.lang.Object 存放在 rt.jar 中,如果编写另外一个 java.lang.Object 并放到 ClassPath 中,程序可以编译通过。由于双亲委派模型的存在,所以在 rt.jar 中的 Object 比在 ClassPath 中的 Object 优先级更高,这是因为 rt.jar 中的 Object 使用的是启动类加载器,而 ClassPath 中的 Object 使用的是应用程序类加载器。rt.jar 中的 Object 优先级更高,那么程序中所有的 Object 都是这个 Object。 @@ -283,4 +391,8 @@ public class FileSystemClassLoader extends ClassLoader { + className.replace('.', File.separatorChar) + ".class"; } } -``` \ No newline at end of file +``` + +# 补充 + +- [main() 方法详解](https://www.cnblogs.com/bingyimeiling/p/10409728.html) \ No newline at end of file diff --git "a/docs/JVM/7_Java\347\250\213\345\272\217\347\274\226\350\257\221\345\222\214\350\277\220\350\241\214\350\277\207\347\250\213.md" "b/docs/JVM/7_Java\347\250\213\345\272\217\347\274\226\350\257\221\345\222\214\350\277\220\350\241\214\350\277\207\347\250\213.md" new file mode 100644 index 00000000..6e207c69 --- /dev/null +++ "b/docs/JVM/7_Java\347\250\213\345\272\217\347\274\226\350\257\221\345\222\214\350\277\220\350\241\214\350\277\207\347\250\213.md" @@ -0,0 +1,84 @@ +# Java 程序编译和运行过程 + +Java程序从 \.java 文件创建到程序运行要经过两大过程: + +- \.java 文件由编译器编译成 \.class文件 +- 字节码由 JVM 解释运行 + +## 编译过程 + +.java 源文件会被 Java编译器进行编译为.class文件: + +- Java 编译一个类时,如果这个类所依赖的类还没有被编译,编译器会自动的先编译这个所依赖的类,然后引用。如果 Java 编译器在指定的目录下找不到该类所依赖的类的 \.class文件或者 \.java源文件,则会报 + "Cant found sysbol" 的异常错误。 +- 编译后的 \.class 文件主要分为两部分:常量池和方法表集合。 + 常量池记录的是代码出现过的(常量、类名、成员变量等)以及符号引用(类引用、方法引用,成员变量引用等); + 方法表集合则记录各个方法的字节码。 + + + +## 运行过程 + +JVM 并不是在运行时就会把所有使用到的类都加载到内存中,而是用到的时候,才加载进方法区,并且只加载一次。 +Java类运行的过程大概分为两个步骤: + +- 类加载 +- 执行类 + +举例说明 Java 程序运行过程: + +```java +public class Person { + private String name; + + public Person(String name){ + this.name=name; + } + + public void sayHello(){ + System.out.println("Hello! My Name is: " + name); + } +} +``` + +```java +public class JVMTest { + public static void main(String[] args) { + Person p=new Person("Li Ming"); + p.sayHello(); + } +} +``` + +### 1. 类加载 + +首先编译 JVMTest.java 文件得到 JVMTest.class 文件,系统启动一个 JVM 进程,从 classpath 路径中找到 JVMTest.class 文件,将 JVMTest 的类信息加载到方法区中,这个过程称为 JVMTest 类的加载。 + +(只有类信息在方法区中,才能创建对象,使用类中的成员变量) + +### 2. JVM 找 main() 方法入口 + +在 main() 方法 入口持有一个指向当前类 (JVMTest) 常量池的指针,常量池中的第一项是一个对 Person 对象的符号引用。 + +main 方法中 `Person p=new Person("Li Ming"),JVM 需要创建一个 Person 对象,但是此时方法区中是没有 Person 类信息的,所以 JVM 需要加载 Person 类,将 Person 类的信息加载到方法区中。 + +JVM 以一个直接指向方法区 Person 类的指针替换了常量池中第一项的符号引用。 + +### 3. 实例化对象 + +加载完 Person 类的信息以后,JVM 就会在堆中为一个 Person 实例分配内存,然后调用构造方法初始化 Person 实例,并且该实例**持有指向方法区中的 Person 类的类型信息(其中包括方法表)的引用**。 + +(p 为指向该 Person 实例的引用,会被放到栈中) + +### 4. 运行方法 + +执行 p.sayHello(),JVM 根据栈中 p 的引用找到 Person 对象,然后根据 Person 对象持有的引用定位到方法区中 Person 类类信息的**方法表**,获得 sayHello 方法的字节码地址,然后开始运行方法。 + +
+ +
+ + +# 补充 + +- [main() 方法详解](https://www.cnblogs.com/bingyimeiling/p/10409728.html) \ No newline at end of file diff --git "a/docs/JavaBasics/10_Java\345\270\270\350\247\201\345\257\271\350\261\241.md" "b/docs/JavaBasics/10_Java\345\270\270\350\247\201\345\257\271\350\261\241.md" index 61c891fa..0608c91a 100644 --- "a/docs/JavaBasics/10_Java\345\270\270\350\247\201\345\257\271\350\261\241.md" +++ "b/docs/JavaBasics/10_Java\345\270\270\350\247\201\345\257\271\350\261\241.md" @@ -1,908 +1,402 @@ -# Java常见对象 +# Java 常见类 I -## Arrays -Arrays:针对数组进行操作的工具类。 +## Object -- Arrays的常用成员方法: -```java -public static String toString(int[] a) //把数组转成字符串 - -public static void sort(int[] a) //对数组进行排序 +Object类是类层次结构的根类。每个类都使用 Object 作为超类。每个类都直接或者间接的继承自Object类。 -public static int binarySearch(int[] a,int key) //二分查找 -``` +Object 中常用方法有: -- toString()源码如下: ```java -public static String toString(int[] a) { - if (a == null) - return "null"; - int iMax = a.length - 1; - if (iMax == -1) - return "[]"; - - StringBuilder b = new StringBuilder(); - b.append('['); - for (int i = 0; ; i++) { - b.append(a[i]); - if (i == iMax) - return b.append(']').toString(); - b.append(", "); - } - } -``` -- binarySearch()调用的是binarySearch0(),源码如下: -```java - private static int binarySearch0(int[] a, int fromIndex, int toIndex,int key) { - int low = fromIndex; - int high = toIndex - 1; - - while (low <= high) { - int mid = (low + high) >>> 1; - int midVal = a[mid]; - - if (midVal < key) - low = mid + 1; - else if (midVal > key) - high = mid - 1; - else - return mid; // key found - } - return -(low + 1); // key not found. - } -``` +public int hashCode() //返回该对象的哈希码值。 +// 注意:哈希值是根据哈希算法计算出来的一个值,这个值和地址值有关,但是不是实际地址值。 -- 使用示例: -```java -public class ArraysDemo { - public static void main(String[] args) { - // 定义一个数组 - int[] arr = { 24, 69, 80, 57, 13 }; +public final Class getClass() //返回此 Object 的运行时类 - // public static String toString(int[] a) 把数组转成字符串 - System.out.println("排序前:" + Arrays.toString(arr));//排序前:[24, 69, 80, 57, 13] +public String toString() //返回该对象的字符串表示。 - // public static void sort(int[] a) 对数组进行排序 - Arrays.sort(arr); - System.out.println("排序后:" + Arrays.toString(arr));//排序后:[13, 24, 57, 69, 80] +protected Object clone() //创建并返回此对象的一个副本。可重写该方法 - // [13, 24, 57, 69, 80] - // public static int binarySearch(int[] a,int key) 二分查找 - System.out.println("binarySearch:" + Arrays.binarySearch(arr, 57));//binarySearch:2 - System.out.println("binarySearch:" + Arrays.binarySearch(arr, 577));//binarySearch:-6 - } -} +protected void finalize() +// 当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。用于垃圾回收,但是什么时候回收不确定。 ``` -## BigDemical -BigDecimal类:不可变的、任意精度的有符号十进制数,可以解决数据丢失问题。 -看如下程序,写出结果 -```java -public static void main(String[] args) { - System.out.println(0.09 + 0.01); - System.out.println(1.0 - 0.32); - System.out.println(1.015 * 100); - System.out.println(1.301 / 100); - System.out.println(1.0 - 0.12); -} -``` -输出结果 -```html -0.09999999999999999 -0.6799999999999999 -101.49999999999999 -0.013009999999999999 -0.88 -``` -结果和我们想的有一点点不一样,这是因为浮点数类型的数据存储和整数不一样导致的。 -它们大部分的时候,都是带有有效数字位。由于在运算的时候,float类型和double很容易丢失精度, -所以,为了能精确的表示、计算浮点数,Java提供了BigDecimal。 +### equals() 方法 -- BigDecimal的常用成员方法: -```java -public BigDecimal(String val) //构造方法 - -public BigDecimal add(BigDecimal augend) //加 - - public BigDecimal subtract(BigDecimal subtrahend)//减 - - public BigDecimal multiply(BigDecimal multiplicand) //乘 - - public BigDecimal divide(BigDecimal divisor) //除 - - public BigDecimal divide(BigDecimal divisor,int scale,int roundingMode)//除法,scale:几位小数,roundingMode:如何舍取 -``` +**1. 等价关系** + +Ⅰ 自反性 -- 使用BigDecimal改进 ```java -public static void main(String[] args) { - /*System.out.println(0.09 + 0.01); - System.out.println(1.0 - 0.32); - System.out.println(1.015 * 100); - System.out.println(1.301 / 100); - System.out.println(1.0 - 0.12);*/ - - BigDecimal bd1 = new BigDecimal("0.09"); - BigDecimal bd2 = new BigDecimal("0.01"); - System.out.println("add:" + bd1.add(bd2));//add:0.10 - System.out.println("-------------------"); - - BigDecimal bd3 = new BigDecimal("1.0"); - BigDecimal bd4 = new BigDecimal("0.32"); - System.out.println("subtract:" + bd3.subtract(bd4));//subtract:0.68 - System.out.println("-------------------"); - - BigDecimal bd5 = new BigDecimal("1.015"); - BigDecimal bd6 = new BigDecimal("100"); - System.out.println("multiply:" + bd5.multiply(bd6));//multiply:101.500 - System.out.println("-------------------"); - - BigDecimal bd7 = new BigDecimal("1.301"); - BigDecimal bd8 = new BigDecimal("100"); - System.out.println("divide:" + bd7.divide(bd8));//divide:0.01301 - - //四舍五入 - System.out.println("divide:" - + bd7.divide(bd8, 3, BigDecimal.ROUND_HALF_UP));//保留三位有效数字 - //divide:0.013 - - System.out.println("divide:" - + bd7.divide(bd8, 8, BigDecimal.ROUND_HALF_UP));//保留八位有效数字 - //divide:0.01301000 -} +x.equals(x); // true ``` -## BigInteger -BigInteger:可以让超过Integer范围内的数据进行运算 +Ⅱ 对称性 ```java -public static void main(String[] args) { - Integer num = new Integer("2147483647"); - System.out.println(num); - - //Integer num2 = new Integer("2147483648"); - // Exception in thread "main" java.lang.NumberFormatException: For input string: "2147483648" - //System.out.println(num2); - - // 通过 BigIntege来创建对象 - BigInteger num2 = new BigInteger("2147483648"); - System.out.println(num2); -} +x.equals(y) == y.equals(x); // true ``` -- BigInteger的常用成员方法: -```java -public BigInteger add(BigInteger val) //加 - -public BigInteger subtract(BigInteger val) //减 +Ⅲ 传递性 -public BigInteger multiply(BigInteger val) //乘 - -public BigInteger divide(BigInteger val) //除 - -public BigInteger[] divideAndRemainder(BigInteger val)//返回商和余数的数组 -``` -- 使用实例: ```java -public class BigIntegerDemo { - public static void main(String[] args) { - Integer num = new Integer("2147483647"); - System.out.println(num); +if (x.equals(y) && y.equals(z)) + x.equals(z); // true; +``` - //Integer num2 = new Integer("2147483648"); - // Exception in thread "main" java.lang.NumberFormatException: For input string: "2147483648" - //System.out.println(num2); +Ⅳ 一致性 - // 通过 BigIntege来创建对象 - BigInteger num2 = new BigInteger("2147483648"); - System.out.println(num2); - } -} -``` +多次调用 equals() 方法结果不变 ```java -public class BigIntegerDemo2 { - public static void main(String[] args) { - BigInteger bi1 = new BigInteger("100"); - BigInteger bi2 = new BigInteger("50"); - - // public BigInteger add(BigInteger val):加 - System.out.println("add:" + bi1.add(bi2)); //add:150 - // public BigInteger subtract(BigInteger Val):减 - System.out.println("subtract:" + bi1.subtract(bi2));//subtract:50 - // public BigInteger multiply(BigInteger val):乘 - System.out.println("multiply:" + bi1.multiply(bi2));//multiply:5000 - // public BigInteger divide(BigInteger val):除 - System.out.println("divide:" + bi1.divide(bi2));//divide:2 - - // public BigInteger[] divideAndRemainder(BigInteger val):返回商和余数的数组 - BigInteger[] bis = bi1.divideAndRemainder(bi2); - System.out.println("divide:" + bis[0]);//divide:2 - System.out.println("remainder:" + bis[1]);//remainder:0 - } -} +x.equals(y) == x.equals(y); // true ``` -## Calendar -Calendar为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等日历字段之间的转换提供了一些方法, -并为操作日历字段(例如获得下星期的日期)提供了一些方法。 +Ⅴ 与 null 的比较 -Calendar中常用的方法: +对任何**不是 null 的对象** x 调用 x.equals(null) 结果都为 false ```java -public int get(int field) //返回给定日历字段的值。日历类中的每个日历字段都是静态的成员变量,并且是int类型。 - -public void add(int field,int amount)//根据给定的日历字段和对应的时间,来对当前的日历进行操作。 - -public final void set(int year,int month,int date)//设置当前日历的年月日 -``` -- 使用示例: -```java -public class CalendarDemo { - public static void main(String[] args) { - // 其日历字段已由当前日期和时间初始化: - Calendar rightNow = Calendar.getInstance(); // 子类对象 - int year=rightNow.get(Calendar.YEAR); - int month=rightNow.get(Calendar.MONTH);//注意月份是从0开始的 - int date=rightNow.get(Calendar.DATE); - System.out.println(year + "年" + (month + 1) + "月" + date + "日"); - //2018年12月25日 - } -} +x.equals(null); // false; ``` -- 使用示例2: -```java -public class CalendarDemo2 { - public static void main(String[] args) { - // 其日历字段已由当前日期和时间初始化: - Calendar calendar = Calendar.getInstance(); // 子类对象 - System.out.println(getYearMonthDay(calendar));//2018年12月25日 - - //三年前的今天 - calendar.add(Calendar.YEAR,-3); - System.out.println(getYearMonthDay(calendar));//2015年12月25日 - - //5年后的10天前 - calendar.add(Calendar.YEAR,5); - calendar.add(Calendar.DATE,-10); - System.out.println(getYearMonthDay(calendar));//2020年12月15日 - - //设置 2011年11月11日 - calendar.set(2011,10,11); - System.out.println(getYearMonthDay(calendar));//2011年11月11日 - } - //获取年、月、日 - public static String getYearMonthDay(Calendar calendar){ - int year=calendar.get(Calendar.YEAR); - int month=calendar.get(Calendar.MONTH); - int date=calendar.get(Calendar.DATE); - return year + "年" + (month + 1) + "月" + date + "日"; - } -} -``` +**2. 等价与相等** -- 小练习:获取任意一年的二月有多少天 -```java -/** - *获取任意一年的二月有多少天 - *分析: - * A:键盘录入任意的年份 - * B:设置日历对象的年月日 - * 年就是输入的数据 - * 月是2 - * 日是1 - * C:把时间往前推一天,就是2月的最后一天 - * D:获取这一天输出即可 - */ -public class CalendarTest { - public static void main(String[] args) { - Scanner sc=new Scanner(System.in); - int year=sc.nextInt(); - Calendar c= Calendar.getInstance(); - c.set(year,2,1); //得到的就是该年的3月1日 - c.add(Calendar.DATE,-1);//把时间往前推一天,就是2月的最后一天 - //public void add(int field,int amount):根据给定的日历字段和对应的时间,来对当前的日历进行操作。 - - System.out.println(year+"年,二月有"+c.get(Calendar.DATE)+"天"); - } -} -``` +- 对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。 +- 对于引用类型,== 判断两个变量是否引用同一个对象,而 equals() 判断引用的对象是否等价。 -## Character -Character 类在对象中包装一个基本类型 char 的值.此外,该类提供了几种方法, -以确定字符的类别(小写字母,数字,等等),并将字符从大写转换成小写,反之亦然。 - -- Character常用方法: ```java -Character(char value) //构造方法 - -public static boolean isUpperCase(char ch) //判断给定的字符是否是大写字符 - -public static boolean isLowerCase(char ch) //判断给定的字符是否是小写字符 +Integer x = new Integer(1); +Integer y = new Integer(1); +System.out.println(x.equals(y)); // true +System.out.println(x == y); // false +``` -public static boolean isDigit(char ch) //判断给定的字符是否是数字字符 +**3. 实现** -public static char toUpperCase(char ch) //把给定的字符转换为大写字符 +- 检查是否为同一个对象的引用,如果是直接返回 true; +- 检查是否是同一个类型,如果不是,直接返回 false; +- 将 Object 对象进行转型; +- 判断每个关键域是否相等。 -public static char toLowerCase(char ch) //把给定的字符转换为小写字符 -``` -- 使用示例: ```java -public class CharacterDemo { - public static void main(String[] args) { - // public static boolean isUpperCase(char ch):判断给定的字符是否是大写字符 - System.out.println("isUpperCase:" + Character.isUpperCase('A'));//true - System.out.println("isUpperCase:" + Character.isUpperCase('a'));//false - System.out.println("isUpperCase:" + Character.isUpperCase('0'));//false - System.out.println("-----------------------------------------"); - - // public static boolean isLowerCase(char ch):判断给定的字符是否是小写字符 - System.out.println("isLowerCase:" + Character.isLowerCase('A'));//false - System.out.println("isLowerCase:" + Character.isLowerCase('a'));//true - System.out.println("isLowerCase:" + Character.isLowerCase('0'));//false - System.out.println("-----------------------------------------"); - - // public static boolean isDigit(char ch):判断给定的字符是否是数字字符 - System.out.println("isDigit:" + Character.isDigit('A'));//false - System.out.println("isDigit:" + Character.isDigit('a'));//false - System.out.println("isDigit:" + Character.isDigit('0'));//true - System.out.println("-----------------------------------------"); - - // public static char toUpperCase(char ch):把给定的字符转换为大写字符 - System.out.println("toUpperCase:" + Character.toUpperCase('A'));//A - System.out.println("toUpperCase:" + Character.toUpperCase('a'));//A - System.out.println("-----------------------------------------"); - - // public static char toLowerCase(char ch):把给定的字符转换为小写字符 - System.out.println("toLowerCase:" + Character.toLowerCase('A'));//a - System.out.println("toLowerCase:" + Character.toLowerCase('a'));//a - } -} -``` +public class EqualExample { -- 小练习:统计一个字符串中大写字母字符,小写字母字符,数字字符出现的次数。(不考虑其他字符) + private int x; + private int y; + private int z; -```java -/** - * 统计一个字符串中大写字母字符,小写字母字符,数字字符出现的次数。(不考虑其他字符) - * - * 分析: - * A:定义三个统计变量。 - * int bigCont=0; - * int smalCount=0; - * int numberCount=0; - * B:键盘录入一个字符串。 - * C:把字符串转换为字符数组。 - * D:遍历字符数组获取到每一个字符 - * E:判断该字符是 - * 大写 bigCount++; - * 小写 smalCount++; - * 数字 numberCount++; - * F:输出结果即可 - */ -public class CharacterTest { - public static void main(String[] args) { - Scanner sc=new Scanner(System.in); - String str=sc.nextLine(); - printCount(str); - printCount2(str); + public EqualExample(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; } - //原来的写法 - public static void printCount(String str) { - int numberCount=0; - int lowercaseCount=0; - int upercaseCount=0; - - for(int index=0;index='0' && ch<='9'){ - numberCount++; - }else if(ch>='A' && ch<='Z'){ - upercaseCount++; - }else if(ch>='a' && ch<='z'){ - lowercaseCount++; - } + @Override + public boolean equals(Object o) { + if (this == o) return true; //检查是否为同一个对象的引用,如果是直接返回 true; + if (o == null || getClass() != o.getClass()){ + //检查是否是同一个类型,如果不是,直接返回 false + return false; } - System.out.println("数字有"+numberCount+"个"); - System.out.println("小写字母有"+lowercaseCount+"个"); - System.out.println("大写字母有"+upercaseCount+"个"); - } - //使用包装类来改进 - public static void printCount2(String str) { - int numberCount=0; - int lowercaseCount=0; - int upercaseCount=0; - - for(int index=0;index 当前时间 - - // Date(long date):根据给定的毫秒值创建日期对象 - //long time = System.currentTimeMillis(); - long time = 1000 * 60 * 60; // 1小时 - Date d2 = new Date(time); - System.out.println("d2:" + d2); - //格林威治时间 1970年01月01日00时00分00 - //Thu Jan 01 09:00:00 GMT+08:00 1970 GMT+表示 标准时间加8小时,因为中国是东八区 - - // 获取时间 - long time2 = d.getTime(); - System.out.println(time2); //1545739438466 毫秒 - System.out.println(System.currentTimeMillis()); - - // 设置时间 - d.setTime(1000*60*60); - System.out.println("d:" + d); - //Thu Jan 01 09:00:00 GMT+08:00 1970 - } -} +EqualExample e1 = new EqualExample(1, 1, 1); +EqualExample e2 = new EqualExample(1, 1, 1); +System.out.println(e1.equals(e2)); // true +HashSet set = new HashSet<>(); +set.add(e1); +set.add(e2); +System.out.println(set.size()); // 2 ``` +理想的散列函数应当具有均匀性,即不相等的对象应当均匀分布到所有可能的散列值上。 +这就要求了散列函数要把**所有域的值都考虑进来**。 +可以将每个域都当成 R 进制的某一位,然后组成一个 R 进制的整数。 +R 一般取 31,因为它是一个奇素数,如果是偶数的话,当出现乘法溢出,信息就会丢失,因为与 2 相乘相当于向左移一位。 -DateForamt:可以进行日期和字符串的格式化和解析,但是由于是抽象类,所以使用具体子类SimpleDateFormat。 -SimpleDateFormat的构造方法: +一个数与 31 相乘可以转换成移位和减法:`31*x == (x<<5)-x`,编译器会自动进行这个优化。 ```java -SimpleDateFormat() //默认模式 - -SimpleDateFormat(String pattern) //给定的模式 +@Override +public int hashCode() { + int result = 17; + result = 31 * result + x; + result = 31 * result + y; + result = 31 * result + z; + return result; +} ``` -这个模式字符串该如何写呢? 通过查看API,我们就找到了对应的模式: +> 了解:IDEA中 Alt+Insert 快捷键就可以快速生成 hashCode() 和 equals() 方法。 -| 中文说明 | 模式字符 | -| :--: | :--: | -| 年 | y | -| 月 | M | -| 日 | d | -| 时 | H | -| 分 | m | -| 秒 | s | +### toString() 方法 -- Date类型和String类型的相互转换 +默认返回 ToStringExample@4554617c 这种形式,其中 @ 后面的数值为散列码的无符号十六进制表示。 ```java -public class DateFormatDemo { - public static void main(String[] args) { - Date date=new Date(); - SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日"); - String s=dateToString(date,sdf); - System.out.println(s); //2018年12月25日 - System.out.println(stringToDate(s,sdf));//Tue Dec 25 00:00:00 GMT+08:00 2018 - } +public class ToStringExample { - /** - * Date -- String(格式化) - * public final String format(Date date) - */ - public static String dateToString(Date d, SimpleDateFormat sdf) { - return sdf.format(d); - } + private int number; - /** - * * String -- Date(解析) - * public Date parse(String source) - */ - public static Date stringToDate(String s, SimpleDateFormat sdf){ - Date date=null; - try { - date=sdf.parse(s); - } catch (ParseException e) { - e.printStackTrace(); - } - return date; + public ToStringExample(int number) { + this.number = number; } } ``` -- 小练习: 算一下你来到这个世界多少天? ```java -/** - * * - * 算一下你来到这个世界多少天? - * - * 分析: - * A:键盘录入你的出生的年月日 - * B:把该字符串转换为一个日期 - * C:通过该日期得到一个毫秒值 - * D:获取当前时间的毫秒值 - * E:用D-C得到一个毫秒值 - * F:把E的毫秒值转换为年 - * /1000/60/60/24 - */ -public class DateTest { - public static void main(String[] args) throws ParseException { - // 键盘录入你的出生的年月日 - Scanner sc = new Scanner(System.in); - System.out.println("请输入你的出生年月日(格式 yyyy-MM-dd):"); - String line = sc.nextLine(); - - // 把该字符串转换为一个日期 - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); - Date d = sdf.parse(line); - long birth=d.getTime(); //出生的时间 - long current=System.currentTimeMillis();//当前时间 - - long days=(current-birth)/1000/60/60/24; - System.out.println("你出生了"+days+"天"); - } -} +ToStringExample example = new ToStringExample(123); +System.out.println(example.toString()); ``` -## Integer -Java就针对每一种基本数据类型提供了对应的类类型: +```html +ToStringExample@4554617c +``` -| 基础类型 | 包装类类型 | -| :--: | :--: | -| byte | Byte | -| short | Short | -| int | Integer | -| long | Long | -| float | Float | -| double | Double | -| char | Character | -| boolean | Boolean | +### clone() 方法 -用于基本数据类型与字符串之间的转换。 +**1. Cloneable** -- Integer的常用成员方法: -```java -public Integer(int value) +clone() 是 Object 的 **protected 方法**,它不是 public,一个类不显式去重写 clone(),其它类就不能直接去调用该类实例的 clone() 方法。 -public Integer(String s) //注意:这个字符串必须是由数字字符组成 +```java +public class CloneExample { + private int a; + private int b; +} ``` -- int和String的相互转化: ```java -String.valueOf(number) //int --> String - -Integer.parseInt(s) //String --> int +CloneExample e1 = new CloneExample(); +// CloneExample e2 = e1.clone(); +// 'clone()' has protected access in 'java.lang.Object' ``` +重写 clone() 得到以下实现: + ```java -public static void main(String[] args) { - System.out.println(intToString(100)); //100 - System.out.println(stringToInt("100")); //100 -} +public class CloneExample { + private int a; + private int b; -//int --> String -public static String intToString(int number) { - //方式一 - /*String strNumber=""+number; - return strNumber;*/ - //方式二 - String strNumber=String.valueOf(number); - return strNumber; + // CloneExample 默认继承 Object + @Override + public CloneExample clone() throws CloneNotSupportedException { + return (CloneExample)super.clone(); + } } +``` -//String --> int -public static int stringToInt(String strNumber) { - //方式一 - /*Integer number=new Integer(strNumber); - return number;*/ - //方式二 - Integer number=Integer.parseInt(strNumber); - return number; +```java +CloneExample e1 = new CloneExample(); +try { + CloneExample e2 = e1.clone(); +} catch (CloneNotSupportedException e) { + e.printStackTrace(); } ``` -- 常用的进制转换: -```java -//常用的基本进制转换 -public static String toBinaryString(int i) +```html +java.lang.CloneNotSupportedException: CloneExample +``` -public static String toOctalString(int i) +以上抛出了 CloneNotSupportedException,这是因为 CloneExample 没有实现 Cloneable 接口。 -public static String toHexString(int i) - -//十进制到其他进制 -public static String toString(int i,int radix)//进制的范围:2-36,为什么呢?0,...9,a...z(10个数字+26个字母) +应该注意的是,**clone() 方法并不是 Cloneable 接口的方法,而是 Object 的一个 protected 方法**。 -//其他进制到十进制 -public static int parseInt(String s,int radix) -``` +**Cloneable 接口只是规定,如果一个类没有实现 Cloneable 接口又调用了 clone() 方法,就会抛出 CloneNotSupportedException**。 ```java -public class IntegerDemo3 { - public static void main(String[] args) { - //test(); - //test2(); - test3(); - } - - //常用的基本进制转换 - public static void test(){ - System.out.println(Integer.toBinaryString(100));//1100100 - System.out.println(Integer.toOctalString(100));//144 - System.out.println(Integer.toHexString(100));//64 - System.out.println("-----------------------------"); - } - - //十进制到其他进制 - public static void test2(){ - System.out.println(Integer.toString(100, 10));//100 - System.out.println(Integer.toString(100, 2));//1100100 - System.out.println(Integer.toString(100, 8));//144 - System.out.println(Integer.toString(100, 16));//64 - System.out.println(Integer.toString(100, 5));//400 - System.out.println(Integer.toString(100, 7));//202 - //进制的范围在2-36之间,超过这个范围,就作为十进制处理 - System.out.println(Integer.toString(100, -7)); //100 - System.out.println(Integer.toString(100, 70));//100 - System.out.println(Integer.toString(100, 1));//100 - System.out.println(Integer.toString(100, 37));//100 - - System.out.println(Integer.toString(100, 17));//5f - System.out.println(Integer.toString(100, 32));//34 - System.out.println(Integer.toString(100, 36));//2s - System.out.println("-------------------------"); - } +public class CloneExample implements Cloneable { + private int a; + private int b; - //任意进制转换为十进制 - public static void test3(){ - System.out.println(Integer.parseInt("100", 10));//100 - System.out.println(Integer.parseInt("100", 2));//4 - System.out.println(Integer.parseInt("100", 8));//64 - System.out.println(Integer.parseInt("100", 16));//256 - System.out.println(Integer.parseInt("100", 23));//529 - //NumberFormatException,因为二进制是不可能存在 123的 - //System.out.println(Integer.parseInt("123", 2)); + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); } } ``` -- JDK5的新特性 - -自动装箱:把基本类型转换为包装类类型 +**2. 浅拷贝** -自动拆箱:把包装类类型转换为基本类型 +拷贝对象和原始对象的引用类型引用同一个对象。 ```java -public class IntegerDemo4 { - public static void main(String[] args) { - Integer num=null; - if(num!=null){ - num+=100; - System.out.println(num); +public class ShallowCloneExample implements Cloneable { + + private int[] arr; + + public ShallowCloneExample() { + arr = new int[10]; + for (int i = 0; i < arr.length; i++) { + arr[i] = i; } - test(); //num:200 - test2(); //num:200 } - //自动装箱,拆箱 - public static void test() { - Integer num=100; - //自动装箱,实际上就是 Integer num=Integer.valueOf(100) - num+=100; - //先拆箱再装箱 num.intValue()+100=200 --> Integer num=Integer.valueOf(num.intValue()+100) - System.out.println(new StringBuilder("num:").append(num).toString()); + public void set(int index, int value) { + arr[index] = value; } - - //手动拆装箱 - public static void test2() { - Integer num=Integer.valueOf(100); //手动装箱 - num=Integer.valueOf(num.intValue()+100); //先手动拆箱,进行运算后,再手动装箱 - System.out.println(new StringBuilder("num:").append(num).toString()); + + public int get(int index) { + return arr[index]; } -} -``` -- 小练习:看程序写结果 -```java -public class IntegerTest { - public static void main(String[] args) { - Integer i1 = new Integer(127); - Integer i2 = new Integer(127); - System.out.println(i1 == i2); - System.out.println(i1.equals(i2)); - System.out.println("-----------"); - - Integer i3 = new Integer(128); - Integer i4 = new Integer(128); - System.out.println(i3 == i4); - System.out.println(i3.equals(i4)); - System.out.println("-----------"); - - Integer i5 = 128; - Integer i6 = 128; - System.out.println(i5 == i6); - System.out.println(i5.equals(i6)); - System.out.println("-----------"); - - Integer i7 = 127; - Integer i8 = 127; - System.out.println(i7 == i8); - System.out.println(i7.equals(i8)); + @Override + protected ShallowCloneExample clone() throws CloneNotSupportedException { + return (ShallowCloneExample) super.clone(); } } ``` -输出结果: -```html -false -true ------------ -false -true ------------ -false -true ------------ -true -true -``` -分析: -Integer的数据直接赋值,如果在-128到127之间,会直接从缓冲池里获取数据。 -通过查看源码,针对-128到127之间的数据,做了一个**数据缓冲池IntegerCache**, -如果数据是该范围内的,每次并不创建新的空间。 ```java -public static Integer valueOf(int i) { - if (i >= IntegerCache.low && i <= IntegerCache.high) - return IntegerCache.cache[i + (-IntegerCache.low)]; - return new Integer(i); - } - - private static class IntegerCache { - static final int low = -128; //low=-128 - static final int high; - static final Integer cache[]; - - static { - // high value may be configured by property - int h = 127; - //... - } - high = h; //high=127 +// 拷贝对象和原始对象的引用类型引用同一个对象。 +ShallowCloneExample e1 = new ShallowCloneExample(); +ShallowCloneExample e2 = null; +try { + e2 = e1.clone(); +} catch (CloneNotSupportedException e) { + e.printStackTrace(); +} +e1.set(2, 222); +System.out.println(e1.get(2)); // 222 +System.out.println(e2.get(2)); // 222 ``` -## Object -Object类是类层次结构的根类。每个类都使用 Object 作为超类。每个类都直接或者间接的继承自Object类。 -- Object类的方法: -```java -public int hashCode() //返回该对象的哈希码值。 -// 注意:哈希值是根据哈希算法计算出来的一个值,这个值和地址值有关,但是不是实际地址值。 +**3. 深拷贝** -public final Class getClass() //返回此 Object 的运行时类 +拷贝对象和原始对象的引用类型引用不同对象。 -public String toString() //返回该对象的字符串表示。 +```java +public class DeepCloneExample implements Cloneable { -protected Object clone() //创建并返回此对象的一个副本。可重写该方法 + private int[] arr; -protected void finalize() -//当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。用于垃圾回收,但是什么时候回收不确定。 -``` + public DeepCloneExample() { + arr = new int[10]; + for (int i = 0; i < arr.length; i++) { + arr[i] = i; + } + } -- [Object的使用 代码示例](https://github.com/DuHouAn/Java/tree/master/JavaBasics/src/code_03_Object) + public void set(int index, int value) { + arr[index] = value; + } -## Scanner -Scanner:用于接收键盘录入数据。 + public int get(int index) { + return arr[index]; + } -- 使用Scanner三部曲: + @Override + protected DeepCloneExample clone() throws CloneNotSupportedException { + DeepCloneExample result = (DeepCloneExample) super.clone(); + // 创建新对象 + result.arr = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + result.arr[i] = arr[i]; + } + return result; + } +} +``` -A:导包 +```java +DeepCloneExample e1 = new DeepCloneExample(); +DeepCloneExample e2 = null; +try { + e2 = e1.clone(); +} catch (CloneNotSupportedException e) { + e.printStackTrace(); +} +e1.set(2, 222); +System.out.println(e1.get(2)); // 222 +System.out.println(e2.get(2)); // 2 +``` -B :创建对象 +**4. clone() 的替代方案** -C :使用相应方法 +使用 clone() 方法来拷贝一个对象即复杂又有风险,它会抛出异常,并且还需要类型转换。 +Effective Java 书上讲到,最好不要去使用 clone(),可以使用**拷贝构造函数或者拷贝工厂来拷贝一个对象**。 -- Scanner常用成员方法 ```java -Scanner(InputStream source) //构造方法 +public class CloneConstructorExample { -public boolean hasNextXxx() //判断是否是某种类型的元素,Xxx表示类型,比如 public boolean hasNextInt() - -public Xxx nextXxx() //获取该元素,Xxx表示类型,比如public int nextInt() -``` -注意:InputMismatchException:表示输入的和你想要的不匹配 + private int[] arr; -- 使用示例1: -```java -public class ScannerDemo { - public static void main(String[] args) { - Scanner sc=new Scanner(System.in); - if(sc.hasNextInt()){ - int x=sc.nextInt(); - System.out.println("x="+x); - }else{ - System.out.println("输入的数据有误"); + public CloneConstructorExample() { //构造函数 + arr = new int[10]; + for (int i = 0; i < arr.length; i++) { + arr[i] = i; } } -} -``` -- 先获取一个数值,换行后,再获取一个字符串,会出现问题。主要原因:就是换行符号的问题。如何解决呢? -```java -public static void main(String[] args) { - Scanner sc=new Scanner(System.in); + public CloneConstructorExample(CloneConstructorExample original) { // 拷贝构造函数 + arr = new int[original.arr.length]; + for (int i = 0; i < original.arr.length; i++) { + arr[i] = original.arr[i]; + } + } - //输入 12 换行后在输出 "sss" - int x=sc.nextInt(); - System.out.println("x:"+x); //x:12 + public void set(int index, int value) { + arr[index] = value; + } - String line=sc.nextLine(); //这里会有问题 因为会将换行符当作字符输输入 - System.out.println("line:"+line); //line: + public int get(int index) { + return arr[index]; + } } ``` -> 解决方案一:先获取一个数值后,再创建一个新的键盘录入对象获取字符串。 - ```java -public static void method() { - Scanner sc=new Scanner(System.in); - int x=sc.nextInt(); - System.out.println("x:"+x); - - Scanner sc2=new Scanner(System.in); - String line=sc2.nextLine(); - System.out.println("line:"+line); -} +CloneConstructorExample e1 = new CloneConstructorExample(); +CloneConstructorExample e2 = new CloneConstructorExample(e1); +e1.set(2, 222); +System.out.println(e1.get(2)); // 222 +System.out.println(e2.get(2)); // 2 ``` -> 解决方案二:把所有的数据都先按照字符串获取,然后要什么,就进行相应的转换。 -```java -public static void method2() { - Scanner sc=new Scanner(System.in); - String xStr=sc.nextLine(); - String line=sc.nextLine(); +## String - int x=Integer.parseInt(xStr); - - System.out.println("x:"+x); - System.out.println("line:"+line); -} +String 被声明为 final,因此它不可被继承。 + +**内部使用 char 数组存储数据,该数组被声明为 final**, +这意味着 value 数组初始化之后就不能再引用其它数组。 +并且 String 内部没有改变 value 数组的方法,因此可以保证 String 不可变。 + +```java +public final class String + implements java.io.Serializable, Comparable, CharSequence { + /** The value is used for character storage. */ + private final char value[]; ``` -## String -字符串:就是由多个字符组成的一串数据。也可以看成是一个字符数组。 -- **String的构造方法方法**: +### 构造方法 + ```java -//String的构造方法 public String() //空构造 public String(byte[] bytes) //把字节数组转成字符串 @@ -916,6 +410,8 @@ public String(char[] value,int index,int count) //把字符数组的一部分转 public String(String original) //把字符串常量值转成字符串 ``` +使用示例: + ```java public class StringDemo { public static void main(String[] args) { @@ -961,7 +457,8 @@ public class StringDemo { } ``` -- 字符串的特点:一旦被赋值,就不能改变 +字符串的特点:一旦被赋值,就不能改变 + ```java public static void test() { String s = "hello"; @@ -970,49 +467,29 @@ public static void test() { } ``` -- 小练习:看程序,写结果: +小练习: + ```java public static void test2(){ String s1 = new String("hello"); String s2 = new String("hello"); - System.out.println(s1 == s2); - System.out.println(s1.equals(s2)); + System.out.println(s1 == s2); // false + System.out.println(s1.equals(s2)); // true String s3 = new String("hello"); String s4 = "hello"; - System.out.println(s3 == s4); - System.out.println(s3.equals(s4)); + System.out.println(s3 == s4); // false + System.out.println(s3.equals(s4)); // true String s5 = "hello"; String s6 = "hello"; - System.out.println(s5 == s6); - System.out.println(s5.equals(s6)); + System.out.println(s5 == s6); // true + System.out.println(s5.equals(s6)); // true } ``` -输出结果: -```html -false -true -false -true -true -true -``` -分析: -> =和qeuals的区别 - -==:比较引用类型,比较的是地址值是否相同 - -equals:比较引用类型,默认也是比较地址值是否相同 - -String类重写了equals()方法,比较的是内容是否相同。 - -> String s = new String(“hello”)和String s = “hello”的区别? +### 判断功能 -前者会创建2个对象,后者创建1个对象。更具体的说,前者会创建2个或者1个对象,后者会创建1个或者0个对象。 - -- **String类的判断功能**: ```java boolean equals(Object obj) //比较字符串的内容是否相同,区分大小写 @@ -1026,12 +503,16 @@ boolean endsWith(String str) //判断字符串是否以某个指定的字符串 boolean isEmpty()// 判断字符串是否为空 ``` + 注意:字符串内容为空和字符串对象为空。 + ```java String s = "";//字符串内容为空 String s = null;//字符串对象为空 ``` +使用示例: + ```java public class StringDemo3 { public static void main(String[] args) { @@ -1080,7 +561,8 @@ public class StringDemo3 { } ``` -- **String类的获取功能**: +### 获取功能 + ```java int length() //获取字符串的长度。 @@ -1099,6 +581,8 @@ String substring(int start) //从指定位置开始截取字符串,默认到末 String substring(int start,int end) //从指定位置开始到指定位置结束截取字符串。左闭右开 ``` +使用示例: + ```java public class StringDemo4 { public static void main(String[] args) { @@ -1153,7 +637,8 @@ public class StringDemo4 { } ``` -- **String的转换功能** +### 转换功能 + ```java byte[] getBytes() //字符串转换为字节数组。 @@ -1170,6 +655,8 @@ String toUpperCase() //把字符串转成大写。 String concat(String str) //把字符串拼接。 ``` +使用示例: + ```java public class StringDemo5 { public static void main(String[] args) { @@ -1220,7 +707,8 @@ public class StringDemo5 { } ``` -- 练习:将一个字符串的首字母转成大写,其余为小写。(只考虑英文大小写字母字符) +练习:将一个字符串的首字母转成大写,其余为小写。(只考虑英文大小写字母字符) + ```java /** * 需求:把一个字符串的首字母转成大写,其余为小写。(只考虑英文大小写字母字符) @@ -1245,7 +733,8 @@ public class StringTest { } ``` -- **String类的其他功能**: +### 其他功能 + ```java //替换功能: String replace(char old,char new) @@ -1260,7 +749,7 @@ int compareTo(String str) int compareToIgnoreCase(String str) ``` -> compareTo()方法源码解析 +compareTo() 方法源码解析: ```java private final char value[]; //字符串会自动转换为一个字符数组。 @@ -1292,7 +781,10 @@ public int compareTo(String anotherString) { } ``` -- 练习1:把数组中的数据按照指定个格式拼接成一个字符串。举例:int[] arr = {1,2,3};输出结果:[1, 2, 3] +### 练习 + +练习1:把数组中的数据按照指定个格式拼接成一个字符串。举例:int[] arr = {1,2,3};输出结果:[1, 2, 3] + ```java /** * @@ -1334,7 +826,8 @@ public class StringTest2 { } ``` -- 练习2:统计大串中小串出现的次数 +练习2:统计大串中小串出现的次数 + ```java /** * 统计大串中小串出现的次数 @@ -1376,9 +869,12 @@ public class StringTest3 { } ``` + + ## StringBuffer -- **StringBuffer的常用成员方法**: +### 常用成员方法 + ```java //StirngBuffer的构造方法 public StringBuffer() //无参构造方法 @@ -1392,6 +888,9 @@ public int capacity() //返回当前容量。 理论值 public int length() //返回长度(字符数)。 实际值 ``` + +使用示例: + ```java public static void main(String[] args) { // public StringBuffer():无参构造方法 @@ -1416,13 +915,18 @@ public static void main(String[] args) { } ``` -- **StringBuffer的添加功能**: +### 添加功能 + ```java -public StringBuffer append(String str) //可以把任意类型数据添加到字符串缓冲区里面,并返回字符串缓冲区本身 +public StringBuffer append(String str) +//可以把任意类型数据添加到字符串缓冲区里面,并返回字符串缓冲区本身 -public StringBuffer insert(int offset,String str) //在指定位置把任意类型的数据插入到字符串缓冲区里面,并返回字符串缓冲区本身 +public StringBuffer insert(int offset,String str) +//在指定位置把任意类型的数据插入到字符串缓冲区里面,并返回字符串缓冲区本身 ``` +使用示例: + ```java public static void main(String[] args) { // 创建字符串缓冲区对象 @@ -1439,12 +943,18 @@ public static void main(String[] args) { } ``` -- **StringBuffer的删除功能** +### 删除功能 + ```java -public StringBuffer deleteCharAt(int index) //删除指定位置的字符,并返回本身 +public StringBuffer deleteCharAt(int index) +//删除指定位置的字符,并返回本身 -public StringBuffer delete(int start,int end) //删除从指定位置开始指定位置结束的内容,并返回本身 +public StringBuffer delete(int start,int end) +//删除从指定位置开始指定位置结束的内容,并返回本身 ``` + +使用示例: + ```java public static void main(String[] args) { // 创建对象 @@ -1465,10 +975,15 @@ public static void main(String[] args) { } ``` -- **StringBuffer的替换功能** +### 替换功能 + ```java -public StringBuffer replace(int start,int end,String str) //从start开始到end用str替换 +public StringBuffer replace(int start,int end,String str) +//从start开始到end用str替换 ``` + +使用示例: + ```java public static void main(String[] args) { // 创建字符串缓冲区对象 @@ -1487,10 +1002,14 @@ public static void main(String[] args) { } ``` -- **StringBuffer的反转功能**: +### 反转功能 + ```java public StringBuffer reverse() ``` + +使用示例: + ```java public static void main(String[] args) { // 创建字符串缓冲区对象 @@ -1506,13 +1025,16 @@ public static void main(String[] args) { } ``` -- **StringBuffer的截取功能**: +### 截取功能 ```java -public String substring(int start) //TODO:注意截取返回的是String,而不是StringBuffer了 +public String substring(int start) // 注意截取返回的是String,而不是StringBuffer了 public String substring(int start,int end) ``` + +使用示例: + ```java public static void main(String[] args) { // 创建字符串缓冲区对象 @@ -1535,7 +1057,10 @@ public static void main(String[] args) { } ``` -- **String和StringBuffer的相互转换**: + + +## String 和 StringBuffer 的相互转换 + ```java public class StringBufferDemo7 { public static void main(String[] args) { @@ -1571,7 +1096,9 @@ public class StringBufferDemo7 { } ``` -- 练习1:将数组拼接成一个字符串 +### 练习 + +练习1:将数组拼接成一个字符串 ```java public class StringBufferTest { @@ -1596,7 +1123,8 @@ public class StringBufferTest { } ``` -- 练习2:判断一个字符串是否是对称字符串 +练习2:判断一个字符串是否是对称字符串 + ```java /** * 判断一个字符串是否是对称字符串 @@ -1637,7 +1165,9 @@ public class StringBufferTest2 { } } ``` -- 练习3:看程序写结果 + +练习3:看程序写结果 + ```java public class StringBufferTest3 { public static void main(String[] args) { @@ -1667,11 +1197,40 @@ public class StringBufferTest3 { } } ``` + 输出结果: + ```html hello---world hello---world hello---world hello---worldworld ``` -String作为参数传递,效果和基本类型作为参数传递是一样的。 \ No newline at end of file + +String作为参数传递,效果和基本类型作为参数传递是一样的。 + +## String, StringBuffer and StringBuilder + +**1. 可变性** + +- String 不可变 +- StringBuffer 和 StringBuilder 可变 + +**2. 线程安全** + +- String 不可变,因此是线程安全的 +- StringBuilder 不是线程安全的 +- StringBuffer 是线程安全的,内部使用 synchronized 进行同步 + +**3. 性能** + +- Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢 +- StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用 +- StringBuilder每次都会对StringBuilder对象本身进行操作,而不是生成新的对象并改变对象引用。 + 相同情况下使用StirngBuilder相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却冒多线程不安全的风险。 + +> **三者使用的总结** + +- 操作少量的数据,使用String +- 单线程操作字符串缓冲区下操作大量数据,使用StringBuilder +- 多线程操作字符串缓冲区下操作大量数据,使用StringBuffer \ No newline at end of file diff --git "a/docs/JavaBasics/10_Java\345\270\270\350\247\201\345\257\271\350\261\241_2.md" "b/docs/JavaBasics/10_Java\345\270\270\350\247\201\345\257\271\350\261\241_2.md" new file mode 100644 index 00000000..ba733417 --- /dev/null +++ "b/docs/JavaBasics/10_Java\345\270\270\350\247\201\345\257\271\350\261\241_2.md" @@ -0,0 +1,687 @@ +# Java 常见类 II + +## Arrays +Arrays 是针对数组进行操作的工具类。 + +### 常用成员方法 + +```java +public static String toString(int[] a) //把数组转成字符串 +public static void sort(int[] a) //对数组进行排序 +public static int binarySearch(int[] a,int key) //二分查找 +``` + +其中,toString() 源码如下: + +```java +public static String toString(int[] a) { + if (a == null) + return "null"; + int iMax = a.length - 1; + if (iMax == -1) + return "[]"; + + StringBuilder b = new StringBuilder(); + b.append('['); + for (int i = 0; ; i++) { + b.append(a[i]); + if (i == iMax) + return b.append(']').toString(); + b.append(", "); + } +} +``` + +binarySearch() 调用的是 binarySearch0(),binarySearch0() 源码如下: + +```java +private static int binarySearch0(int[] a, int fromIndex, int toIndex,int key) { + int low = fromIndex; + int high = toIndex - 1; + + while (low <= high) { + int mid = (low + high) >>> 1; + int midVal = a[mid]; + + if (midVal < key) + low = mid + 1; + else if (midVal > key) + high = mid - 1; + else + return mid; // key found + } + return -(low + 1); // key not found. +} +``` + +### 使用示例 + +```java +public class ArraysDemo { + public static void main(String[] args) { + // 定义一个数组 + int[] arr = { 24, 69, 80, 57, 13 }; + + // public static String toString(int[] a) 把数组转成字符串 + System.out.println("排序前:" + Arrays.toString(arr));//排序前:[24, 69, 80, 57, 13] + + // public static void sort(int[] a) 对数组进行排序 + Arrays.sort(arr); + System.out.println("排序后:" + Arrays.toString(arr));//排序后:[13, 24, 57, 69, 80] + + // [13, 24, 57, 69, 80] + // public static int binarySearch(int[] a,int key) 二分查找 + System.out.println("binarySearch:" + Arrays.binarySearch(arr, 57));//binarySearch:2 + System.out.println("binarySearch:" + Arrays.binarySearch(arr, 577));//binarySearch:-6 + } +} +``` + + + +## BigDemical + +BigDecimal类:不可变的、任意精度的有符号十进制数,可以解决数据丢失问题。 + +看如下程序,写出结果: +```java +public static void main(String[] args) { + System.out.println(0.09 + 0.01); // 0.09999999999999999 + System.out.println(1.0 - 0.32); // 0.6799999999999999 + System.out.println(1.015 * 100); // 101.49999999999999 + System.out.println(1.301 / 100); // 0.013009999999999999 + System.out.println(1.0 - 0.12); // 0.88 +} +``` +结果和我们想的有一点点不一样,这是因为浮点数类型的数据存储和整数不一样导致的。 +它们大部分的时候,都是带有有效数字位。由于在运算的时候,float类型和double很容易丢失精度, +所以,为了能精确的表示、计算浮点数,Java 提供了 BigDecimal。 + +### 常用成员方法 + +```java +public BigDecimal(String val) //构造方法 + +public BigDecimal add(BigDecimal augend) //加 + +public BigDecimal subtract(BigDecimal subtrahend)//减 + +public BigDecimal multiply(BigDecimal multiplicand) //乘 + +public BigDecimal divide(BigDecimal divisor) //除 + +public BigDecimal divide(BigDecimal divisor,int scale,int roundingMode) +//除法,scale:几位小数,roundingMode:如何舍取 +``` + +### 使用示例 + +```java +public static void main(String[] args) { + /*System.out.println(0.09 + 0.01); + System.out.println(1.0 - 0.32); + System.out.println(1.015 * 100); + System.out.println(1.301 / 100); + System.out.println(1.0 - 0.12);*/ + + BigDecimal bd1 = new BigDecimal("0.09"); + BigDecimal bd2 = new BigDecimal("0.01"); + System.out.println("add:" + bd1.add(bd2));//add:0.10 + System.out.println("-------------------"); + + BigDecimal bd3 = new BigDecimal("1.0"); + BigDecimal bd4 = new BigDecimal("0.32"); + System.out.println("subtract:" + bd3.subtract(bd4));//subtract:0.68 + System.out.println("-------------------"); + + BigDecimal bd5 = new BigDecimal("1.015"); + BigDecimal bd6 = new BigDecimal("100"); + System.out.println("multiply:" + bd5.multiply(bd6));//multiply:101.500 + System.out.println("-------------------"); + + BigDecimal bd7 = new BigDecimal("1.301"); + BigDecimal bd8 = new BigDecimal("100"); + System.out.println("divide:" + bd7.divide(bd8));//divide:0.01301 + + //四舍五入 + System.out.println("divide:" + + bd7.divide(bd8, 3, BigDecimal.ROUND_HALF_UP));//保留三位有效数字 + //divide:0.013 + + System.out.println("divide:" + + bd7.divide(bd8, 8, BigDecimal.ROUND_HALF_UP));//保留八位有效数字 + //divide:0.01301000 +} +``` + + + +## BigInteger + +BigInteger:可以让超过 Integer 范围内的数据进行运算。 + +### 常用成员方法 + +```java +public BigInteger add(BigInteger val) //加 + +public BigInteger subtract(BigInteger val) //减 + +public BigInteger multiply(BigInteger val) //乘 + +public BigInteger divide(BigInteger val) //除 + +public BigInteger[] divideAndRemainder(BigInteger val)//返回商和余数的数组 +``` + +### 使用示例 + +示例1: + +```java +public class BigIntegerDemo { + public static void main(String[] args) { + Integer num = new Integer("2147483647"); + System.out.println(num); + + //Integer num2 = new Integer("2147483648"); + // Exception in thread "main" java.lang.NumberFormatException: For input string: "2147483648" + //System.out.println(num2); + + // 通过 BigIntege来创建对象 + BigInteger num2 = new BigInteger("2147483648"); + System.out.println(num2); + } +} +``` + +示例2: + +```java +public class BigIntegerDemo2 { + public static void main(String[] args) { + BigInteger bi1 = new BigInteger("100"); + BigInteger bi2 = new BigInteger("50"); + + // public BigInteger add(BigInteger val):加 + System.out.println("add:" + bi1.add(bi2)); //add:150 + // public BigInteger subtract(BigInteger Val):减 + System.out.println("subtract:" + bi1.subtract(bi2));//subtract:50 + // public BigInteger multiply(BigInteger val):乘 + System.out.println("multiply:" + bi1.multiply(bi2));//multiply:5000 + // public BigInteger divide(BigInteger val):除 + System.out.println("divide:" + bi1.divide(bi2));//divide:2 + + // public BigInteger[] divideAndRemainder(BigInteger val):返回商和余数的数组 + BigInteger[] bis = bi1.divideAndRemainder(bi2); + System.out.println("divide:" + bis[0]);//divide:2 + System.out.println("remainder:" + bis[1]);//remainder:0 + } +} +``` + + + + + +## Character + +Character 类在对象中包装一个基本类型 char 的值.此外,该类提供了几种方法, +以确定字符的类别(小写字母,数字,等等),并将字符从大写转换成小写,反之亦然。 + +### 常用成员方法 + +```java +Character(char value) //构造方法 + +public static boolean isUpperCase(char ch) //判断给定的字符是否是大写字符 + +public static boolean isLowerCase(char ch) //判断给定的字符是否是小写字符 + +public static boolean isDigit(char ch) //判断给定的字符是否是数字字符 + +public static char toUpperCase(char ch) //把给定的字符转换为大写字符 + +public static char toLowerCase(char ch) //把给定的字符转换为小写字符 +``` + +### 使用示例 + +```java +public class CharacterDemo { + public static void main(String[] args) { + // public static boolean isUpperCase(char ch):判断给定的字符是否是大写字符 + System.out.println("isUpperCase:" + Character.isUpperCase('A'));//true + System.out.println("isUpperCase:" + Character.isUpperCase('a'));//false + System.out.println("isUpperCase:" + Character.isUpperCase('0'));//false + System.out.println("-----------------------------------------"); + + // public static boolean isLowerCase(char ch):判断给定的字符是否是小写字符 + System.out.println("isLowerCase:" + Character.isLowerCase('A'));//false + System.out.println("isLowerCase:" + Character.isLowerCase('a'));//true + System.out.println("isLowerCase:" + Character.isLowerCase('0'));//false + System.out.println("-----------------------------------------"); + + // public static boolean isDigit(char ch):判断给定的字符是否是数字字符 + System.out.println("isDigit:" + Character.isDigit('A'));//false + System.out.println("isDigit:" + Character.isDigit('a'));//false + System.out.println("isDigit:" + Character.isDigit('0'));//true + System.out.println("-----------------------------------------"); + + // public static char toUpperCase(char ch):把给定的字符转换为大写字符 + System.out.println("toUpperCase:" + Character.toUpperCase('A'));//A + System.out.println("toUpperCase:" + Character.toUpperCase('a'));//A + System.out.println("-----------------------------------------"); + + // public static char toLowerCase(char ch):把给定的字符转换为小写字符 + System.out.println("toLowerCase:" + Character.toLowerCase('A'));//a + System.out.println("toLowerCase:" + Character.toLowerCase('a'));//a + } +} +``` + +### 练习 + +统计一个字符串中大写字母字符,小写字母字符,数字字符出现的次数。(不考虑其他字符) + +```java +/** + * 统计一个字符串中大写字母字符,小写字母字符,数字字符出现的次数。(不考虑其他字符) + * + * 分析: + * A:定义三个统计变量。 + * int bigCont=0; + * int smalCount=0; + * int numberCount=0; + * B:键盘录入一个字符串。 + * C:把字符串转换为字符数组。 + * D:遍历字符数组获取到每一个字符 + * E:判断该字符是 + * 大写 bigCount++; + * 小写 smalCount++; + * 数字 numberCount++; + * F:输出结果即可 + */ +public class CharacterTest { + public static void main(String[] args) { + Scanner sc=new Scanner(System.in); + String str=sc.nextLine(); + printCount(str); + printCount2(str); + } + + //原来的写法 + public static void printCount(String str) { + int numberCount=0; + int lowercaseCount=0; + int upercaseCount=0; + + for(int index=0;index='0' && ch<='9'){ + numberCount++; + }else if(ch>='A' && ch<='Z'){ + upercaseCount++; + }else if(ch>='a' && ch<='z'){ + lowercaseCount++; + } + } + System.out.println("数字有"+numberCount+"个"); + System.out.println("小写字母有"+lowercaseCount+"个"); + System.out.println("大写字母有"+upercaseCount+"个"); + } + + //使用包装类来改进 + public static void printCount2(String str) { + int numberCount=0; + int lowercaseCount=0; + int upercaseCount=0; + + for(int index=0;index 解决方案一:先获取一个数值后,再创建一个新的键盘录入对象获取字符串。 + +```java +public static void method() { + Scanner sc=new Scanner(System.in); + int x=sc.nextInt(); + System.out.println("x:"+x); + + Scanner sc2=new Scanner(System.in); + String line=sc2.nextLine(); + System.out.println("line:"+line); +} +``` + +> 解决方案二:把所有的数据都先按照字符串获取,然后要什么,就进行相应的转换。 + +```java +public static void method2() { + Scanner sc=new Scanner(System.in); + + String xStr=sc.nextLine(); + String line=sc.nextLine(); + + int x=Integer.parseInt(xStr); + + System.out.println("x:"+x); + System.out.println("line:"+line); +} +``` + + + +## Calendar + +Calendar为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等日历字段之间的转换提供了一些方法, +并为操作日历字段(例如获得下星期的日期)提供了一些方法。 + +### 常用成员方法 + +```java +public int get(int field) +//返回给定日历字段的值。日历类中的每个日历字段都是静态的成员变量,并且是int类型。 + +public void add(int field,int amount) +//根据给定的日历字段和对应的时间,来对当前的日历进行操作。 + +public final void set(int year,int month,int date) +//设置当前日历的年月日 +``` + +### 使用示例 + +示例1: + +```java +public class CalendarDemo { + public static void main(String[] args) { + // 其日历字段已由当前日期和时间初始化: + Calendar rightNow = Calendar.getInstance(); // 子类对象 + int year=rightNow.get(Calendar.YEAR); + int month=rightNow.get(Calendar.MONTH);//注意月份是从0开始的 + int date=rightNow.get(Calendar.DATE); + System.out.println(year + "年" + (month + 1) + "月" + date + "日"); + //2018年12月25日 + } +} +``` + +示例2: + +```java +public class CalendarDemo2 { + public static void main(String[] args) { + // 其日历字段已由当前日期和时间初始化: + Calendar calendar = Calendar.getInstance(); // 子类对象 + System.out.println(getYearMonthDay(calendar));//2018年12月25日 + + //三年前的今天 + calendar.add(Calendar.YEAR,-3); + System.out.println(getYearMonthDay(calendar));//2015年12月25日 + + //5年后的10天前 + calendar.add(Calendar.YEAR,5); + calendar.add(Calendar.DATE,-10); + System.out.println(getYearMonthDay(calendar));//2020年12月15日 + + //设置 2011年11月11日 + calendar.set(2011,10,11); + System.out.println(getYearMonthDay(calendar));//2011年11月11日 + } + + //获取年、月、日 + public static String getYearMonthDay(Calendar calendar){ + int year=calendar.get(Calendar.YEAR); + int month=calendar.get(Calendar.MONTH); + int date=calendar.get(Calendar.DATE); + return year + "年" + (month + 1) + "月" + date + "日"; + } +} +``` + +### 练习 + +获取任意一年的二月有多少天 + +```java +/** + *获取任意一年的二月有多少天 + *分析: + * A:键盘录入任意的年份 + * B:设置日历对象的年月日 + * 年就是输入的数据 + * 月是2 + * 日是1 + * C:把时间往前推一天,就是2月的最后一天 + * D:获取这一天输出即可 + */ +public class CalendarTest { + public static void main(String[] args) { + Scanner sc=new Scanner(System.in); + int year=sc.nextInt(); + Calendar c= Calendar.getInstance(); + c.set(year,2,1); //得到的就是该年的3月1日 + c.add(Calendar.DATE,-1);//把时间往前推一天,就是2月的最后一天 + //public void add(int field,int amount):根据给定的日历字段和对应的时间,来对当前的日历进行操作。 + + System.out.println(year+"年,二月有"+c.get(Calendar.DATE)+"天"); + } +} +``` + + + +## Date + +Date: 表示特定的瞬间,精确到毫秒。 + +### 常用成员方法 + +```java +Date() //根据当前的默认毫秒值创建日期对象 + +Date(long date) //根据给定的毫秒值创建日期对象 + +public long getTime() //获取时间,以毫秒为单位 + +public void setTime(long time) //设置时间 +``` + +### 使用示例 + +```java +/** + * 把一个毫秒值转换为Date,有两种方式: + * (1)构造方法 + * (2)setTime(long time) + */ +public class DateDemo { + public static void main(String[] args) { + // Date():根据当前的默认毫秒值创建日期对象 + Date d = new Date(); + System.out.println("d:" + d); + //d:Tue Dec 25 20:01:17 GMT+08:00 2018 --> 当前时间 + + // Date(long date):根据给定的毫秒值创建日期对象 + //long time = System.currentTimeMillis(); + long time = 1000 * 60 * 60; // 1小时 + Date d2 = new Date(time); + System.out.println("d2:" + d2); + //格林威治时间 1970年01月01日00时00分00 + //Thu Jan 01 09:00:00 GMT+08:00 1970 GMT+表示 标准时间加8小时,因为中国是东八区 + + // 获取时间 + long time2 = d.getTime(); + System.out.println(time2); //1545739438466 毫秒 + System.out.println(System.currentTimeMillis()); + + // 设置时间 + d.setTime(1000*60*60); + System.out.println("d:" + d); + //Thu Jan 01 09:00:00 GMT+08:00 1970 + } +} +``` + +### Date 和 String 类型的相互转换 + +```java +public class DateFormatDemo { + public static void main(String[] args) { + Date date=new Date(); + SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日"); + String s=dateToString(date,sdf); + System.out.println(s); //2018年12月25日 + System.out.println(stringToDate(s,sdf));//Tue Dec 25 00:00:00 GMT+08:00 2018 + } + + /** + * Date -- String(格式化) + * public final String format(Date date) + */ + public static String dateToString(Date d, SimpleDateFormat sdf) { + return sdf.format(d); + } + + /** + * * String -- Date(解析) + * public Date parse(String source) + */ + public static Date stringToDate(String s, SimpleDateFormat sdf){ + Date date=null; + try { + date=sdf.parse(s); + } catch (ParseException e) { + e.printStackTrace(); + } + return date; + } +} +``` + +## DateFormat + +DateForamt:可以进行日期和字符串的格式化和解析,但是由于是抽象类,所以使用具体子类SimpleDateFormat。 + +### SimpleDateFormat 构造方法 + +```java +SimpleDateFormat() //默认模式 + +SimpleDateFormat(String pattern) //给定的模式 +``` + +这个模式字符串该如何写呢? 通过查看 API,我们就找到了对应的模式: + +| 中文说明 | 模式字符 | +| :------: | :------: | +| 年 | y | +| 月 | M | +| 日 | d | +| 时 | H | +| 分 | m | +| 秒 | s | + +### 练习 + +算一下你来到这个世界多少天? + +```java +小练习:/** + * * + * 算一下你来到这个世界多少天? + * + * 分析: + * A:键盘录入你的出生的年月日 + * B:把该字符串转换为一个日期 + * C:通过该日期得到一个毫秒值 + * D:获取当前时间的毫秒值 + * E:用D-C得到一个毫秒值 + * F:把E的毫秒值转换为年 + * /1000/60/60/24 + */ +public class DateTest { + public static void main(String[] args) throws ParseException { + // 键盘录入你的出生的年月日 + Scanner sc = new Scanner(System.in); + System.out.println("请输入你的出生年月日(格式 yyyy-MM-dd):"); + String line = sc.nextLine(); + + // 把该字符串转换为一个日期 + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + Date d = sdf.parse(line); + long birth=d.getTime(); //出生的时间 + long current=System.currentTimeMillis();//当前时间 + + long days=(current-birth)/1000/60/60/24; + System.out.println("你出生了"+days+"天"); + } +} +``` + diff --git "a/docs/JavaBasics/12_\345\205\266\344\273\226.md" "b/docs/JavaBasics/12_\345\205\266\344\273\226.md" deleted file mode 100644 index 54d1f98b..00000000 --- "a/docs/JavaBasics/12_\345\205\266\344\273\226.md" +++ /dev/null @@ -1,54 +0,0 @@ -# 其他 - -## Java 各版本的新特性 - -**New highlights in Java SE 8** - -1. Lambda Expressions -2. Pipelines and Streams -3. Date and Time API -4. Default Methods -5. Type Annotations -6. Nashhorn JavaScript Engine -7. Concurrent Accumulators -8. Parallel operations -9. PermGen Error Removed - -**New highlights in Java SE 7** - -1. Strings in Switch Statement -2. Type Inference for Generic Instance Creation -3. Multiple Exception Handling -4. Support for Dynamic Languages -5. Try with Resources -6. Java nio Package -7. Binary Literals, Underscore in literals -8. Diamond Syntax - -- [Difference between Java 1.8 and Java 1.7?](http://www.selfgrowth.com/articles/difference-between-java-18-and-java-17) -- [Java 8 特性](http://www.importnew.com/19345.html) - -## Java 与 C++ 的区别 - -- Java 是纯粹的面向对象语言,所有的对象都继承自 java.lang.Object,C++ 为了兼容 C 即支持面向对象也支持面向过程。 -- Java 通过虚拟机从而实现跨平台特性,但是 C++ 依赖于特定的平台。 -- Java 没有指针,它的引用可以理解为安全指针,而 C++ 具有和 C 一样的指针。 -- Java 支持自动垃圾回收,而 C++ 需要手动回收。 -- Java 不支持多重继承,只能通过实现多个接口来达到相同目的,而 C++ 支持多重继承。 -- Java 不支持操作符重载,虽然可以对两个 String 对象执行加法运算,但是这是语言内置支持的操作,不属于操作符重载,而 C++ 可以。 -- Java 的 goto 是保留字,但是不可用,C++ 可以使用 goto。 -- Java 不支持条件编译,C++ 通过 #ifdef #ifndef 等预处理命令从而实现条件编译。 - -[What are the main differences between Java and C++?](http://cs-fundamentals.com/tech-interview/java/differences-between-java-and-cpp.php) - -## JRE or JDK - -- JRE is the JVM program, Java application need to run on JRE. -- JDK is a superset of JRE, JRE + tools for developing java programs. e.g, it provides the compiler "javac" - -## Java基础学习书籍推荐 -- 《Head First Java.第二版》 - -- 《Java核心技术卷1+卷2》 - -- 《Java编程思想(第4版)》 diff --git "a/docs/JavaBasics/1_\346\225\260\346\215\256\347\261\273\345\236\213.md" "b/docs/JavaBasics/1_\346\225\260\346\215\256\347\261\273\345\236\213.md" index 03e7d73e..13d74dc6 100644 --- "a/docs/JavaBasics/1_\346\225\260\346\215\256\347\261\273\345\236\213.md" +++ "b/docs/JavaBasics/1_\346\225\260\346\215\256\347\261\273\345\236\213.md" @@ -1,18 +1,20 @@ # 数据类型 ## 包装类型 -八个基本类型: +Java 八个基本类型,基本类型都有对应的包装类型: -- boolean/1 -- byte/8 -- char/16 -- short/16 -- int/32 -- float/32 -- long/64 -- double/64 +| 基本类型 | 大小(bit) | 最小值 | 最大值 | 默认值 | 包装类 | +| :------: | :---------: | :-------: | :------------: | :---------------: | :-------: | +| boolean | 1 | - | - | false | Boolean | +| char | 16 | Unicode 0 | Unicode 2^16-1 | \u0000(Unicode 0) | Character | +| byte | 8 | -128 | 127 | (byte)0 | Byte | +| short | 16 | -2^15 | 2^15-1 | (short)0 | Short | +| int | 32 | -2^31 | 2^31-1 | 0 | Int | +| long | 64 | -2^63 | 2^63-1 | 0L | Long | +| float | 32 | - | - | 0.0F | Float | +| double | 64 | - | - | 0.0D | Double | -基本类型都有对应的包装类型,基本类型与其对应的包装类型之间的赋值使用**自动装箱**与**自动拆箱**完成。 + 基本类型与其对应的包装类型之间的赋值使用**自动装箱**与**自动拆箱**完成。 ```java //自动装箱和自动拆箱 @@ -26,6 +28,14 @@ x=Integer.valueOf(x.intValue()+5);//x.intValue()就是拆箱-->先拆箱,再 ## 缓存池 +Java 基本类型的包装类的大部分都实现了缓存池技术,对应的缓冲池如下: + +- Boolean 直接返回 true / false +- Byte/ Short / Integer / Long 创建了数值范围在 [-128,127] 的相应类型的缓存数据 +- Character 创建了数值在 [0,127] 的缓存数据 + +如果超出对应范围仍然会去创建新的对象。 + new Integer(123) 与 Integer.valueOf(123) 的区别在于: - new Integer(123) 每次都会新建一个对象; @@ -92,12 +102,12 @@ Integer n = 123; System.out.println(m == n); // true ``` -基本类型对应的缓冲池如下: +\+ 不适用于 Integer 对象,首先 i,j 进行自动拆箱,然后数值相加,得到数值 80,进行自动装箱后,和 m 引用相同的对象。Integer 对象无法与数值直接进行比较,自动拆箱后转为数值 80,显然 80 == 80,输出 true。 -- boolean values true and false -- all byte values -- short values between -128 and 127 -- int values between -128 and 127 -- char in the range \u0000 to \u007F - -在使用这些基本类型对应的包装类型时,就可以**直接使用缓冲池中的对象**。 +```java +Integer i = 40; +Integer j = 40; +Integer m = 80; +System.out.println(m == i+j); // true +System.out.println(80 == i+j); // true +``` diff --git "a/docs/JavaBasics/3_\350\277\220\347\256\227.md" "b/docs/JavaBasics/3_\350\277\220\347\256\227.md" index 2bb2cdb5..35b2fa42 100644 --- "a/docs/JavaBasics/3_\350\277\220\347\256\227.md" +++ "b/docs/JavaBasics/3_\350\277\220\347\256\227.md" @@ -1,4 +1,43 @@ -# 运算 +# 基本运算 + +## == 和 equals() + +- == 判断两个对象的地址是不是相等。即判断两个对象是不是同一个对象。 + - 基本数据类型:== 比较的是值 + - 引用数据类型:== 比较的是内存地址 + +- equals() 判断两个对象是否相等。但它一般有两种使用情况: + - 情况1:类没有重写 equals() 方法。等价于“==”。 + - 情况2:类重写了 equals() 方法。一般用来**比较两个对象的内容**,若它们的内容相等,则返回 true (即,认为这两个对象相等)。 + +注意: + +- String 中的 **equals 方法是被重写过**的,因为 object 的 equals 方法是比较的对象的内存地址, 而 String 的 equals 方法比较的是对象的值。 + +- 当创建 String 类型的对象时,虚拟机会在**常量池**中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。 + 如果没有就在常量池中重新创建一个 String 对象。 + +```java +public class EqualsDemo { + public static void main(String[] args) { + String a = new String("ab"); // a 为一个引用 + String b = new String("ab"); // b为另一个引用,对象的内容一样 + String aa = "ab"; // 放在常量池中 + String bb = "ab"; // 从常量池中查找 + if (aa == bb) // true + System.out.println("aa==bb"); + if (a == b) // false,非同一对象 + System.out.println("a==b"); + if (a.equals(b)) // true + System.out.println("aEQb"); + if (42 == 42.0) { // true + System.out.println("true"); + } + } +} +``` + + ## 参数传递 @@ -6,8 +45,7 @@ Java 的参数是以**值传递**的形式传入方法中,而不是引用传 以下代码中 Dog dog 的 dog 是一个指针,存储的是对象的地址。 在将一个参数传入一个方法时,本质上是**将对象的地址以值的方式传递到形参中**。 -因此在方法中使指针引用其它对象,那么这两个指针此时指向的是完全不同的对象, -在一方改变其所指向对象的内容时对另一方没有影响。 +因此在方法中使指针引用其它对象,那么这两个指针此时指向的是完全不同的对象,在一方改变其所指向对象的内容时对另一方没有影响。 ```java public class Dog { @@ -67,47 +105,7 @@ class PassByValueExample { } ``` -## ==和equals() -- == 判断两个对象的地址是不是相等。即判断两个对象是不是同一个对象。 - - -基本数据类型"=="比较的是值 -​ -引用数据类型"=="比较的是内存地址 -​ -- equals()判断两个对象是否相等。但它一般有两种使用情况: -情况1:类没有重写 equals() 方法。等价于“==”。 - -情况2:类重写了 equals() 方法。一般用来**比较两个对象的内容**; -若它们的内容相等,则返回 true (即,认为这两个对象相等)。 - -```java -public class EqualsDemo { - public static void main(String[] args) { - String a = new String("ab"); // a 为一个引用 - String b = new String("ab"); // b为另一个引用,对象的内容一样 - String aa = "ab"; // 放在常量池中 - String bb = "ab"; // 从常量池中查找 - if (aa == bb) // true - System.out.println("aa==bb"); - if (a == b) // false,非同一对象 - System.out.println("a==b"); - if (a.equals(b)) // true - System.out.println("aEQb"); - if (42 == 42.0) { // true - System.out.println("true"); - } - } -} -``` -> **说明** - -- String 中的 **equals 方法是被重写过**的,因为 object 的 equals 方法是比较的对象的内存地址, -而 String 的 equals 方法比较的是对象的值。 - -- 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。 -如果没有就在常量池中重新创建一个 String 对象。 ## float 与 double @@ -125,6 +123,8 @@ Java 不能隐式执行向下转型,因为这会使得精度降低。 float f = 1.1f; ``` + + ## 隐式类型转换 因为字面量 1 是 int 类型,它比 short 类型精度要高,因此不能隐式地将 int 类型下转型为 short 类型。 @@ -147,6 +147,8 @@ s1 += 1; s1 = (short) (s1 + 1); ``` + + ## switch 从 Java 7 开始,可以在 switch 条件判断语句中使用 String 对象。 diff --git "a/docs/JavaBasics/4_Object\351\200\232\347\224\250\346\226\271\346\263\225.md" "b/docs/JavaBasics/4_Object\351\200\232\347\224\250\346\226\271\346\263\225.md" index 26786ca0..08d0ac74 100644 --- "a/docs/JavaBasics/4_Object\351\200\232\347\224\250\346\226\271\346\263\225.md" +++ "b/docs/JavaBasics/4_Object\351\200\232\347\224\250\346\226\271\346\263\225.md" @@ -382,4 +382,5 @@ CloneConstructorExample e2 = new CloneConstructorExample(e1); e1.set(2, 222); System.out.println(e1.get(2)); // 222 System.out.println(e2.get(2)); // 2 -``` \ No newline at end of file +``` + diff --git "a/docs/JavaBasics/5_\345\205\263\351\224\256\345\255\227.md" "b/docs/JavaBasics/5_\345\205\263\351\224\256\345\255\227.md" index 8548e4b3..627747c6 100644 --- "a/docs/JavaBasics/5_\345\205\263\351\224\256\345\255\227.md" +++ "b/docs/JavaBasics/5_\345\205\263\351\224\256\345\255\227.md" @@ -1,6 +1,6 @@ -# 关键字 +# final 和 static 关键字 -## final +## final 关键字 **1. 数据** @@ -26,7 +26,7 @@ private 方法隐式地被指定为 final,如果在子类中定义的方法和 **声明类不允许被继承**。 -## static +## static 关键字 **1. 静态变量** diff --git "a/docs/JavaBasics/6_\345\217\215\345\260\204.md" "b/docs/JavaBasics/6_\345\217\215\345\260\204.md" index cf38577a..ba3e78a5 100644 --- "a/docs/JavaBasics/6_\345\217\215\345\260\204.md" +++ "b/docs/JavaBasics/6_\345\217\215\345\260\204.md" @@ -1,4 +1,237 @@ -# 反射 +# 异常-Exception + +## 异常的概念 + +Java 异常是一个描述在代码段中**发生异常的对象**,当发生异常情况时,一个代表该异常的对象被创建并且在导致该异常的方法中被抛出,而该方法可以选择自己处理异常或者传递该异常。 + +## 异常继承体系 + +
+ +Throwable 可以用来表示任何可以作为异常抛出的类,分为两种: **Error** 和 **Exception**。 + +- Error:通常是灾难性的致命的错误,是**程序无法控制和处理**的,当出现这些错误时,建议终止程序; +- Exception:通常情况下是可以被程序处理的,捕获后可能恢复,并且在程序中应该**尽可能地去处理**这些异常。 + +Java 异常分为两种: + +- 受检异常:**除了 RuntimeException 及其子类以外,其他的 Exception 类及其子类都属于这种异常**。 +- 非受检异常:包括 RuntimeException 及其子类和 Error。 + +
+ +注意:非受检查异常为编译器不要求强制处理的异常,受检异常则是编译器要求必须处置的异常。 + +Exception 这类异常分为**运行时异常**和**非运行时异常(编译异常)**: + +- 运行时异常 :包括 RuntimeException 及其子类。比如 NullPointerException、IndexOutOfBoundsException。属于非受检异常,可以进行捕捉处理,也可以不处理。 +- 非运行时异常(编译异常):RuntimeExcaption 以外的 Exception。IOException、SQLException 已经自定义的异常,必须要进行处理。 + + + +## Java 异常的处理机制 + +Java 异常处理机制本质上就是**抛出异常**和**捕捉异常**。 + +**抛出异常** + +i.普通问题:指在当前环境下能得到足够的信息,总能处理这个错误。 + +ii.异常情形:是指**阻止当前方法或作用域继续执行的问题**。对于异常情形,已经程序无法执行继续下去了, +因为在当前环境下无法获得必要的信息来解决问题,我们所能做的就是从当前环境中跳出,并把问题提交给上一级环境, +这就是抛出异常时所发生的事情。 + +iii.抛出异常后,会有几件事随之发生: +​ +第一:像创建普通的java对象一样将使用new在堆上创建一个异常对象 + + 第二:当前的执行路径(已经无法继续下去了)被终止,并且从当前环境中弹出对异常对象的引用。 +​ +此时,**异常处理机制接管程序,并开始寻找一个恰当的地方继续执行程序**, +这个恰当的地方就是异常处理程序或者异常处理器, +它的任务是**将程序从错误状态中恢复**,以使程序要么换一种方式运行,要么继续运行下去。 +​ +**捕捉异常** + +在方法抛出异常之后,运行时系统将转为寻找合适的**异常处理器**(exception handler)。 +潜在的异常处理器是异常发生时依次存留在**调用栈**中的方法的集合。 +当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适的异常处理器。 +运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。 +当运行时系统遍历调用栈而未找到合适的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。 + +注意: + +对于运行时异常、错误和受检异常,Java技术所要求的异常处理方式有所不同。 + +(1)由于**运行时异常及其子类**的不可查性,为了更合理、更容易地实现应用程序, +Java规定,运行时异常将由Java运行时系统自动抛出,**允许应用程序忽略运行时异常**。 + +(2)对于方法运行中可能出现的Error,当运行方法不欲捕捉时,Java允许该方法不做任何抛出声明。 +因为,大多数Error异常属于永远不能被允许发生的状况,也属于合理的应用程序不该捕捉的异常。 + +(3)对于所有的受检异常, +Java规定:一个方法必须捕捉,或者声明抛出方法之外。 +也就是说,当一个方法选择不捕捉受检异常时,它必须声明将抛出异常。 + +## Java 异常的处理原则 + +- **具体明确**:抛出的异常应能通过异常类名和message准确说明异常的类型和产生异常的原因; +- **提早抛出**:应尽可能早地发现并抛出异常,便于精确定位问题; +- **延迟捕获**:异常的捕获和处理应尽可能延迟,让掌握更多信息的作用域来处理异常 + +## Java 常见异常以及错误 + +| 类型 | 说明 | +| :--------------------------------------: | :----------------------------------------------------------: | +| **RuntimeException 子类** | | +| java.lang.ArrayIndexOutOfBoundsException | 数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出 | +| java.lang.ArithmeticException | 算术条件异常。譬如:整数除零等 | +| java.lang.NullPointerException | 空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等 | +| java.lang.ClassNotFoundException | 找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常 | +| java.lang.SecurityException | 安全性异常 | +| java.lang.IllegalArgumentException | 非法参数异常 | +| **IOException** | | +| IOException | 操作输入流和输出流时可能出现的异常 | +| EOFException | 文件已结束异常 | +| FileNotFoundException | 文件未找到异常 | +| **其他** | | +| ClassCastException | 类型转换异常类 | +| ArrayStoreException | 数组中包含不兼容的值抛出的异常 | +| SQLException | 操作数据库异常类 | +| NoSuchFieldException | 字段未找到异常 | +| NumberFormatException | 字符串转换为数字抛出的异常 | +| StringIndexOutOfBoundsException | 字符串索引超出范围抛出的异常 | +| IllegalAccessException | 不允许访问某类异常 | +| **Error** | | +| NoClassDefFoundError | 找不到class定义的错误 | +| StackOverflowError | 深递归导致栈被耗尽而抛出的错误 | +| OutOfMemoryError | 内存溢出错误 | + +## try-catch-finally语句块的执行 + +

+ +(1) try 块:用于捕获异常。其后可接零个或多个 catch 块,如果没有 catch 块,则必须跟一个 finally 块。 + +(2) catch 块:用于处理try捕获到的异常。 + +(3) finally 块:无论是否捕获或处理异常,finally 块里的语句都会被执行。 +当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在**方法返回之前**被执行。 + +在以下 4 种特殊情况下,finally 语句块不会被执行: + +- 在finally语句块中发生了异常 +- 在前面的代码中用了System.exit()退出程序。 +- 程序所在的线程死亡。 +- 关闭 CPU。 + +## try catch代码块的性能如何 + +- 会影响 JVM 的重排序优化; +- 异常对象实例需要保存栈快照等信息,开销比较大。 + +## try-with-resources + +适用范围:任何实现 java.lang.AutoCloseable 或者 java.io.Closeable 的对象。 + + 在 try-with-resources 语句中,任何 catch 或 finally 代码块在声明的资源关闭后运行。 + +使用 try-catch-finally 关闭资源: + +```java +//读取文本文件的内容 +Scanner scanner = null; +try { + scanner = new Scanner(new File("D://read.txt")); + while (scanner.hasNext()) { + System.out.println(scanner.nextLine()); + } +} catch (FileNotFoundException e) { + e.printStackTrace(); +} finally { + if (scanner != null) { + scanner.close(); + } +} +``` + +使用 try-with-resources 语句改造上面的代码: + +```java +try (Scanner scanner = new Scanner(new File("test.txt"))) { + while (scanner.hasNext()) { + System.out.println(scanner.nextLine()); + } +} catch (FileNotFoundException e) { + e.printStackTrace(); +} +``` + +使用 try-with-resources 语句关闭多个资源: + +```java +try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new File("test.txt"))); // 这里采用 ";" 进行分割 + BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")))) { + int b; + while ((b = bin.read()) != -1) { + bout.write(b); + } +} +catch (IOException e) { + e.printStackTrace(); +} +``` + + + +## final & finally & finalize + +final:最终的意思,可以修饰类,修饰成员变量,修饰成员方法 + +修饰类:类不能被继承 + +修饰变量:变量是常量 + +修饰方法:方法不能被重写(Override) + +(2)finally:是异常处理的关键字,用于释放资源。一般来说,代码必须执行(特殊情况:在执行到finally JVM就退出了) + +(3)finalize:是Object的一个方法,用于垃圾回收。 + +## 看程序写结果 + +```java +public class TestException { + public static void main(String[] args) { + System.out.println(getInt()); + } + + public static int getInt(){ + int a=10; + try{ + System.out.println(a/0); + }catch (ArithmeticException e){ + a=30; + return a; + }finally { + a=40; + } + System.out.println("a="+a); + return a; + } +} +``` + +结果为: + +```html +30 +``` + + + +# 反射-Reflection + ## 反射概念 Java 的反射机制是指在运行状态中: @@ -43,4 +276,12 @@ Java 的反射机制是指在运行状态中: ## 反射的不足 -性能时一个问题。反射相当于一系列解释操作,通知 JVM 要做什么,性能比直接的 Java 要慢很多。 \ No newline at end of file +性能是一个问题。反射相当于一系列解释操作,通知 Java 虚拟机要做什么,性能比直接的 Java 要慢很多。 + + + +# 补充 + +- [Java提高篇——Java异常处理](https://www.cnblogs.com/Qian123/p/5715402.html) + +- [深入解析Java反射(1) - 基础](https://www.sczyh30.com/posts/Java/java-reflection-1/) \ No newline at end of file diff --git "a/docs/JavaBasics/7_\345\274\202\345\270\270.md" "b/docs/JavaBasics/7_\345\274\202\345\270\270.md" deleted file mode 100644 index 7d5aa8f3..00000000 --- "a/docs/JavaBasics/7_\345\274\202\345\270\270.md" +++ /dev/null @@ -1,184 +0,0 @@ -# 异常 - -## 异常的概念 -Java 异常是一个描述在代码段中**发生异常的对象**,当发生异常情况时,一个代表该异常的对象被创建并且在导致该异常的方法中被抛出,而该方法可以选择自己处理异常或者传递该异常。 - -## 异常继承体系 - -
- - - -Throwable 可以用来表示任何可以作为异常抛出的类,分为两种: **Error** 和 **Exception**。 - -- Error:通常是灾难性的致命的错误,是**程序无法控制和处理**的,当出现这些错误时,建议终止程序; -- Exception:通常情况下是可以被程序处理的,捕获后可能恢复,并且在程序中应该**尽可能地去处理**这些异常。 - -Java 异常分为两种: - - -- 受检异常:**除了 RuntimeException 及其子类以外,其他的 Exception 类及其子类都属于这种异常**。 -- 非受检异常:包括 RuntimeException 及其子类和 Error。 - -
- -注意:非受检查异常为编译器不要求强制处理的异常,受检异常则是编译器要求必须处置的异常。 - -Exception 这类异常分为**运行时异常**和**非运行时异常(编译异常)**: - -- 运行时异常 :包括 RuntimeException 及其子类。比如 NullPointerException、IndexOutOfBoundsException。属于非受检异常,可以进行捕捉处理,也可以不处理。 -- 非运行时异常(编译异常):RuntimeExcaption 以外的 Exception。IOException、SQLException 已经自定义的异常,必须要进行处理。 - - - -## Java 异常的处理机制 - -Java 异常处理机制本质上就是**抛出异常**和**捕捉异常**。 - -**抛出异常** - -i.普通问题:指在当前环境下能得到足够的信息,总能处理这个错误。 - -ii.异常情形:是指**阻止当前方法或作用域继续执行的问题**。对于异常情形,已经程序无法执行继续下去了, -因为在当前环境下无法获得必要的信息来解决问题,我们所能做的就是从当前环境中跳出,并把问题提交给上一级环境, -这就是抛出异常时所发生的事情。 - -iii.抛出异常后,会有几件事随之发生: -​ -第一:像创建普通的java对象一样将使用new在堆上创建一个异常对象 - - 第二:当前的执行路径(已经无法继续下去了)被终止,并且从当前环境中弹出对异常对象的引用。 -​ -此时,**异常处理机制接管程序,并开始寻找一个恰当的地方继续执行程序**, -这个恰当的地方就是异常处理程序或者异常处理器, -它的任务是**将程序从错误状态中恢复**,以使程序要么换一种方式运行,要么继续运行下去。 -​ -**捕捉异常** - -在方法抛出异常之后,运行时系统将转为寻找合适的**异常处理器**(exception handler)。 -潜在的异常处理器是异常发生时依次存留在**调用栈**中的方法的集合。 -当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适的异常处理器。 -运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。 -当运行时系统遍历调用栈而未找到合适的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。 - -注意: - -对于运行时异常、错误和受检异常,Java技术所要求的异常处理方式有所不同。 - -(1)由于**运行时异常及其子类**的不可查性,为了更合理、更容易地实现应用程序, -Java规定,运行时异常将由Java运行时系统自动抛出,**允许应用程序忽略运行时异常**。 - -(2)对于方法运行中可能出现的Error,当运行方法不欲捕捉时,Java允许该方法不做任何抛出声明。 -因为,大多数Error异常属于永远不能被允许发生的状况,也属于合理的应用程序不该捕捉的异常。 - -(3)对于所有的受检异常, -Java规定:一个方法必须捕捉,或者声明抛出方法之外。 -也就是说,当一个方法选择不捕捉受检异常时,它必须声明将抛出异常。 - -## Java 异常的处理原则 - -- **具体明确**:抛出的异常应能通过异常类名和message准确说明异常的类型和产生异常的原因; -- **提早抛出**:应尽可能早地发现并抛出异常,便于精确定位问题; -- **延迟捕获**:异常的捕获和处理应尽可能延迟,让掌握更多信息的作用域来处理异常 - -## Java 常见异常以及错误 - -| 类型 | 说明 | -| :--: | :--: | -| **RuntimeException 子类** | | -| java.lang.ArrayIndexOutOfBoundsException | 数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出 | -| java.lang.ArithmeticException | 算术条件异常。譬如:整数除零等 | -| java.lang.NullPointerException | 空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等 | -| java.lang.ClassNotFoundException | 找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常 | -| java.lang.SecurityException | 安全性异常 | -| java.lang.IllegalArgumentException | 非法参数异常 | -| **IOException** | | -| IOException | 操作输入流和输出流时可能出现的异常 | -| EOFException | 文件已结束异常 | -| FileNotFoundException | 文件未找到异常 | -| **其他** | | -| ClassCastException | 类型转换异常类 | -| ArrayStoreException | 数组中包含不兼容的值抛出的异常 | -| SQLException | 操作数据库异常类 | -| NoSuchFieldException | 字段未找到异常 | -| NumberFormatException | 字符串转换为数字抛出的异常 | -| StringIndexOutOfBoundsException | 字符串索引超出范围抛出的异常 | -| IllegalAccessException | 不允许访问某类异常 | -| **Error** | | -| NoClassDefFoundError | 找不到class定义的错误 | -| StackOverflowError | 深递归导致栈被耗尽而抛出的错误 | -| OutOfMemoryError | 内存溢出错误 | - -## Java异常常见面试题 - -### try-catch-finally语句块的执行 - -

- -(1)try 块:用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。 - -(2)catch 块:用于处理try捕获到的异常。 - -(3)finally 块:无论是否捕获或处理异常,finally块里的语句都会被执行。 -当在try块或catch块中遇到return语句时,finally语句块将在**方法返回之前**被执行。 - -在以下4种特殊情况下,finally块不会被执行: - -1)在finally语句块中发生了异常。 -​ -2)在前面的代码中用了System.exit()退出程序。 - -3)程序所在的线程死亡。 - -4)关闭CPU。 - -### try catch代码块的性能如何? - -1. 会影响JVM的重排序优化; -2. 异常对象实例需要保存栈快照等信息,开销比较大。 - -### final和finally和finalize的区别? - -(1)final:最终的意思,可以修饰类,修饰成员变量,修饰成员方法 - -修饰类:类不能被继承 -​ -修饰变量:变量是常量 -​ -修饰方法:方法不能被重写(Override) - -(2)finally:是异常处理的关键字,用于释放资源。一般来说,代码必须执行(特殊情况:在执行到finally JVM就退出了) - -(3)finalize:是Object的一个方法,用于垃圾回收。 - -- 看程序写结果 - -```java -public class TestException { - public static void main(String[] args) { - System.out.println(getInt()); - } - - public static int getInt(){ - int a=10; - try{ - System.out.println(a/0); - }catch (ArithmeticException e){ - a=30; - return a; - }finally { - a=40; - } - System.out.println("a="+a); - return a; - } -} -``` -结果为: -```html -30 -``` - -- [Java异常常见面试题及答案1](https://github.com/DuHouAn/Java/blob/master/JavaBasics/01Java%E5%BC%82%E5%B8%B8%E5%B8%B8%E8%A7%81%E9%9D%A2%E8%AF%95%E9%A2%98%E5%8F%8A%E7%AD%94%E6%A1%88.txt) - -- [Java异常常见面试题及答案2](https://github.com/DuHouAn/Java/blob/master/JavaBasics/02Java%E5%BC%82%E5%B8%B8%E5%B8%B8%E8%A7%81%E9%9D%A2%E8%AF%95%E9%A2%98%E5%8F%8A%E7%AD%94%E6%A1%88.txt) \ No newline at end of file diff --git "a/docs/JavaBasics/8_\346\263\233\345\236\213.md" "b/docs/JavaBasics/8_\346\263\233\345\236\213.md" index 158550d8..4c2e8e02 100644 --- "a/docs/JavaBasics/8_\346\263\233\345\236\213.md" +++ "b/docs/JavaBasics/8_\346\263\233\345\236\213.md" @@ -1,4 +1,4 @@ -# 泛型 +# 泛型-Generics ## 泛型的概念 在集合中存储对象并在**使用前进行类型转换**是多么的不方便。泛型防止了那种情况的发生。 它提供了**编译期**的类型安全,确保你只能把正确类型的对象放入集合中,避免了在运行时出现ClassCastException。 @@ -655,8 +655,267 @@ Java 的泛型是一种**伪泛型,编译为字节码时参数类型会在代 假设参数类型的占位符为T,擦除规则(保留上界)如下: -(1) 擦除后变为 Object +(1) \ 擦除后变为 Object (2) 擦除后变为 A -(3) <? super A> 擦除后变为 Object \ No newline at end of file +(3) <? super A> 擦除后变为 Object + +# 注解-Annontation + +## 注解概述 + +Annontation 是 Java5 开始引入的新特征,中文名称叫注解。 +注解是插入到代码中的一种**注释**或者说是一种**元数据**。 + +这些注解信息可以在编译期使用编译工具进行处理,也可以在运行期使用 Java 反射机制进行处理。 + +## 注解的用处 + +- 生成文档。这是最常见的,也是java 最早提供的注解。常用的有@param @return 等 +- 跟踪代码依赖性,实现替代**配置文件**功能。如Spring中@Autowired; +- 在编译时进行格式检查。如@override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出。 + +## 注解的原理 + +注解**本质是一个继承了Annotation的特殊接口**,其具体实现类是Java运行时生成的**动态代理类**。 +我们通过反射获取注解时,返回的是Java运行时生成的**动态代理对象**$Proxy1。 +通过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandler的invoke方法。 +该方法会从memberValues这个Map中索引出对应的值。 +而memberValues的来源是Java常量池。 + +### 元注解 + +java.lang.annotation提供了四种元注解,专门注解其他的注解(在自定义注解的时候,需要使用到元注解): + +| 注解 | 说明 | +| :---------: | :-----------------------: | +| @Documented | 是否将注解包含在JavaDoc中 | +| @Retention | 什么时候使用该注解 | +| @Target | 注解用于什么地方 | +| @Inherited | 是否允许子类继承该注解 | + +- @Documented + +一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中。 + +- @Retention + +定义该注解的生命周期。 + +(1)RetentionPolicy.SOURCE : 在编译阶段丢弃。 + 这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。 + @Override, @SuppressWarnings都属于这类注解。 + +(2)RetentionPolicy.CLASS : 在类加载的时候丢弃。 +在字节码文件的处理中有用。注解默认使用这种方式 + +(3)RetentionPolicy.RUNTIME : 始终不会丢弃,运行期也保留该注解, +因此**可以使用反射机制读取该注解的信息**。我们自定义的注解通常使用这种方式。 + +- @Target + +表示该注解用于什么地方。 +默认值为任何元素,表示该注解用于什么地方。可用的ElementType参数包括: + +| 参数 | 说明 | +| :--------------------------------------------------------: | :----------------------------------: | +| ElementType.CONSTRUCTOR | 用于描述构造器 | +| ElementType.FIELD | 成员变量、对象、属性(包括enum实例) | +| ElementType.LOCAL_VARIABLE | 用于描述局部变量 | +| ElementType.METHOD | 用于描述方法 | +| ElementType.PACKAGE | 用于描述包 | +| ElementType.PARAMETER | 用于描述参数 | +| ElementType.TYPE:用于描述类、接口(包括注解类型) 或enum声明 | | + +- @Inherited + +定义该注释和子类的关系。 +@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。 +如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。 + +### 常见标准的Annotation + +- Override (RetentionPolicy.SOURCE : 在编译阶段丢弃。属于@Retention) + + Override是一个标记类型注解,它被用作标注方法。 + 它说明了被标注的方法重载了父类的方法,起到了断言的作用。 + 如果我们使用了这种注解在一个没有覆盖父类方法的方法时,**java编译器将以一个编译错误来警示**。 + +- Deprecated + +Deprecated也是一种标记类型注解。 +当一个类型或者类型成员使用@Deprecated修饰的话,编译器将不鼓励使用这个被标注的程序元素。 +所以使用这种修饰具有一定的“延续性”: + 如果我们在代码中通过继承或者覆盖的方式使用了这个过时的类型或者成员, + 虽然继承或者覆盖后的类型或者成员并不是被声明为@Deprecated,但编译器仍然要报警。 + +- SuppressWarnings + +SuppressWarning不是一个标记类型注解。 +它有一个类型为String[]的成员,这个成员的值为**被禁止的警告名**。 +对于javac编译器来讲,对-Xlint选项有效的警告名也同样对@SuppressWarings有效,同时编译器忽略掉无法识别的警告名。 +@SuppressWarnings("unchecked") + +### 自定义注解使用的规则 + +自定义注解类编写的一些规则: +(1) Annotation型定义为@interface, +所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口 + +(2)参数成员只能用public或默认(default)这两个访问权修饰 + +(3)参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型 +和String、Enum、Class、Annotations等数据类型,以及这一些类型的数组 + +(4)要获取类方法和字段的注解信息,必须通过Java的反射技术来获取Annotation对象, +因为除此之外没有别的获取注解对象的方法 + +(5)注解也可以没有定义成员, 不过这样注解就没啥用了 + +注意:**自定义注解需要使用到元注解** + +> 自定义注解示例 + +自定义水果颜色注解 + +```java +/** + * 水果颜色注解 + */ +@Target(FIELD) +@Retention(RUNTIME) +@Documented +@interface FruitColor { + /** + * 颜色枚举 + */ + public enum Color{绿色,红色,青色}; + + /** + * 颜色属性 (注意:这里的属性指的就是方法) + */ + Color fruitColor() default Color.绿色;//默认是是绿色的 +} +``` + +自定义水果名称注解 + +```java +/** + * 水果名称注解 + */ +@Target(FIELD) //ElementType.FIELD:成员变量、对象、属性(包括enum实例) +@Retention(RUNTIME)// 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。 +@Documented // Deprecated也是一种标记类型注解。 +public @interface FruitName { + public String fruitName() default ""; +} +``` + +水果供应商注解 + +```java +/** + * 水果供应者注解 + */ +@Target(FIELD) +@Retention(RUNTIME) +@Documented +public @interface FruitProvider { + /** + * 供应者编号 + */ + public int id() default -1; + + /** + * 供应商名称 + */ + public String name() default ""; + + /** + * 供应商地址 + */ + public String address() default ""; +} +``` + +通过反射来获取水果信息 + +```java +/** + * 通过反射获取水果信息 + */ +public class FruitInfoUtil { + public static void getFruitInfo(Class clazz){ + String strFruitName=" 水果名称:"; + String strFruitColor=" 水果颜色:"; + String strFruitProvider="供应商信息:"; + + //获取属性值 + Field[] fields=clazz.getDeclaredFields(); + for(Field field:fields){ + if(field.isAnnotationPresent(FruitName.class)){ + //判断注解是不是 FruitName + FruitName fruitName=field.getAnnotation(FruitName.class); + strFruitName=strFruitName+fruitName.fruitName(); + System.out.println(strFruitName); + }else if(field.isAnnotationPresent(FruitColor.class)){ + FruitColor fruitColor=field.getAnnotation(FruitColor.class); + strFruitColor=strFruitColor+fruitColor.fruitColor().toString(); + System.out.println(strFruitColor); + }else if(field.isAnnotationPresent(FruitProvider.class)){ + FruitProvider fruitProvider=field.getAnnotation(FruitProvider.class); + strFruitProvider=strFruitProvider + + "[ 供应商编号:"+fruitProvider.id() + +" 供应商名称:" +fruitProvider.name() + +" 供应商地址:"+fruitProvider.address()+"]"; + System.out.println(strFruitProvider); + } + } + } +} +``` + +使用注解初始化实例类 + +```java +/** + * 定义一个实例类 + * 这里使用注解来初始化 + */ +public class Apple { + @FruitName(fruitName = "苹果") + private String appleName; + + @FruitColor(fruitColor = FruitColor.Color.红色) + private String appleColor; + + @FruitProvider(id=1,name="红富士",address="陕西省西安市延安路89号红富士大厦") + private String appleProvider; + + public String getAppleName() { + return appleName; + } + + public void setAppleName(String appleName) { + this.appleName = appleName; + } + + public String getAppleColor() { + return appleColor; + } + + public void setAppleColor(String appleColor) { + this.appleColor = appleColor; + } + + public String getAppleProvider() { + return appleProvider; + } + + public void setAppleProvider(String appleProvider) { + this.appleProvider = appleProvider; + } +} +``` \ No newline at end of file diff --git "a/docs/JavaBasics/9_\346\263\250\350\247\243.md" "b/docs/JavaBasics/9_\346\263\250\350\247\243.md" deleted file mode 100644 index fff5cbb8..00000000 --- "a/docs/JavaBasics/9_\346\263\250\350\247\243.md" +++ /dev/null @@ -1,243 +0,0 @@ -# 注解 -## 注解概述 -Annontation 是 Java5 开始引入的新特征,中文名称叫注解。 -注解是插入到代码中的一种**注释**或者说是一种**元数据**。 - -这些注解信息可以在编译期使用编译工具进行处理,也可以在运行期使用 Java 反射机制进行处理。 - -## 注解的用处 -- 生成文档。这是最常见的,也是java 最早提供的注解。常用的有@param @return 等 -- 跟踪代码依赖性,实现替代**配置文件**功能。如Spring中@Autowired; -- 在编译时进行格式检查。如@override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出。 - -## 注解的原理 -注解**本质是一个继承了Annotation的特殊接口**,其具体实现类是Java运行时生成的**动态代理类**。 -我们通过反射获取注解时,返回的是Java运行时生成的**动态代理对象**$Proxy1。 -通过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandler的invoke方法。 -该方法会从memberValues这个Map中索引出对应的值。 -而memberValues的来源是Java常量池。 - -### 元注解 -java.lang.annotation提供了四种元注解,专门注解其他的注解(在自定义注解的时候,需要使用到元注解): - -| 注解 | 说明 | -| :--: | :--: | -| @Documented | 是否将注解包含在JavaDoc中 | -| @Retention | 什么时候使用该注解 | -| @Target | 注解用于什么地方 | -| @Inherited | 是否允许子类继承该注解 | - -- @Documented - -一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中。 - -- @Retention - -定义该注解的生命周期。 - -(1)RetentionPolicy.SOURCE : 在编译阶段丢弃。 - 这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。 - @Override, @SuppressWarnings都属于这类注解。 - -(2)RetentionPolicy.CLASS : 在类加载的时候丢弃。 -在字节码文件的处理中有用。注解默认使用这种方式 - -(3)RetentionPolicy.RUNTIME : 始终不会丢弃,运行期也保留该注解, -因此**可以使用反射机制读取该注解的信息**。我们自定义的注解通常使用这种方式。 - -- @Target - -表示该注解用于什么地方。 -默认值为任何元素,表示该注解用于什么地方。可用的ElementType参数包括: - -| 参数 | 说明 | -| :--: | :--: | -|ElementType.CONSTRUCTOR | 用于描述构造器 | -| ElementType.FIELD | 成员变量、对象、属性(包括enum实例)| -| ElementType.LOCAL_VARIABLE | 用于描述局部变量 | -| ElementType.METHOD | 用于描述方法 | -| ElementType.PACKAGE | 用于描述包 | -| ElementType.PARAMETER | 用于描述参数 | -| ElementType.TYPE:用于描述类、接口(包括注解类型) 或enum声明 | - -- @Inherited - -定义该注释和子类的关系。 -@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。 -如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。 - -### 常见标准的Annotation -- Override (RetentionPolicy.SOURCE : 在编译阶段丢弃。属于@Retention) - - Override是一个标记类型注解,它被用作标注方法。 - 它说明了被标注的方法重载了父类的方法,起到了断言的作用。 - 如果我们使用了这种注解在一个没有覆盖父类方法的方法时,**java编译器将以一个编译错误来警示**。 - -- Deprecated - - -Deprecated也是一种标记类型注解。 -当一个类型或者类型成员使用@Deprecated修饰的话,编译器将不鼓励使用这个被标注的程序元素。 -所以使用这种修饰具有一定的“延续性”: - 如果我们在代码中通过继承或者覆盖的方式使用了这个过时的类型或者成员, - 虽然继承或者覆盖后的类型或者成员并不是被声明为@Deprecated,但编译器仍然要报警。 - -- SuppressWarnings - -SuppressWarning不是一个标记类型注解。 -它有一个类型为String[]的成员,这个成员的值为**被禁止的警告名**。 -对于javac编译器来讲,对-Xlint选项有效的警告名也同样对@SuppressWarings有效,同时编译器忽略掉无法识别的警告名。 -@SuppressWarnings("unchecked") - -### 自定义注解使用的规则 -自定义注解类编写的一些规则: -(1) Annotation型定义为@interface, -所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口 - -(2)参数成员只能用public或默认(default)这两个访问权修饰 - -(3)参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型 -和String、Enum、Class、Annotations等数据类型,以及这一些类型的数组 - -(4)要获取类方法和字段的注解信息,必须通过Java的反射技术来获取Annotation对象, -因为除此之外没有别的获取注解对象的方法 - -(5)注解也可以没有定义成员, 不过这样注解就没啥用了 - -注意:**自定义注解需要使用到元注解** - -> 自定义注解示例 - -自定义水果颜色注解 -```java -/** - * 水果颜色注解 - */ -@Target(FIELD) -@Retention(RUNTIME) -@Documented -@interface FruitColor { - /** - * 颜色枚举 - */ - public enum Color{绿色,红色,青色}; - - /** - * 颜色属性 (注意:这里的属性指的就是方法) - */ - Color fruitColor() default Color.绿色;//默认是是绿色的 -} -``` -自定义水果名称注解 -```java -/** - * 水果名称注解 - */ -@Target(FIELD) //ElementType.FIELD:成员变量、对象、属性(包括enum实例) -@Retention(RUNTIME)// 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。 -@Documented // Deprecated也是一种标记类型注解。 -public @interface FruitName { - public String fruitName() default ""; -} -``` -水果供应商注解 -```java -/** - * 水果供应者注解 - */ -@Target(FIELD) -@Retention(RUNTIME) -@Documented -public @interface FruitProvider { - /** - * 供应者编号 - */ - public int id() default -1; - - /** - * 供应商名称 - */ - public String name() default ""; - - /** - * 供应商地址 - */ - public String address() default ""; -} -``` -通过反射来获取水果信息 -```java -/** - * 通过反射获取水果信息 - */ -public class FruitInfoUtil { - public static void getFruitInfo(Class clazz){ - String strFruitName=" 水果名称:"; - String strFruitColor=" 水果颜色:"; - String strFruitProvider="供应商信息:"; - - //获取属性值 - Field[] fields=clazz.getDeclaredFields(); - for(Field field:fields){ - if(field.isAnnotationPresent(FruitName.class)){ - //判断注解是不是 FruitName - FruitName fruitName=field.getAnnotation(FruitName.class); - strFruitName=strFruitName+fruitName.fruitName(); - System.out.println(strFruitName); - }else if(field.isAnnotationPresent(FruitColor.class)){ - FruitColor fruitColor=field.getAnnotation(FruitColor.class); - strFruitColor=strFruitColor+fruitColor.fruitColor().toString(); - System.out.println(strFruitColor); - }else if(field.isAnnotationPresent(FruitProvider.class)){ - FruitProvider fruitProvider=field.getAnnotation(FruitProvider.class); - strFruitProvider=strFruitProvider - + "[ 供应商编号:"+fruitProvider.id() - +" 供应商名称:" +fruitProvider.name() - +" 供应商地址:"+fruitProvider.address()+"]"; - System.out.println(strFruitProvider); - } - } - } -} -``` -使用注解初始化实例类 -```java -/** - * 定义一个实例类 - * 这里使用注解来初始化 - */ -public class Apple { - @FruitName(fruitName = "苹果") - private String appleName; - - @FruitColor(fruitColor = FruitColor.Color.红色) - private String appleColor; - - @FruitProvider(id=1,name="红富士",address="陕西省西安市延安路89号红富士大厦") - private String appleProvider; - - public String getAppleName() { - return appleName; - } - - public void setAppleName(String appleName) { - this.appleName = appleName; - } - - public String getAppleColor() { - return appleColor; - } - - public void setAppleColor(String appleColor) { - this.appleColor = appleColor; - } - - public String getAppleProvider() { - return appleProvider; - } - - public void setAppleProvider(String appleProvider) { - this.appleProvider = appleProvider; - } -} -``` \ No newline at end of file diff --git "a/docs/JavaBasics/JDK8\346\226\260\347\211\271\346\200\247.md" "b/docs/JavaBasics/JDK8\346\226\260\347\211\271\346\200\247.md" new file mode 100644 index 00000000..f774bf07 --- /dev/null +++ "b/docs/JavaBasics/JDK8\346\226\260\347\211\271\346\200\247.md" @@ -0,0 +1,3 @@ +# JDK 8 新特性 + +待补充。 \ No newline at end of file diff --git "a/docs/JavaBasics/\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" "b/docs/JavaBasics/\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" new file mode 100644 index 00000000..7217feb9 --- /dev/null +++ "b/docs/JavaBasics/\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" @@ -0,0 +1,173 @@ +# 正则表达式 + +## 概述 + +是指一个用来描述或者匹配一系列符合某个句法规则的字符串的单个字符串。 + +其实就是**一种规则**。 + +## 组成规则 + +规则字符在java.util.regex Pattern类中:[Pattern API](https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html) + +## 常见组成规则 + +### 1. 字符 + +| 元字符 | 说明 | +| :----: | :-----------------------: | +| x | 字符 x | +| \ | 反斜线字符 | +| \n | 新行(换行)符 ('\u000A') | +| \r | 回车符 ('\u000D') | + +### 2. 字符类 + +| 元字符 | 说明 | +| :------: | :----------------------------------------: | +| [abc] | a、b 或 c(简单类) | +| [^abc] | 任何字符,除了 a、b 或 c(否定) | +| [a-zA-Z] | a到 z 或 A到 Z,两头的字母包括在内(范围) | +| [0-9] | 0到9的字符都包括 | + +### 3. 预定义字符类 + +| 元字符 | 说明 | +| :----: | :--------------------------: | +| . | 任何字符 | +| \d | 数字。等价于[0-9] | +| \w | 单词字符。等待雨[a-zA-Z_0-9] | + +### 4. 边界匹配器 + +| 元字符 | 说明 | +| :----: | :------: | +| ^ | 行的开头 | +| $ | 行的结尾 | +| \b | 单词边界 | + +### 5. 数量词 + +| 元字符 | 说明 | +| :----: | :---------------------------: | +| X? | X,零次或一次 | +| X* | X,零次或多次 | +| X+ | X,一次或多次 | +| X{n} | X,恰好 n 次 | +| X{n,} | X,至少 n 次 | +| X{n,m} | X,至少 n 次,但是不超过 m 次 | + +## 应用 + +### 判断功能 + +String 类 matches 方法 + +```java +public boolean matches(String regex) +``` + +```java +/** +* 判断手机号码是否满足要求 +*/ +public static void main(String[] args) { + //键盘录入手机号码 + Scanner sc = new Scanner(System.in); + System.out.println("请输入你的手机号码:"); + String phone = sc.nextLine(); + + //定义手机号码的规则 + String regex = "1[38]\\d{9}"; + + //调用功能,判断即可 + boolean flag = phone.matches(regex); + + //输出结果 + System.out.println("flag:"+flag); +} +``` + + + +### 分割功能 + +String类的 split 方法 + +```java +public String[] split(String regex) +``` + +```java +/** +* 根据给定正则表达式的匹配拆分此字符串 +*/ +public static void main(String[] args) { + //定义一个年龄搜索范围 + String ages = "18-24"; + + //定义规则 + String regex = "-"; + + //调用方法 + String[] strArray = ages.split(regex); + int startAge = Integer.parseInt(strArray[0]); + int endAge = Integer.parseInt(strArray[1]); +} +``` + + + +### 替换功能 + +String 类的 replaceAll 方法 + +```java +public String replaceAll(String regex,String replacement) +``` + +```java +/** +* 去除所有的数字 +*/ +public static void main(String[] args) { + // 定义一个字符串 + String s = "helloqq12345worldkh622112345678java"; + + + // 直接把数字干掉 + String regex = "\\d+"; + String ss = ""; + + String result = s.replaceAll(regex, ss); + System.out.println(result); +} +``` + + + +### 获取功能 + +Pattern和Matcher类的使用 + +```java +/** +* 模式和匹配器的基本顺序 +*/ +public static void main(String[] args) { + // 模式和匹配器的典型调用顺序 + // 把正则表达式编译成模式对象 + Pattern p = Pattern.compile("a*b"); + // 通过模式对象得到匹配器对象,这个时候需要的是被匹配的字符串 + Matcher m = p.matcher("aaaaab"); + // 调用匹配器对象的功能 + boolean b = m.matches(); + System.out.println(b); + + //这个是判断功能,但是如果做判断,这样做就有点麻烦了,我们直接用字符串的方法做 + String s = "aaaaab"; + String regex = "a*b"; + boolean bb = s.matches(regex); + System.out.println(bb); +} +``` \ No newline at end of file diff --git "a/docs/JavaContainer/1_Java\345\256\271\345\231\250\346\246\202\350\247\210.md" "b/docs/JavaContainer/1_\345\256\271\345\231\250\346\246\202\350\247\210.md" similarity index 84% rename from "docs/JavaContainer/1_Java\345\256\271\345\231\250\346\246\202\350\247\210.md" rename to "docs/JavaContainer/1_\345\256\271\345\231\250\346\246\202\350\247\210.md" index b30706a7..26515fb0 100644 --- "a/docs/JavaContainer/1_Java\345\256\271\345\231\250\346\246\202\350\247\210.md" +++ "b/docs/JavaContainer/1_\345\256\271\345\231\250\346\246\202\350\247\210.md" @@ -4,7 +4,9 @@ ## Collection -
+Collection 集合体系图: + +
### 1. Set @@ -33,7 +35,9 @@ ## Map -
+Map 集合体系图: + +
- TreeMap:基于红黑树实现。 diff --git "a/docs/JavaContainer/2_\345\256\271\345\231\250\344\270\255\347\232\204\350\256\276\350\256\241\346\250\241\345\274\217.md" "b/docs/JavaContainer/2_\345\256\271\345\231\250\344\270\255\347\232\204\350\256\276\350\256\241\346\250\241\345\274\217.md" index e06fb004..f795cada 100644 --- "a/docs/JavaContainer/2_\345\256\271\345\231\250\344\270\255\347\232\204\350\256\276\350\256\241\346\250\241\345\274\217.md" +++ "b/docs/JavaContainer/2_\345\256\271\345\231\250\344\270\255\347\232\204\350\256\276\350\256\241\346\250\241\345\274\217.md" @@ -2,9 +2,7 @@ ## 迭代器模式 -[参考迭代器模式笔记](https://github.com/DuHouAn/Java/blob/master/Object_Oriented/notes/02%E8%A1%8C%E4%B8%BA%E5%9E%8B.md#4-%E8%BF%AD%E4%BB%A3%E5%99%A8iterator) - -

+

Collection 继承了 Iterable 接口,其中的 iterator() 方法能够产生一个 Iterator 对象,通过这个对象就可以迭代遍历 Collection 中的元素。 @@ -21,8 +19,6 @@ for (String item : list) { ## 适配器模式 -[参考适配器模式笔记](https://github.com/DuHouAn/Java/blob/master/Object_Oriented/notes/03%E7%BB%93%E6%9E%84%E5%9E%8B.md#1-%E9%80%82%E9%85%8D%E5%99%A8adapter) - java.util.Arrays#asList() 可以把数组类型转换为 List 类型。 ```java diff --git "a/docs/JavaContainer/3_\345\256\271\345\231\250\346\272\220\347\240\201\345\210\206\346\236\220 - List.md" "b/docs/JavaContainer/3_\345\256\271\345\231\250\346\272\220\347\240\201\345\210\206\346\236\220 - List.md" index fdff5b13..161d764e 100644 --- "a/docs/JavaContainer/3_\345\256\271\345\231\250\346\272\220\347\240\201\345\210\206\346\236\220 - List.md" +++ "b/docs/JavaContainer/3_\345\256\271\345\231\250\346\272\220\347\240\201\345\210\206\346\236\220 - List.md" @@ -1,6 +1,6 @@ # 容器源码分析 - List -如果没有特别说明,以下源码分析基于 JDK 1.8。 +以下源码分析基于 JDK 1.8。 ## ArrayList @@ -176,7 +176,7 @@ private void readObject(java.io.ObjectInputStream s) } ``` -### 6.System.arraycopy()和Arrays.copyOf()方法 +### 6. System.arraycopy()和Arrays.copyOf()方法 Arrays.copyOf() 的源代码内部调用了 System.arraycopy() 方法。 - System.arraycopy() 方法需要目标数组,将原数组拷贝到目标数组里,而且可以选择**拷贝的起点和长度以及放入新数组中的位置**; @@ -225,6 +225,8 @@ List synList = Collections.synchronizedList(list); List list = new CopyOnWriteArrayList<>(); ``` + + ## LinkedList ### 1. 概览 @@ -246,10 +248,11 @@ transient Node first; transient Node last; ``` -

+
### 2. 添加元素 -- 将元素添加到链表尾部 +将元素添加到链表尾部: + ```java public boolean add(E e) { linkLast(e);//这里就只调用了这一个方法 @@ -274,7 +277,8 @@ void linkLast(E e) { } ``` -- 将元素添加到链表头部 +将元素添加到链表头部: + ```java public void addFirst(E e) { linkFirst(e); diff --git "a/docs/JavaContainer/4_\345\256\271\345\231\250\346\272\220\347\240\201\345\210\206\346\236\220 - Map.md" "b/docs/JavaContainer/4_\345\256\271\345\231\250\346\272\220\347\240\201\345\210\206\346\236\220 - Map.md" index ea04d9b5..595262cc 100644 --- "a/docs/JavaContainer/4_\345\256\271\345\231\250\346\272\220\347\240\201\345\210\206\346\236\220 - Map.md" +++ "b/docs/JavaContainer/4_\345\256\271\345\231\250\346\272\220\347\240\201\345\210\206\346\236\220 - Map.md" @@ -1,6 +1,6 @@ # 容器源码分析 - Map -如果没有特别说明,以下源码分析基于 JDK 1.8。 +以下源码分析基于 JDK 1.8。 ## HashMap @@ -18,7 +18,7 @@ Entry 存储着键值对。它包含了四个字段,从 next 字段我们可 即数组中的每个位置被当成一个桶,一个桶存放一个链表。HashMap 使用**拉链法**来解决冲突, 同一个链表中存放哈希值相同的 Entry。 -

+

```java static class Entry implements Map.Entry { @@ -98,7 +98,7 @@ map.put("K3", "V3"); - 计算键值对所在的桶; - 在链表上顺序查找,时间复杂度显然和链表的长度成正比。 -

+

### 3. put 操作 diff --git "a/docs/JavaContainer/5_\345\256\271\345\231\250\346\272\220\347\240\201\345\210\206\346\236\220 - \345\271\266\345\217\221\345\256\271\345\231\250.md" "b/docs/JavaContainer/5_\345\256\271\345\231\250\346\272\220\347\240\201\345\210\206\346\236\220 - \345\271\266\345\217\221\345\256\271\345\231\250.md" index 26f41211..98396c27 100644 --- "a/docs/JavaContainer/5_\345\256\271\345\231\250\346\272\220\347\240\201\345\210\206\346\236\220 - \345\271\266\345\217\221\345\256\271\345\231\250.md" +++ "b/docs/JavaContainer/5_\345\256\271\345\231\250\346\272\220\347\240\201\345\210\206\346\236\220 - \345\271\266\345\217\221\345\256\271\345\231\250.md" @@ -1,6 +1,6 @@ # 容器源码分析 - 并发容器 -如果没有特别说明,以下源码分析基于 JDK 1.8。 +以下源码分析基于 JDK 1.8。 ## CopyOnWriteArrayList @@ -103,7 +103,7 @@ final Segment[] segments; static final int DEFAULT_CONCURRENCY_LEVEL = 16; ``` -
+
### 2. size 操作 @@ -223,24 +223,16 @@ static final Node tabAt(Node[] tab, int i) { **底层数据结构:** -- JDK1.7 的ConcurrentHashMap底层采用**分段的数组+链表**实现, - JDK1.8 的ConcurrentHashMap底层采用的数据结构与JDK1.8 的HashMap的结构一样,**数组+链表/红黑二叉树**。 -- Hashtable和JDK1.8 之前的HashMap的底层数据结构类似都是采用**数组+链表**的形式, +- JDK1.7 的 ConcurrentHashMap 底层采用**分段的数组+链表**实现, + JDK1.8 的 ConcurrentHashMap 底层采用的数据结构与 JDK1.8 的 HashMap 的结构一样,**数组+链表/红黑二叉树**。 +- Hashtable 和 JDK1.8 之前的HashMap的底层数据结构类似都是采用**数组+链表**的形式, 数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的。 **实现线程安全的方式** -- JDK1.7的ConcurrentHashMap(分段锁)对整个桶数组进行了分割分段(Segment), +- JDK1.7 的 ConcurrentHashMap(分段锁)对整个桶数组进行了分割分段(Segment), 每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问度。 - JDK 1.8 采用**数组+链表/红黑二叉树**的数据结构来实现,并发控制使用**synchronized和CAS**来操作。 -- Hashtable:使用 synchronized 来保证线程安全,效率非常低下。 + JDK 1.8 采用**数组+链表/红黑二叉树**的数据结构来实现,并发控制使用 **synchronized** 和 **CAS**来操作。 +- Hashtable 使用 synchronized 来保证线程安全,效率非常低下。 当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态, 如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈。 - -Hashtable 全表锁 - -
- -ConcurrentHashMap 分段锁 - -
\ No newline at end of file diff --git "a/docs/JavaIO/1_\346\246\202\350\247\210.md" "b/docs/JavaIO/1_\346\246\202\350\247\210.md" index 44cbf9ac..5260aca5 100644 --- "a/docs/JavaIO/1_\346\246\202\350\247\210.md" +++ "b/docs/JavaIO/1_\346\246\202\350\247\210.md" @@ -1,16 +1,45 @@ -# 概览 +# Java I/O 概览 -## Java 的 I/O 大概可以分成以下几类: +## I/O 介绍 + +I/O(**I**nput/**O**utpu) 即**输入/输出** 。 + +学术的说 I/O 是信息处理系统(计算机)与外界(人或信息处理系统)间的通信。如计算机,即 CPU 访问任何寄存器和 Cache 等封装以外的数据资源都可当成 I/O ,包括且不限于内存,磁盘,显卡。 + +软件开发中的 I/O 则常指磁盘、网络 IO。 + +补充: + +- [如何完成一次IO](https://llc687.top/126.html) +- [5 种IO模型](https://mp.weixin.qq.com/s?__biz=Mzg3MjA4MTExMw==&mid=2247484746&idx=1&sn=c0a7f9129d780786cabfcac0a8aa6bb7&source=41&scene=21#wechat_redirect) + +## 同步 & 异步 + +同步、异步是消息通知机制。 + +- 同步:同步就是发起一个调用后,被调用者未处理完请求之前,调用不返回。 +- 异步:异步就是发起一个调用后,立刻得到被调用者的回应表示已接收到请求,但是被调用者并没有返回结果,此时我们可以处理其他的请求,被调用者通常依靠事件,回调等机制来通知调用者其返回结果。 + +同步和异步的区别最大在于异步的话调用者不需要等待处理结果,被调用者会通过回调等机制来通知调用者其返回结果。 + +## 阻塞 & 非阻塞 + +阻塞、非阻塞是等待通知时的状态。 + +- 阻塞: 阻塞就是发起一个请求,调用者一直等待请求结果返回,也就是当前线程会被挂起,无法从事其他任务,只有当条件就绪才能继续。 +- 非阻塞:非阻塞就是发起一个请求,调用者不用一直等着结果返回,可以先去干其他事情。 + +举个生活中简单的例子: + +你妈妈让你烧水,小时候你比较笨啊,在哪里傻等着水开( **同步阻塞** )。等你稍微再长大一点,你知道每次烧水的空隙可以去干点其他事,然后只需要时不时来看看水开了没有( **同步非阻塞** )。后来,你们家用上了水开了会发出声音的壶,这样你就只需要听到响声后就知道水开了,在这期间你可以随便干自己的事情( **异步非阻塞** )。 + +## Java I/O 分类 + +Java 的 I/O 大概可以分成以下几类: - 磁盘操作:File - 字节操作:InputStream 和 OutputStream - 字符操作:Reader 和 Writer - 对象操作:Serializable - 网络操作:Socket -- 新的输入/输出:NIO - -## IO流的分类: - -

- -注意:一般我们在探讨IO流的时候,如果没有明确说明按哪种分类来说,默认情况下是按照数据类型来分的。 +- 新的输入/输出:NIO & AIO diff --git "a/docs/JavaIO/3_\345\255\227\350\212\202\346\223\215\344\275\234.md" "b/docs/JavaIO/3_\345\255\227\350\212\202\346\223\215\344\275\234.md" index 9ca0b8b5..c0c9cc63 100644 --- "a/docs/JavaIO/3_\345\255\227\350\212\202\346\223\215\344\275\234.md" +++ "b/docs/JavaIO/3_\345\255\227\350\212\202\346\223\215\344\275\234.md" @@ -309,7 +309,7 @@ Java I/O 使用了装饰者模式来实现。以 InputStream 为例, - FileInputStream 是 InputStream 的子类,属于具体组件,提供了字节流的输入操作; - FilterInputStream 属于抽象装饰者,装饰者用于装饰组件,为组件提供额外的功能。例如 BufferedInputStream 为 FileInputStream 提供缓存的功能。 -

+

实例化一个具有缓存功能的字节流对象时,只需要在 FileInputStream 对象上再套一层 BufferedInputStream 对象即可。 diff --git "a/docs/JavaIO/5_\345\257\271\350\261\241\346\223\215\344\275\234.md" "b/docs/JavaIO/5_\345\257\271\350\261\241\346\223\215\344\275\234.md" index b3c8891d..bb15fcd9 100644 --- "a/docs/JavaIO/5_\345\257\271\350\261\241\346\223\215\344\275\234.md" +++ "b/docs/JavaIO/5_\345\257\271\350\261\241\346\223\215\344\275\234.md" @@ -1,8 +1,12 @@ # 对象操作 -## 序列化 +## 序列化 & 反序列化 -序列化就是将一个对象转换成字节序列,方便存储和传输。 +如果我们需要持久化 Java 对象比如将 Java 对象保存在文件中,或者在网络传输 Java 对象,这些场景都需要用到序列化。 + +序列化就是将数据结构或对象转换成二进制字节流的过程。 + +反序列化将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程。 - 序列化:ObjectOutputStream.writeObject() - 反序列化:ObjectInputStream.readObject() diff --git "a/docs/JavaIO/6_\347\275\221\347\273\234\346\223\215\344\275\234.md" "b/docs/JavaIO/6_\347\275\221\347\273\234\346\223\215\344\275\234.md" index e9edb409..eaa5928e 100644 --- "a/docs/JavaIO/6_\347\275\221\347\273\234\346\223\215\344\275\234.md" +++ "b/docs/JavaIO/6_\347\275\221\347\273\234\346\223\215\344\275\234.md" @@ -67,7 +67,7 @@ public static void main(String[] args) throws IOException { - Socket:客户端类 - 服务器和客户端通过 InputStream 和 OutputStream 进行输入输出。 -
+
## Datagram diff --git a/docs/JavaIO/7_NIO.md b/docs/JavaIO/7_NIO.md index 969c8b2b..537fe3a5 100644 --- a/docs/JavaIO/7_NIO.md +++ b/docs/JavaIO/7_NIO.md @@ -1,8 +1,8 @@ -# NIO +# 一、NIO -新的输入/输出 (NIO) 库是在 JDK 1.4 中引入的,弥补了原来的 I/O 的不足,提供了高速的、面向块的 I/O。 +NIO(New I/O)即新的输入/输出库是在 JDK 1.4 中引入的,弥补了原来的 I/O 的不足,提供了高速的、面向块的 I/O。 -NIO核心组件: +NIO 核心组件: - 通道(Channels) @@ -65,23 +65,23 @@ I/O 包和 NIO 已经很好地集成了,java.io.\* 已经以 NIO 为基础重 ① 新建一个大小为 8 个字节的缓冲区,此时 position 为 0,而 limit = capacity = 8。capacity 变量不会改变,下面的讨论会忽略它。 -

+

② 从输入通道中读取 5 个字节数据写入缓冲区中,此时 position 为 5,limit 保持不变。 -

+

③ 在将缓冲区的数据写到输出通道之前,需要先调用 flip() 方法,这个方法将 limit 设置为当前 position,并将 position 设置为 0。 -

+

④ 从缓冲区中取 4 个字节到输出缓冲中,此时 position 设为 4。 -

+

⑤ 最后需要调用 clear() 方法来清空缓冲区,此时 position 和 limit 都被设置为最初位置。 -

+

## 文件 NIO 实例 ### FileChannel的使用 @@ -427,7 +427,7 @@ NIO 实现了 IO 多路复用中的 **Reactor 模型**,一个线程 Thread 使 应该注意的是,只有套接字 Channel 才能配置为非阻塞,而 FileChannel 不能, 为 FileChannel 配置非阻塞也没有意义。 -

+

使用Selector的优点: @@ -660,7 +660,9 @@ public class NIOClient { ## 内存映射文件 -内存映射文件 I/O 是一种读和写文件数据的方法,它可以比常规的基于流或者基于通道的 I/O 快得多。 +内存映射文件((memory-mapped file)) I/O 是一种读和写文件数据的方法,它可以比常规的基于流或者基于通道的 I/O 快得多。 + +内存映射文件能让你创建和修改那些大到无法读入内存的文件。有了内存映射文件,可以认为文件已经全部读进了内存,然后把它当成一个非常大的数组来访问了。将文件的一段区域映射到内存中,比传统的文件处理速度要快很多。内存映射文件它虽然最终也是要从磁盘读取数据,但是它并不需要将数据读取到 OS 内核缓冲区,而是直接将进程的用户私有地址空间中的一部分区域与文件对象建立起映射关系,就好像直接从内存中读、写文件一样,速度当然快了。 向内存映射文件写入可能是危险的,只是改变数组的单个元素这样的简单操作,就可能会直接修改磁盘上的文件。修改数据与将数据保存到磁盘是没有分开的。 @@ -670,13 +672,7 @@ public class NIOClient { MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024); ``` -## NIO与IO对比 - -NIO 与普通 I/O 的区别主要有以下三点: -- NIO 是非阻塞的; -- NIO 面向块,I/O 面向流。 -- NIO有选择器,而I/O没有。 ## Path Java7中文件IO发生了很大的变化,专门引入了很多新的类来取代原来的 @@ -927,6 +923,205 @@ public class FilesDemo7 { paths:[a.txt, test2.txt, test.txt, test3.txt] ``` -# 参考资料 +# 二、AIO + +AIO(Asynchronous I/O)即异步输入/输出库是在 JDK 1.7 中引入的。虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。 + +对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行IO操作,IO操作本身是同步的。但是对AIO来说,则更加进了一步,它不是在 IO 准备好时再通知线程,而是在 IO 操作已经完成后,再给线程发出通知。因此 AIO 是不会阻塞的,此时我们的业务逻辑将变成一个**回调函数**,等待 IO 操作完成后,由系统自动触发。 + +## AsynchronousServerSocketChannel + +在 AIO Socket 编程中,服务端通道是 AsynchronousServerSocketChannel,该类主要有如下方法: + +- 提供了一个 open() 静态工厂 +- bind() 方法用于绑定服务端 IP 地址 + 端口号 +- accept() 用于接收用户连接请求 + +## AsynchronousSocketChannel + +在客户端使用的通道是 AsynchronousSocketChannel,这个通道处理除了提供 open 静态工厂方法外,还提供了read() 和 write() 方法。 + +## CompletionHandler + +在AIO编程中,发出一个事件(accept read write等)之后要指定事件处理类(回调函数),AIO 中的事件处理类是 CompletionHandler,这个接口定义了如下两个方法,分别在异步操作成功和失败时被回调: + +```java +void completed(V result,A attachment); +void failed(Throwable exc,A attachment); +``` + +## 示例 + +### 服务端 + +```java +package com.southeast.cn; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousServerSocketChannel; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.CompletionHandler; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class AIOEchoServer { + + public final static int PORT = 8001; + public final static String IP = "127.0.0.1"; + + private AsynchronousServerSocketChannel server = null; + + public AIOEchoServer(){ + try { + //同样是利用工厂方法产生一个通道,异步通道 AsynchronousServerSocketChannel + server = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(IP,PORT)); + } catch (IOException e) { + e.printStackTrace(); + } + } + + //使用这个通道(server)来进行客户端的接收和处理 + public void start(){ + System.out.println("Server listen on "+PORT); + + //注册事件和事件完成后的处理器,这个CompletionHandler就是事件完成后的处理器 + server.accept(null,new CompletionHandler(){ + + final ByteBuffer buffer = ByteBuffer.allocate(1024); + + // 回调函数 + @Override + public void completed(AsynchronousSocketChannel result,Object attachment) { + + System.out.println(Thread.currentThread().getName()); + Future writeResult = null; + + try{ + buffer.clear(); + result.read(buffer).get(100,TimeUnit.SECONDS); + + System.out.println("In server: "+ new String(buffer.array())); + + //将数据写回客户端 + buffer.flip(); + writeResult = result.write(buffer); + }catch(InterruptedException | ExecutionException | TimeoutException e){ + e.printStackTrace(); + }finally{ + server.accept(null,this); + try { + writeResult.get(); + result.close(); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + } + + // 回调函数 + @Override + public void failed(Throwable exc, Object attachment) { + System.out.println("failed:"+exc); + } + + }); + } + + public static void main(String[] args) { + new AIOEchoServer().start(); + while(true){ + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + +} +``` + + + +### 客户端 + +```java +package com.southeast.cn; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.CompletionHandler; + +public class AIOClient { + + public static void main(String[] args) throws IOException { + + final AsynchronousSocketChannel client = AsynchronousSocketChannel.open(); + + InetSocketAddress serverAddress = new InetSocketAddress("127.0.0.1",8001); -- https://juejin.im/post/5b4570cce51d451984695a9b \ No newline at end of file + CompletionHandler handler = new CompletionHandler(){ + + @Override + public void completed(Void result, Object attachment) { + client.write(ByteBuffer.wrap("Hello".getBytes()),null, + new CompletionHandler(){ + + @Override + public void completed(Integer result, + Object attachment) { + final ByteBuffer buffer = ByteBuffer.allocate(1024); + client.read(buffer,buffer,new CompletionHandler(){ + + @Override + public void completed(Integer result, + ByteBuffer attachment) { + buffer.flip(); + System.out.println(new String(buffer.array())); + try { + client.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void failed(Throwable exc, + ByteBuffer attachment) { + } + + }); + } + + @Override + public void failed(Throwable exc, Object attachment) { + } + + }); + } + + @Override + public void failed(Throwable exc, Object attachment) { + } + + }; + + client.connect(serverAddress, null, handler); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + +} +``` \ No newline at end of file diff --git a/docs/JavaIO/8_AIO.md b/docs/JavaIO/8_AIO.md new file mode 100644 index 00000000..f8e8666a --- /dev/null +++ b/docs/JavaIO/8_AIO.md @@ -0,0 +1,205 @@ +# AIO + +AIO(Asynchronous I/O)即异步输入/输出库是在 JDK 1.7 中引入的。虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。 + +对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行IO操作,IO操作本身是同步的。但是对AIO来说,则更加进了一步,它不是在 IO 准备好时再通知线程,而是在 IO 操作已经完成后,再给线程发出通知。因此 AIO 是不会阻塞的,此时我们的业务逻辑将变成一个**回调函数**,等待 IO 操作完成后,由系统自动触发。 + +## AsynchronousServerSocketChannel + +在 AIO Socket 编程中,服务端通道是 AsynchronousServerSocketChannel,该类主要有如下方法: + +- 提供了一个 open() 静态工厂 +- bind() 方法用于绑定服务端 IP 地址 + 端口号 +- accept() 用于接收用户连接请求 + +## AsynchronousSocketChannel + +在客户端使用的通道是 AsynchronousSocketChannel,这个通道处理除了提供 open 静态工厂方法外,还提供了read() 和 write() 方法。 + +## CompletionHandler + +在AIO编程中,发出一个事件(accept read write等)之后要指定事件处理类(回调函数),AIO 中的事件处理类是 CompletionHandler,这个接口定义了如下两个方法,分别在异步操作成功和失败时被回调: + +```java +void completed(V result,A attachment); +void failed(Throwable exc,A attachment); +``` + +## 示例 + +### 服务端 + +```java +package com.southeast.cn; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousServerSocketChannel; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.CompletionHandler; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class AIOEchoServer { + + public final static int PORT = 8001; + public final static String IP = "127.0.0.1"; + + private AsynchronousServerSocketChannel server = null; + + public AIOEchoServer(){ + try { + //同样是利用工厂方法产生一个通道,异步通道 AsynchronousServerSocketChannel + server = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(IP,PORT)); + } catch (IOException e) { + e.printStackTrace(); + } + } + + //使用这个通道(server)来进行客户端的接收和处理 + public void start(){ + System.out.println("Server listen on "+PORT); + + //注册事件和事件完成后的处理器,这个CompletionHandler就是事件完成后的处理器 + server.accept(null,new CompletionHandler(){ + + final ByteBuffer buffer = ByteBuffer.allocate(1024); + + // 回调函数 + @Override + public void completed(AsynchronousSocketChannel result,Object attachment) { + + System.out.println(Thread.currentThread().getName()); + Future writeResult = null; + + try{ + buffer.clear(); + result.read(buffer).get(100,TimeUnit.SECONDS); + + System.out.println("In server: "+ new String(buffer.array())); + + //将数据写回客户端 + buffer.flip(); + writeResult = result.write(buffer); + }catch(InterruptedException | ExecutionException | TimeoutException e){ + e.printStackTrace(); + }finally{ + server.accept(null,this); + try { + writeResult.get(); + result.close(); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + } + + // 回调函数 + @Override + public void failed(Throwable exc, Object attachment) { + System.out.println("failed:"+exc); + } + + }); + } + + public static void main(String[] args) { + new AIOEchoServer().start(); + while(true){ + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + +} +``` + + + +### 客户端 + +```java +package com.southeast.cn; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.CompletionHandler; + +public class AIOClient { + + public static void main(String[] args) throws IOException { + + final AsynchronousSocketChannel client = AsynchronousSocketChannel.open(); + + InetSocketAddress serverAddress = new InetSocketAddress("127.0.0.1",8001); + + CompletionHandler handler = new CompletionHandler(){ + + @Override + public void completed(Void result, Object attachment) { + client.write(ByteBuffer.wrap("Hello".getBytes()),null, + new CompletionHandler(){ + + @Override + public void completed(Integer result, + Object attachment) { + final ByteBuffer buffer = ByteBuffer.allocate(1024); + client.read(buffer,buffer,new CompletionHandler(){ + + @Override + public void completed(Integer result, + ByteBuffer attachment) { + buffer.flip(); + System.out.println(new String(buffer.array())); + try { + client.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void failed(Throwable exc, + ByteBuffer attachment) { + } + + }); + } + + @Override + public void failed(Throwable exc, Object attachment) { + } + + }); + } + + @Override + public void failed(Throwable exc, Object attachment) { + } + + }; + + client.connect(serverAddress, null, handler); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + +} +``` + + + diff --git "a/docs/JavaIO/8_JavaIO\346\226\271\345\274\217.md" "b/docs/JavaIO/8_JavaIO\346\226\271\345\274\217.md" index 1524bd76..5ba72d74 100644 --- "a/docs/JavaIO/8_JavaIO\346\226\271\345\274\217.md" +++ "b/docs/JavaIO/8_JavaIO\346\226\271\345\274\217.md" @@ -1,34 +1,23 @@ -# JavaIO 方式 +# Java I/O 方式 -## 同步、异步和阻塞、非阻塞 - -- 同步、异步是消息通知机制 -- 阻塞、非阻塞是**等待通知时的状态** - -## Java 3 种 IO 方式 +## BIO & NIO & AIO Java IO 的方式通常分为阻塞的 BIO(Blocking IO)、同步非阻塞的 NIO(New IO) 和异步非阻塞的 AIO(Asynchronous IO)。 JDK1.4 之前只支持 BIO,JDK1.4 以后开始支持 NIO,JDK1.7 开始支持 AIO。 - - ### 1. BIO BIO 是同步阻塞的。服务器的模式为**一个连接一个线程**。 客户端有连接请求时,就需要启动一个线程进行处理。如果这个连接不做任何事情,就会造成不必要的开销,可以通过[线程池机制](https://duhouan.github.io/Java/#/Java_Concurrency/8_%E7%BA%BF%E7%A8%8B%E6%B1%A0)改善。 - - ### 2. NIO NIO 是同步非阻塞的。服务器模式为**一个请求一个线程**。 NIO 最重要的地方是当一个连接建立后,不需要对应一个线程,这个连接会被注册到多路复用器上面,所以所有的连接只需要一个线程即可,当这个线程中的多路复用器进行轮询时,发现连接上有请求的话,才开启一个线程进行处理。 - - ### 3. AIO AIO 是异步阻塞的。服务器模式为**一个有效请求一个线程**。 diff --git "a/docs/Java_Concurrency/1_\345\237\272\347\241\200\347\237\245\350\257\206.md" "b/docs/Java_Concurrency/1_\345\237\272\347\241\200\347\237\245\350\257\206.md" index 5a066dab..4850e65d 100644 --- "a/docs/Java_Concurrency/1_\345\237\272\347\241\200\347\237\245\350\257\206.md" +++ "b/docs/Java_Concurrency/1_\345\237\272\347\241\200\347\237\245\350\257\206.md" @@ -1,54 +1,52 @@ -# 基础知识 +# 一、进程和线程 -## 一、进程和线程 +## 进程 -### 1. 进程 +进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。在 Java 中,当启动 main 方法时其实就是启动了一个 JVM 的进程,而 main 方法所在的线程就是这个进程中的一个线程,称为**主线程**。 -进程是**资源分配的基本单位**。 +进程是**资源分配的基本单位**。每一个进程都有它自己的内存空间和系统资源。 -进程控制块 (Process Control Block, PCB) 描述进程的基本信息和运行状态,所谓的创建进程和撤销进程,都是指对 PCB 的操作。 +## 线程 -下图显示了 4 个程序创建了 4 个进程,这 4 个进程可以并发地执行。 +线程与进程相似,但线程是一个比进程更小的**执行单位**,是进程中的单个顺序控制流,是一条执行路径。 -![img](https://gitee.com/duhouan/ImagePro/raw/master/java-notes/os/a6ac2b08-3861-4e85-baa8-382287bfee9f.png) +一个进程如果只有一条执行路径,则称为单线程程序;一个进程如果有多条执行路径,则称为多线程程序。 -### 2. 线程 +一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享进程的**堆**和**方法区**资源,但每个线程有自己的**程序计数器**、**虚拟机栈**和**本地方法栈**,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为**轻量级进程**。 -**线程是 CPU 调度的基本单位**。 +Java 程序是多线程程序,这是因为 JVM 启动至少了主线程和垃圾回收线程。 -一个进程中可以有多个线程,它们共享进程资源。 - -QQ 和浏览器是两个进程,浏览器进程里面有很多线程,例如 HTTP 请求线程、事件响应线程、渲染线程等等,线程的并发执行使得在浏览器中点击一个新链接从而发起 HTTP 请求时,浏览器还可以响应用户的其它事件。 - -![img](https://gitee.com/duhouan/ImagePro/raw/master/java-notes/os/3cd630ea-017c-488d-ad1d-732b4efeddf5.png) - -### 3. 进程与线程的区别 +## 进程和线程的区别 - **资源** - 进程是资源调度的基本单位。 + 一个进程中可以有多个线程,多个线程共享进程的**堆**和**方法区**资源,但是每个线程有自己的**程序计数器**、**虚拟机栈** 和**本地方法栈**。 - 线程不占有任何资源。 +- **是否独立** -- **调度** + 线程是进程划分成的更小的运行单位。线程和进程最大的不同在于基本上各进程是独立的,而各线程则不一定,因为同一进程中的线程极有可能会相互影响。 - 线程是独立调度的基本单位。 +- **开销** - 同一进程中,线程的切换不会引起进程切换,但一个进程中线程切换到另一个进程中的线程时,就会引起进程切换了。 + 线程执行开销小,但不利于资源的管理和保护,而进程正相反。 -- **系统开销** +> 思考 1:程序计数器线程私有? - 进程的创建和撤销所付出的开销比线程的创建和撤销要大很多。 +程序计数器是主要有以下两个作用: - 类似的,进程切换的开销也比线程切换的开销要大很多,因为进程切换时,涉及到**当前执行进程的 CPU 环境的保存**和**新调度进程 CPU 环境的设置**,而线程切换时只需要设置和保存少量的寄存器内容,开销很小。 +- 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。 +- 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。 -- **通信** +所以,程序计数器私有主要是为了**线程切换后能恢复到正确的执行位置**。 - 线程可以通过**直接读写同一进程中的数据**进行通信。 +> 思考 2 :Java 虚拟机栈、本地方法栈线程私有? - 进程之间的通信需要借助 IPC (InterProcess Communication)。 +- Java 虚拟机栈中每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息。从方法调用直至执行完成的过程,就对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。 +- 本地方法栈和虚拟机栈所发挥的作用非常相似,它们之间的区别只不过是本地方法栈为本地方法服务。 -### 4. 多进程与多线程 +所以,为了**保证线程中的局部变量不被别的线程访问到**,Java 虚拟机栈和本地方法栈是线程私有的。 + +## 多进程与多线程 多进程的目的是**提高 CPU 的使用率**。 @@ -56,49 +54,125 @@ QQ 和浏览器是两个进程,浏览器进程里面有很多线程,例如 H 多线程共享同一进程资源(堆内存和方法区),但是栈内存是独立的,JVM 中一个线程对应一个线程栈。多线程抢夺的是 CPU 资源,一个时间点上只有一个线程运行。 -### 5. 并发与并行 +## 上下文切换 + +线程在执行过程中会有自己的运行条件和状态,即上下文,比如程序计数器,栈信息等。当出现以下几种情况的时,线程会从占用 CPU 状态中退出: + +- 主动让出 CPU,比如调用了 `sleep()`, `wait()` 等。 +- 时间片用完,因为操作系统要防止一个线程或者进程长时间占用 CPU 导致其他线程或者进程饿死。 +- 调用了阻塞类型的系统中断,比如请求 IO,线程被阻塞。 +- 被终止或结束运行 -- 并发:一个处理器同时处理多个任务,是**逻辑上的同时发生**。 +其中前三种都会发生线程切换,线程切换意味着需要保存当前线程的上下文,留待线程下次占用 CPU 的时候恢复现场,并加载下一个将要占用 CPU 的线程上下文。这就是**上下文切换**。 -- 并行:多个处理器或者多核处理器同时处理多个不同的任务,是**物理上的同时发生**。 +上下文切换是现代操作系统的基本功能,因其每次需要保存信息恢复信息,这将会占用 CPU,内存等系统资源进行处理,也就意味着效率会有一定损耗,如果频繁切换就会造成整体效率低下。 -来个比喻:并发是一个人同时吃三馒头,而并行就是三个人同时吃三个馒头。 +## 并发与并行 -下图反映了一个包含 8 个操作的任务在一个有两核心的 CPU 中创建四个线程运行的情况。 +并发:一个处理器同时处理多个任务,是**逻辑上的同时发生**。 -假设每个核心有两个线程,那么每个CPU中两个线程会交替并发,两个CPU之间的操作会并行运算。 +并行:多个处理器或者多核处理器同时处理多个不同的任务,是**物理上的同时发生**。 + +下图反映了一个包含 8 个操作的任务在一个有两核心的 CPU 中创建四个线程运行的情况。 假设每个核心有两个线程,那么每个CPU中两个线程会交替并发,两个CPU之间的操作会并行运算。 单就一个 CPU 而言两个线程可以解决线程阻塞造成的不流畅问题,其本身运行效率并没有提高, 多 CPU 的并行运算才真正解决了运行效率问题,这也正是并发和并行的区别。 -
+
+ + + +## 多线程的好处 + +- **从计算机底层来说:** 线程可以比作是**轻量级的进程**,是程序执行的最小单位,线程间的切换和调度的成本远远小于进程。另外,多核 CPU 时代意味着多个线程可以同时运行,这减少了线程上下文切换的开销。 +- **从当代互联网发展趋势来说:** 现在的系统动不动就要求百万级甚至千万级的并发量,而多线程并发编程正是开发高并发系统的基础,利用好多线程机制可以大大提高系统整体的并发能力以及性能。 + -## 二、创建线程的几种方式 -### 1. 三种创建线程的方式 +## 多线程带来的问题 + +并发编程的目的就是为了能提高程序的执行效率,从而提高程序运行速度,但是并发编程并不总是能提高程序运行速度的,而且并发编程可能会遇到很多问题,比如:**内存泄漏**、**死锁**、**线程不安全**等等。 + + + +# 二、创建线程 + +## 三种创建线程的方式 Java 有 3 种创建线程的方式: - 继承 Thread 类,重写 run() 方法 +- 实现 Runnable 接口,重写 run() 方法 +- 实现 Callable 接口,重写 call() 方法 - ```java - public class MyThread extends Thread{ - private void attack() { - System.out.println("Fight"); - System.out.println("Current Thread is : " + Thread.currentThread().getName()); - } - - @Override - public void run() { //重写 run() 方法 - attack(); - } - } - ``` +### 继承 Thread 类 -- 实现 Runnable 接口,重写 run() 方法 +需要实现 run() 方法,因为 Thread 类也实现了 Runable 接口。 -- 实现 Callable 接口,重写 call() 方法 +当调用 start() 方法启动一个线程时,虚拟机会将该线程放入就绪队列中等待被调度,当一个线程被调度时会执行该线程的 run() 方法。 -### 2. 选择 +```java +public class MyThread extends Thread { + public void run() { + // ... + } +} +``` + +```java +public static void main(String[] args) { + MyThread mt = new MyThread(); + mt.start(); +} +``` + +### 实现 Runnable 接口 + +需要实现接口中的 run() 方法。使用 Runnable 实例再创建一个 Thread 实例,然后调用 Thread 实例的 start() 方法来启动线程。 + +```java +public class MyRunnable implements Runnable { + @Override + public void run() { + // ... + } +} +``` + +```java +public static void main(String[] args) { + MyRunnable instance = new MyRunnable(); + Thread thread = new Thread(instance); + thread.start(); +} +``` + + + +### 实现 Callable 接口 + +需要实现接口中的 call() 方法。 call() 方法有返回值,返回值通过 FutureTask 进行封装。 + +```java +public class MyCallable implements Callable { + public Integer call() { + return 1; + } +} +``` + +```java +public static void main(String[] args) throws ExecutionException, InterruptedException { + MyCallable mc = new MyCallable(); + FutureTask ft = new FutureTask<>(mc); // 返回值通过 FutureTask 进行封装 + Thread thread = new Thread(ft); + thread.start(); + System.out.println(ft.get()); +} +``` + + + +## 选择线程创建方式 实现接口会更好一些: @@ -118,7 +192,7 @@ Java 有 3 种创建线程的方式: 注意:一个线程只能 start 一次,如果多次 start,会出现 `java.lang.IllegalException`。 -### 3. Runnable 和 Callable 的区别 +## Runnable 和 Callable 的区别 - Callable 的实现方法是 **call()** @@ -132,9 +206,9 @@ Java 有 3 种创建线程的方式: Runnable 的 run() 方法不能抛出异常 -### 4. 实现处理线程的返回值 +## 实现处理线程的返回值 -#### 方式一:主线程等待法 +### 方式一:主线程等待法 如果未获取到值,则主线程等待,一直到获取值为止。 @@ -168,7 +242,7 @@ public class CycleWait implements Runnable{ } ``` -#### 方式二:join() +### 方式二:join() 使用 Thread 的 join() **阻塞当前线程以等待子线程处理完毕**。 @@ -200,7 +274,7 @@ public class CycleWait2 implements Runnable{ } ``` -#### 方式三:Callable + FutureTask +### 方式三:Callable + FutureTask 实现 Callablee 接口,使用 FutureTask 接收 call() 方法返回的数据。 @@ -233,7 +307,7 @@ public class CycleWait3 implements Callable{ } ``` -#### 方式四:线程池 + Future +### 方式四:线程池 + Future 线程池执行任务,返回的结果使用 Future 接收。 @@ -269,67 +343,11 @@ public class CycleWait4 implements Callable{ } ``` -## 三、线程的几种状态 - -![img](https://gitee.com/duhouan/ImagePro/raw/master/pics/concurrent/96706658-b3f8-4f32-8eb3-dcb7fc8d5381.jpg) - -### 新建(New) - -创建后尚未启动。 - -### 可运行(Runnable) - -可能正在运行,也可能正在等待 CPU 时间片。 - -包含了操作系统线程状态中的 Running 和 Ready。 - -调用 `start()` 方法后开始运行,线程这时候处于 Ready 状态。可运行状态的线程获得了 CPU 时间片后就进入Running 状态。 - -### 阻塞(Blocked) - -等待获取一个**排它锁**,如果其他线程释放了锁就会结束此状态。 - -### 无限期等待(Waiting) - -等待其它线程显式地唤醒,否则不会被分配 CPU 时间片。 - -| 进入方法 | 退出方法 | -| ------------------------------------------ | ------------------------------------ | -| 没有设置 Timeout 参数的 Object.wait() 方法 | Object.notify() / Object.notifyAll() | -| 没有设置 Timeout 参数的 Thread.join() 方法 | 被调用的线程执行完毕 | -| LockSupport.park() 方法 | LockSupport.unpark(Thread) | - -### *限期等待(Timed Waiting) - -无需等待其它线程显式地唤醒,在一定时间之后会被系统自动唤醒。 - -调用 Thread.sleep() 方法使线程进入限期等待状态时,常常用“使一个线程睡眠”进行描述。 - -调用 Object.wait() 方法使线程进入限期等待或者无限期等待时,常常用“挂起一个线程”进行描述。 -**睡眠和挂起是用来描述行为,而阻塞和等待用来描述状态**。 -| 进入方法 | 退出方法 | -| ---------------------------------------- | ----------------------------------------------- | -| Thread.sleep() 方法 | 时间结束 | -| 设置了 Timeout 参数的 Object.wait() 方法 | 时间结束 / Object.notify() / Object.notifyAll() | -| 设置了 Timeout 参数的 Thread.join() 方法 | 时间结束 / 被调用的线程执行完毕 | -| LockSupport.parkNanos() 方法 | LockSupport.unpark(Thread) | -| LockSupport.parkUntil() 方法 | LockSupport.unpark(Thread) | +# 三、基础线程机制 -**阻塞和等待的区别**在于: - -阻塞是被动的,它是在等待获取一个排它锁。 - -等待是主动的,通过调用 Thread.sleep() 和 Object.wait() 等方法进入。 - -### 死亡(Terminated) - -可以是线程完成任务后自己结束,或者产生了异常而结束。 - -## 四、基础线程机制 - -### Executor +## Executor Executor 管理多个异步任务的执行,而无需程序员显式地管理线程的生命周期。这里的异步是指**多个任务的执行互不干扰,不需要进行同步操作**。 @@ -349,7 +367,7 @@ public static void main(String[] args) { } ``` -### Daemon +## Daemon Java 中有两类线程: @@ -367,7 +385,7 @@ public static void main(String[] args) { } ``` -### start() 和 run() +## start() & run() - start() @@ -381,7 +399,7 @@ public static void main(String[] args) { 单独调用 run() 不会启动新线程 -### sleep() 和 wait() +## sleep() & wait() sleep() 和 wait() 的区别: @@ -403,7 +421,7 @@ sleep() 和 wait() 的区别: sleep() 可在任何地方使用 -### yield() 和 wait() +## yield() & wait() Thread.yield() 声明了当前线程已经完成了生命周期中最重要的部分,可以切换给其它线程来执行。该方法只是对线程调度器的一个建议,而且也只是建议具有相同优先级的其它线程可以运行。 @@ -421,7 +439,7 @@ yield() 和 wait() 的区别: wait() 会释放同步锁,使得其他线程可以使用同步块或者同步方法 -### notify() 和 notifyAll() +## notify() & notifyAll() 锁池:线程 A 已经拥有某个对象的锁,线程 B、C 想要调用这个对象的同步方法,此时线程 B、C 就会被阻塞,进入一个地方去等待锁的释放,这个地方就是该对象的锁池。 @@ -431,20 +449,98 @@ yield() 和 wait() 的区别: -## 五、终止线程的几种方式 +# 四、线程生命周期和状态 + +## 6 种线程状态 -### 1. 退出标志 +### 新建(New) + +线程被创建,但是还没有调用 start() 方法 + +### 可运行(Runnable) + +可能正在运行,也可能正在等待 CPU 时间片。 + +包含了操作系统线程状态中的运行状态(Running)和就绪状态( Ready)。 + +调用 start() 方法后开始运行,线程这时候处于 Ready 状态。Ready 状态的线程获得了 CPU 时间片后就进入Running 状态。 + +### 阻塞(Blocked) + +等待获取一个**排它锁**,如果其他线程释放了锁就会结束此状态。 + +### 无限期等待(Waiting) + +等待其它线程显式地唤醒,否则不会被分配 CPU 时间片。 + +| 进入方法 | 退出方法 | +| ------------------------------------------ | ------------------------------------ | +| 没有设置 Timeout 参数的 Object.wait() 方法 | Object.notify() / Object.notifyAll() | +| 没有设置 Timeout 参数的 Thread.join() 方法 | 被调用的线程执行完毕 | +| LockSupport.park() 方法 | LockSupport.unpark(Thread) | + +### 限期等待(Timed Waiting) + +无需等待其它线程显式地唤醒,在一定时间之后会被系统自动唤醒。 + +| 进入方法 | 退出方法 | +| ---------------------------------------- | ----------------------------------------------- | +| Thread.sleep() 方法 | 时间结束 | +| 设置了 Timeout 参数的 Object.wait() 方法 | 时间结束 / Object.notify() / Object.notifyAll() | +| 设置了 Timeout 参数的 Thread.join() 方法 | 时间结束 / 被调用的线程执行完毕 | +| LockSupport.parkNanos() 方法 | LockSupport.unpark(Thread) | +| LockSupport.parkUntil() 方法 | LockSupport.unpark(Thread) | + +调用 Thread.sleep() 方法使线程进入限期等待状态时,常常用“使一个线程睡眠”进行描述。 + +调用 Object.wait() 方法使线程进入限期等待或者无限期等待时,常常用“挂起一个线程”进行描述。 + +**睡眠和挂起是用来描述行为,而阻塞和等待用来描述状态**。 + +**阻塞和等待的区别**在于: + +阻塞是被动的,它是在等待获取一个排它锁。 + +等待是主动的,通过调用 Thread.sleep() 和 Object.wait() 等方法进入。 + +### 死亡(Terminated) + +可以是线程完成任务后自己结束,或者产生了异常而结束。 + + + +## 线程生命周期 + +线程在生命周期中并不是固定处于某一个状态而是随着代码的执行在不同状态之间切换: + +
+ + + +线程创建之后它将处于NEW 状态,调用 `start()` 方法后开始运行,线程这时候处于 READY 状态。Ready 状态的线程获得了 CPU 时间片(timeslice)后就处于 RUNNING 状态。 + +当线程执行 `wait()`方法之后,线程进入 WAITING 状态。进入 WAITING 状态的线程需要依靠其他线程的通知才能够返回到运行状态,而 TIME_WAITING 状态相当于在等待状态的基础上增加了超时限制,比如通过 `sleep(long millis)`方法或 `wait(long millis)`方法可以将 Java 线程置于 TIMED WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。 + +当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 BLOCKED 状态。 + +线程在执行 Runnable 的`run()`方法之后将会进入到 TERMINATE 状态。 + + + +# 五、终止线程的几种方式 + +## 1. 退出标志 使用退出标志,使线程正常退出,也就是 run 方法完成后线程终止。 需要 while() 循环以某种特定的条件下退出,最直接的方法就是设计一个 boolean 类型的标志,并且通过设置这个标志来控制循环是否退出。 一般需要加上 volatile 来保证标志的可见性。 -### 2. Thread.stop() +## 2. Thread.stop() Thread.stop() 强制终止线程(不建议使用)。 -### 3. interrupt() 中断线程 +## 3. interrupt() 中断线程 - 线程处于阻塞状态,使用 interrupt() 则会抛出 InteruptedException 异常。 @@ -517,4 +613,5 @@ t1 (RUNNABLE) loop 2 t1 (TIMED_WAITING) is interrupted. t1 (RUNNABLE) catch InterruptedException. t1 (TERMINATED) is interrupted now. -``` \ No newline at end of file +``` + diff --git "a/docs/Java_Concurrency/2_\345\271\266\345\217\221\347\220\206\350\256\272.md" "b/docs/Java_Concurrency/2_\345\271\266\345\217\221\347\220\206\350\256\272.md" index 65864941..e354e7ad 100644 --- "a/docs/Java_Concurrency/2_\345\271\266\345\217\221\347\220\206\350\256\272.md" +++ "b/docs/Java_Concurrency/2_\345\271\266\345\217\221\347\220\206\350\256\272.md" @@ -6,123 +6,57 @@ Java 内存模型(即 Java Memory Model,简称 JMM)试图屏蔽各种硬 本身是一种**抽象的概念,并不真实存在**,它描述的是一组规则或规范。 -### 1. 主内存与工作内存 +### CPU 缓存 -处理器上的寄存器的读写的速度比内存快几个数量级,为了解决这种速度矛盾,在它们之间加入了高速缓存。 +**CPU 缓存的是内存数据用于解决 CPU 处理速度和内存不匹配的问题**。 -加入高速缓存带来了一个新的问题:缓存一致性。如果多个缓存共享同一块主内存区域,那么多个缓存的数据可能会不一致,需要一些协议来解决这个问题。 +通常情况下,当一个 CPU 需要读取主存数据时,它会将主存的数据读到 CPU 缓存中,甚至可能将缓存中的部分内容读到它的内部寄存器中,然后在寄存器中执行操作。 -![img](https://gitee.com/duhouan/ImagePro/raw/master/pics/concurrent/d2a12961-2b36-4463-b017-ca46a3308b8e.png) +当 CPU 需要将结果写回到主存中去时,它会将内部寄存器的值刷新到缓存中,然后在某个时间点将值刷新回主存。 -JMM 规定所有的变量都存储在主内存中,每个线程还有自己的工作内存,**工作内存存储在高速缓存或者寄存器**中,保存了该线程使用的**变量的主内存副本拷贝**。 +
-**线程只能直接操作工作内存中的变量,不同线程之间的变量值传递需要通过主内存来完成**。 - -![img](https://gitee.com/duhouan/ImagePro/raw/master/pics/concurrent/8162aebb-8fd2-4620-b771-e65751ba7e41.png) - -### 2. JMM 三大特性 - -> **内存间的交互操作** - -Java 内存模型定义了 8 个操作来完成主内存和工作内存的交互操作。 - -![img](https://gitee.com/duhouan/ImagePro/raw/master/pics/concurrent/b6a7e8af-91bf-44b2-8874-ccc6d9d52afc.jpg) +加入 CPU 缓存带来了一些新的问题: -- read:把一个变量的值从主内存传输到工作内存中 -- load:在 read 之后执行,把 read 得到的值放入工作内存的变量副本中 -- use:把工作内存中一个变量的值传递给执行引擎 -- assign:把一个从执行引擎接收到的值赋给工作内存的变量 -- store:把工作内存的一个变量的值传送到主内存中 -- write:在 store 之后执行,把 store 得到的值放入主内存的变量中 -- lock:作用于主内存的变量 -- unlock +- 缓存一致性问题:当多个处理器的运算任务都涉及同一块主内存区域时,将可能导致各自的缓存数据不一致的情况,如果真的发生这种情况,需要各个处理器访问缓存时都遵循一些协议,在读写时要根据协议来进行操作,这类协议有 MSI、MESI 等。 +- 指令重排序问题: 为了使得处理器内部的运算单元能尽量被充分利用,处理器可能会对输入代码进行**乱序执行(Out-Of-Order Execution)优化**,处理器会在计算之后将乱序执行的结果重组,保证该结果与顺序执行的结果是一致的,但并不保证程序中各个语句计算的先后顺序与输入代码中的顺序一致。 -#### 特性一:原子性 +### 主内存与工作内存 -**Java 内存模型保证了 read、load、use、assign、store、write、lock 和 unlock 操作具有原子性**。 +JMM 规定线程之间的共享变量存放在主内存(主内存就是硬件内存)中,每个线程还有自己的工作内存,存放该线程读/写共享变量的拷贝副本,**工作内存存储在 CPU 高速缓存或者寄存器中**。 -例如对一个 int 类型的变量执行 assign 赋值操作,这个操作就是原子性的。但是 Java 内存模型允许虚拟机将没有被 volatile 修饰的 64 位数据(long,double)的读写操作划分为两次 32 位的操作来进行,即 load、store、read 和 write 操作可以不具备原子性。 - -有一个错误认识就是,int 等原子性的类型在多线程环境中不会出现线程安全问题。前面的线程不安全示例代码中,cnt 属于 int 类型变量,1000 个线程对它进行自增操作之后,得到的值为 997 而不是 1000。 - -为了方便讨论,将内存间的交互操作简化为 3 个:load、assign、store。 +**线程只能直接操作工作内存中的变量,不同线程之间的变量值传递需要通过主内存来完成**。 -下图演示了两个线程同时对 cnt 进行操作,load、assign、store 这一系列操作整体上看不具备原子性,那么在 T1 修改 cnt 并且还没有将修改后的值写入主内存,T2 依然可以读入旧值。可以看出,这两个线程虽然执行了两次自增运算,但是主内存中 cnt 的值最后为 1 而不是 2。因此对 int 类型读写操作满足原子性只是说明 load、assign、store 这些单个操作具备原子性。 +
-![img](https://gitee.com/duhouan/ImagePro/raw/master/pics/concurrent/847b9ba1-b3cd-4e52-aa72-dee0222ae09f.jpg) +### 主内存和工作内存的交互 -AtomicInteger 能保证多个线程修改的原子性。 +JMM 定义了 8 个操作来完成主内存和工作内存的交互操作。 -![img](https://gitee.com/duhouan/ImagePro/raw/master/pics/concurrent/3144015c-dcfb-47ac-94a5-bab3b78b0f14.jpg) +
-使用 AtomicInteger 重写之前线程不安全的代码之后得到以下线程安全实现: +- lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。 +- unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。 +- read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用 +- load(载入):作用于工作内存的变量,它把 read 操作从主内存中得到的变量值放入工作内存的变量副本中。 +- use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。 +- assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。 +- store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的 write 的操作。 +- write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。 -```java -public class AtomicExample { - private AtomicInteger cnt = new AtomicInteger(); - public void add() { - cnt.incrementAndGet(); - } - public int get() { - return cnt.get(); - } -}Copy to clipboardErrorCopied -public static void main(String[] args) throws InterruptedException { - final int threadSize = 1000; - AtomicExample example = new AtomicExample(); // 只修改这条语句 - final CountDownLatch countDownLatch = new CountDownLatch(threadSize); - ExecutorService executorService = Executors.newCachedThreadPool(); - for (int i = 0; i < threadSize; i++) { - executorService.execute(() -> { - example.add(); - countDownLatch.countDown(); - }); - } - countDownLatch.await(); - executorService.shutdown(); - System.out.println(example.get()); -}Copy to clipboardErrorCopied -1000Copy to clipboardErrorCopied -``` +### 内存模型三大特性 -除了使用原子类之外,也可以使用 synchronized 互斥锁来保证操作的原子性。它对应的内存间交互操作为:lock 和 unlock,在虚拟机实现上对应的字节码指令为 monitorenter 和 monitorexit。 +#### 原子性 -```java -public class AtomicSynchronizedExample { - private int cnt = 0; +原子性指一次的操作或多次操作,要么所有的操作全部都得到执行并且不会受到任何因素的干扰而中断,要么所有的操作都执行,要么都不执行。 - public synchronized void add() { - cnt++; - } +**JMM 保证了 read、load、use、assign、store、write、lock 和 unlock 操作具有原子性**。例如对一个 int 类型的变量执行 assign 赋值操作,这个操作就是原子性的。但是 JMM 允许虚拟机将没有被 volatile 修饰的 64 位数据(long,double)的读写操作划分为两次 32 位的操作来进行,即 load、store、read 和 write 操作可以不具备原子性。 - public synchronized int get() { - return cnt; - } -}Copy to clipboardErrorCopied -public static void main(String[] args) throws InterruptedException { - final int threadSize = 1000; - AtomicSynchronizedExample example = new AtomicSynchronizedExample(); - final CountDownLatch countDownLatch = new CountDownLatch(threadSize); - ExecutorService executorService = Executors.newCachedThreadPool(); - for (int i = 0; i < threadSize; i++) { - executorService.execute(() -> { - example.add(); - countDownLatch.countDown(); - }); - } - countDownLatch.await(); - executorService.shutdown(); - System.out.println(example.get()); -} -``` +AtomicInteger 等原子操作类能保证多个线程修改的原子性。除了使用原子操作类之外,也可以使用 synchronized 互斥锁来保证操作的原子性。它对应的内存间交互操作为:lock 和 unlock,在虚拟机实现上对应的字节码指令为 monitorenter 和 monitorexit。 -```java -1000 -``` - -#### 特性二:可见性 +#### 可见性 可见性指当一个线程修改了**共享变量的值**,其它线程能够立即得知这个修改。 @@ -134,7 +68,7 @@ public static void main(String[] args) throws InterruptedException { 注意: volatile 并不能保证操作的原子性。 -#### 特性三:有序性 +#### 有序性 有序性是指在一个线程内观察,所有的操作都是有序的。在一个线程观察另一个线程,所有操作都是无序的,**无序是因为发生了指令重排序**。 @@ -145,67 +79,26 @@ volatile 和 synchronized 都可保证有序性: - volatile 关键字通过添加**内存屏障**的方式来禁止指令重排,即重排序时不能把后面的指令放到内存屏障之前。 - synchronized 来保证有序性,它**保证每个时刻只有一个线程执行同步代码**,相当于是让线程顺序执行同步代码。 -### 3. 先行发生原则 - -#### 规则一:单一线程原则 - -> Single Thread rule - -在一个线程内,在程序前面的操作先行发生于后面的操作。 - -![img](https://gitee.com/duhouan/ImagePro/raw/master/pics/concurrent/94414cd3-5db9-4aca-a2af-539140955c62.jpg) - -#### 规则二:管程锁定规则 - -一个 unlock 操作先行发生于后面对同一个锁的 lock 操作。 - -![img](https://gitee.com/duhouan/ImagePro/raw/master/pics/concurrent/de9d8133-4c98-4e07-b39c-302e162784ea.jpg) - -#### 规则三:volatile 变量规则 - -> Volatile Variable Rule - -对一个 volatile 变量的写操作先行发生于后面对这个变量的读操作。 - -![img](https://gitee.com/duhouan/ImagePro/raw/master/pics/concurrent/5e6e05d6-1028-4f5c-b9bd-1a40b90d6070.jpg) - -#### 规则四:线程启动规则 - -> Thread Start Rule - -Thread 对象的 start() 方法调用先行发生于此线程的每一个动作。 - -![img](https://gitee.com/duhouan/ImagePro/raw/master/pics/concurrent/bc5826f5-014d-47b4-9a76-d86b80968643.jpg) - -#### 规则五:线程加入规则 - -> Thread Join Rule - -Thread 对象的结束先行发生于 join() 方法返回。 - -![img](https://gitee.com/duhouan/ImagePro/raw/master/pics/concurrent/54e6d499-80df-488e-aa7e-081766c41538.jpg) - -#### 规则六:线程中断规则 - -> Thread Interruption Rule - -对线程 interrupt() 方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过 interrupted() 方法检测到是否有中断发生。 - -#### 规则七:对象终结规则 +### 先行发生原则 -> Finalizer Rule +除了使用 volatile 和 synchronized 来保证有序性之外,JVM 还规定了先行发生原则,让一个操作无需控制就能先于另一个操作完成。 -一个对象的初始化完成(构造函数执行结束)先行发生于它的 finalize() 方法的开始。 +- 单一线程原则(Single Thread Rule):在一个线程内,在程序前面的操作先行发生于后面的操作。 +- 管程锁定规则(Monitor Lock Rule):一个 unlock 操作先行发生于后面对同一个锁的 lock 操作。 +- volatile 变量规则(Volatile Variable Rule):对一个 volatile 变量的写操作先行发生于后面对这个变量的读操作。 +- 线程启动规则(Thread Start Rule):Thread 对象的 start() 方法调用先行发生于此线程的每一个动作。 +- 线程加入规则(Thread Join Rule):Thread 对象的结束先行发生于 join() 方法返回。 +- 线程中断规则(Thread Interruption Rule):对线程 interrupt() 方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过 interrupted() 方法检测到是否有中断发生。 +- 对象终结规则(Finalizer Rule):一个对象的初始化完成(构造函数执行结束)先行发生于它的 finalize() 方法的开始。 +- 传递性(Transitivity):如果操作 A 先行发生于操作 B,操作 B 先行发生于操作 C,那么操作 A 先行发生于操作 C。 -#### 规则八:传递性 +如果两个操作之间的关系不在此列,并且无法从以上规则中推导出来的话,则它们就没有顺序性保障,虚拟机可以对它们随意地进行重排序。 -> Transitivity -如果操作 A 先行发生于操作 B,操作 B 先行发生于操作 C,那么操作 A 先行发生于操作 C。 -## 乐观锁和悲观锁 +## 乐观锁和悲观锁思想 -### 1. 悲观锁 +### 悲观锁 互斥同步最主要的问题就是线程阻塞和唤醒所带来的性能问题,因此这种同步也称为阻塞同步。 @@ -213,13 +106,13 @@ Thread 对象的结束先行发生于 join() 方法返回。 Java 中 **synchronized** 和 **ReentrantLock** 等独占锁就是悲观锁思想的实现。 -### 2. 乐观锁 +### 乐观锁 乐观锁基于**冲突检测**的乐观并发策略:**先进行操作,如果没有其他线程争用共享数据,操作成功;如果数据存在竞争,就采用补偿措施(常见的有不断重试,直到成功)**。这种乐观的并发策略的许多实现是不需要将线程挂起的,因此这种同步操作称为**非阻塞同步**。 -### 3. CAS +### CAS -乐观锁需要**操作**和**冲突检测**这两个步骤具备原子性,这里就不能再使用互斥同步来保证了,只能靠硬件来完成。、 +乐观锁需要**操作**和**冲突检测**这两个步骤具备原子性,这里就不能再使用互斥同步来保证了,只能靠**硬件**来完成。 硬件支持的原子性操作最典型的是:CAS(Compare-and-Swap)。 diff --git "a/docs/Java_Concurrency/3_\345\271\266\345\217\221\345\205\263\351\224\256\345\255\227.md" "b/docs/Java_Concurrency/3_\345\271\266\345\217\221\345\205\263\351\224\256\345\255\227.md" index e017026c..b5f51931 100644 --- "a/docs/Java_Concurrency/3_\345\271\266\345\217\221\345\205\263\351\224\256\345\255\227.md" +++ "b/docs/Java_Concurrency/3_\345\271\266\345\217\221\345\205\263\351\224\256\345\255\227.md" @@ -139,7 +139,7 @@ public class SynchronizedDemo { } ``` -
+
任意一个对象都拥有自己的 Monitor,当这个对象由同步块或者同步方法调用时, 执行方法的线程必须先获取该对象的 Monitor 才能进入同步块和同步方法, 如果没有获取到 Monitor 的线程将会被阻塞在同步块和同步方法的入口处,进入到 BLOCKED 状态。 @@ -159,12 +159,7 @@ synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令 JDK 1.6 之后对 synchronized 进行优化。 -锁的 4 种状态: - -- 无锁 -- 偏向锁 -- 轻量级锁 -- 重量级锁 +锁的 4 种状态:无锁、偏向锁、轻量级锁、重量级锁。 #### 1. 自旋锁 @@ -236,4 +231,7 @@ public static String concatString(String s1, String s2, String s3) { - volatile 修饰的变量不会被编译器优化 - synchronized 修饰的变量可以被编译器优化 \ No newline at end of file + synchronized 修饰的变量可以被编译器优化 + + + diff --git "a/docs/Java_Concurrency/4_Lock \344\275\223\347\263\273.md" "b/docs/Java_Concurrency/4_Lock \344\275\223\347\263\273.md" index aba74653..12de88f0 100644 --- "a/docs/Java_Concurrency/4_Lock \344\275\223\347\263\273.md" +++ "b/docs/Java_Concurrency/4_Lock \344\275\223\347\263\273.md" @@ -1,5 +1,27 @@ # Lock 体系 +## AQS + +AQS(AbtsractQueueSynchronized) 即同步队列器。 + +AQS 是一个**抽象类**,本身并没有实现任何同步接口的,只是通过提供**同步状态的获取和释放**来供自定义的同步组件使用。 + +AQS 的实现依赖内部的双向队列(底层是双向链表),称为同步队列。 + +如果当前线程获取同步状态失败,则会将该线程以及等待状态等信息封装为 Node,将其**加入同步队列的尾部,同时阻塞当前线程**,当同步状态释放时,唤醒队列的头结点。 + +AQS 使用 CAS 对该同步状态进行原子操作实现对其值的修改。 + +```java +private transient volatile Node head; //同步队列的头结点 +private transient volatile Node tail; //同步队列的尾结点 +private volatile int state; //同步状态。 +// state=0,表示同步状态可用 +// state=1,表示同步状态已被占用 +``` + +补充:[AQS详解](https://www.cnblogs.com/chengxiao/archive/2017/07/24/7141160.html) + ## Condition 条件对象 条件对象是**线程同步对象中的一种**,主要用来等待某种条件的发生,条件发生后,可以唤醒等待在该条件上的一个线程或者所有线程。 @@ -11,61 +33,51 @@ ReentrantLock lock = new ReentrantLock(); Condition condition = lock.newCondition(); ``` +Condition 中有 awai()、signal()、signalAll() 方法,分别对应 Object 中放入 wait()、notify()、notifyAll() 方法,其实 Condition 也有上述三种方法,改变方法名称是为了避免使用上语义的混淆。 +补充:[Condition 详解](https://blog.csdn.net/qq_38293564/article/details/80554516) -注意: -- Condition 中有 await、signal、signalAll ,分别对应 Object 中放入 wait、notify、notifyAll 方法,其实 Condition 也有上述三种方法,改变方法名称是为了避免使用上语义的混淆。 - await 和 signal / signalAll 方法就像一个开关控制着线程 A(等待方)和线程 B(通知方)。 +## 可重入 - ![img](https://gitee.com/duhouan/ImagePro/raw/master/pics/blog/aqs_10.png) +某个线程试图获取一个已经由该线程持有的锁,那么这个请求就会成功。“重入”意味着获取的锁的操作的粒度是“线程”而不是“调用”。比如一个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可锁重入的话,就会造成死锁。 - 线程 awaitThread 先通过 lock.lock() 方法获取锁成功后调用了 condition.await 方法进入**等待队列**, +重入的一种实现方法是:为每个锁关联一个**计数器**(方便解锁)和一个**所有者线程**(知道是哪个线程是可重入的),同一个线程每次获取锁,锁的计数器都自增 1,所以要等到锁的计数器下降为 0 时才能释放锁。 - 另一个线程 signalThread 通过 lock.lock() 方法获取锁成功后调用了 **condition.signal / signalAll, 使得线程 awaitThread 能够有机会移入到同步队列**中, 当其他线程释放 Lock 后使得线程 awaitThread 能够有机会获取 Lock, 从而使得线程 awaitThread 能够从 await 方法中退出,然后执行后续操作。 如果 awaitThread 获取 Lock 失败会直接进入到同步队列。 -- 一个 Lock 可以与多个 Condition 对象绑定。 -## AQS +## 公平锁与非公平锁 -AQS(AbtsractQueueSynchronized) 即同步队列器。 +公平锁是指多个线程在等待同一个锁时,**按照申请锁的顺序来依次获取锁**。 -AQS 是一个抽象类,本身并没有实现任何同步接口的,只是通过提供**同步状态的获取和释放**来供自定义的同步组件使用。 +### 公平锁 -AQS 的实现依赖内部的双向队列(底层是双向链表)。 +- 公平锁每次获取到锁为同步队列中的第一个节点,保证请求资源时间上的绝对顺序。 -如果当前线程获取同步状态失败,则会将该线程以及等待状态等信息封装为 Node,将其**加入同步队列的尾部,同时阻塞当前线程**,当同步状态释放时,唤醒队列的头结点。 +- 公平锁为了保证时间上的绝对顺序,需要频繁的上下文切换,性能较差 -```java -private transient volatile Node head; //同步队列的头结点 -private transient volatile Node tail; //同步队列的尾结点 -private volatile int state; //同步状态。 -// state=0,表示同步状态可用;state=1,表示同步状态已被占用 -``` +### 非公平锁 -## 可重入 +- 非公平锁有可能刚释放锁的线程下次继续获取该锁,则有可能导致其他线程永远无法获取到锁,造成 “饥饿” 现象。 -某个线程试图获取一个已经有该线程持有的锁,那么这个请求就会成功。“重入”意味着获取的锁的操作的粒度是“线程”而不是“调用”。重入的一种实现方法是,为每个锁关联一个**计数器**(方便解锁)和一个**所有者线程**(知道是哪个线程是可重入的)。 +- 非公平锁会**降低一定的上下文切换**,降低性能开销。**ReentrantLock 默认选择的是非公平锁**。 -## 公平锁与非公平锁 - -公平锁是指多个线程在等待同一个锁时,**按照申请锁的顺序来依次获取锁**。 -| 公平锁 | 非公平锁 | -| :----------------------------------------------------------: | :----------------------------------------------------------: | -| 公平锁每次获取到锁为同步队列中的第一个节点,
保证请求资源时间上的绝对顺序 | 非公平锁有可能刚释放锁的线程下次继续获取该锁,
则有可能导致其他线程永远无法获取到锁,造成“饥饿”现象。 | -| 公平锁为了保证时间上的绝对顺序,
需要频繁的上下文切换 | 非公平锁会**降低一定的上下文切换**,降低性能开销
因此,ReentrantLock 默认选择的是非公平锁 | ## 独占锁和共享锁 -独占锁模式下,每次只能有一个线程能持有锁,ReentrantLock 就是以独占方式实现的互斥锁。 +### 独占锁 + +- 独占锁模式下,每次只能有一个线程能持有锁,ReentrantLock 就是以独占方式实现的互斥锁。 +- 独占锁是一种悲观保守的加锁策略,它**避免了读/读冲突**,如果某个只读线程获取锁,则其他读线程都只能等待,这种情况下就限制了不必要的并发性,因为读操作并不会影响数据的一致性。 + +### 共享锁 -共享锁,则允许多个线程同时获取锁,并发访问共享资源,如:ReadWriteLock。 +- 共享锁允许多个线程同时获取锁,并发访问共享资源,如:ReadWriteLock。 +- 共享锁则是一种乐观锁,它放宽了加锁策略,允许多个执行读操作的线程同时访问共享资源。 -很显然,独占锁是一种悲观保守的加锁策略,它避免了读/读冲突,如果某个只读线程获取锁,则其他读线程都只能等待,这种情况下就限制了不必要的并发性,因为读操作并不会影响数据的一致性。 -共享锁则是一种乐观锁,它放宽了加锁策略,允许多个执行读操作的线程同时访问共享资源。 ## ReentrantLock @@ -85,11 +97,11 @@ static final class NonfairSync extends Sync { } ``` -- Sync 是一个继承 AQS 的抽象类,并发控制就是通过 Sync 实现的。 +- Sync 是一个继承 AQS 的抽象类,并重写了 tryRelease() 方法,并发控制就是通过 Sync 实现的。 - 重写了 tryRelease() , 有两个子类 FiarSync 和 NonfairSync,即公平锁和非公平锁。 + 此外 Sync 还有两个子类 FiarSync 和 NonfairSync,即公平锁和非公平锁。 -- 由于 Sync 重写 tryRealese() 方法,并且 FairSync 和 NonfairSync没有再次重写该方法,所以 **公平锁和非公平锁释放锁的操作是一样的**,即**唤醒等待队列中第一个被挂起的线程**。 +- 由于 Sync 重写 tryRealese() 方法,并且 FairSync 和 NonfairSync 没有再次重写该方法,所以**公平锁和非公平锁释放锁的操作是一样的**,即**唤醒等待队列中第一个被挂起的线程**。 - 公平锁和非公平锁获取锁的方式是不同的。 @@ -97,17 +109,19 @@ static final class NonfairSync extends Sync { 非公平锁获取锁的方式是一种**抢占式**的,不考虑线程等待时间,无论是哪个线程获取了锁,则其他线程就进入等待队列。 -```java -private final Sync sync; - -public ReentrantLock() { //默认是非公平锁 - sync = new NonfairSync(); -} +- ReentrantLock 默认选择的是非公平锁。 -public ReentrantLock(boolean fair) { //可设置为公平锁 - sync = fair ? new FairSync() : new NonfairSync(); -} -``` + ```java + private final Sync sync; + + public ReentrantLock() { // 默认是非公平锁 + sync = new NonfairSync(); + } + + public ReentrantLock(boolean fair) { // 可设置为公平锁 + sync = fair ? new FairSync() : new NonfairSync(); + } + ``` ## ReentrantLock 与 synchronized 的区别 @@ -137,17 +151,15 @@ public ReentrantLock(boolean fair) { //可设置为公平锁 ## LockSupport -LockSupport 位于 java.util.concurrent.locks 包下。 LockSupprot 是线程的**阻塞原语**,用来**阻塞线程**和**唤醒线程**。 - -每个使用 LockSupport 的线程都会与一个许可关联, +LockSupport 位于 java.util.concurrent.locks 包下。 -如果该许可可用,并且可在线程中使用,则调用 park() 将会立即返回,否则可能阻塞。 +LockSupprot 是线程的**阻塞原语**,用来**阻塞线程**和**唤醒线程**。 -如果许可尚不可用,则可以调用 unpark 使其可用。 +每个使用 LockSupport 的线程都会与一个许可关联, 如果该许可可用,并且可在线程中使用,则调用 park() 将会立即返回,否则可能阻塞。 如果许可尚不可用,则可以调用 unpark() 方法使其可用。 但是注意**许可不可重入**,也就是说只能调用一次 park() 方法,否则会一直阻塞。 -### LockSupport 中方法 +### 成员方法 | 方法 | 说明 | | :-------------------------------------------: | :----------------------------------------------------------: | @@ -163,40 +175,39 @@ LockSupport 位于 java.util.concurrent.locks 包下。 LockSupprot 是线程的 - 调用 park() 方法 dump 线程: -``` +```html "main" #1 prio=5 os_prio=0 tid=0x02cdcc00 nid=0x2b48 waiting on condition [0x00d6f000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:304) at learn.LockSupportDemo.main(LockSupportDemo.java:7) -Copy to clipboardErrorCopied ``` - 调用 park(Object blocker) 方法 dump 线程: -```java +```html "main" #1 prio=5 os_prio=0 tid=0x0069cc00 nid=0x6c0 waiting on condition [0x00dcf000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x048c2d18> (a java.lang.String) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) - at learn.LockSupportDemo.main(LockSupportDemo.java:7)Copy to clipboardErrorCopied + at learn.LockSupportDemo.main(LockSupportDemo.java:7) ``` 通过分别调用这两个方法然后 dump 线程信息可以看出, 带 Object 的 park 方法相较于无参的 park 方法会增加 ```java -- parking to wait for <0x048c2d18> (a java.lang.String)Copy to clipboardErrorCopied +- parking to wait for <0x048c2d18> (a java.lang.String) ``` 这种信息就类似于记录“案发现场”,有助于工程人员能够迅速发现问题解决问题。 注意: -- synchronized 使线程阻塞,线程会进入到 BLOCKED 状态 -- 调用 LockSupprt 方法阻塞线程会使线程进入到 WAITING 状态 +- synchronized 使线程阻塞,线程会进入到 Blocked 状态(阻塞状态) +- 调用 LockSupport 方法阻塞线程会使线程进入到 Waiting 状态(等待状态) -### LockSupport 使用示例 +### 使用示例 ```java import java.util.concurrent.locks.LockSupport; diff --git "a/docs/Java_Concurrency/5_\345\216\237\345\255\220\346\223\215\344\275\234\347\261\273.md" "b/docs/Java_Concurrency/5_\345\216\237\345\255\220\346\223\215\344\275\234\347\261\273.md" index 879713c3..a6a706d3 100644 --- "a/docs/Java_Concurrency/5_\345\216\237\345\255\220\346\223\215\344\275\234\347\261\273.md" +++ "b/docs/Java_Concurrency/5_\345\216\237\345\255\220\346\223\215\344\275\234\347\261\273.md" @@ -1,5 +1,7 @@ # 原子操作类 +## AtomicInteger 源码解析 + `java.util.concurrent.atomic`下的所有原子操作类都实现了 CAS。 AtomicInteger 内部维护一个变量 **Unsafe**: @@ -49,7 +51,31 @@ public final int getAndAddInt(Object var1, long var2, int var4) { } ``` -## 1. 原子更新基本类 +除了 CAS 操作外,AtomicInteger 还使用 volatile 和 native 方法来保证原子操作,从而避免 synchronized 的高开销,执行效率大为提升。 + +```java + +private static final Unsafe unsafe = Unsafe.getUnsafe(); +private static final long valueOffset; + +static { + try { + valueOffset = unsafe.objectFieldOffset + (AtomicInteger.class.getDeclaredField("value")); + } catch (Exception ex) { throw new Error(ex); } +} + +private volatile int value; +``` + +UnSafe 类的 objectFieldOffset() 方法是一个 native 方法,这个方法是用来拿到 “原来的值” 的内存地址,返回值是 valueOffset。 + +另外 value 是一个volatile变量,在内存中可见,因此 JVM 可以保证任何时刻任何线程总能拿到该变量的最新值。 + + + +## 原子更新基本类 + atomic 包提高原子更新基本类的工具类,如下: - AtomicBoolean @@ -133,7 +159,7 @@ public final boolean compareAndSet(boolean expect, boolean update) { ``` 可以看出,compareAndSet 方法的实际上也是先转换成 0,1 的整型变量,然后是通过针对 int 型变量的原子更新方法 compareAndSwapInt 来实现的。 -## 2. 原子更新数组 +## 原子更新数组 atomic 包下提供能原子更新数组中元素的类有: - AtomicIntegerArray @@ -170,7 +196,10 @@ public class AtomicIntegerArrayDemo { 2 //注意仍然返回原来的旧值 ``` -## 3. 原子更新引用类型 + + +## 原子更新引用类型 + 如果需要原子更新引用类型变量的话,为了保证线程安全,atomic 也提供了相关的类: - AtomicReference @@ -219,7 +248,10 @@ User{userName='b', age=2} ``` 首先将对象 user1 用 AtomicReference 进行封装,然后调用 getAndSet 方法,从结果可以看出,该方法会原子更新引用的 User 对象,变为`User{userName='b', age=2}`,返回的是原来的User 对象`User{userName='a', age=1}`。 -## 4. 原子更新字段类型 + + +## 原子更新字段类型 + 如果需要更新对象的某个字段,并在多线程的情况下,能够保证线程安全,atomic同样也提供了相应的原子操作类: - AtomicIntegeFieldUpdater diff --git "a/docs/Java_Concurrency/6_\345\271\266\345\217\221\345\256\271\345\231\250.md" "b/docs/Java_Concurrency/6_\345\271\266\345\217\221\345\256\271\345\231\250.md" index ceab71e6..910c97a6 100644 --- "a/docs/Java_Concurrency/6_\345\271\266\345\217\221\345\256\271\345\231\250.md" +++ "b/docs/Java_Concurrency/6_\345\271\266\345\217\221\345\256\271\345\231\250.md" @@ -113,7 +113,7 @@ final Segment[] segments; static final int DEFAULT_CONCURRENCY_LEVEL = 16; ``` -
+
### 2. size 操作 @@ -244,34 +244,3 @@ static final Node tabAt(Node[] tab, int i) { - JDK1.7的ConcurrentHashMap(分段锁)对整个桶数组进行了分割分段(Segment), 每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问度。 JDK 1.8 采用**数组+链表/红黑二叉树**的数据结构来实现,并发控制使用**synchronized和CAS**来操作。 - Hashtable:使用 synchronized 来保证线程安全,效率非常低下。 当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态, 如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈。 -Hashtable 全表锁 - -![img](https://gitee.com/duhouan/ImagePro/raw/master/java-notes/java/16_03.jpg) - -ConcurrentHashMap 分段锁 - -![img](https://gitee.com/duhouan/ImagePro/raw/master/java-notes/java/16_00.jpg) - -# 补充 - -## 红黑树性质 - -1. 每个节点或者是红色的,或者是黑色的 - -2. 根节点是黑色的 - -3. 每一个叶子节点(最后的空节点)是黑色的 - - > 这其实是一个定义 - -4. 如果一个节点是红色的,那么它们的孩子节点都是黑色的 - - > 因为红节点和其父亲节点表示一个3节点,黑色节点的右孩子一定是黑色节点。 - -5. 从任意一个节点到叶子节点经过的**黑色节点**都是一样的 - - > 因为2-3树是绝对平衡的 - -![img](https://gitee.com/duhouan/ImagePro/raw/master/java-notes/dataStructure/redBlackTree//12_11.png) - -相较于 AVL 树,**红黑树上的查找的操作的效率的低于 AVL 树的。但对于增删操作,红黑树的性能是更优的。** \ No newline at end of file diff --git "a/docs/Java_Concurrency/7_\345\271\266\345\217\221\345\267\245\345\205\267.md" "b/docs/Java_Concurrency/7_\345\271\266\345\217\221\345\267\245\345\205\267.md" index 9e7897e6..35812b70 100644 --- "a/docs/Java_Concurrency/7_\345\271\266\345\217\221\345\267\245\345\205\267.md" +++ "b/docs/Java_Concurrency/7_\345\271\266\345\217\221\345\267\245\345\205\267.md" @@ -1,22 +1,270 @@ -# 并发工具 +# 一、并发容器 + +## CopyOnWriteArrayList + +### 1. 读写分离 + +**写操作在一个复制的数组**上进行,**读操作还是在原数组**中进行,**读写分离**,互不影响。 + +写操作需要加锁,防止并发写入时导致写入数据丢失。 + +写操作结束之后需要把原数组指向新的复制数组。 + +```java +//写操作: +//通过过创建底层数组的新副本来实现的。 +//当 List 需要被修改的时候,并不修改原有内容,而是对原有数据进行一次复制,将修改的内容写入副本。 +//写完之后,把原数组指向新的复制数组。 +//这样可以保证写操作实在一个复制的数组上进行,而读操作还是在原数组中进行,不会影响读操作。 +public boolean add(E e) { + //加锁 + final ReentrantLock lock = this.lock; + lock.lock(); + try { + Object[] elements = getArray(); + int len = elements.length; + // newElements 是一个复制的数组 + Object[] newElements = Arrays.copyOf(elements, len + 1); + newElements[len] = e; + // 写操作在一个复制的数组上进行 + setArray(newElements); + return true; + } finally { + lock.unlock(); + } +} + +final void setArray(Object[] a) { + array = a; +} +``` + +```java +//读操作 +//读操作没有任何同步控制和锁操作, +//因为内部数组 array 不会被修改。 +private transient volatile Object[] array; + +public E get(int index) { + return get(getArray(), index); +} + +@SuppressWarnings("unchecked") +private E get(Object[] a, int index) { + return (E) a[index]; +} + +final Object[] getArray() { + return array; +} +``` + +### 2. 适用场景 + +CopyOnWriteArrayList 在写操作的同时允许读操作,大大提高了读操作的性能,很适合**读多写少**的应用场景。 + +CopyOnWriteArrayList 有其缺陷: + +- 内存占用:在写操作时需要复制一个新的数组,使得内存占用为原来的两倍左右; +- 数据不一致:读操作不能读取实时性的数据,因为部分写操作的数据还未同步到读数组中。 + +所以 CopyOnWriteArrayList 不适合**内存敏感**以及对**实时性要求很高**的场景。 + +## ConcurrentHashMap + +### 1. 存储结构 + +```java +static final class HashEntry { + final int hash; + final K key; + volatile V value; + volatile HashEntry next; +} +``` + +ConcurrentHashMap 采用了**分段锁**(Segment),每个分段锁维护着几个桶(HashEntry),**多个线程可以同时访问不同分段锁上的桶**, 从而使其并发度更高(并发度就是 Segment 的个数)。 + +```java +//Segment 继承自 ReentrantLock。 +static final class Segment extends ReentrantLock implements Serializable { + + private static final long serialVersionUID = 2249069246763182397L; + + static final int MAX_SCAN_RETRIES = + Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1; + + transient volatile HashEntry[] table; + + transient int count; + + transient int modCount; + + transient int threshold; + + final float loadFactor; +} +``` + +```java +final Segment[] segments; + +//默认的并发级别为 16,也就是说默认创建 16 个 Segment。 +static final int DEFAULT_CONCURRENCY_LEVEL = 16; +``` + +
+ +### 2. size 操作 + +每个 Segment 维护了一个 count 变量来统计该 Segment 中的键值对个数。 + +```java +/** + * The number of elements. Accessed only either within locks + * or among other volatile reads that maintain visibility. + */ +transient int count;Copy to clipboardErrorCopied +``` + +在执行 size 操作时,需要遍历所有 Segment 然后把 count 累计起来。 + +ConcurrentHashMap 在执行 size 操作时**先尝试不加锁,如果连续两次不加锁操作得到的结果一致,那么可以认为这个结果是正确的**。 + +尝试次数使用 RETRIES_BEFORE_LOCK 定义,该值为 2,retries 初始值为 -1,因此尝试次数为 3。 + +如果尝试的次数超过 3 次,就需要对每个 Segment 加锁。 + +```java +/** + * Number of unsynchronized retries in size and containsValue + * methods before resorting to locking. This is used to avoid + * unbounded retries if tables undergo continuous modification + * which would make it impossible to obtain an accurate result. + */ +static final int RETRIES_BEFORE_LOCK = 2; + +public int size() { + // Try a few times to get accurate count. On failure due to + // continuous async changes in table, resort to locking. + final Segment[] segments = this.segments; + int size; + boolean overflow; // true if size overflows 32 bits + long sum; // sum of modCounts + long last = 0L; // previous sum + int retries = -1; // first iteration isn't retry + try { + for (;;) { + // 超过尝试次数,则对每个 Segment 加锁 + if (retries++ == RETRIES_BEFORE_LOCK) { + for (int j = 0; j < segments.length; ++j) + ensureSegment(j).lock(); // force creation + } + sum = 0L; + size = 0; + overflow = false; + for (int j = 0; j < segments.length; ++j) { + Segment seg = segmentAt(segments, j); + if (seg != null) { + sum += seg.modCount; + int c = seg.count; + if (c < 0 || (size += c) < 0) + overflow = true; + } + } + // 连续两次得到的结果一致,则认为这个结果是正确的 + if (sum == last) + break; + last = sum; + } + } finally { + if (retries > RETRIES_BEFORE_LOCK) { + for (int j = 0; j < segments.length; ++j) + segmentAt(segments, j).unlock(); + } + } + return overflow ? Integer.MAX_VALUE : size; +}Copy to clipboardErrorCopied +``` + +### 3. JDK 1.8 的改动 + +ConcurrentHashMap 取消了 Segment 分段锁。 + +JDK 1.8 使用 **CAS 操作**来支持更高的并发度,在 CAS 操作失败时使用**内置锁 synchronized**。 + +数据结构与HashMap 1.8 的结构类似,数组+链表 / 红黑二叉树(链表长度 > 8 时,转换为红黑树 )。synchronized 只锁定当前**链表或红黑二叉树的首节点**,这样只要 Hash 值不冲突,就不会产生并发。 + +### 4. JDK 1.8 中的 put 方法 + +(1)hash 算法 + +```java +static final int spread(int h) { + return (h ^ (h >>> 16)) & HASH_BITS; +} +``` + +(2)定位索引位置 + +```java +i = (n - 1) & hash +``` + +(3)获取 table 中对应索引的元素 f + +```java +f = tabAt(tab, i = (n - 1) & hash +``` + +```java +// Unsafe.getObjectVolatile 获取 f +// 因为可以直接指定内存中的数据,保证了每次拿到的数据都是新的 +static final Node tabAt(Node[] tab, int i) { + return (Node)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE); +} +``` + +(4)如果 f 是 null,说明 table 中是第一次插入数据,利用 + +- 如果 CAS 成功,说明 Node 节点插入成功 +- 如果 CAS 失败,说明有其他线程提前插入了节点,**自旋重新尝试**在该位置插入 Node + +(5)其余情况把新的 Node 节点按链表或红黑树的方式插入到合适位置,这个过程采用内置锁实现并发。 + +### 5. 和 Hashtable 的区别 + +**底层数据结构:** + +- JDK1.7 的ConcurrentHashMap底层采用**分段的数组+链表**实现, JDK1.8 的ConcurrentHashMap底层采用的数据结构与JDK1.8 的HashMap的结构一样,**数组+链表/红黑二叉树**。 +- Hashtable和JDK1.8 之前的HashMap的底层数据结构类似都是采用**数组+链表**的形式, 数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的。 + +**实现线程安全的方式** + +- JDK1.7的ConcurrentHashMap(分段锁)对整个桶数组进行了分割分段(Segment), 每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问度。 JDK 1.8 采用**数组+链表/红黑二叉树**的数据结构来实现,并发控制使用**synchronized和CAS**来操作。 +- Hashtable:使用 synchronized 来保证线程安全,效率非常低下。 当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态, 如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈。 + + + +# 二、并发工具 ## J.U.C - AQS java.util.concurrent(J.U.C)大大提高了并发性能,AQS 被认为是 J.U.C 的核心。 -### 1. CountDownLatch +### CountDownLatch 用来控制一个或者多个线程等待多个线程。 维护了一个计数器 cnt,每次调用 countDown() 方法会让计数器的值减 1,减到 0 的时候,那些因为调用 await() 方法而在等待的线程就会被唤醒。 -![img](https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/ba078291-791e-4378-b6d1-ece76c2f0b14.png) +
```java public class CountdownLatchExample { public static void main(String[] args) throws InterruptedException { - final int totalThread = 10; + final int totalThread = 3; CountDownLatch countDownLatch = new CountDownLatch(totalThread); ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < totalThread; i++) { @@ -33,10 +281,10 @@ public class CountdownLatchExample { ``` ```html -run..run..run..run..run..run..run..run..run..run..end +run..run..run..end ``` -### 2. CyclicBarrier +### CyclicBarrier 用来控制多个线程互相等待,只有当多个线程都到达时,这些线程才会继续执行。 @@ -59,7 +307,7 @@ public CyclicBarrier(int parties) { } ``` -![img](https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/f71af66b-0d54-4399-a44b-f47b58321984.png) +
```java public class CyclicBarrierExample { @@ -88,7 +336,7 @@ public class CyclicBarrierExample { before..before..before..before..before..before..before..before..before..before..after..after..after..after..after..after..after..after..after..after.. ``` -### 3. Semaphore +### Semaphore Semaphore 类似于操作系统中的信号量,可以控制对互斥资源的访问线程数。 @@ -144,7 +392,7 @@ Thread: 8 Thread: 9 ``` -### 4. CountDownLatch 和 CyclicBarrier 比较 +### CountDownLatch 和 CyclicBarrier 比较 - **循环使用** @@ -172,7 +420,7 @@ Thread: 9 ## J.U.C - 其它组件 -### 1. FutureTask +### FutureTask 在介绍 Callable 时我们知道它可以有返回值,返回值通过 Future 进行封装。FutureTask 实现了 RunnableFuture 接口,该接口继承自 Runnable 和 Future 接口,这使得 FutureTask 既可以当做一个任务执行,也可以有返回值。 @@ -222,7 +470,7 @@ other task is running... 4950 ``` -### 2. BlockingQueue +### BlockingQueue java.util.concurrent.BlockingQueue 接口有以下阻塞队列的实现: @@ -299,7 +547,7 @@ consume... -### 3. ForkJoin +### ForkJoin 主要用于并行计算中,和 MapReduce 原理类似,都是把大的计算任务拆分成多个小任务并行计算。 diff --git "a/docs/Java_Concurrency/8_\347\272\277\347\250\213\346\261\240.md" "b/docs/Java_Concurrency/8_\347\272\277\347\250\213\346\261\240.md" index e8c38876..3dc260da 100644 --- "a/docs/Java_Concurrency/8_\347\272\277\347\250\213\346\261\240.md" +++ "b/docs/Java_Concurrency/8_\347\272\277\347\250\213\346\261\240.md" @@ -6,7 +6,7 @@ Executor 框架是在 Java5 中引入的,**通过该框架来控制线程的 Executor 基于**生产者-消费者模式**,**提交任务的线程相当于生产者,执行任务的线程相当于消费者**。同时,Executor 的实现还提供了对任务执行的生命周期管理的支持。 -![img](https://gitee.com/duhouan/ImagePro/raw/master/pics/concurrent/c_19.png) +
- Executor @@ -48,7 +48,7 @@ Eexcutor 框架由 3 大部分组成: 异步任务需要返回结果,提交任务后需要返回 Future, FutureTask 实现。 -
+
运行过程: @@ -190,7 +190,7 @@ private static ExecutorService executor = 状态转换图: -![img](https://gitee.com/duhouan/ImagePro/raw/master/pics/concurrent/c_22.png) +![img](https://github.com/DuHouAn/ImagePro/raw/master/pics/concurrent/c_22.png) shutdown() 和 shutdownNow() 这两个方法的原理都是**遍历线程池中所有的线程,然后依次中断线程**。 shutdown() 和 shutdownNow() 还是有不一样的地方: @@ -205,9 +205,9 @@ shutdown() 和 shutdownNow() 这两个方法的原理都是**遍历线程池中 #### 1. 任务的性质 -**CPU 密集型任务**配置尽可能少的线程数量,如配置 (N cpu) + 1 个线程的线程池。 +**CPU 密集型任务**(指利用 CPU 计算能力的任务,比如在内存中对大量数据进行排序)配置尽可能少的线程数量,如配置 (N cpu) + 1 个线程的线程池。 -**IO 密集型任务**则由于需要等待 IO 操作,线程并不是一直在执行任务,则配置尽可能多的线程,如 2 * (N cpu) +1。 +**IO 密集型任务**(比如网络读取、文件读取)则由于需要等待 IO 操作,线程并不是一直在执行任务,则配置尽可能多的线程,如 2 * (N cpu) +1。 **混合型的任务**,如果可以拆分,则将其拆分成一个 CPU 密集型任务和一个 IO 密集型任务,只要这两个任务执行的时间相差不是太大,那么分解后执行的吞吐率要高于串行执行的吞吐率,如果这两个任务执行时间相差太大,则没必要进行分解。 diff --git "a/docs/Java_Concurrency/9_\345\271\266\345\217\221\345\256\236\350\267\265.md" "b/docs/Java_Concurrency/9_\345\271\266\345\217\221\345\256\236\350\267\265.md" index ae614b5d..3707d113 100644 --- "a/docs/Java_Concurrency/9_\345\271\266\345\217\221\345\256\236\350\267\265.md" +++ "b/docs/Java_Concurrency/9_\345\271\266\345\217\221\345\256\236\350\267\265.md" @@ -1,6 +1,143 @@ -# 并发实践 +# 一、死锁 -## 线程不安全示例 +## 线程死锁 + +**死锁是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象**。 + +假设线程 A 持有资源 1,线程 B 持有资源 2,它们同时都想申请对方的资源,那么这两个线程就会互相等待而进入死锁状态。 + +
+ +使用 Java 代码模拟上述死锁场景: + +```java +public class Resources { + public static final Object resource1 = new Object(); // 资源 1 + public static final Object resource2 = new Object(); // 资源 2 +} +``` + +```java +public class ThreadA extends Thread{ + + @Override + public void run() { + synchronized (Resources.resource1) { + System.out.println(Thread.currentThread().getName() + " get Resource-1"); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println(Thread.currentThread().getName() + " waiting get Resource-2"); + synchronized (Resources.resource2) { + System.out.println(Thread.currentThread().getName() + " get Resource-2"); + } + } + } +} +``` + +```java +public class ThreadB extends Thread{ + + @Override + public void run() { + synchronized (Resources.resource2) { + System.out.println(Thread.currentThread().getName() + " get Resource-2"); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println(Thread.currentThread().getName() + " waiting get Resource-1"); + synchronized (Resources.resource1) { + System.out.println(Thread.currentThread().getName() + " get Resource-1"); + } + } + } +} +``` + +```java +public class DeadLockDemo { + public static void main(String[] args) { + Thread threadA=new ThreadA(); + threadA.setName("Thread A"); + threadA.start(); + + Thread threadB=new ThreadB(); + threadB.setName("Thread B"); + threadB.start(); + } +} +``` + +输出结果: + +```html +Thread A get Resource-1 +Thread B get Resource-2 +Thread B waiting get Resource-1 +Thread A waiting get Resource-2 +``` + +线程 A 通过 `synchronized (resource1)` 获得 `resource1` 的监视器锁,然后线程 A 休眠 1s,执行线程 B 获取到 resource2 的监视器锁。线程 A 和线程 B 休眠结束了都开始企图请求获取对方的资源,然后这两个线程就会陷入互相等待的状态,这也就产生了死锁。 + +上面的例子符合产生死锁的四个必要条件: + +- 互斥:每个资源要么已经分配给了一个进程,要么就是可用的。 +- 占有和等待:已经得到了某个资源的进程可以再请求新的资源。 +- 不可抢占:已经分配给一个进程的资源不能强制性地被抢占,它只能被占有它的进程显式地释放。 +- 环路等待:有两个或者两个以上的进程组成一条环路,该环路中的每个进程都在等待下一个进程所占有的资源。 + +## 死锁预防 + +在程序运行之前预防发生死锁。 + +- 破坏占有和等待条件:一种实现方式是规定所有进程在开始执行前请求所需要的全部资源。 +- 破坏不可抢占条件:占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。 +- 破坏环路等待条件:给资源统一编号,进程只能按编号顺序来请求资源。 + +我们对线程 B 的代码进行如下改造,就不会出现死锁了。 + +```JAVA +public class ThreadB extends Thread{ + + @Override + public void run() { + synchronized (Resources.resource1) { + System.out.println(Thread.currentThread().getName() + " get Resource-1"); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println(Thread.currentThread().getName() + " waiting get Resource-2"); + synchronized (Resources.resource1) { + System.out.println(Thread.currentThread().getName() + " get Resource-2"); + } + } + } +} +``` + +输出结果: + +```html +Thread A get Resource-1 +Thread A waiting get Resource-2 +Thread A get Resource-2 +Thread B get Resource-1 +Thread B waiting get Resource-2 +Thread B get Resource-2 +``` + +线程 A 首先获得到 resource1 的监视器锁,这时候线程 2 就获取不到了。然后线程 1 再去获取 resource2 的监视器锁,可以获取到。线程 1 释放了对 resource1、resource2 的监视器锁的占用,线程 2 获取到就可以执行了。这样就破坏了环路等待条件,因此避免了死锁。 + + + +# 二、线程不安全示例 如果多个线程对同一个共享数据进行访问而不采取同步操作的话,那么操作的结果是不一致的。 @@ -176,19 +313,15 @@ public static void main(String[] args) throws InterruptedException { 1000 ``` -## 线程安全 - -多个线程不管以何种方式访问某个类,并且在**主调代码中不需要进行同步**,都能表现正确的行为。 -Servlet 存在线程安全问题,原因如下: -Servlet 在 Tomcat 中是以**单例模式**存在的,也就是说,当客户端第一次请求 Servlet 时,Tomcat 会根据 web.xml 生成对应的 Servlet 实例,当客户端第二次请求的是同一个 Servlet,Tomcat 不会再生成新的TServlet。**不同请求共享同一 Servlet 实例的资源,从而导致线程不安全的问题**。 +# 三、实现线程安全方式 -多个线程不管以何种方式访问某个类,并且在主调代码中不需要进行同步,都能表现正确的行为。 +所谓线程安全就是说,多个线程不管以何种方式访问某个类,并且在**主调代码中不需要进行同步**,都能表现正确的行为。 -线程安全有以下几种实现方式: +线程安全有以下几种实现方式:不可变、互斥同步、非阻塞同步、无同步方案(栈封闭和线程本地存储) -### 方式一:不可变 +## 不可变 不可变(Immutable)的对象一定是线程安全的,不需要再采取任何的线程安全保障措施。只要一个不可变的对象被正确地构建出来,永远也不会看到它在多个线程之中处于不一致的状态。多线程环境下,应当尽量使对象成为不可变,来满足线程安全。 @@ -208,10 +341,13 @@ public class ImmutableExample { Map unmodifiableMap = Collections.unmodifiableMap(map); unmodifiableMap.put("a", 1); } -}Copy to clipboardErrorCopied +} +``` + +```html Exception in thread "main" java.lang.UnsupportedOperationException at java.util.Collections$UnmodifiableMap.put(Collections.java:1457) - at ImmutableExample.main(ImmutableExample.java:9)Copy to clipboardErrorCopied + at ImmutableExample.main(ImmutableExample.java:9) ``` Collections.unmodifiableXXX() 先对原始的集合进行拷贝,需要对集合进行修改的方法都直接抛出异常。 @@ -219,22 +355,40 @@ Collections.unmodifiableXXX() 先对原始的集合进行拷贝,需要对集 ```java public V put(K key, V value) { throw new UnsupportedOperationException(); -}Copy to clipboardErrorCopied +} ``` -### 方式二:互斥同步 +## 互斥同步 + +互斥同步最主要的问题就是线程阻塞和唤醒所带来的性能问题,因此这种同步也称为阻塞同步。 + +互斥同步属于一种悲观的并发策略:**总是认为只要不去做正确的同步措施,那就肯定会出现问题**。无论共享数据是否真的会出现竞争,它都要进行加锁。 + +Java 中 **synchronized** 和 **ReentrantLock** 等独占锁就是悲观锁思想的实现。 + +## 非阻塞同步 + +乐观锁基于**冲突检测**的乐观并发策略:**先进行操作,如果没有其他线程争用共享数据,操作成功;如果数据存在竞争,就采用补偿措施(常见的有不断重试,直到成功)**。这种乐观的并发策略的许多实现是不需要将线程挂起的,因此这种同步操作称为**非阻塞同步**。 + +### 1. CAS + +乐观锁需要**操作**和**冲突检测**这两个步骤具备原子性,这里就不能再使用互斥同步来保证了,只能靠硬件来完成。、 + +硬件支持的原子性操作最典型的是:CAS(Compare-and-Swap)。 + +当多个线程尝试使用 CAS 同时更新一个共享变量时,只有其中一个线程能够更新共享变量中的值,其他线程都失败,失败的线程不会被挂起,而是被告知在这次竞争中失败,并且可以再次尝试。 -synchronized 和 ReentrantLock。 +CAS 指令需要有 3 个操作数,分别是内存地址 V、旧的预期值 A 和新值 B。当执行操作时,只有当 V 的值等于 A,才将 V 的值更新为 B。 -### 方式三:非阻塞同步 +### 2. 原子操作类 -原子操作类(Unsafe 的 CAS 操作)。 +J.U.C 包里面的原子操作类的方法调用了 Unsafe 类的 CAS 操作。 -### 方式四:无同步方案 +## 无同步方案 要保证线程安全,并不是一定就要进行同步。如果一个方法本来就不涉及共享数据,那它自然就无须任何同步措施去保证正确性。 -#### 1. 栈封闭 +### 1. 栈封闭 **多个线程访问同一个方法的局部变量**时,不会出现线程安全问题,因为局部变量存储在虚拟机栈中,属于线程私有的。 @@ -247,27 +401,35 @@ public class StackClosedExample { } System.out.println(cnt); } -}Copy to clipboardErrorCopied +} +``` + +```java public static void main(String[] args) { StackClosedExample example = new StackClosedExample(); ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(() -> example.add100()); executorService.execute(() -> example.add100()); executorService.shutdown(); -}Copy to clipboardErrorCopied +} +``` + +```html +100 100 -100Copy to clipboardErrorCopied ``` -#### 2. 线程本地存储(Thread Local Storage) + + +### 2. 线程本地存储(Thread Local Storage) 如果一段代码中所需要的数据必须与其他代码共享,那就看看这些共享数据的代码是否能保证在同一个线程中执行。如果能保证,我们就可以把共享数据的可见范围限制在同一个线程之内,这样,无须同步也能保证线程之间不出现数据争用的问题。 符合这种特点的应用并不少见,大部分使用消费队列的架构模式(如“生产者-消费者”模式)都会将产品的消费过程尽量在一个线程中消费完。其中最重要的一个应用实例就是经典 Web 交互模型中的“一个请求对应一个服务器线程”(Thread-per-Request)的处理方式,这种处理方式的广泛应用使得很多 Web 服务端应用都可以使用线程本地存储来解决线程安全问题。 -可以使用 java.lang.ThreadLocal 类来实现线程本地存储功能。 +可以**使用 java.lang.ThreadLocal 类来实现线程本地存储功能**。如果创建一个 ThreadLocal 变量,那么访问这个变量的**每个线程都会有这个变量的一个副本**,在实际多线程操作的时候,操作的是**自己本地内存中的变量**,从而规避了线程安全问题。 -对于以下代码,thread1 中设置 threadLocal 为 1,而 thread2 设置 threadLocal 为 2。过了一段时间之后,thread1 读取 threadLocal 依然是 1,不受 thread2 的影响。 +#### ThreadLocal 示例 ```java public class ThreadLocalExample { @@ -290,54 +452,44 @@ public class ThreadLocalExample { thread1.start(); thread2.start(); } -}Copy to clipboardErrorCopied -1Copy to clipboardErrorCopied +} ``` -为了理解 ThreadLocal,先看以下代码: - -```java -public class ThreadLocalExample1 { - public static void main(String[] args) { - ThreadLocal threadLocal1 = new ThreadLocal(); - ThreadLocal threadLocal2 = new ThreadLocal(); - Thread thread1 = new Thread(() -> { - threadLocal1.set(1); - threadLocal2.set(1); - }); - Thread thread2 = new Thread(() -> { - threadLocal1.set(2); - threadLocal2.set(2); - }); - thread1.start(); - thread2.start(); - } -}Copy to clipboardErrorCopied +```html +1 ``` -它所对应的底层结构图为: +thread1 中设置 threadLocal 为 1,而 thread2 设置 threadLocal 为 2。过了一段时间之后,thread1 读取 threadLocal 依然是 1,不受 thread2 的影响。 -![img](https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/6782674c-1bfe-4879-af39-e9d722a95d39.png) +#### ThreadLocal 原理 每个 Thread 都有一个 ThreadLocal.ThreadLocalMap 对象。 ```java -/* ThreadLocal values pertaining to this thread. This map is maintained - * by the ThreadLocal class. */ -ThreadLocal.ThreadLocalMap threadLocals = null;Copy to clipboardErrorCopied +public class Thread implements Runnable { + + // 与此线程有关的 ThreadLocal 值,由 ThreadLocal 类维护 + ThreadLocal.ThreadLocalMap threadLocals = null; +} ``` -当调用一个 ThreadLocal 的 set(T value) 方法时,先得到当前线程的 ThreadLocalMap 对象,然后将 ThreadLocal->value 键值对插入到该 Map 中。 +默认情况下 threadLocals 变量值为 null,只有当前线程调用 ThreadLocal 类的 set() 或 get() 方法时才创建,实际上调用这两个方法的时候,我们调用的是ThreadLocalMap 类对应的 get()、set()方法。 + +当调用一个 ThreadLocal 的 set(T value) 方法时,先得到当前线程的 ThreadLocalMap 对象,然后将 `` 键值对插入到该 Map 中。**变量是存放在当前线程的 ThreadLocalMap 中,并不是存在 ThreadLocal 中**。 ```java public void set(T value) { Thread t = Thread.currentThread(); - ThreadLocalMap map = getMap(t); + ThreadLocalMap map = getMap(t); // 获取当前线程的 ThreadLocalMap 对象 if (map != null) map.set(this, value); else createMap(t, value); -}Copy to clipboardErrorCopied +} + +ThreadLocalMap getMap(Thread t) { + return t.threadLocals; +} ``` get() 方法类似。 @@ -345,7 +497,7 @@ get() 方法类似。 ```java public T get() { Thread t = Thread.currentThread(); - ThreadLocalMap map = getMap(t); + ThreadLocalMap map = getMap(t); // 获取当前线程的 ThreadLocalMap 对象 if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @@ -355,18 +507,63 @@ public T get() { } } return setInitialValue(); -}Copy to clipboardErrorCopied +} ``` -ThreadLocal 从理论上讲并不是用来解决多线程并发问题的,因为根本不存在多线程竞争。 +所以对于以下代码: -在一些场景 (尤其是使用线程池) 下,由于 ThreadLocal.ThreadLocalMap 的底层数据结构导致 ThreadLocal 有内存泄漏的情况,应该尽可能在每次使用 ThreadLocal 后手动调用 remove(),以避免出现 ThreadLocal 经典的内存泄漏甚至是造成自身业务混乱的风险。 +```java +public class ThreadLocalExample1 { + public static void main(String[] args) { + ThreadLocal threadLocal1 = new ThreadLocal(); + ThreadLocal threadLocal2 = new ThreadLocal(); + Thread thread1 = new Thread(() -> { + threadLocal1.set(1); + threadLocal2.set(1); + }); + Thread thread2 = new Thread(() -> { + threadLocal1.set(2); + threadLocal2.set(2); + }); + thread1.start(); + thread2.start(); + } +} +``` + +其对应的底层结构图为: + +
+ +由上图可以看出,ThreadLocal 从理论上讲并不是用来解决多线程并发问题的,因为根本不存在多线程竞争。 + +#### ThreadLocal 内存泄露问题 + +ThreadLocalMap 中使用的 **key 为 ThreadLocal 的弱引用,而 value 是强引用**。 + +```java +static class Entry extends WeakReference> { + /** The value associated with this ThreadLocal. */ + Object value; + + Entry(ThreadLocal k, Object v) { + super(k); + value = v; + } +} +``` + +如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候,key 会被清理掉,而 value 不会被清理掉。ThreadLocalMap 中就会出现 key 为 null 的 Entry,如果我们不做任何措施的话,value 永远无法被 GC 回收,这个时候就可能会产生内存泄露。 + +ThreadLocalMap 实现中已经考虑了这种情况,在调用 set()、get()、remove() 方法的时候,会清理掉 key 为 null 的记录。应该尽可能在每次使用 ThreadLocal 后手动调用 remove(),以避免出现 ThreadLocal 经典的内存泄漏甚至是造成自身业务混乱的风险。 -## 线程间通信 + + +# 四、线程间通信 当多个线程可以一起工作去解决某个问题时,如果某些部分必须在其它部分之前完成,那么就需要对线程进行协调。 -### join() +## join() 在线程中调用另一个线程的 join() 方法,会将当前线程挂起,而不是忙等待,直到目标线程结束。 @@ -422,7 +619,7 @@ B -### wait() notify() notifyAll() +## wait() & notify()/notifyAll() 调用 wait() 使得线程等待某个条件满足,线程在等待时会被挂起,当其他线程的运行使得这个条件满足时,其它线程会调用 notify() 或者 notifyAll() 来唤醒挂起的线程。 @@ -463,7 +660,7 @@ before after ``` -### await() signal() signalAll() +## await() & signal()/signalAll() java.util.concurrent 类库中提供了 Condition 类来实现线程之间的协调,可以在 Condition 上调用 await() 方法使线程等待,其它线程调用 signal() 或 signalAll() 方法唤醒等待的线程。 @@ -515,9 +712,9 @@ after -## 实现生产者和消费者 +# 五、实现生产者和消费者 -### 1. wait() / notifyAll() +## wait() & notifyAll() ```java public class ProducerConsumer { @@ -621,7 +818,7 @@ public class ProducerConsumer { -### 2. await() / sigalAll() +## await() & sigalAll() ```java public class ProducerConsumer { @@ -732,7 +929,7 @@ public class ProducerConsumer { -### 3. 阻塞队列 +## 阻塞队列 ```java public class ProducerConsumer { @@ -812,60 +1009,19 @@ public class ProducerConsumer { 消费者consumer正在消费数据-1171326759 ``` -## LeetCode 上多线程编程问题 - -[1114. 按序打印](https://leetcode-cn.com/problems/print-in-order/) - -我们提供了一个类: - -```java -public class Foo { - public void one() { print("one"); } - public void two() { print("two"); } - public void three() { print("three"); } -} -``` - -三个不同的线程将会共用一个 Foo 实例。 - -- 线程 A 将会调用 one() 方法 -- 线程 B 将会调用 two() 方法 -- 线程 C 将会调用 three() 方法 - -请设计修改程序,以确保 two() 方法在 one() 方法之后被执行,three() 方法在 two() 方法之后被执行。 - -示例 1: - -```html -输入: [1,2,3] -输出: "onetwothree" -解释: -有三个线程会被异步启动。 -输入 [1,2,3] 表示线程 A 将会调用 one() 方法,线程 B 将会调用 two() 方法,线程 C 将会调用 three() 方法。 -正确的输出是 "onetwothree"。 -``` - -示例 2: -```html -输入: [1,3,2] -输出: "onetwothree" -解释: -输入 [1,3,2] 表示线程 A 将会调用 one() 方法,线程 B 将会调用 three() 方法,线程 C 将会调用 two() 方法。 -正确的输出是 "onetwothree"。 -``` +# 六、多线程编程实战 -注意: +## LeetCode-按序打印 -尽管输入中的数字似乎暗示了顺序,但是我们并不保证线程在操作系统中的调度顺序。 +[1114. 按序打印](https://leetcode-cn.com/problems/print-in-order/) -你看到的输入格式主要是为了确保测试的全面性。 +参考代码: ```java import java.util.concurrent.CountDownLatch; -//解法一: class Foo { private CountDownLatch countDownLatchA; //等待 A 线程执行完 @@ -900,9 +1056,7 @@ class Foo { } ``` - - -## 多线程良好开发实践 +## 多线程良好开发习惯 - 给线程起个有意义的名字,这样可以方便找 Bug。 - 缩小同步范围,从而减少锁争用。例如对于 synchronized,应该尽量使用同步块而不是同步方法。 diff --git a/docs/Kafka&RabbitMQ/Kafka.md b/docs/Kafka&RabbitMQ/Kafka.md new file mode 100644 index 00000000..9d38cbb8 --- /dev/null +++ b/docs/Kafka&RabbitMQ/Kafka.md @@ -0,0 +1,331 @@ +# 一、Kafka 的相关概念 + +Kafka 是一种高吞吐、分布式、基于**发布订阅模型**的消息系统。Kafka 用于离线和在线消息的消费。主要有以下 3 个功能: + +- 消息队列:发布和订阅消息流 +- 容错的持久化方式存储记录消息流:Kafka 将消息数据按顺序保存在磁盘上,并在集群内以副本的形式存储以防止数据丢失 +- 流式处理平台:在消息发布的时候进行处理,Kafka 提供了一个完整的流式处理类库。 + +此外,Kafka 依赖 Zookeeper 进行集群的管理。 + +## Message + +消息(Message)是 Kafka 中**最基本的数据单元**。Kafka 消息由一个**定长的 Header 和变长的字节数组**组成。 + +## Broker + +Kafka 集群包含一个或多个服务器,这些服务器就被称为 Broker。 + +## Topic + +Kafka 根据主题(Topic)对消息进行归类,**发布到 Kafka 集群的每条消息(Message)都需要指定一个 Topic**。 + +## Partition + +Partition 即分区,每个 Topic 包含一个或多个分区。 + +消息发送时都被发送到一个 Topic 中,其本质就是一个目录,而 Topic 由是由一些 Partition Logs(分区日志)组成,其组织结构如下: + +
+ +**每个 Partition 中的消息都是有序的**,生产的消息被不断追加到 Partition Log 上,其中的每一个消息都被赋予了一个唯一的 offset 值,Kafka 通过 **offset 保证消息在分区内的顺序**,offset 的顺序性不跨分区,即 Kafka 只保证在同一个分区内的消息是有序的;同一 Topic 的多个分区内的消息,Kafka 并不保证其顺序性。 + +**Kafka 集群会保存所有的消息,不管消息有没有被消费**;可以设定消息的过期时间,只有过期的数据才会被自动清除以释放磁盘空间。比如设置消息过期时间为 2 天,那么这 2 天内的所有消息都会被保存到集群中,数据只有超过了 2 天才会被清除。 + +Kafka 需要维持的元数据只有一个,即消费消息在 Partition 中的 offset 值,Consumer 每消费一个消息,offset就会 +1。其实消息的状态完全是由 Consumer 控制的,**Consumer 可以跟踪和重设这个 offset 值,Consumer 就可以读取任意位置的消息**。 + +把消息日志以 Partition 的形式存放有多重考虑: + +- 第一,方便在集群中扩展,每个 Partition 可以通过调整以适应它所在的机器,而一个 Topic 又可以由多个Partition 组成,因此整个集群就可以适应任意大小的数据了; +- 第二,可以提高并发,因为是**以 Partition 为单位进行读写**,Partition 的个数对应了消费者个生产者的并发度,比如 Partition 的个数为 3,则集群中最多同时有 3 个线程的消费者并发处理数据。 + +注意:Partition 并不是越多越好的(Partition 的个数不能超过 Broker 结点),原因如下: + +- 分区越多,服务端和客户端需要使用的**内存**就越多。 +- 会降低一定的可用性。某个 Leader 挂了,相比较较少分区的情况,重新选出 Leader,花的时间就会更长。 + +## Replication + +Kafka 中每个 Partition 可以有多个副本(Replication),每个副本中包含的消息是一样的。 + +每个分区的副本集合中,都会选举出一个副本作为 Leader 副本,Kafka 在不同的场景下会采用不同的选举策略。**所有的读写请求都由选举出的 Leader 副本处理**,**Follower 副本仅仅是从 Leader 副本处把数据拉取(pull)到本地之后,同步更新到自己的 Log 中**。 + +一般情况下,同一分区的多个副本会被分配到不同的 Broker上。当 Leader 所在的 Broker 宕机之后,可以重新选举新的 Leader,继续对外提供服务。 + +
+ +## Producer + +消息生产者(Producer),向 Broker 发送消息的客户端。 + +**Producer 直接发送消息到 Broker上的 Leader Partition**,不需要经过任何中介或其他路由转发。 + +**Producer 客户端自己控制着消息被推送(push)到哪些 Partition**。Kafka 提供了接口供用户实现自定义的 Partition,用户可以为每个消息指定一个 Partition Key,通过这个 Key 来实现一些 Hash 分区算法。比如,把 userid 作为 Partition Key 的话,相同 userid 的消息将会被推送到同一个 Partition。**Producer 可以通过随机或者 Hash 等方式将消息平均发送到多个 Partition 上以实现负载均衡**。 + +### 批量发送消息 + +批量发送消息是提高吞吐量的重要方式。Kafka Producer 可以将消息在内存中累计到一定数量后作为一个**批量发送请求**。批量发送的数量大小可以通过Producer 的参数控制,参数值可以设置为累计的消息的数量(如 500 条)、累计的时间间隔(如 100ms )或者累计的数据大小(64 KB)。通过增加 Batch 的大小,可以减少网络请求和磁盘 I / O 的次数,当然具体参数设置需要在**效率**和**时效性**方面做一个权衡。 + +### 压缩消息 + +Producer 端可通过 gzip 或者 snappy 格式对消息集合进行压缩。消息在 Producer 端进行压缩,在 Consumer 端进行解压。压缩的好处是减少网络传输的数据量,减轻对网络带宽传输的压力。 + +### 异步并行发送消息 + +Producer 可以**异步地并行地**向 Kafka发送消息,但是通常 Producer 在发送完消息之后会得到一个 future 响应,返回的是 offset 值或者发送过程中遇到的错误。通过 request.required.acks 参数来设置 Leader Partition 收到确认的副本个数。ack 参数的具体说明如下: + +| ack | 说明 | +| :--: | :----------------------------------------------------------: | +| 0 | Producer **不会等待 Broker 的响应**
Producer 无法知道消息是否发送成功, 这样**可能会导致数据丢失**,但会得到最大的系统吞吐量。 | +| 1 | Producer 会在 **Leader Partition** 收到消息时得到 Broker 的一个确认
这样会有更好的可靠性,因为客户端会等待直到 Broker 确认收到消息。 | +| -1 | Producer 会在**所有备份的 Partition** 收到消息时得到 Broker 的确认
这个设置可以得到最高的可靠性保证。 | + +发布消息时,Kafka Client 先构造一条消息,将消息加入到消息集 set 中(Kafka支持批量发布,可以往消息集合中添加多条消息,一次行发布),send 消息时,Producer Client 需指定消息所属的 Topic。 + +## Consumer + +消息消费者(Consumer),从 Broker 读取消息的客户端。 + +消费者(Consumer)的主要工作是从 Topic 中拉取消息,并对消息进行消费。某个消费者消费到 Partition 的哪个位置(offset)的相关信息,是 Consumer 自己维护的。Consumer 可以自己决定如何读取 Kafka 中的数据。比如,Consumer 可以通过重设 offset 值来重新消费已消费过的数据。不管有没有被消费,Kafka 会保存数据一段时间,这个时间周期是可配置的,只有到了过期时间,Kafka 才会删除这些数据。 + +这样设计非常巧妙,**避免了 Kafka Server 端维护消费者消费位置的开销**,尤其是在消费数量较多的情况下。另一方面,如果是由 Kafka Server 端管理每个 Consumer 消费状态,一旦 Kafka Server 端出现延时或是消费状态丢失,将会影响大量的 Consumer。另一方面,这一设计也提高了 Consumer 的灵活性,Consumer 可以按照自己需要的顺序和模式拉取消息进行消费。例如:Consumer 可以通过修改其消费的位置实现针对某些特殊 key 的消息进行反复消费,或是跳过某些消息的需求。 + +Kafka 提供了两套 Consumer Api,分为 Simple Api 和 High-Level Api。 + +- Simple Api 是一个底层的 API,它维持了一个和单一 Broker 的连接,并且这个 API 是完全无状态的,每次请求都需要指定 offset 值,因此,这套 API 也是最灵活的。 + +- High-Level API 封装了对集群中一系列 Broker 的访问,可以透明的消费一个 Topic。它自己维持了已消费消息的状态,即每次消费的都是下一个消息。 + + High-Level API 还支持以组的形式消费 Topic,如果 Consumers 有同一个组名,那么 Kafka 就相当于一个队列消息服务,而各个 Consumer 均衡地消费相应 Partition 中的数据。若 Consumers 有不同的组名,那么此时 Kafka 就相当于一个广播服务,会把 Topic 中的所有消息广播到每个 Consumer。 + +
+ +## Consumer Group + +Consumer Group 即消费者组,多个 Consumer 可以组成一个 Consumer Group,一个 Consumer 只能属于一个 Consumer Group。**同一 Consumer Group 中的多个 Consumer 不能同时消费同一个 Partition 上的数据。**如果不同 Consumer Group 订阅了同一 Topic,Consumer Group 彼此之间不会干扰。这样,如果要实现一个消息可以被多个消费者同时消费(“广播”)的效果,则将每个消费者放入单独的一个 Consumer Group;如果要实现一个消息只被一个消费者消费(“独占”)的效果,则将所有的 Consumer 放入一个 Consumer Group 中。 + +注意:Consumer Group 中消费者的数量并不是越多越好,当其中消费者数量超过分区的数量时,会导致有消费者分配不到分区,从而造成消费者的浪费。 + +Producer、Consumer 和 Consumer Group 之间的关系如下图: + +
+ + + +# 二、Kafka 与 Zookeeper 关系 + +Zookeeper 为 Kafka 提供集群的管理。不仅保存着集群的 Broker、Topic、Partition 等元数据,还负责 Broker 故障发现、Leader 选举、负载均衡等。 + +- Broker 元数据管理 + + 在 Zookeeper 上会有一个专门**用来进行 Broker 服务器列表记录**的节点。每个 Broker 在启动时,都会到 Zookeeper 上进行注册,即到 `/brokers/ids` 下创建属于自己的节点,每个 Broker 会将自己的 IP 地址和端口等信息记录到该节点中去。 + +- Topic 元数据管理 + + 在 Kafka 中,同一个Topic 的消息会被分成多个分区并将其分布在多个 Broker 上,这些分区信息及与 Broker 的对应关系由 Zookeeper 维护。比如 my-topic 的 Topic 有 2 个分区,对应到 Zookeeper 中会创建这些文件夹:`/brokers/topics/my-topic/Partitions/0`、`/brokers/topics/my-topic/Partitions/1` + +- 负载均衡 + + 对于同一个 Topic 的不同 Partition,Kafka 会将这些 Partition 分布到不同的 Broker 服务器上,生产者产生消息后也会尽量投递到不同 Broker 的 Partition 中,当 Consumer 消费的时候,Zookeeper 可以根据当前的 Partition 数量以及 Consumer 数量来实现动态负载均衡。 + +# 三、Kafka 集群 + +## 典型拓扑结构 + +
+ +Kafka 集群包含若干个 Producer,若干个 Broker (Kafka 集群支持水平扩展,一般 Broker 数量越多,整个 Kafka 集群的吞吐量也就越高),若干个 Consumer Group,以及一个 Zookeeper 集群。 + +Kafka 通过 Zookeeper 管理集群配置。 + +Producer 使用 Push 模式将消息发布到 Broker 上,Consumer 使用 Pull 模式从 Broker 上订阅并消费消息。 + +## Kafka 数据流 + +
+ +Producers 往 Brokers 中指定的 Topic Push 消息,Consumers 从 Brokers 里面 Pull 指定 Topic 的消息,然后进行业务处理。 + +图中有两个 Topic,并且每个 Partition 上色数据在其他 Broker 服务器上都有一份副本: + +- Topic-0 有两个 Partition,Partition-0 和 Partition-1; +- Topic-1 有一个 Partition。 + +可以看到 Consumer-Group-1 中的 Consumer-2 没有分到 Partition 处理,这是有可能出现的。 + +# 四、Kafka 的数据存储设计 + +## Segment 数据文件 + +Partition 在物理上由多个 Segment 数据文件组成,每个 Segment 数据文件大小相等、按顺序读写。每个 Segment 数据文件的第一个文件名从 0 开始,后续每个 Segment 文件名为上一个全局 Partition 的最大offset,文件扩展名为 .log。在查找指定 Offset 的 Message 中,用二分查找就可以定位到该 Message 在哪个 Segment 数据文件中。 + +Segment 数据文件会首先被存储在内存中,当 Segment 上的消息条数达到配置值或消息发布时间超过阈值时,Segment 上的消息会被 flush 到磁盘,只有 flush 到磁盘上的消息才能被 Consumer 消费,Segment 达到一定的大小(默认是 1 GB,可配置)后将不会再往该 Segment写数据,Broker 会创建新的 Segment。 + +## Segment 索引文件 + +Kafka 为每个 Segment 数据文件都建立了索引文件,索引文件的文件名与数据文件的文件名一致,不同的是索引文件的扩展名为 .index。比如: + +
+ +Segment 索引文件索引文件并不会为数据文件中的每条 Message 都建立索引,而是采用系数索引的方式,每个一定字节建立一条索引。这样可有效降低索引文件大小,方便将索引文件加载到内存中,提高集群的吞吐量。 + +如下图所示,000000000000036869.index 文件中记录了 (3,497) ,在数据文件中表示第 3 个 Message(在全局Partition 表示第 368772 个 Message),该 Message 的物理偏移地址为 497。 + +
+ +## Partition 中通过 Offset 查找 Message + +比如读取 Offset=368776 的 Message,需要通过下面 2 个步骤进行查找: + +- 查找 Segment 文件 + + 其中00000000000000000000.index 表示最开始的文件,起始 Offset=0。第二个文件 00000000000000368769.index 文件的起始偏移量为 368770 (368769 + 1)。同样,第三个文件00000000000000737337.index 文件的起始偏移量为 737338 (737337 + 1) ,其他后续文件依次类推,以起始偏移量命名并排序这些文件,根据 Offset **二分查找**文件列表,快速定位到 00000000000000368769.index 文件。 + +- 通过 Segment 文件查找 Message + + 当 Offset=368776 时,依次定位到 00000000000000368769.index 的元数据物理位置和 00000000000000368769.log 的物理偏移地址,然后再通过 00000000000000368769.log 顺序查找直到 Offset=368776为止。 + +# 五、Kafka 的高可用原理 + +Kafka 集群由若干个 Broker 组成,Topic 由若干个 Partition 组成,每个 Partition 可存在不同的 Broker 上。可以这样说,一个 Topic 的数据,分散在多个机器上,即每个机器上都存放一部分数据。 + +## Kafka 0.8 以前 + +Kafka 0.8 以前是没有高可用机制的。 + +假设一个 Topic,由 3 个 Partiton 组成。3 个 Partition 在不同机器上,如果其中某一台机器宕掉了,则 Topic 的部分数据就丢失了。 + +## Kafka 0.8 以后 + +Kafka 0.8 以后,通过**副本机制**来实现高可用。 + +每个 Partition 的数据都会同步到其他机器上,形成多个 Partition 副本。从每个 Partition 的副本集合中,选举出 Leader,其他的都是 Follower。Producer 和 Consumer 就和 Leader 打交道: + +- 写数据时,Leader 会将所用数据都同步到 Follower 上 +- 读数据时,直接读取 Leader 上的数据 + +这样,若某个 Broker 宕机了,Broker 上的 Partition 在其他机器上是有副本的;若宕机的 Broker 上面有某个 Partition 的 Leader,则此时会从 Follower 中重新选择一个新的 Leader 出来。 + +注意:Leader 的读写细节 + +- 写数据时,Producer 向 Leader 写入,接着其他 Follower 主动从 Leader 中 pull 数据,当所有 Follower 同步好数据,就发送确认信息给 Leader,Leader 收到 Follower 的确认后,就返回写成功消息给 Producer。 +- 读数据时,只会从 Leader 中读取,但当只有一个消息已被所有 Follower 都同步成功,返回确认后时,这个消息才被消费者读到。 + +# 六、Kafka 中一些常见的问题 + +## 消息消费的顺序问题 + +消息在被追加到 Partition 的时候都会分配一个特定的偏移量(offset),Kafka 通过偏移量(offset)来保证消息在分区内的顺序性。为了保证 Kafka 中消息消费的顺序,可以采用以下 2 种方法: + +- 设置 1 个 Topic 只对应一个 Partition + + 破坏了 Kafka 的设计初衷,不推荐使用。 + +- 发送消息的时候指定 key + + 同一个 key 的消息可以保证只发送到同一个 Partition。 + +**提升:[如何保证消息的顺序性?](https://doocs.github.io/advanced-java/#/./docs/high-concurrency/how-to-ensure-the-order-of-messages)** + +## 消息丢失问题 + +### Producer 丢失数据 + +如果 Producer 端设置了 `acks=all`,则不会丢失数据。 + +Leader 在所有的 Follower 都同步到了消息之后,才认为本次写成功。如果没满足这个条件,生产者会进行无限次重试。 + +### Consumer 丢失数据 + +默认情况下,Kafka 会自动提交 Offset,Kafka 认为 Consumer 已经处理消息了,但是 Consumer 可能在处理消息的过程中挂掉了。重启系统后,Consumer 会根据提交的 Offset 进行消费,也就丢失了一部分数据。 + +解决:关闭自动提交 Offset,在处理完之后自己手动提交 Offset,就可以保证数据不会丢失。但可能会存在消息重复消费问题。 + +### Broker 丢失数据 + +比较常见的一个场景:Kafka 某个Leader 所在的 Broker 宕机,需要重新选举新的 Leader ,但此时其他的 Follower 部分数据尚未同步完成,选举某个 Follower 成 Leader 后就丢失一部分数据。 + +所以此时一般设置如下 4 个参数: + +- Topic 设置 `replication.factor` 参数 + + 参数值必须大于 1,要求每个 Partition 必须有至少 2 个副本。 + +- Kafka 服务端设置 `min.insync.replicas` 参数 + + 参数值必须大于 1,要求每个 Partition 必须有至少 2 个副本。 + +- Producer 设置 `acks=all` + + 要求每条数据,必须是**写入所有副本,才认为写成功**。 + +- Producer 端设置 `retries=MAX` + + MAX 即是一个超级大的数字,表示无限次重试。`retries=MAX `要求一旦写入数据失败,就无限重试。 + +## 消息重复消费问题 + +Consumer 消费了数据后,每个一段时间,会将已消费过的消息的 Offset 进行提交,这样,重启后,可以继续从上次消费过的 Offset 来继续消费。测试时,直接 kill 进程,然后再重启后,会导致 Consumer 将有些消息处理了,但是还未来得及提交 Offset,重启后,少数消息会再消费一次。 + +解决:需要结合具体业务来思考,可从以下几个思路来考虑: + +- 如果要将数据写入数据库中,先根据主键查查询,如果这数据已存在,就不用插入数据了。 +- 向 Redis 中写入数据,可以使用 set,这样数据不会重复 +- 基于数据库的唯一键来保证重复数据不会重复插入多条。因为有唯一键约束了,重复数据插入只会报错,不会导致数据库中出现脏数据。 + +# 七、Kafka 特性总结 + +Kafka 特性可总结如下: + +## 1. 高可用 + +Kafka 0.8 以前是没有高可用机制的。 + +Kafka 0.8 以后,通过**副本机制**来实现高可用,基于**副本机制**实现 Kafka 的高可用。 + +## 2. 持久性 + +Kafka 集群接收到 Producer 发过来的消息后,将其持久化到磁盘。此外,还支持数据备份。 + +## 3. 数据不易丢失 + +通过合理的配置,Kafka 消息不易丢失。 + +## 4. 高吞吐量 + +Kafka 高吞吐量的原因:分区、批量发送和压缩消息、顺序读写、零拷贝 + +### 分区 + +当生产者向对应 Topic 传递消息,消息通过**负载均衡机制**传递到不同的 Partition 以减轻单个服务器实例的压力; + +一个 Consumer Group 中可以有多个 Consumer,多个 Consumer 可以同时消费不同 Partition 的消息,大大的提高了消费者的并行消费能力。 + +### 批量发送和压缩消息 + +- 批量发送:在发送消息的时候,Kafka 不会直接将少量数据发送出去,否则每次发送少量的数据会增加网络传输频率,降低网络传输效率。Kafka 会先将消息缓存在内存中,当超过一个的大小或者超过一定的时间,那么会将这些消息进行批量发送。 +- 端到端压缩消息: Kfaka会将这些批量的数据进行压缩,将一批消息打包后进行压缩,发送给 Broker 服务器后,最终这些数据还是提供给消费者用,所以数据在服务器上还是保持压缩状态,不会进行解压,而且频繁的压缩和解压也会降低性能,最终还是以压缩的方式传递到消费者的手上,在 Consumer 端进行解压。 + +### 顺序读写 + +Kafka 是个可持久化的日志服务,它将数据以数据日志的形式进行追加,最后持久化在磁盘中。 + +Kafka 消息存储时依赖于**文件系统**。为了利用数据的**局部相关性**:操作系统从磁盘中**以数据块为单位**读取数据,将一个数据块读入内存中,如果有相邻的数据,就不用再去磁盘中读取。所以,在某些情况下,**顺序磁盘访问能比随机内存访问还要快**。同时在写数据的时候也是将一整块数据块写入磁盘中,大大提升 I / O 效率。 + +### 零拷贝 + +普通的数据拷贝 read & write: + +
+ +零拷贝主要的任务是**避免 CPU 做大量的数据拷贝任务,减少不必要的拷贝**。 + +**内存映射文件(Memory Mapped Files,mmap)**在 64 位操作系统中一般可以表示 20G 的数据文件,它的工作原理是直接利用操作系统的页缓存来实现文件到物理内存的直接映射。 + +
+ +显然,使用 mmap 替代 read 很明显减少了 1 次拷贝,当拷贝数据量很大时,无疑提升了效率。 + +# 补充 + +- [Kafka史上最详细原理总结](https://blog.csdn.net/lingbo229/article/details/80761778?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-0.no_search_link&spm=1001.2101.3001.4242) \ No newline at end of file diff --git a/docs/Kafka&RabbitMQ/Kafka_RabbitMQ.md b/docs/Kafka&RabbitMQ/Kafka_RabbitMQ.md new file mode 100644 index 00000000..a9ebb4a9 --- /dev/null +++ b/docs/Kafka&RabbitMQ/Kafka_RabbitMQ.md @@ -0,0 +1,47 @@ +## Kafka 和 RabbitMQ 比较 + +从以下几个方面比较 Kafka 和 RabbitMQ: + +- 吞吐量 + + Kafka:十万数量级,高吞吐量 + + RabbitMQ:万数量级 + +- Topic 数量对吞吐量影响 + + Kafka 的 Topic 可达百/千级,吞吐量下降幅度小,在同等机器下,可以支撑大量的 Topic。 + + RabbitMQ 无 Topic 概念。 + +- 时效性 + + Kafka 毫秒级;RabbitMQ 微秒级 + +- 可用性 + + Kafka 基于分布式架构,可用性非常高 + + RabbitMQ 基于主从架构实现高可用 + +- 可靠性 + + Kafka 优化参数配置,可以做到零丢失 + + RabbitMQ 基本不会丢失数据 + +- 功能 + + Kafka 功能较为简单,主要支持简单的消息队列功能,在大数据领域的实时计算以及日志采集被大规模使用 + + RabbitMQ 基于 ErLang 开发,并发能力很强,性能极好,延时很低 + +总结: + +- Kafka 主要特点是基于 Pull 的模式来处理消息消费,追求高吞吐量,一开始的目的就是用于日志收集和传输。0.8 版本开始支持复制,不支持事务,对消息的重复、丢失、错误没有严格要求,**适合产生大量数据的互联网服务的数据收集业务**。 +- RabbitMQ 是使用 Erlang 语言开发的开源消息队列系统,基于 AMQP 协议来实现。AMQP 的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。AMQP 协议更多用在企业系统内,**对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求还在其次**。 + + + + + diff --git a/docs/Kafka&RabbitMQ/RabbitMQ.md b/docs/Kafka&RabbitMQ/RabbitMQ.md new file mode 100644 index 00000000..45ae2732 --- /dev/null +++ b/docs/Kafka&RabbitMQ/RabbitMQ.md @@ -0,0 +1,1930 @@ +# 一、Rabbit 概述 + +RabbitMQ 是一个开源的消息代理和队列服务器,用来通过普通协议在完全不同的应用中间共享数据,RabbitMQ 是使用 **Erlang 语言**来编写的,并且 RabbitMQ 是基于 AMQP 协议的。 + +特点: + +- 开源、性能优秀 + + **Erlang 语言**最初用在交换机的架构模式,这样使得 RabbitMQ 在 Broker 之间进行数据交互的性能时非常优秀的。Erlang 的优点:Erlang 有着和原生 Socket 一样的延迟。 + +- 可靠性 + + 提供可靠性消息投递模式(confirm)、返回模式(return)。 + +- 扩展性 + + 多个RabbitMQ 节点可以组成一个集群,也可以根据实际业务情况动态地扩展集群中节点。 + +- 与 SpringAOP 完美的整合、API 丰富 +- 保证数据不丢失的前提做到高可靠性、可用性 + +# 二、AMQP 协议 + +AMQP (Advanced Message Queuing Protocol) 即高级消息队列协议,是一个进程间传递**异步消息**的**网络协议**。 + +## AMQP 模型 + +
+ +工作过程如下:首先发布者(Publisher)发布消息(Message),经由交换机 Exchange。交换机根据**路由规则**将收到的消息分发给与该交换机绑定的 Queue。最后 AMQP 代理会将消息投递给订阅了此队列的消费者,或者消费者按照需求自行获取。 + +关于 AMQP 模型的几点说明: + +- 发布者、交换机、队列、消费者都可以有多个。AMQP 是一个网络协议,所以这个过程中的发布者,消费者,消息代理可以分别存在于不同的设备上。 +- 布者发布消息时可以给消息指定各种消息属性(Message Meta-data)。有些属性有可能会被消息代理(Brokers)使用,然而其他的属性则是完全不透明的,它们只能被接收消息的应用所使用。 +- 从安全角度考虑,网络是不可靠的,又或是消费者在处理消息的过程中意外挂掉,这样没有处理成功的消息就会丢失。基于此原因,AMQP 模块包含了一个消息确认机制:当一个消息从队列中投递给消费者后,不会立即从队列中删除,直到它收到来自消费者的确认回执(Acknowledgement)后,才完全从队列中删除。 +- 在某些情况下,例如当一个消息无法被成功路由时(无法从交换机分发到队列),消息或许会被返回给发布者并被丢弃。或者,如果消息代理执行了**延期操作**,消息会被放入一个**死信队列**中。此时,消息发布者可以选择某些参数来处理这些特殊情况。 + +## Producer & Consumer + +消息生产者(Producer),向 Broker 发送消息的客户端。 + +消息消费者(Consumer),从 Broker 消费消息的客户端。 + +## Broker + +一个 RabbitMQ Broker 可以简单地看作一个 RabbitMQ 服务节点,或者 RabbitMQ 服务实例。大多数情况下可以将一个 RabbitMQ Broker 看作一台 RabbitMQ 服务器。 + +## Exchange + +Exchange 即交换器,是用来发送消息的 AMQP 实体。Exchange 拿到一个消息之后将它**路由**给一个或零个队列。Exchange 使用哪种路由算法是由**交换机类型**和**绑定(Bindings)规则**所决定的。 + +### Binding + +**Producer 将消息发给 Exchange 时,一般会指定一个 RoutingKey (路由键),用来指定这个消息的路由规则,而这个 RoutingKey 需要与交换器类型和 BindingKey (绑定键) 联合使用才能最终生效**。 + +RabbitMQ 中通过 **Binding (绑定)** 将 Exchange 与 Queue(消息队列) 关联起来,在绑定时一般会指定一个 BindingKey,这样 RabbitMQ 就知道如何正确将消息路由到 Queue 中。一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则。 + +生产者将消息发送给交换器,当 BindingKey 和 RoutingKey 相匹配时,消息会被路由到对应的队列中。注意BindingKey 并不是在所有的情况下都生效,它依赖于交换器类型,比如 fanout 类型的交换器就会无视,而是将消息路由到所有绑定到该交换器的队列中。 + +### Exchange 类型 + +Exchange 有以下 4 种类型,不同的类型对应着不同的路由策略: + +#### direct + +Exchange 默认类型。**路由规则是把消息路由到 Bindingkey 与 RoutingKey 完全匹配的 Queue 中**。direct 类型常用在**处理有优先级的任务**,根据任务的优先级把消息发送到对应的队列,这样可以指派更多的资源去处理高优先级的队列。 + +
+ +以上图为例,如果发送消息的时候 RoutingKey="booking",那么消息会路由到 Queue1 和 Queue2。如果在发送消息的时候设置 RoutingKey="create" 或 "confirm",消息只会路由到Queue2。如果以其他的 RoutingKey 发送消息,则消息不会路由到这两个队列中。 + +#### fanout + +路由规则是把所有发送到该 Exchange 的消息路由到所有与它绑定的 Queue 中,不需要做任何判断操作,所以 fanout 类型是所有的交换机类型里面速度最快的。**fanout 类型常用来广播消息**。 + +#### topic + +direct 类型的 Exchange 路由规则是完全匹配 BindingKey 和 RoutingKey ,但是这种严格的匹配方式在很多情况下不能满足实际业务的需求。 + +topic 类型的 Exchange 在匹配规则上进行了扩展,它与 direct 类型的 Exchange 相似,也是将消息路由到 BindingKey 和 RoutingKey 相匹配的队列中,但这里的匹配规则有些不同,它约定: + +- RoutingKey 为一个点号`.`分隔的字符串,其中**被 `.`分隔开的每一段独立的字符串称为一个单词**; +- BindingKey 和 RoutingKey 一样也是点号 `.` 分隔的字符串; +- BindingKey 中可以存在两种特殊字符串 `*` 和 `#`,用于做模糊匹配,其中 `*` 用于匹配一个单词, `#` 用于匹配零个或多个单词。 + +
+ +以上图为例,如果发送消息的时候 RoutingKey 为 + +- "com.rabbitmq.client",那么消息会路由到 Queue1 和 Queue2 +- "com.hidden.client",那么消息只会路由到 Queue2 中 +- "com.hidden.demo",那么消息只会路由到 Queue2 中 +- "java.rabbitmq.demo",那么消息只会路由到 Queue1 中 +- "java.util.concurrent",那么消息将会被丢弃或者返回给生产者,因为它没有匹配任何路由键。 + +#### headers + +headers 类型的 Exchange 不依赖于路由键的匹配规则来路由消息,而是根据发送的消息内容中的 headers 属性进行匹配。在绑定队列和交换器时指定一组键值对,当发送消息到交换器时,RabbitMQ 会获取到该消息的 headers(也是一个键值对的形式),对比其中的键值对是否完全匹配队列和交换器绑定时指定的键值对,如果完全匹配则消息会路由到该队列,否则不会路由到该队列。headers 类型的 Exchange 性能会很差,不推荐使用。 + +## Queue + +Queue 其实是 Message Queue 即消息队列,保存消息并将它们转发给消费者。Queue 是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其消费。 + +**RabbitMQ 中消息只能存储在队列中**,而 Kafka 将消息存储在 **Topic** 中,即该 Topic 对应的 Partition 中。RabbitMQ 的生产者生产消息并最终投递到队列中,消费者可以从队列中获取消息并消费。 + +当多个消费者订阅同一个队列时,队列中的消息会被平均分摊(Round-Robin,即轮询)给多个消费者进行处理,而不是每个消费者都收到所有的消息并处理,这样避免消息被重复消费。 + +### 队列属性 + +Queue 跟 Exchange 共享某些属性,但是队列也有一些另外的属性: + +- Name +- Durable:消息代理重启后,队列依旧存在 +- Exclusive:只被一个连接使用,而且当连接关闭后队列即被删除 +- Auto-delete:当最后一个消费者退订后即被删除 +- Arguments:一些消息代理用他来完成类似与 TTL 的某些额外功能 + +### 队列创建 + +队列在声明(declare)后才能被使用。 + +如果一个队列尚不存在,声明一个队列会创建它。如果声明的队列已经存在,并且属性完全相同,那么此次声明不会对原有队列产生任何影响。如果声明中的属性与已存在队列的属性有差异,那么一个错误代码为 406 的通道级异常就会被抛出。 + +### 队列持久化 + +持久化队列(Durable Queues)会被存储在磁盘上,当消息代理(Broker)重启的时候,它依旧存在。没有被持久化的队列称作暂存队列(Transient Queues)。并不是所有的场景和案例都需要将队列持久化。 + +持久化的队列并不会使得路由到它的消息也具有持久性。倘若消息代理挂掉了,重新启动,那么在重启的过程中持久化队列会被重新声明,无论怎样,**只有经过持久化的消息才能被重新恢复**。 + +## 消息机制 + +### 消息确认 + +AMQP 代理在什么时候删除消息才是正确的?AMQP 0-9-1 规范给我们两种建议: + +- **自动确认模式**:当消息代理(Broker)将消息发送给应用后立即删除。(使用 AMQP 方法:basic.deliver 或 basic.get-ok) + +- **显示确认模式**:待 Consumer 发送一个确认回执(acknowledgement)后再删除消息。(使用 AMQP 方法:basic.ack) + + 如果一个消费者在尚未发送确认回执的情况下挂掉了,那 AMQP 代理会将消息重新投递给另一个消费者。如果当时没有可用的消费者了,消息代理会死等下一个注册到此队列的消费者,然后再次尝试投递。 + +### 拒绝消息 + +当拒绝某条消息时,应用可以告诉消息代理销毁该条消息或者重新将该条消息放入队列。 + +当此队列只有一个消费者时,有可能存在拒绝消息并将消息重新放入队列的行为而引起消息在同一个消费者身上无限循环的情况。 + +### 预取消息 + +在多个消费者共享一个队列时,明确指定在收到下一个确认回执前每个消费者一次可以接受多少条消息是非常有用的。这可以在试图批量发布消息的时候起到**简单的负载均衡和提高消息吞吐量**的作用。 + +### 消息属性 + +AMQP 模型中的消息(Message)对象是带有属性(Attributes)的: + +| 属性 | 说明 | +| --------------------------------- | ------------------------------ | +| Content type | 内容类型 | +| Content encoding | 内容编码 | +| **Routing key** | 路由键 | +| Delivery mode (persistent or not) | 投递模式(持久化 或 非持久化) | +| Message priority | 消息优先权 | +| Message publishing timestamp | 消息发布的时间戳 | +| Expiration period | 消息有效期 | +| Publisher application id | 发布应用的 ID | + +有些属性是被 AMQP 代理所使用的,但是大多数是开放给接收它们的应用解释器用的。有些属性是可选的也被称作消息头(headers)。和 HTTP 协议的 X-Headers 很相似,消息属性需要在消息被发布的时候定义。 + +### 消息主体 + +AMQP 的消息除属性外,也含有一个有效载荷 Payload(消息实际携带的数据),它被 AMQP 代理当作不透明的字节数组来对待。 + +消息代理不会检查或者修改 Payload,消息可以只包含属性而不携带有效载荷,它通常会使用类似 JSON 这种序列化的格式数据。 + +### 消息持久化 + +消息能够以持久化的方式发布,AMQP 代理会将此消息存储在磁盘上。如果服务器重启,系统会确认收到的持久化消息未丢失。 + +简单地将消息发送给一个持久化的交换机或者路由给一个持久化的队列,并不会使得此消息具有持久化性质:它完全取决与消息本身的持久模式(persistence mode)。将消息以持久化方式发布时,会对性能造成一定的影响(就像数据库操作一样,健壮性的存在必定造成一些性能损失)。 + +# 三、RabbitMQ 命令行操作 + +
+ +## 启动 & 停止服务器 + +- 启动服务器 + + ```html + rabbitmq-server start & + ``` + +- 停止服务器 + + ```html + rabbitmqctl stop_app + ``` + +## 查看管控台 + +```html +http://localhost:15672/ + +# 用户名 guest +# 密码 guest +``` + + + +## 命令行基础操作 + +### 1. 应用 + +- 关闭应用 + + ```html + rabbitmqctl stop_app + ``` + +- 启动应用 + + ```html + rabbitmqctl start_app + ``` + +- 查看节点状态 + + ```html + rabbitmqctl status + ``` + +### 2. 用户 + +- 添加用户 + + ```html + rabbitmqctl add_user username password + ``` + +- 删除用户 + + ```html + rabbitmqctl delete_user username + ``` + +- 列出所有用户 + + ```html + rabbitmqctl list_users + ``` + +- 清除用户权限 + + ```html + rabbitmqctl clear_permissions -p vhostpath username + ``` + +- 列出用户权限 + + ```html + rabbitmqctl list_user_permissions username + ``` + +- 修改密码 + + ```html + rabbitmqctl change_password username newpassword + ``` + +- 设置用户权限 + + ```html + rabbitmqctl set_permissions -p vhostpath username ".*" ".*" ".*" + ``` + +### 3. 虚拟主机 + +- 创建虚拟主机 + + ```html + rabbitmqctl add_vhost vhostpath + ``` + +- 删除虚拟主机 + + ```html + rabbitmqctl delete_vhost vhostpath + ``` + +- 列出所有虚拟主机 + + ```html + rabbitmqctl list_vhosts + ``` + +- 列出虚拟主机上所有权限 + + ```html + rabbitmqctl list_permissions -p vhostpath + ``` + +### 4. 队列 + +- 查看所有队列信息 + + ```html + rabbitmqctl list_queues + ``` + +- 清除队列里的消息 + + ```html + rabbitmqctl -p vhostpath purge_queue blue + ``` + + + +## 命令行高级操作 + +- 移除所有数据 + + ```html + rabbitmqctl reset + # 要在 rabbitmqctl stop_app 之后使用 + ``` + +- 组成集群命令 + + ```html + rabbitmqctl join_cluster [--ram] + ``` + +- 查看集群状态 + + ```html + rabbitmq cluster_status + ``` + +- 修改集群节点的存储形式 + + ```html + rabbitmqctl change_cluser_node_type disc | ram + ``` + +- 摘除节点(忘记节点) + + ```html + rabbitmqctl forget_cluster_node [--offline] + ``` + +- 修改节点名称 + + ```html + rabbitmqctl rename_cluster_node oldnode1 newnode1 [oldnode2] [newnode2] + ``` + +# 四、Rabbit MQ 入门 + +## 简单案例:消息生产与消费 + +pom.xml 配置 + +```html + + com.rabbitmq + amqp-client + + 3.6.5 + +``` + +### 生产者 + +```java +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; + +import java.io.IOException; +import java.util.concurrent.TimeoutException; + +/** + *

简单案例:消息生产与消费

+ * 消息生产者 + * Created by DHA on 2019/11/18. + */ +public class Producer { + public static void main(String[] args) throws IOException, TimeoutException { + + //1 创建一个 Connectionfactory,并进行设置 + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.setHost("localhost"); + connectionFactory.setPort(5672); + connectionFactory.setVirtualHost("/"); + + //2 通过连接工厂创建连接 + Connection connection = connectionFactory.newConnection(); + + //3 通过 connecion 创建一个 Channel + Channel channel = connection.createChannel(); + + //4 通过 chanel 发送数据 + for(int i=0;i<10;i++){ + String data="Hello!"; + channel.basicPublish("","test001",null,data.getBytes()); + } + + //5 关闭相关连接 + channel.close(); + connection.close(); + } +} +``` + +### 消费者 + +```java +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; + +import java.io.IOException; +import java.util.concurrent.TimeoutException; + +/** + *

简单案例:消息生产与消费

+ * 消息生产者 + * Created by DHA on 2019/11/18. + */ +public class Producer { + public static void main(String[] args) throws IOException, TimeoutException { + + //1 创建一个 Connectionfactory,并进行设置 + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.setHost("localhost"); + connectionFactory.setPort(5672); + connectionFactory.setVirtualHost("/"); + + //2 通过连接工厂创建连接 + Connection connection = connectionFactory.newConnection(); + + //3 通过 connecion 创建一个 Channel + Channel channel = connection.createChannel(); + + //4 通过 chanel 发送数据 + for(int i=0;i<10;i++){ + String data="Hello!"; + channel.basicPublish("","test001",null,data.getBytes()); + } + + //5 关闭相关连接 + channel.close(); + connection.close(); + } +} +``` + +## Direct Exchange + +### 生产者 + +```java +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; + +import java.io.IOException; +import java.util.concurrent.TimeoutException; + +/** + *

Direct Exchange

+ * 所有发送到 Direct Exchange 的消息被转发到 routing key 中指定的 Queue。 + * 消息生产者 + * Created by DHA on 2019/11/19. + */ +public class Producer4DirectExchange { + public static void main(String[] args) throws IOException, TimeoutException { + + //1 创建一个 Connectionfactory,并进行设置 + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.setHost("localhost"); + connectionFactory.setPort(5672); + connectionFactory.setVirtualHost("/"); + + //2 通过连接工厂创建连接 + Connection connection = connectionFactory.newConnection(); + + //3 通过 connecion 创建一个 Channel + Channel channel = connection.createChannel(); + + //4 声明 + //声明 exchange 名称 + String exchangeName="test_direct_exchange"; + String routingKey = "test.direct"; + + //5 通过 chanel 发送数据 + String msg = "Hello World RabbitMQ 4 Direct Exchange Message ... "; + channel.basicPublish(exchangeName, routingKey , null , msg.getBytes()); + + //6 关闭相关连接 + channel.close(); + connection.close(); + } +} +``` + + + +### 消费者 + +```java +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; +import com.rabbitmq.client.QueueingConsumer; + +import java.io.IOException; +import java.util.concurrent.TimeoutException; + +/** + *

Direct Exchange

+ * 所有发送到 Direct Exchange 的消息被转发到 routing key 中指定的 Queue。 + * 消息消费者 + * Created by DHA on 2019/11/19. + */ +public class Consumer4DirectExchange { + public static void main(String[] args) throws IOException, TimeoutException, InterruptedException { + + //1 创建一个 Connectionfactory,并进行设置 + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.setHost("localhost"); + connectionFactory.setPort(5672); + connectionFactory.setVirtualHost("/"); + + //2 通过连接工厂创建连接 + Connection connection = connectionFactory.newConnection(); + + //3 通过 connecion 创建一个 Channel + Channel channel = connection.createChannel(); + + //4 声明 + String exchangeName="test_direct_exchange"; + String exchangeType="direct"; + String queueName="test_direct_queue"; + String routingKey="test.direct"; + + // 声明一个交换机 + channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null); + // 声明一个队列 + channel.queueDeclare(queueName,false,false,false,null); + // 绑定:将一个队列绑定到一个交换机上 + channel.queueBind(queueName,exchangeName,routingKey); + + //5 创建消费者 + QueueingConsumer queueingConsumer=new QueueingConsumer(channel); + + //6 设置 channel + channel.basicConsume(queueName,true,queueingConsumer); + + //7 获取数据 + while(true){ + QueueingConsumer.Delivery delivery=queueingConsumer.nextDelivery(); + String msg=new String(delivery.getBody()); + System.out.println("消费端:"+msg); + } + } +} +``` + + + +## Topic Exchange + +### 生产者 + +```java +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; + +import java.io.IOException; +import java.util.concurrent.TimeoutException; + +/** + *

Topic Exchange

+ * Topic Exchange 将 routing key 与某 Topic 进行模糊匹配,此时队列需要绑定一个 Topic。 + * 消息生产者 + * Created by DHA on 2019/11/19. + */ +public class Producer4TopicExchange { + public static void main(String[] args) throws IOException, TimeoutException { + + //1 创建一个 Connectionfactory,并进行设置 + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.setHost("localhost"); + connectionFactory.setPort(5672); + connectionFactory.setVirtualHost("/"); + + //2 通过连接工厂创建连接 + Connection connection = connectionFactory.newConnection(); + + //3 通过 connecion 创建一个 Channel + Channel channel = connection.createChannel(); + + //4 声明 + //声明 exchange 名称 + String exchangeName="test_topic_exchange"; + String routingKey1 = "user.save"; + String routingKey2 = "user.update"; + String routingKey3 = "user.delete.abc"; + + //5 通过 chanel 发送数据 + String msg = "Hello World RabbitMQ 4 Topic Exchange Message ... "; + channel.basicPublish(exchangeName, routingKey1 , null , msg.getBytes()); + channel.basicPublish(exchangeName, routingKey2 , null , msg.getBytes()); + channel.basicPublish(exchangeName, routingKey3 , null , msg.getBytes()); + + //6 关闭相关连接 + channel.close(); + connection.close(); + } +} +``` + + + +### 消费者 + +```java +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; +import com.rabbitmq.client.QueueingConsumer; + +import java.io.IOException; +import java.util.concurrent.TimeoutException; + +/** + *

Topic Exchange

+ * Topic Exchange 将 routing key 与某 Topic 进行模糊匹配,此时队列需要绑定一个 Topic。 + * 消息消费者 + * Created by DHA on 2019/11/19. + */ +public class Consumer4TopicExchange { + public static void main(String[] args) throws IOException, TimeoutException, InterruptedException { + + //1 创建一个 Connectionfactory,并进行设置 + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.setHost("localhost"); + connectionFactory.setPort(5672); + connectionFactory.setVirtualHost("/"); + + //2 通过连接工厂创建连接 + Connection connection = connectionFactory.newConnection(); + + //3 通过 connecion 创建一个 Channel + Channel channel = connection.createChannel(); + + //4 声明 + String exchangeName="test_topic_exchange"; + String exchangeType="topic"; + String queueName="test_topic_queue"; + String routingKey="user.*"; + + // 声明一个交换机 + channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null); + // 声明一个队列 + channel.queueDeclare(queueName,false,false,false,null); + // 绑定:将一个队列绑定到一个交换机上 + channel.queueBind(queueName,exchangeName,routingKey); + + //5 创建消费者 + QueueingConsumer queueingConsumer=new QueueingConsumer(channel); + + //6 设置 channel + channel.basicConsume(queueName,true,queueingConsumer); + + //7 获取数据 + while(true){ + QueueingConsumer.Delivery delivery=queueingConsumer.nextDelivery(); + String msg=new String(delivery.getBody()); + System.out.println("消费端:"+msg); + } + } +} +``` + + + +## Fanout Exchange + +### 生产者 + +```java +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; + +import java.io.IOException; +import java.util.concurrent.TimeoutException; + +/** + *

Fanout Exchange

+ * Fanout Exchange 不处理 routing key,只需要简单的将队列绑定到交换机上,发送到交换机的消息都会被转发到交换机绑定的所有队列上。 + * 消息生产者 + * Created by DHA on 2019/11/19. + */ +public class Producer4FanoutExchange { + public static void main(String[] args) throws IOException, TimeoutException { + + //1 创建一个 Connectionfactory,并进行设置 + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.setHost("localhost"); + connectionFactory.setPort(5672); + connectionFactory.setVirtualHost("/"); + + //2 通过连接工厂创建连接 + Connection connection = connectionFactory.newConnection(); + + //3 通过 connecion 创建一个 Channel + Channel channel = connection.createChannel(); + + //4 声明 + //声明 exchange 名称 + String exchangeName="test_fanout_exchange"; + + //5 通过 chanel 发送数据 + for(int i = 0; i < 10; i ++) { + String msg = "Hello World RabbitMQ 4 Fanout Exchange Message ..."; + channel.basicPublish(exchangeName, "", null , msg.getBytes()); + } + + //6 关闭相关连接 + channel.close(); + connection.close(); + } +} +``` + + + +### 消费者 + +```java +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; +import com.rabbitmq.client.QueueingConsumer; + +import java.io.IOException; +import java.util.concurrent.TimeoutException; + +/** + *

Fanout Exchange

+ * Fanout Exchange 不处理 routing key,只需要简单的将队列绑定到交换机上,发送到交换机的消息都会被转发到交换机绑定的所有队列上。 + * 消息消费者 + * Created by DHA on 2019/11/19. + */ +public class Consumer4FanoutExchange { + public static void main(String[] args) throws IOException, TimeoutException, InterruptedException { + + //1 创建一个 Connectionfactory,并进行设置 + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.setHost("localhost"); + connectionFactory.setPort(5672); + connectionFactory.setVirtualHost("/"); + + //2 通过连接工厂创建连接 + Connection connection = connectionFactory.newConnection(); + + //3 通过 connecion 创建一个 Channel + Channel channel = connection.createChannel(); + + //4 声明 + String exchangeName="test_fanout_exchange"; + String exchangeType="fanout"; + String queueName="test_fanout_queue"; + String routingKey=""; + + // 声明一个交换机 + channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null); + // 声明一个队列 + channel.queueDeclare(queueName,false,false,false,null); + // 绑定:将一个队列绑定到一个交换机上 + channel.queueBind(queueName,exchangeName,routingKey); + + //5 创建消费者 + QueueingConsumer queueingConsumer=new QueueingConsumer(channel); + + //6 设置 channel + channel.basicConsume(queueName,true,queueingConsumer); + + //7 获取数据 + while(true){ + QueueingConsumer.Delivery delivery=queueingConsumer.nextDelivery(); + String msg=new String(delivery.getBody()); + System.out.println("消费端:"+msg); + } + } +} +``` + + + +## 设置消息属性 + +### 生产者 + +```java +import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeoutException; + +/** + *

消息属性设置

+ * 消息生产者 + * Created by DHA on 2019/11/18. + */ +public class Producer { + public static void main(String[] args) throws IOException, TimeoutException { + + //1 创建一个 Connectionfactory,并进行设置 + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.setHost("localhost"); + connectionFactory.setPort(5672); + connectionFactory.setVirtualHost("/"); + + //2 通过连接工厂创建连接 + Connection connection = connectionFactory.newConnection(); + + //3 通过 connecion 创建一个 Channel + Channel channel = connection.createChannel(); + + // 设置自定义属性 + Map headers = new HashMap<>(); + headers.put("attr1", "111"); + headers.put("attr2", "222"); + + //4 设置消息属性 + AMQP.BasicProperties properties=new AMQP.BasicProperties.Builder() + .deliveryMode(2) // 2 表示持久化的投递 + .contentEncoding("UTF-8") // 设置内容编码 + .expiration("10000") // 设置过期时间为 10 秒 + .headers(headers) // 自定义属性 + .build(); + + //5 通过 chanel 发送数据 + for(int i=0;i<5;i++){ + String data="Hello!"; + channel.basicPublish("","test001",properties,data.getBytes()); + } + + //6 关闭相关连接 + channel.close(); + connection.close(); + } +} +``` + + + +### 消费者 + +```java +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; +import com.rabbitmq.client.QueueingConsumer; +import com.rabbitmq.client.QueueingConsumer.Delivery; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.TimeoutException; + +/** + *

消息属性设置

+ * 消息消费者 + * Created by DHA on 2019/11/18. + */ +public class Consumer { + public static void main(String[] args) throws IOException, TimeoutException, InterruptedException { + + //1 创建一个 Connectionfactory,并进行设置 + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.setHost("localhost"); + connectionFactory.setPort(5672); + connectionFactory.setVirtualHost("/"); + + //2 通过连接工厂创建连接 + Connection connection = connectionFactory.newConnection(); + + //3 通过 connecion 创建一个 Channel + Channel channel = connection.createChannel(); + + //4 声明一个队列 + String queueName="test001"; + channel.queueDeclare(queueName,true,false,false,null); + + //5 创建消费者 + QueueingConsumer queueingConsumer=new QueueingConsumer(channel); + + //6 设置 channel + channel.basicConsume(queueName,true,queueingConsumer); + + //7 获取数据 + while(true){ + Delivery delivery=queueingConsumer.nextDelivery(); + String msg=new String(delivery.getBody()); + System.out.println("消费端:"+msg); + // 获取自定义属性数据 + Map headers=delivery.getProperties().getHeaders(); + System.err.println("headers get attribute attr1 value: " + headers.get("attr1")); + } + } +} +``` + + + +# 五、RabbitMQ 高级特性 + +## 消息100%可靠性投递的解决方案 + +### 生产端可靠性投递 + +- 保障消息成功发出 +- 保障 MQ 节点的成功接收 +- 发送端收到 MQ 节点(Broker)确认应答 +- **完善的消息补偿机制** + +### 解决方案1:消息落库 + +消息落库,对消息状态进行打标。 + +
+ + + +### 解决方案2:二次确认,回调检查 + +消息的延迟投递,做二次确认,回调检查。 + +
+ + + +## 消费端幂等性操作 + +- 唯一 ID + 指纹码 机制,利用数据库主键去重 + + 优点:实现简单 + + 缺点:高并罚下有数据库写入的性能瓶颈 + + 解决方案:根据 ID 进行分库分表进行算法路由 + +- 利用 Redis 原子特性实现 + +## Confirm 消息机制 + +消息的确认是指生产者投递消息后,如果 Broker 收到消息,则会给生产者一个应答,生产者进行接收应答,用来确定这条消息是否正常地发送到 Broker。 + +
+ +实现机制: + +- 第一步:在 channel 上开启确认模式 + + ```java + channel.confirmSelect() + ``` + +- 第二步:在 channel 上添加监听 + + ```java + channel.addConfirmListener() + ``` + + 监听成功和失败的返回结果,根据具体的结果对消息进行重新发送或记录日志等后续处理。 + +### 生产者 + +```java +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.ConfirmListener; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; + +import java.io.IOException; +import java.util.concurrent.TimeoutException; + +/** + *

Confirm 消息机制

+ * 消息的确认是指生产者投递消息后,如果 Broker 收到消息,则会给生产者一个应答,生产者进行接收应答,用来确定这条消息是否正常地发送到 Broker。 + * 消息生产者 + * Created by DHA on 2019/11/18. + */ +public class Producer { + public static void main(String[] args) throws IOException, TimeoutException { + + //1 创建一个 Connectionfactory,并进行设置 + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.setHost("localhost"); + connectionFactory.setPort(5672); + connectionFactory.setVirtualHost("/"); + + //2 通过连接工厂创建连接 + Connection connection = connectionFactory.newConnection(); + + //3 通过 connecion 创建一个 Channel + Channel channel = connection.createChannel(); + + //4 指定消息投递模式:confirm 模式 + channel.confirmSelect(); + + String exchangeName = "test_confirm_exchange"; + String routingKey = "confirm.save"; + + //5 通过 chanel 发送数据 + String msg="Hello!"; + channel.basicPublish(exchangeName,routingKey,null,msg.getBytes()); + + //6 添加一个确认监听 + channel.addConfirmListener(new ConfirmListener() { + @Override + public void handleAck(long l, boolean b) throws IOException { + System.out.println("------ack!-------"); + } + + @Override + public void handleNack(long l, boolean b) throws IOException { + System.out.println("------Nack!-------"); + } + }); + } +} +``` + + + +### 消费者 + +```java +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; +import com.rabbitmq.client.QueueingConsumer; +import com.rabbitmq.client.QueueingConsumer.Delivery; + +import java.io.IOException; +import java.util.concurrent.TimeoutException; + +/** + *

Confirm 消息机制

+ * 消息的确认是指生产者投递消息后,如果 Broker 收到消息,则会给生产者一个应答,生产者进行接收应答,用来确定这条消息是否正常地发送到 Broker。 + * 消息消费者 + * Created by DHA on 2019/11/18. + */ +public class Consumer { + public static void main(String[] args) throws IOException, TimeoutException, InterruptedException { + + //1 创建一个 Connectionfactory,并进行设置 + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.setHost("localhost"); + connectionFactory.setPort(5672); + connectionFactory.setVirtualHost("/"); + + //2 通过连接工厂创建连接 + Connection connection = connectionFactory.newConnection(); + + //3 通过 connecion 创建一个 Channel + Channel channel = connection.createChannel(); + + //4 声明 + String exchangeName="test_confirm_exchange"; + String exchangeType="topic"; + String queueName="test_confirm_queue"; + String routingKey="confirm.#"; + + // 声明一个交换机 + channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null); + // 声明一个队列 + channel.queueDeclare(queueName,false,false,false,null); + // 绑定:将一个队列绑定到一个交换机上 + channel.queueBind(queueName,exchangeName,routingKey); + + //5 创建消费者 + QueueingConsumer queueingConsumer=new QueueingConsumer(channel); + + //6 设置 channel + channel.basicConsume(queueName,queueingConsumer); + + //7 获取数据 + while(true){ + Delivery delivery=queueingConsumer.nextDelivery(); + String msg=new String(delivery.getBody()); + System.out.println("消费端:"+msg); + } + } +} +``` + + + +## Return 消息机制 + +消息生产者通过制动一个 Exchange 和 routing key,把消息送达到某一个队列中去,然后消费者监听队列,进行消费处理操作。 + +在某些情况下,如果我们在发送消息的时候,当前的 **Exchange 不存在**或者指定的 **routing key路由不到**,此时我们需要监听这种不可达的消息,就要使用 Return Listener。 + +基础 API 有一个配置项 mandatory + +- 如果为 true,那么监听器会接收到路由不可达的消息,然后进行后续处理 +- 如果为 false, 那么 Broker 端自动删除该消息 + + + +
+ + + +### 生产者 + +```java +import com.rabbitmq.client.*; + +import java.io.IOException; +import java.util.concurrent.TimeoutException; + +/** + *

Return 消息机制

+ * 消息生产者通过制动一个 Exchange 和 routing key,把消息送达到某一个队列中去,然后消费者监听队列,进行消费处理操作。 + * 在某些情况下,如果我们在发送消息的时候,当前的 Exchange 不存在或者指定的 routing key路由不到,此时我们需要监听这种不可达的消息,就要使用 Return Listener。 + * + * 消息生产者 + * Created by DHA on 2019/11/18. + */ +public class Producer { + public static void main(String[] args) throws IOException, TimeoutException { + + //1 创建一个 Connectionfactory,并进行设置 + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.setHost("localhost"); + connectionFactory.setPort(5672); + connectionFactory.setVirtualHost("/"); + + //2 通过连接工厂创建连接 + Connection connection = connectionFactory.newConnection(); + + //3 通过 connecion 创建一个 Channel + Channel channel = connection.createChannel(); + + //4 指定消息投递模式:confirmListener 模式 + channel.confirmSelect(); + + String exchangeName = "test_return_exchange"; + String routingKey = "returnListener.save"; + String routingKeyError = "return.save"; + + //5 通过 chanel 发送数据 + String msg="Hello!"; + // mandatory 如果为 true,那么监听器会接收到路由不可达的消息,然后进行后续处理 + // mandatory 如果为 false, 那么 Broker 端自动删除该消息 + channel.basicPublish(exchangeName,routingKeyError,true,null,msg.getBytes()); + + //6 添加一个监听 + channel.addReturnListener(new ReturnListener() { + @Override + public void handleReturn(int replyCode, String replyText, String exchange, + String routingKey, AMQP.BasicProperties properties, byte[] body) + throws IOException { + System.err.println("---------handle return----------"); + System.err.println("replyCode: " + replyCode); + System.err.println("replyText: " + replyText); + System.err.println("exchange: " + exchange); + System.err.println("routingKey: " + routingKey); + System.err.println("properties: " + properties); + System.err.println("body: " + new String(body)); + } + }); + + } +} +``` + + + +### 消费者 + +```java +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; +import com.rabbitmq.client.QueueingConsumer; +import com.rabbitmq.client.QueueingConsumer.Delivery; + +import java.io.IOException; +import java.util.concurrent.TimeoutException; + +/** + *

Return 消息机制

+ * 消息生产者通过制动一个 Exchange 和 routing key,把消息送达到某一个队列中去,然后消费者监听队列,进行消费处理操作。 + * 在某些情况下,如果我们在发送消息的时候,当前的 Exchange 不存在或者指定的 routing key路由不到,此时我们需要监听这种不可达的消息,就要使用 Return Listener。 + * + * 消息消费者 + * Created by DHA on 2019/11/18. + */ +public class Consumer { + public static void main(String[] args) throws IOException, TimeoutException, InterruptedException { + + //1 创建一个 Connectionfactory,并进行设置 + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.setHost("localhost"); + connectionFactory.setPort(5672); + connectionFactory.setVirtualHost("/"); + + //2 通过连接工厂创建连接 + Connection connection = connectionFactory.newConnection(); + + //3 通过 connecion 创建一个 Channel + Channel channel = connection.createChannel(); + + //4 声明 + String exchangeName="test_return_exchange"; + String exchangeType="topic"; + String queueName="test_return_queue"; + String routingKey="returnListener.#"; + + // 声明一个交换机 + channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null); + // 声明一个队列 + channel.queueDeclare(queueName,false,false,false,null); + // 绑定:将一个队列绑定到一个交换机上 + channel.queueBind(queueName,exchangeName,routingKey); + + //5 创建消费者 + QueueingConsumer queueingConsumer=new QueueingConsumer(channel); + + //6 设置 channel + channel.basicConsume(queueName,queueingConsumer); + + //7 获取数据 + while(true){ + Delivery delivery=queueingConsumer.nextDelivery(); + String msg=new String(delivery.getBody()); + System.out.println("消费端:"+msg); + } + } +} +``` + + + +## 消费端自定义监听 + +我们一般在代码中编写 while 循环,进行 consumer.nextDelivery 方法获取下一条消息,然后进行消费处理! + +但是,我们使用自定义的 Counsumer 更加方便,解耦性更强,在实际工作中广泛使用。 + +### 自定义消费者 + +实现步骤: + +- 先继承 `com.rabbitmq.client.DefaultConsumer` +- 再重写 `handleDelivery()` 方法 + +```java +import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.DefaultConsumer; +import com.rabbitmq.client.Envelope; + +import java.io.IOException; + +/** + *

自定义消费者

+ * 1 先继承 DefaultConsumer + * 2 然后重写 handleDelivery() 方法 + * + * Created by DHA on 2019/11/20. + */ +public class MyConsumer extends DefaultConsumer{ + public MyConsumer(Channel channel) { + super(channel); + } + + @Override + public void handleDelivery(String consumerTag, Envelope envelope, + AMQP.BasicProperties properties, byte[] body) + throws IOException { + System.err.println("----------consumer message-----------"); + System.err.println("consumerTag:"+consumerTag); + System.err.println("envelope:"+envelope); + System.err.println("properties:"+properties); + System.err.println("body:"+new String(body)); + } +} +``` + + + +### 生产者 + +```java +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; + +import java.io.IOException; +import java.util.concurrent.TimeoutException; + +/** + *

消费端自定义监听

+ * 消息生产者 + * Created by DHA on 2019/11/19. + */ +public class Producer { + public static void main(String[] args) throws IOException, TimeoutException { + + //1 创建一个 Connectionfactory,并进行设置 + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.setHost("localhost"); + connectionFactory.setPort(5672); + connectionFactory.setVirtualHost("/"); + + //2 通过连接工厂创建连接 + Connection connection = connectionFactory.newConnection(); + + //3 通过 connecion 创建一个 Channel + Channel channel = connection.createChannel(); + + //4 声明 + //声明 exchange 名称 + String exchangeName="test_consumer_exchange"; + String routingKey = "consumer.save"; + + //5 通过 chanel 发送数据 + String msg = "Hello World RabbitMQ 4 Consumer Exchange Message ... "; + channel.basicPublish(exchangeName, routingKey , true,null , msg.getBytes()); + } +} +``` + + + +### 消费者 + +```java +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; +import com.rabbitmq.client.QueueingConsumer; +import com.rabbitmq.client.QueueingConsumer.Delivery; + +public class Consumer { + + public static void main(String[] args) throws Exception { + //1 创建一个 Connectionfactory,并进行设置 + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.setHost("localhost"); + connectionFactory.setPort(5672); + connectionFactory.setVirtualHost("/"); + + //2 通过连接工厂创建连接 + Connection connection = connectionFactory.newConnection(); + + //3 通过 connecion 创建一个 Channel + Channel channel = connection.createChannel(); + + //4 声明 + String exchangeName = "test_consumer_exchange"; + String exchangeType= "topic"; + String routingKey = "consumer.#"; + String queueName = "test_consumer_queue"; + + // 声明一个交换机 + channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null); + // 声明一个队列 + channel.queueDeclare(queueName,false,false,false,null); + // 绑定:将一个队列绑定到一个交换机上 + channel.queueBind(queueName,exchangeName,routingKey); + + /* + //5 创建消费者 + QueueingConsumer queueingConsumer=new QueueingConsumer(channel); + + //6 设置 channel + channel.basicConsume(queueName,true,queueingConsumer); + + //7 获取数据 + while(true){ + QueueingConsumer.Delivery delivery=queueingConsumer.nextDelivery(); + String msg=new String(delivery.getBody()); + System.out.println("消费端:"+msg); + } + */ + + //5 消费端自定义监听 使用 MyConsumer 相应实例 + channel.basicConsume(queueName, true, new MyConsumer(channel)); + } +} +``` + + + +## 消费端限流 + +RabbitMQ 提供了一种 QoS(服务质量保证) 功能,**在非自动确认消息的前提下**,如果一定数目的消息(通过基于 Consume 或者 Channel 设置 QoS 值)未被确认前,不进行消费新的消息。 + +涉及到的方法: + +```erlang +void BasicQoS(unit prefetchSize,ushort prefetchCount,bool global) +``` + +- prefetchSize:0 +- prefetchCount:告知 RabbitMQ 不要同时给一个消费者推送多个 N 个消息,即一旦有 N 个消息还没有 ACK,则该 Consumer 将 block 掉,一直到有消息 ack +- golbal:true 表示将上面设置应用于 Channel;true 表示将上面设置应用于 Consumer。 + +注意: + +- prefetchSize 和 global 这两项,RabbitMQ 没有实现,暂且不研究 +- prefetchCount 在 no_ask-false 的情况下生效,即在自动应答的情况下是不生效的 + +### 生产者 + +```java +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; + +import java.io.IOException; +import java.util.concurrent.TimeoutException; + +/** + *

消费端限流

+ * 消息生产者 + * Created by DHA on 2019/11/19. + */ +public class Producer { + public static void main(String[] args) throws IOException, TimeoutException { + + //1 创建一个 Connectionfactory,并进行设置 + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.setHost("localhost"); + connectionFactory.setPort(5672); + connectionFactory.setVirtualHost("/"); + + //2 通过连接工厂创建连接 + Connection connection = connectionFactory.newConnection(); + + //3 通过 connecion 创建一个 Channel + Channel channel = connection.createChannel(); + + //4 声明 + //声明 exchange 名称 + String exchangeName="test_qos_exchange"; + String routingKey = "qos.save"; + + //5 通过 chanel 发送数据 + for(int i=0;i<5;i++){ + String msg = "Hello World RabbitMQ 4 Qos Message ... "; + channel.basicPublish(exchangeName, routingKey , true,null , msg.getBytes()); + } + } +} +``` + + + +### 消费者 + +```java +import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.DefaultConsumer; +import com.rabbitmq.client.Envelope; + +import java.io.IOException; + +/** + *

自定义消费者

+ * 1 先继承 DefaultConsumer + * 2 然后重写 handleDelivery() 方法 + * + * Created by DHA on 2019/11/20. + */ +public class MyConsumer extends DefaultConsumer{ + + // channel 进行签收 + private Channel channel; + + public MyConsumer(Channel channel) { + super(channel); + this.channel=channel; + } + + @Override + public void handleDelivery(String consumerTag, Envelope envelope, + AMQP.BasicProperties properties, byte[] body) + throws IOException { + System.err.println("----------consumer message-----------"); + System.err.println("consumerTag:"+consumerTag); + System.err.println("envelope:"+envelope); + System.err.println("properties:"+properties); + System.err.println("body:"+new String(body)); + + // false 表示不支持批量签收 + channel.basicAck(envelope.getDeliveryTag(),false); + } +} +``` + + + +```java +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; + +/** + *

消费端限流

+ * 消息消费者 + * + * basicQoS(prefetchSize,refetchCount,global) + * - prefetchSize:0 + * - prefetchCount:告知 RabbitMQ 不要同时给一个消费者推送多个 N 个消息,即一旦有 N 个消息还没有 ACK, + * 则该 Consumer 将 block 掉,一直到有消息 ack + * - golbal:true 表示将上面设置应用于 Channel;true 表示将上面设置应用于 Consumer。 + * + * Created by DHA on 2019/11/19. + */ +public class Consumer { + + public static void main(String[] args) throws Exception { + //1 创建一个 Connectionfactory,并进行设置 + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.setHost("localhost"); + connectionFactory.setPort(5672); + connectionFactory.setVirtualHost("/"); + + //2 通过连接工厂创建连接 + Connection connection = connectionFactory.newConnection(); + + //3 通过 connecion 创建一个 Channel + Channel channel = connection.createChannel(); + + //4 声明 + String exchangeName = "test_qos_exchange"; + String exchangeType= "topic"; + String routingKey = "qos.#"; + String queueName = "test_qos_queue"; + + // 声明一个交换机 + channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null); + // 声明一个队列 + channel.queueDeclare(queueName,false,false,false,null); + // 绑定:将一个队列绑定到一个交换机上 + channel.queueBind(queueName,exchangeName,routingKey); + + // 第二个参数为 1,表示一次处理一条消息 + // 第三个参数为 false,表示应用到 Consumer 级别 + channel.basicQos(0,1,false); + + //5 消费端自定义监听 + // 首先将第二个参数设置为 false,进行手动签收 + channel.basicConsume(queueName, false, new MyConsumer(channel)); + } +} +``` + + + +## 消费端 ACK 与重回队列 + +- **消费端的手工 ACK 和 NACK** + + 消费端进行消费时: + + 如果由于业务异常,我们可以进行日志的记录,然后进行补偿; + + 如果由于服务器宕机等严重问题,那么需要手工进行 ACK 保障消费端消费成功 + +- **消费端的重回队列** + + 消费端重回队列是为了对没有成功的消息, 消息会被重新投递给 Broker。一般在使用应用中,都会关闭重回队列,即设置为 false。 + +### 生产者 + +```java +import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeoutException; + +/** + *

消费端的手工 ACK 和 NACK

+ * 消息生产者 + * + * Created by DHA on 2019/11/19. + */ +public class Producer { + public static void main(String[] args) throws IOException, TimeoutException { + + //1 创建一个 Connectionfactory,并进行设置 + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.setHost("localhost"); + connectionFactory.setPort(5672); + connectionFactory.setVirtualHost("/"); + + //2 通过连接工厂创建连接 + Connection connection = connectionFactory.newConnection(); + + //3 通过 connecion 创建一个 Channel + Channel channel = connection.createChannel(); + + //4 声明 + //声明 exchange 名称 + String exchangeName="test_ack_exchange"; + String routingKey = "ack.save"; + + //5 通过 chanel 发送数据 + for(int i =0; i<5; i ++){ + + Map headers = new HashMap(); + headers.put("num", i); + + AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder() + .deliveryMode(2) + .contentEncoding("UTF-8") + .headers(headers) + .build(); + String msg = "Hello RabbitMQ ACK Message " + i; + channel.basicPublish(exchangeName, routingKey, true, properties, msg.getBytes()); + } + } +} +``` + + + +### 消费者 + +```java +import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.DefaultConsumer; +import com.rabbitmq.client.Envelope; + +import java.io.IOException; + +/** + *

消费端的重回队列

+ * 消费端重回队列是为了对没有成功的消息, 消息会被重新投递给 Broker。 + * 一般在使用应用中,都会关闭重回队列,即设置为 false。 + * + * Created by DHA on 2019/11/20. + */ +public class MyConsumer extends DefaultConsumer{ + + // channel 进行签收 + private Channel channel; + + public MyConsumer(Channel channel) { + super(channel); + this.channel=channel; + } + + @Override + public void handleDelivery(String consumerTag, Envelope envelope, + AMQP.BasicProperties properties, byte[] body) + throws IOException { + System.err.println("-----------consume message----------"); + System.err.println("body: " + new String(body)); + + // 为了实验效果明显 + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + Integer num=(Integer) properties.getHeaders().get("num"); + if(num==0){ + // 第二个参数表示是否支持批量签收,如果为 false,表示不支持批量签收 + // 第三个参数表示是否重回队列,如果为 true,表示支持重回队列,则会重回到队列的尾端 + channel.basicNack(envelope.getDeliveryTag(),false,true); + }else{ + // false 表示不支持批量签收 + channel.basicAck(envelope.getDeliveryTag(),false); + } + //channel.basicAck(envelope.getDeliveryTag(),false); + } +} +``` + + + +```java +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; + +/** + *

消费端的手工 ACK 和 NACK

+ * 消息消费者 + * + * Created by DHA on 2019/11/19. + */ +public class Consumer { + + public static void main(String[] args) throws Exception { + //1 创建一个 Connectionfactory,并进行设置 + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.setHost("localhost"); + connectionFactory.setPort(5672); + connectionFactory.setVirtualHost("/"); + + //2 通过连接工厂创建连接 + Connection connection = connectionFactory.newConnection(); + + //3 通过 connecion 创建一个 Channel + Channel channel = connection.createChannel(); + + //4 声明 + String exchangeName = "test_ack_exchange"; + String exchangeType= "topic"; + String routingKey = "ack.#"; + String queueName = "test_ack_queue"; + + // 声明一个交换机 + channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null); + // 声明一个队列 + channel.queueDeclare(queueName,false,false,false,null); + // 绑定:将一个队列绑定到一个交换机上 + channel.queueBind(queueName,exchangeName,routingKey); + + //5 消费端自定义监听 + // 首先将第二个参数 autoACK 设置为 false,进行手动签收 + channel.basicConsume(queueName, false, new MyConsumer(channel)); + } +} +``` + + + +## TTL + +TTL(Time To Live)即生存时间。 + +- RabbitMQ 支持**消息**的过期时间,在消息发送时可以进行指定 +- RabbitMQ 支持**队列**的过期时间,从消息如队列开始计算,只要超过了队列的超时时间配置,那么会自动清除消息 + +## 死信队列(DLX,Dead-Letter-Exchange ) + +利用 DLX,当消息在一个队列中变成死信(dead message)之后,其能被重新 publish 到另一个 Exchange,这个 Exchange 就是 DLX。 + +消息变成死信的几种情况: + +- 消息被拒绝(basic.reject / basic.nack),并且 requeue=false +- 消息 TTL 过期 +- 队列达到最大长度 + +注意: + +- DLX 也是一个正常的 Exchange,和一般的 Exchange 没有区别,它能在任何队列上被指定,实际上就是设置某个队列的属性。 + +- 当这个队列中有死信时,RabbitMQ 就会自动的将这个消息重新发布到设置的 Exchange 上去,进而被路由到另一个队列。 + +- 死信队列设置需要设置 Exchange 和 队列,然后绑定 + + ```java + channel.exchangeDeclare("dlx.exchange", "topic", true, false, null); + channel.queueDeclare("dlx.queue", true, false, false, null); + channel.queueBind("dlx.queue", "dlx.exchange", "#"); + ``` + + 然后我们进行正常声明 Exchange、队列和绑定,此时需要在队列上加上参数 arguments + + ```java + Map agruments = new HashMap(); + agruments.put("x-dead-letter-exchange", "dlx.exchange"); + //这个agruments属性,要设置到声明队列上 + channel.queueDeclare(queueName, true, false, false, agruments); + ``` + +### 生产者 + +```java +import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; + +/** + *

死信队列

+ * 利用 DLX,当消息在一个队列中变成死信(dead message)之后, + * 其能被重新 publish 到另一个 Exchange,这个 Exchange 就是 DLX。 + * + * 消息生产者 + * Created by DHA on 2019/11/20. + */ +public class Producer { + + public static void main(String[] args) throws Exception { + + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.setHost("localhost"); + connectionFactory.setPort(5672); + connectionFactory.setVirtualHost("/"); + + Connection connection = connectionFactory.newConnection(); + Channel channel = connection.createChannel(); + + String exchange = "test_dlx_exchange"; + String routingKey = "dlx.save"; + + String msg = "Hello RabbitMQ DLX Message"; + + AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder() + .deliveryMode(2) + .contentEncoding("UTF-8") + .expiration("10000") + .build(); + channel.basicPublish(exchange, routingKey, true, properties, msg.getBytes()); + } +} +``` + + + +### 消费者 + +```java +import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.DefaultConsumer; +import com.rabbitmq.client.Envelope; + +import java.io.IOException; + +/** + *

自定义消费者

+ * 1 先继承 DefaultConsumer + * 2 然后重写 handleDelivery() 方法 + * + * Created by DHA on 2019/11/20. + */ +public class MyConsumer extends DefaultConsumer{ + public MyConsumer(Channel channel) { + super(channel); + } + + @Override + public void handleDelivery(String consumerTag, Envelope envelope, + AMQP.BasicProperties properties, byte[] body) + throws IOException { + System.err.println("----------consumer message-----------"); + System.err.println("consumerTag:"+consumerTag); + System.err.println("envelope:"+envelope); + System.err.println("properties:"+properties); + System.err.println("body:"+new String(body)); + } +} +``` + + + +```java +import java.util.HashMap; +import java.util.Map; + +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; + +/** + *

死信队列

+ * 利用 DLX,当消息在一个队列中变成死信(dead message)之后, + * 其能被重新 publish 到另一个 Exchange,这个 Exchange 就是 DLX。 + * + * 消息消费者 + * Created by DHA on 2019/11/20. + */ +public class Consumer { + + public static void main(String[] args) throws Exception { + + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.setHost("localhost"); + connectionFactory.setPort(5672); + connectionFactory.setVirtualHost("/"); + + Connection connection = connectionFactory.newConnection(); + Channel channel = connection.createChannel(); + + // 这就是一个普通的交换机 和 队列 以及路由 + String exchangeName = "test_dlx_exchange"; + String routingKey = "dlx.#"; + String queueName = "test_dlx_queue"; + + channel.exchangeDeclare(exchangeName, "topic", true, false, null); + Map agruments = new HashMap(); + agruments.put("x-dead-letter-exchange", "dlx.exchange"); + //这个agruments属性,要设置到声明队列上 + channel.queueDeclare(queueName, true, false,false,agruments); + channel.queueBind(queueName, exchangeName, routingKey); + + //要进行死信队列的声明: + channel.exchangeDeclare("dlx.exchange", "topic", true, false, null); + channel.queueDeclare("dlx.queue", true, false, false, null); + channel.queueBind("dlx.queue", "dlx.exchange", "#"); + + channel.basicConsume(queueName, true, new MyConsumer(channel)); + } +} +``` + diff --git "a/docs/Kafka/2_Kafka\347\232\204\346\236\266\346\236\204.md" "b/docs/Kafka/2_Kafka\347\232\204\346\236\266\346\236\204.md" deleted file mode 100644 index 87998f60..00000000 --- "a/docs/Kafka/2_Kafka\347\232\204\346\236\266\346\236\204.md" +++ /dev/null @@ -1,164 +0,0 @@ -# Kafka的架构 - -## Kafka 的基本组成 - -Kafka 集群由若干个 Broker 组成,Topic 由若干个 Partition 组成,每个 Partition 里面的消息通过 Offset 来获取。 - -### 1. Message - -消息(Message)是 Kafka 中**最基本的数据单元**。 - -Kafka 消息由一个**定长的 Header 和变长的字节数组**组成,其中主要由 key 和 value 构成,key 和 value 也都是字节数组。 - - - -### 2. Broker - -Kafka 集群包含一个或多个服务器,这种服务器被称为 Broker。 - - - -### 3. Topic - -Kafka 根据主题(Topic)对消息进行归类,**发布到 Kafka 集群的每条消息(Message)都需要指定一个 Topic**。 - - - -### 4. Partition - -**物理概念**,每个 Topic 包含一个或多个分区(Partition)。 - -消息发送时都被发送到一个 Topic,其本质就是一个目录,而 Topic 由是由一些 Partition Logs(分区日志)组成,其组织结构如下: - -
- -**每个 Partition 中的消息都是有序的**,生产的消息被不断追加到 Partition Log 上,其中的每一个消息都被赋予了一个唯一的 offset 值,Kafka 通过 **offset 保证消息在分区内的顺序**,offset 的顺序性不跨分区,即 Kafka 只保证在同一个分区内的消息是有序的;同一 Topic 的多个分区内的消息,Kafka 并不保证其顺序性。 - -**Kafka 集群会保存所有的消息,不管消息有没有被消费**; - -我们可以设定消息的过期时间,只有过期的数据才会被自动清除以释放磁盘空间。比如我们设置消息过期时间为 2 天,那么这 2天内的所有消息都会被保存到集群中,数据只有超过了两天才会被清除。 - -Kafka 需要维持的元数据只有一个,即消费消息在 Partition 中的 offset 值,Consumer 每消费一个消息,offset就会 +1。其实消息的状态完全是由 Consumer 控制的,**Consumer 可以跟踪和重设这个 offset 值,Consumer 就可以读取任意位置的消息**。 - -把消息日志以 Partition 的形式存放有多重考虑: - -- 第一,方便在集群中扩展,每个 Partition 可以通过调整以适应它所在的机器,而一个 Topic 又可以由多个Partition 组成,因此整个集群就可以适应任意大小的数据了; -- 第二,就是可以提高并发,因为是以 Partition 为单位进行读写。 - -注意:Partition 并不是越多越好的 - -原因: - -- 分区越多,服务端和客户端需要使用的内存就越多 -- 会降低一定的可用性。某个 Leader 挂了,相比较较少分区的情况,重新选出 Leader,花的时间就会更长。 - - - -### 5. Replication - -Kafka 中每个 Partition 可以有多个副本(Replication),每个副本中包含的消息是一样的。 - -每个分区的副本集合中,都会选举出一个副本作为 Leader 副本,Kafka 在不同的场景下会采用不同的选举策略。所有的读写请求都由选举出的 Leader 副本处理,其他都作为 Follower 副本,**Follower 副本仅仅是从 Leader 副本处把数据拉取(pull)到本地之后,同步更新到自己的 Log 中**。 - -一般情况下,同一分区的多个副本会被分配到不同的 Broker上。当 Leader 所在的 Broker 宕机之后,可以重新选举新的 Leader,继续对外提供服务。 - -
- - - -### 6. Producer - -消息生产者(Producer),向 Broker 发送消息的客户端。 - -Producer 直接发送消息到 Broker上的 Leader Partition,不需要经过任何中介或其他路由转发。 - -**Producer 客户端自己控制着消息被推送(push)到哪些 Partition**。实现方式可以是随机分配、实现一类随机负载均衡算法,或者指定一些分区算法。Kafka 提供了接口供用户实现自定义的 Partition,用户可以为每个消息指定一个 Partition Key,通过这个 key 来实现一些 Hash 分区算法。比如,把 userid 作为 Partition Key 的话,相同 userid 的消息将会被推送到同一个 Partition。 - -Kafka Producer 可以将消息在内存中累计到一定数量后作为一个**批量发送请求**。批量发送的数量大小可以通过Producer 的参数控制,参数值可以设置为累计的消息的数量(如 500 条)、累计的时间间隔(如 100ms )或者累计的数据大小(64 KB)。通过增加 Batch的大小,可以减少网络请求和磁盘 I / O 的次数,当然具体参数设置需要在**效率**和**时效性**方面做一个权衡。 - -Producer 可以**异步地并行地**向 Kafka发送消息,但是通常 Producer 在发送完消息之后会得到一个 future响应,返回的是 offset 值或者发送过程中遇到的错误。通过 request.required.acks 参数来设置 Leader Partition 收到确认的副本个数: - -| ack | 说明 | -| :--: | :----------------------------------------------------------: | -| 0 | Producer **不会等待 Broker 的响应**
Producer 无法知道消息是否发送成功, 这样**可能会导致数据丢失**,但会得到最大的系统吞吐量。 | -| 1 | Producer 会在 **Leader Partition** 收到消息时得到 Broker 的一个确认
这样会有更好的可靠性,因为客户端会等待直到 Broker 确认收到消息。 | -| -1 | Producer 会在**所有备份的 Partition** 收到消息时得到 Broker 的确认
这个设置可以得到最高的可靠性保证。 | - -发布消息时,Kafka Client 先构造一条消息,将消息加入到消息集 set 中(Kafka支持批量发布,可以往消息集合中添加多条消息,一次行发布),send 消息时,Producer Client 需指定消息所属的 Topic。 - - - -### 7. Consumer - -消息消费者(Consumer),从 Broker 读取消息的客户端。 - -消费者(Consumer)的主要工作是从 Topic 中拉取消息,并对消息进行消费。某个消费者消费到 Partition 的哪个位置(offset)的相关信息,是 Consumer 自己维护的。Consumer 可以自己决定如何读取 Kafka 中的数据。比如,Consumer 可以通过重设 offset 值来重新消费已消费过的数据。不管有没有被消费,Kafka 会保存数据一段时间,这个时间周期是可配置的,只有到了过期时间,Kafka 才会删除这些数据。 - -这样设计非常巧妙,**避免了 Kafka Server 端维护消费者消费位置的开销**,尤其是在消费数量较多的情况下。另一方面,如果是由 Kafka Server 端管理每个 Consumer 消费状态,一旦 Kafka Server 端出现延时或是消费状态丢失,将会影响大量的 Consumer。另一方面,这一设计也提高了 Consumer 的灵活性,Consumer 可以按照自己需要的顺序和模式拉取消息进行消费。例如:Consumer 可以通过修改其消费的位置实现针对某些特殊 key 的消息进行反复消费,或是跳过某些消息的需求。 - -Kafka 提供了两套 Consumer Api,分为 Simple Api 和 High-Level Api。 - -- Simple Api 是一个底层的 API,它维持了一个和单一 Broker 的连接,并且这个 API 是完全无状态的,每次请求都需要指定 offset 值,因此,这套 API 也是最灵活的。 - -- High-Level API 封装了对集群中一系列 Broker 的访问,可以透明的消费一个 Topic。它自己维持了已消费消息的状态,即每次消费的都是下一个消息。 - - High-Level API 还支持以组的形式消费 Topic,如果 Consumers 有同一个组名,那么 Kafka 就相当于一个队列消息服务,而各个 Consumer 均衡地消费相应 Partition 中的数据。若 Consumers 有不同的组名,那么此时 Kafka 就相当于一个广播服务,会把 Topic 中的所有消息广播到每个 Consumer。 - -
- - - -### 8. Consumer Group - -在 Kafka 中,多个 Consumer 可以组成一个 Consumer Group,一个 Consumer 只能属于一个 Consumer Group。**Consumer Group 保证其订阅的 Topic 的每个 Partition 只会被此 Consumer Group 中的一个 Consumer 处理**。如果不同 Consumer Group 订阅了同一 Topic,Consumer Group 彼此之间不会干扰。这样,如果要实现一个消息可以被多个消费者同时消费(“广播”)的效果,则将每个消费者放入单独的一个 Consumer Group;如果要实现一个消息只被一个消费者消费(“独占”)的效果,则将所有的 Consumer 放入一个 Consumer Group 中。 - -注意:Consumer Group 中消费者的数量并不是越多越好,当其中消费者数量超过分区的数量时,会导致有消费者分配不到分区,从而造成消费者的浪费。 - -Producer、Consumer 和 Consumer Group 之间的关系: - -
- - - -### 9. Zookeeper - -存放 Kafka 集群相关元数据的组件。在 Zookeeper 集群中会 - -- **保存 Topic 的状态信息**,例如分区的个数、分区的组成、分区的分布情况等; -- **保存 Broker 的状态信息**; -- **保存 Consumer 的消费信息** - -通过这些信息,Kafka 很好地将消息生产、消息存储、消息消费的过程结合起来。 - - - -## Kafka 的拓扑结构 - -### 典型的 Kafka 集群的拓扑结构 - - - -
- - - -Kafka 集群包含若干个 Producer,若干个 Broker (Kafka 集群支持水平扩展,一般 Broker 数量越多,整个 Kafka 集群的吞吐率也就越高),若干个 Consumer Group,以及一个 Zookeeper 集群。 - -Kafka 通过 Zookeeper 管理集群配置。 - -Producer 使用 Push 模式将消息发布到 Broker 上,Consumer 使用 Pull 模式从 Broker 上订阅并消费消息。 - - - -### Kafka 数据流 - -
- -Producers 往 Brokers 中指定的 Topic Push 消息,Consumers 从 Brokers 里面 Pull 指定 Topic 的消息,然后进行业务处理。 - -图中有两个 Topic: - -- Topic-0 有两个 Partition,Partition-0 和 Partition-1; -- Topic-1 有一个 Partition。 - -可以看到 Consumer-Group-1 中的 Consumer-2 没有分到 Partition 处理,这是有可能出现的。 \ No newline at end of file diff --git "a/docs/Kafka/3_Kafka\347\232\204\351\253\230\345\217\257\347\224\250\345\216\237\347\220\206.md" "b/docs/Kafka/3_Kafka\347\232\204\351\253\230\345\217\257\347\224\250\345\216\237\347\220\206.md" deleted file mode 100644 index 4b45a705..00000000 --- "a/docs/Kafka/3_Kafka\347\232\204\351\253\230\345\217\257\347\224\250\345\216\237\347\220\206.md" +++ /dev/null @@ -1,25 +0,0 @@ -# Kafka 的高可用原理 - -Kafka 集群由若干个 Broker 组成,Topic 由若干个 Partition 组成,每个 Partition 可存在不同的 Broker 上。可以这样说,一个 Topic 的数据,分散在多个机器上,即每个机器上都存放一部分数据。 - -## Kafka 0.8 以前 - -Kafka 0.8 以前是没有高可用机制的。 - -假设一个 Topic,由 3 个 Partiton 组成。3 个 Partition 在不同机器上,如果其中某一台机器宕掉了,则 Topic 的部分数据就丢失了。 - -## Kafka 0.8 以后 - -Kafka 0.8 以后,通过**副本机制**来实现高可用。 - -每个 Partition 的数据都会同步到其他机器上,形成多个 Partition 副本。从每个 Partition 的副本集合中,选举出 Leader,其他的都是 Follower。Producer 和 Consumer 就和 Leader 打交道: - -- 写数据时,Leader 会将所用数据都同步到 Follower 上 -- 读数据时,直接读取 Leader 上的数据 - -这样,若某个 Broker 宕机了,Broker 上的 Partition 在其他机器上是有副本的;若宕机的 Broker 上面有某个 Partition 的 Leader,则此时会从 Follower 中重新选择一个新的 Leader 出来。 - -注意:Leader 的读写细节 - -- 写数据时,Producer 向 Leader 写入,接着其他 Follower 主动从 Leader 中 pull 数据,当所有 Follower 同步好数据,就发送确认信息给 Leader,Leader 收到 Follower 的确认后,就返回写成功消息给 Producer。 -- 读数据时,只会从 Leader 中读取,但当只有一个消息已被所有 Follower 都同步成功,返回确认后时,这个消息才被消费者读到。 \ No newline at end of file diff --git "a/docs/Kafka/4_Kafka\344\270\255\344\270\200\344\272\233\345\270\270\350\247\201\351\227\256\351\242\230.md" "b/docs/Kafka/4_Kafka\344\270\255\344\270\200\344\272\233\345\270\270\350\247\201\351\227\256\351\242\230.md" deleted file mode 100644 index e4ffec07..00000000 --- "a/docs/Kafka/4_Kafka\344\270\255\344\270\200\344\272\233\345\270\270\350\247\201\351\227\256\351\242\230.md" +++ /dev/null @@ -1,57 +0,0 @@ -# Kafka 中一些常见的问题 - -## 消息丢失问题 - -### 1. 消费端丢失数据 - -默认情况下,Kafka 会自动提交 Offset,Kafka 认为 Consumer 已经处理消息了,但是 Consumer 可能在处理消息的过程中挂掉了。重启系统后,Consumer 会根据提交的 Offset 进行消费,也就丢失了一部分数据。 - -解决:关闭自动提交 Offset,在处理完之后自己手动提交 Offset,就可以保证数据不会丢失。但可能会存在消息重复消费问题。 - - - -### 2. Kafka 丢失数据 - -比较常见的一个场景:Kafka 某个 Broker 宕机,然后重新选举新的 Leader ,但此时其他的 Follower 部分数据尚未同步,结果此时 Leader 挂了,然后选举某个 Follower 成 Leader ,丢失一部分数据。 - -所以此时一般设置如下 4 个参数: - -- Topic 设置 `replication.factor` 参数 - - 参数值必须大于 1,要去每个 Partition 必须有至少 2 个副本。 - -- Kafka 服务端设置 `min.insync.replicas` 参数 - - 参数值必须大于 1,要去每个 Partiton 必须有至少 2 个副本。 - -- Producer 设置 `acks=all` - - 要求每条数据,必须是**写入所有副本,才认为写成功**。 - -- Producer 端设置 `retries=MAX` - - MAX 即是一个超级大的数字,表示无限次重试。`retries=MAX`要求一旦写入数据失败,就无限重试。 - - - -### 3. Producer 丢失数据 - -如果 Producer 端设置了 `acks=all`,则不会丢失数据。 - -Leader 在所有的 Follower 都同步到了消息之后,才认为本次写成功。如果没满足这个条件,生产者会进行无限次重试。 - - - -## 消息重复消费问题 - -### 原理 - -Consumer 消费了数据后,每个一段时间,会将已消费过的消息的 Offset 进行提交,这样,重启后,可以继续从上次消费过的 Offset 来继续消费。测试时,直接 kill 进程,然后再重启后,会导致 Consumer 将有些消息处理了,但是还未来得及提交 Offset,重启后,少数消息会再消费一次。 - -### 解决 - -需要结合具体业务来思考,可从以下几个思路来考虑: - -- 如果要将数据写入数据库中,先根据主键查查询,如果这数据已存在,就不用插入数据了。 -- 向 Redis 中写入数据,可以使用 set,这样数据不会重复 -- 基于数据库的唯一键来保证重复数据不会重复插入多条。因为有唯一键约束了,重复数据插入只会报错,不会导致数据库中出现脏数据。 \ No newline at end of file diff --git "a/docs/Kafka/5_Kafka\347\211\271\347\202\271.md" "b/docs/Kafka/5_Kafka\347\211\271\347\202\271.md" deleted file mode 100644 index 0db5f19f..00000000 --- "a/docs/Kafka/5_Kafka\347\211\271\347\202\271.md" +++ /dev/null @@ -1,78 +0,0 @@ -# Kafka 特点 - -Kafka 特点: - -- 高可用 -- 持久性 -- 数据不易丢失 -- 高吞吐量 - -## Kafka 高可用 - -基于**副本机制**实现 Kafka 的高可用。 - - - -## Kafka 持久性 - -Kafka 集群接收到 Producer 发过来的消息后,将其持久化到磁盘。此外,还支持数据备份。 - - - -## Kafka 数据不易丢失 - -通过合理的配置,Kafka 消息不易丢失。 - - - -## Kafka 高吞吐量 - -Kafka 高吞吐量的原因: - -- 分区 -- 网路传输 -- 顺序读写 -- 零拷贝 - -### 分区 - -当生产者向对应 Topic 传递消息,消息通过**负载均衡机制**传递到不同的 Partition 以减轻单个服务器实例的压力; - -一个 Consumer Group 中可以有多个 Consumer,多个 Consumer 可以同时消费不同 Partition 的消息,大大的提高了消费者的并行消费能力。 - - - -### 网络传输 - -- 批量发送:在发送消息的时候,Kafka 不会直接将少量数据发送出去,否则每次发送少量的数据会增加网络传输频率,降低网络传输效率。Kafka 会先将消息缓存在内存中,当超过一个的大小或者超过一定的时间,那么会将这些消息进行批量发送。 -- 端到端压缩: Kfaka会将这些批量的数据进行压缩,将一批消息打包后进行压缩,发送给 Broker 服务器后,最终这些数据还是提供给消费者用,所以数据在服务器上还是保持压缩状态,不会进行解压,而且频繁的压缩和解压也会降低性能,最终还是以压缩的方式传递到消费者的手上,在 Consumer 端进行解压。 - - - -### 顺序读写 - -Kafka 是个可持久化的日志服务,它将数据以数据日志的形式进行追加,最后持久化在磁盘中。 - -Kafka 消息存储时依赖于**文件系统**。为了利用数据的**局部相关性**:操作系统从磁盘中**以数据块为单位**读取数据,将一个数据块读入内存中,如果有相邻的数据,就不用再去磁盘中读取。所以,在某些情况下,**顺序磁盘访问能比随机内存访问还要快**。同时在写数据的时候也是将一整块数据块写入磁盘中,大大提升 I / O 效率。 - - - -### 零拷贝 - -普通的数据拷贝: - -
- -零拷贝主要的任务是避免 CPU 做大量的数据拷贝任务,减少不必要的拷贝。 - -**内存映射文件(Memory Mapped Files,mmap)**在 64 位操作系统中一般可以表示 20G 的数据文件,它的工作原理是直接利用操作系统的页缓存来实现文件到物理内存的直接映射。 - -
- -使用 mmap 替代 read 很明显减少了 1 次拷贝,当拷贝数据量很大时,无疑提升了效率。 - - - -# 参考资料 - -- [解密Kafka吞吐量高的原因](https://news.cndns.com/ArticlesDetail/articlesdel/id/9685) \ No newline at end of file diff --git "a/docs/MySQL/1_\351\224\201\346\234\272\345\210\266.md" "b/docs/MySQL/1_\351\224\201\346\234\272\345\210\266.md" index bbd7bcc4..d3998817 100644 --- "a/docs/MySQL/1_\351\224\201\346\234\272\345\210\266.md" +++ "b/docs/MySQL/1_\351\224\201\346\234\272\345\210\266.md" @@ -50,7 +50,7 @@ - 会出现死锁 - 锁定粒度介于表锁和行锁之间,并发度一般 -
+
@@ -104,4 +104,13 @@ 先执行,如果存在冲突,则采取一个补偿措施(比如告知用户失败)。一般有 2 种实现方式: - 使用版本号 -- 使用时间戳 \ No newline at end of file +- 使用时间戳 + +## MySQL 隐式与显式锁定 +MySQL 的 InnoDB 存储引擎采用两段锁协议,会根据隔离级别在需要的时候自动加锁,并且所有的锁都是在同一时刻被释放,这被称为隐式锁定。 + +InnoDB 也可以使用特定的语句进行显示锁定: +```sql +SELECT ... LOCK In SHARE MODE; +SELECT ... FOR UPDATE; +``` diff --git "a/docs/MySQL/2_\344\272\213\345\212\241\351\232\224\347\246\273\347\272\247\345\210\253\345\256\236\347\216\260.md" "b/docs/MySQL/2_\344\272\213\345\212\241\351\232\224\347\246\273\347\272\247\345\210\253\345\256\236\347\216\260.md" index 832247db..575e0580 100644 --- "a/docs/MySQL/2_\344\272\213\345\212\241\351\232\224\347\246\273\347\272\247\345\210\253\345\256\236\347\216\260.md" +++ "b/docs/MySQL/2_\344\272\213\345\212\241\351\232\224\347\246\273\347\272\247\345\210\253\345\256\236\347\216\260.md" @@ -1,36 +1,107 @@ # 事务的隔离级别的实现 -## 多版本并发控制 +## 多版本并发控制-MVCC -多版本并发控制(Multi-Version Concurrency Control, MVCC)以乐观锁为理论基础,和基于锁的并发控制最大的区别和优点是:**读不加锁,读写不冲突**。 +多版本并发控制(Multi-Version Concurrency Control, MVCC)以**乐观锁**为理论基础,和基于锁的并发控制最大的区别和优点是:**读不加锁,读写不冲突**。 -### Undo 日志 +### **事务版本号** + +每次事务开启前都会从数据库获得一个自增长的事务 id,可以从事务 id 判断事务的执行先后顺序。 + +### 隐藏字段 + +InnoDB 存储引擎为每行记录都添加了 3 个隐藏字段: + +- DB_ROW_ID:数据行 id,用于标识一行数据。并不是必要的,如果创建的表中有主键或者非 NULL 唯一键时都不会包含 DB_ROW_ID 列 + +- DB_TRX_ID:表示最后一次插入或更新该行的事务 id。此外,delete 操作在内部被视为更新,只不过会在记录头中的 deleted_flag 字段将其标记为已删除 + +- DB_ROLL_PTR:当前数据记录的上一个版本的指针。每次对某条数据记录进行改动时,都会把旧版本数据记录按照一定格式写入到回滚日志 (undo log) 中,而 DB_ROLL_PTR 列则保存了该旧版本数据记录在回滚日志中的位置,相当于一个指针。 + +### undo log MVCC 将每一个更新的数据标记一个版本号,在更新时进行版本号的递增,插入时新建一个版本号,同时旧版本数据存储在 Undo 日志中,该日志通过**回滚指针**把一个数据行(Record)的所有快照连接起来。 -
+假设在 MySQL 创建一个表 user,包含主键 id 和一个字段 name。我们先插入一个数据行,然后对该数据行执行两次更新操作。 + +```sql +INSERT INTO user(id, name) VALUES(1, "a"); +UPDATE name SET name="b" WHERE id=1; +UPDATE name SET name="c" WHERE id=1; +``` + +因为没有使用 `START TRANSACTION` 将上面的操作当成一个事务来执行,根据 MySQL 的 AUTOCOMMIT 机制,每个操作都会被当成一个事务来执行,所以上面的操作总共涉及到三个事务。 + +
+ +undo log 主要有两个作用: + +- 当事务回滚时用于将数据恢复到修改前的样子,保证事务原子性。 +- 另一个作用是实现 MVCC ,当读取记录时,若该记录被其他事务占用或当前版本对该事务不可见,则可以通过 undo log 读取之前的版本数据,以此实现非锁定读 + +### 快照(ReadView) + +#### ReadView 结构 + +```c +class ReadView { + private: + trx_id_t m_low_limit_id; /* 大于这个 id 的事务均不可见 */ + trx_id_t m_up_limit_id; /* 小于这个 id 的事务均可见 */ + trx_id_t m_creator_trx_id; /* 创建该 ReadView 的事务 id */ + ids_t m_ids; /* 创建 ReadView 时的活跃事务列表 */ + m_closed; /* 标记 ReadView 是否 close */ +} +``` + +ReadView 主要有以下字段: + +- m_creator_trx_id:创建该 ReadView 的事务 id + +- m_ids:ReadView 创建时其他未提交的活跃事务 id 列表。创建 ReadView 时,将当前未提交事务 ID 记录下来,后续即使它们修改了记录行的数据,对于当前事务也是不可见的。注意 m_ids 不包括当前事务自己和已提交的事务(正在内存中) + +- m_low_limit_id:目前出现过的**最大的事务 id+1**,即下一个将被分配的事务 id。 +- m_up_limit_id :活跃事务列表 m_ids 中**最小的事务 id**,如果 m_ids 为空,则 m_up_limit_id 等于 m_low_limit_id。 + +#### 数据可见性算法 + +InnoDB 存储引擎在开启一个新事务后,执行每个 select 语句前,都会创建一个 ReadView,ReadView 中保存了当前数据库系统中正处于活跃(没有 commit)的事务的 id 号,即系统中当前不应该被该事务看到的其他事务 id 列表 m_ids。当用户在该事务读取某行记录时,InnoDB 会将该行记录的 DB_TRX_ID 与 ReadView 中的一些变量及当前事务 id 进行比较,判断是否满足可见性条件: + +- DB_TRX_ID < m_up_limit_id,表明最新修改该行的事务(DB_TRX_ID)在当前事务创建快照之前就提交了,所以该记录行的值对当前事务是可见的 + +- DB_TRX_ID >= m_low_limit_id,那么表明最新修改该行的事务(DB_TRX_ID)在当前事务创建快照之后才修改该行,所以该记录行的值对当前事务不可见 +- m_ids 为空,则表明在当前事务创建快照之前,修改该行的事务就已经提交了,所以该记录行的值对当前事务是可见的 +- m_up_limit_id <= DB_TRX_ID < m_up_limit_id,表明最新修改该行的事务(DB_TRX_ID)在当前事务创建快照的时候可能处于“活动状态”或者“已提交状态”;所以就要对活跃事务列表 m_ids 进行二分查找(m_ids 是有序的): + - 在 m_ids 中找到 DB_TRX_ID,表明在当前事务创建快照前,该记录行的值被事务 id 为 DB_TRX_ID 的事务修改了,但没有提交;或者在当前事务创建快照后,该记录行的值被事务 id 为 DB_TRX_ID 的事务修改了。这些情况下,这个记录行的值对当前事务都是不可见的 + - 在 m_ids 中找不到 DB_TRX_ID,表明是事务 id 为 DB_TRX_ID 的事务在修改该记录行的值后,在当前事务创建快照前就已经提交了,所以记录行对当前事务可见 -### 快照读与当前读 +在记录行快照不可见的情况下,在该记录行的 DB_ROLL_PTR 指针所指向的 undo log 取出快照记录,用快照记录的 DB_TRX_ID 再重新开始判断,直到找到可见的快照版本或返回空。 -- 快照读 +事务可见性示意图: - 快照读只是针对于目标数据的版本号小于等于当前事务的版本号,也就是说读数据的时候可能读到旧数据,但是这种快照读不需要加锁。也就是说,使用 MVCC 读取的是快照中的数据,这样可以减少加锁所带来的开销。 +
- ```sql - select * from table ...; - ``` +### 快照读 & 当前读 -- 当前读 +#### 快照读 - 当前读是读取当前数据的最新版本,但是更新等操作会对数据加锁,所以当前读需要获取记录的行锁,存在锁争的问题。以下第一个语句需要加 S 锁,其它都需要加 X 锁。 +快照读只是针对于目标数据的版本号小于等于当前事务的版本号,也就是说读数据的时候可能读到旧数据,但是这种快照读不需要加锁。也就是说,使用 MVCC 读取的是快照中的数据,这样可以减少加锁所带来的开销。 - ```sql - select * from table where ? lock in share mode; # 加 S 锁 - select * from table where ? for update; - insert; - update; - delete; - ``` +```sql +select * from table ...; +``` + +#### 当前读 + +当前读是读取当前数据的最新版本,但是更新等操作会对数据加锁,所以当前读需要获取记录的行锁,存在锁争的问题。以下第一个语句需要加 S 锁,其它都需要加 X 锁。 + +```sql +select * from table where ? lock in share mode; # 加 S 锁 +select * from table where ? for update; +insert; +update; +delete; +``` @@ -44,30 +115,21 @@ MVCC 将每一个更新的数据标记一个版本号,在更新时进行版本 ### 2. 提交读(READ COMMITTED) 和可重复读(REPEATABLE READ) -对于读操作,由于 MVCC 的引入,分为快照读和当前读: +RR 是 InnoDB 存储引擎的默认事务隔离级别。 -- 快照读:读取的是快照中的数据,不需加锁 -- 当前读:读取的是最新的数据,需要加锁 +RC 和 RR 都是基于 MVCC 实现的,但生成快照的时机不同: -RC 和 RR 都是基于 MVCC 实现的,但是读取的快照数据是不相同的: - -- RC 级别下。读取的总是最新的数据。有可能会出现一个事务中两次读到了不同的结果。 -- RR 级别下。总是读到小于等于此事务的数据,也就实现了可重复读。 +- RC 级别下。**每次 select 查询**前都会生成一个 ReadView。有可能会出现一个事务中两次读到了不同的结果。 +- RR 级别下。只在事务开始后, **第一次 select 查询**前生成一个ReadView。 ### 3. 未提交读(READ UNCOMMITTED) 总是读取最新的数据行,无需使用 MVCC。 - - -## Next-Key Locks +## InnoDB 行锁算法 Next-Key Locks 是 MySQL 的 InnoDB 存储引擎的一种锁实现。 -MVCC 不能解决幻影读问题,Next-Key Locks 就是为了解决这个问题而存在的。 - -**在可重复读(REPEATABLE READ)隔离级别下,使用 MVCC + Next-Key Locks 可以解决幻读问题。** - ### Record Locks **锁定一个记录上的索引,而不是记录本身**。 @@ -76,7 +138,9 @@ MVCC 不能解决幻影读问题,Next-Key Locks 就是为了解决这个问题 ### Gap Locks -**锁定索引之间的间隙,但是不包含索引本身**。例如当一个事务执行以下语句,其它事务就不能在 c 中插入 15。 +**锁定索引之间的间隙,但是不包含索引本身**。 + +例如当一个事务执行以下语句,其它事务就不能在 c 中插入 15。 ```sql SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE; @@ -84,17 +148,20 @@ SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE; ### Next-Key Locks -它是 Record Locks 和 Gap Locks 的结合,**不仅锁定一个记录上的索引,也锁定索引之间的间隙,是一个前开后闭区间**。例如一个索引包含以下值:10, 11, 13, and 20,那么就需要锁定以下区间: +它是 Record Locks 和 Gap Locks 的结合,**不仅锁定一个记录上的索引,也锁定索引之间的间隙,是一个前开后闭区间**。例如一个索引包含以下值:10, 11 and 20,那么就需要锁定以下区间: ```sql (-∞, 10] (10, 11] -(11, 13] -(13, 20] -(20, +supremum) +(11, 20] +(20, +∞) ``` -### 关于 Next-Key Locks 的几个问题 +如下图所示: + +
+ +### 几个问题 > 问题一:对主键索引或唯一索引会使用间隙锁吗? @@ -110,11 +177,9 @@ delete from tb where id = 9 -- key 是唯一索引 ``` -根据 id=9 条件定位,此时给 id = 9 的索引加上记录锁,根据 name 值到主索引中检索获得记录,再给该记录加上记录锁。 - -
- +根据 id=9 条件定位,此时给 id = 9 的索引加上记录锁,根据 name 值(name是主键)到主索引中检索获得记录,再给该记录加上记录锁。 +
> 问题二:间隙锁是否用在非唯一索引的当前读中? @@ -126,11 +191,9 @@ delete from tb1 where id = 9 -- key 是非唯一索引 ``` -
- -可以看出,在 (6,9] 、(9,11] 加了间隙锁。 - +
+可以看出,在 (6,9]、(9,11] 加了间隙锁。 > 问题三:间隙锁是否用在不走索引的当前读中? @@ -142,7 +205,7 @@ delete from tb2 where id = 9 -- 没有为 id 建立索引 ``` -
+
此时对所有的间隙都上锁(功能上相当于锁表)。 @@ -158,11 +221,25 @@ delete from tb2 where id = 9 会加间隙锁 -- 不走索引: +- 不走索引: + + 对所有间隙都加间隙锁,相当于锁表 + +## 解决幻读问题 + +InnoDB 存储引擎在 RR 级别下通过 MVCC + Next-key Lock 来解决幻读问题: + +- 执行 `select * from table ...;` 会以 MVCC **快照读**的方式读取数据。 + + 在快照读的情况下,RR 隔离级别只会在事务开启后的第一次查询生成 ReadView,使用至事务提交。 在生成 ReadView 之后其它事务所做的更新、插入记录版本对当前事务并不可见,实现了可重复读和防止快照读下的 “幻读”。 + +- 执行 `select * from table where ? lock in share mode/for update; `、`insert`、`update`、`delete` 会以 MVCC **当前读**的方式读取数据。 + + 在当前读的情况下,读取的都是最新的数据,如果存在其它事务插入新的记录,并且刚好在当前事务查询范围内,就会产生幻读。 - 对所有间隙都加间隙锁,相当于锁表 + InnoDB 就使用 Next-key Lock 来防止这种情况:当执行当前读时,在锁定读取到的记录时,也会锁定它们的间隙,防止其它事务在查询范围内插入数据。 # 参考资料 - [MySQL-InnoDB-MVCC多版本并发控制](https://segmentfault.com/a/1190000012650596) -- [Innodb中的事务隔离级别和锁的关系](https://tech.meituan.com/2014/08/20/innodb-lock.html) \ No newline at end of file +- [Innodb中的事务隔离级别和锁的关系](https://tech.meituan.com/2014/08/20/innodb-lock.html) diff --git "a/docs/MySQL/3_\347\264\242\345\274\225.md" "b/docs/MySQL/3_\347\264\242\345\274\225.md" index ed77f74a..b08a6ddf 100644 --- "a/docs/MySQL/3_\347\264\242\345\274\225.md" +++ "b/docs/MySQL/3_\347\264\242\345\274\225.md" @@ -6,9 +6,7 @@ ## 目的 -数据库索引好比一本书的目录,提高查询效率。 - -但是为表设置索引要付出相应的代价: +数据库索引好比一本书的目录,提高查询效率。但是为表设置索引要付出相应的代价: - 增加了数据库的存储空间 - 在插入和修改时需花费更多的时间(因为索引也要随之变动) @@ -57,7 +55,7 @@ c)非叶子节点的指针:P[1],P[2],... ,P[M];其中 P[1] 指向关键字小于 K[1] 的子树,P[M] 指向关键字大于 K[M-1] 的子树,其他 P[i] 关键字属于(K[i-1],K[i]) 的子树 -
+
### B+ 树 @@ -71,7 +69,7 @@ B+ 树是 B 树的变体,其定义基本与 B 树相同,除了: - 所有叶子节点均有一个链指针指向下一个叶子节点 -
+
数据库系统普遍采用 B+ 树作为索引结构,主要有以下原因: @@ -106,13 +104,13 @@ B+ 树是 B 树的变体,其定义基本与 B 树相同,除了: ## 索引的物理存储 -MySQL 索引使用的是B树中的 B+ 树,但对于主要的两种存储引擎的实现方式是不同的。 +MySQL 索引使用的是 B 树中的 B+ 树,但索引是在存储引擎层实现的,而不是在服务器层实现的,所以不同存储引擎具有不同的索引类型和实现。 ### MyISAM 索引存储机制 MyISAM 引擎使用 B+ 树作索引结构,**叶子节点的 data 域存放的是数据记录的地址**,所有索引均是非聚集索引。 -
+
上图是一个 MyISAM 表的主索引(Primary key)示意图。 @@ -120,7 +118,7 @@ MyISAM 引擎使用 B+ 树作索引结构,**叶子节点的 data 域存放的 在 MyISAM 中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求 key 是唯一的,而辅助索引的 **key 可以重复**。如果在 Col2 上建立一个辅助索引,则该辅助索引的结构如下: -
+
同样也是一棵 B+ 树,data 域保存数据记录的地址。 @@ -132,7 +130,7 @@ InnoDB 也使用 B+ 树作为索引结构。有且仅有一个聚集索引,和 InnoDB 的数据文件本身就是索引文件。MyISAM 索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在 InnoDB 中,表数据文件本身就是按 B+ 树组织的一个索引结构,这棵树的**叶子节点 data 域保存了完整的数据记录**。这个索引的 key 是数据表的主键,因此 **InnoDB 表数据文件本身就是主索引**。 -
+
上图是 InnoDB 主索引(同时也是数据文件)的示意图。可以看到叶子节点包含了完整的数据记录。 @@ -144,7 +142,7 @@ InnoDB 的数据文件本身就是索引文件。MyISAM 索引文件和数据文 与 MyISAM 索引的不同是 **InnoDB 的辅助索引 data 域存储相应记录主键的值**而不是地址。例如,定义在 Col3 上的一个辅助索引: -
+
@@ -162,6 +160,12 @@ InnoDB 的数据文件本身就是索引文件。MyISAM 索引文件和数据文 +## 索引的优点 + +- 大大减少了服务器需要扫描的数据行数。 +- 帮助服务器避免进行排序和分组,以及避免创建临时表(B+Tree 索引是有序的,可以用于 ORDER BY 和 GROUP BY 操作。临时表主要是在排序和分组过程中创建,不需要排序和分组,也就不需要创建临时表)。 +- 将随机 I/O 变为顺序 I/O(B+Tree 索引是有序的,会将相邻的数据都存储在一起)。 + ## 建索引的原则 - 最左前缀匹配原则 diff --git "a/docs/MySQL/4_MySQL\346\236\266\346\236\204.md" "b/docs/MySQL/4_MySQL\346\236\266\346\236\204.md" index ae1262e8..38a768d9 100644 --- "a/docs/MySQL/4_MySQL\346\236\266\346\236\204.md" +++ "b/docs/MySQL/4_MySQL\346\236\266\346\236\204.md" @@ -12,7 +12,7 @@ 主要负责数据的存储和读取,采用可以替换的插件式架构,支持 InnoDB、MyISAM、Memory 等多个存储引擎,其中 InnoDB 引擎有自有的日志模块 redolog 模块。**现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始就被当做默认存储引擎了**。 -
+
@@ -62,7 +62,7 @@ MySQL 没有命中缓存,那么就会进入分析器,分析器主要是用 ## 存储引擎 -### 1. InnoDB +### InnoDB **是 MySQL 默认的事务型存储引擎,只有在需要它不支持的特性时,才考虑使用其它存储引擎**。 @@ -74,7 +74,7 @@ MySQL 没有命中缓存,那么就会进入分析器,分析器主要是用 支持真正的在线热备份。其它存储引擎不支持在线热备份,要获取一致性视图需要停止对所有表的写入,而在读写混合场景中,停止写入可能也意味着停止读取。 -### 2. MyISAM +### MyISAM 设计简单,数据以紧密格式存储。对于只读数据,或者表比较小、可以容忍修复操作,则依然可以使用它。 @@ -108,14 +108,14 @@ MySQL 没有命中缓存,那么就会进入分析器,分析器主要是用 -## 语句执行分析* +## 语句执行分析 一条 sql 语句是如何执行的呢?其实我们的 sql 可以分为两种: - 一种是查询 - 一种是更新(增加,更新,删除) -### 1. 查询语句分析 +### 查询语句分析 ```sql SELECT * FROM @@ -145,9 +145,8 @@ WHERE age='18' and name=' 张三 '; 如果有权限就会调用数据库引擎接口,返回引擎的执行结果。 - -### 2. 更新语句分析 +### 更新语句分析 ```sql UPDATE tb_student SET age='19' diff --git "a/docs/MySQL/5_MySQL\344\274\230\345\214\226.md" "b/docs/MySQL/5_MySQL\344\274\230\345\214\226.md" index b1eafade..d470d532 100644 --- "a/docs/MySQL/5_MySQL\344\274\230\345\214\226.md" +++ "b/docs/MySQL/5_MySQL\344\274\230\345\214\226.md" @@ -1,42 +1,76 @@ -# MySQL 优化 +# MySQL 调优 ## SQL 语句优化 ### 查询优化 -1. 先开启慢查询日志 +#### 1. Explain 分析 + +- 先开启慢查询日志 ```sql set global slow_query_log = on # 开启慢查询日志,默认是关闭的 set global long_qurey_time=0.5 # 设置慢查询时间阈值,单位:秒 ``` -2. 定位慢查询语句 +- 定位慢查询语句 +- explain 进行分析,相应字段: + - id:id 值越大,越先执行。无子查询时,id=1 + - type: + - NULL,执行时甚至不用访问表或索引 + - eq_ref,使用的是唯一索引 + - ref,使用的是非唯一索引或者是唯一索引的前缀 + - index,类似全表扫描,按照索引次序扫描表 + - rows:表扫描的行数 + - key:实际用到的索引,为空表没有用到索引 + - extra:十分重要的额外信息 + - using filesort + - using temporary + +#### 2. 优化数据访问 + +**减少请求的数据量** + +- 只返回必要的列:最好不要使用 SELECT * 语句。 +- 只返回必要的行:使用 LIMIT 语句来限制返回的数据。 +- 缓存重复查询的数据:使用缓存可以避免在数据库中进行查询,特别在要查询的数据经常被重复查询时,缓存带来的查询性能提升将会是非常明显的。 + +**减少服务器端扫描的行数** + +最有效的方式是使用索引来覆盖查询。 + +#### 3. 重构查询方式 + +**切分大查询** + +一个大查询如果一次性执行的话,可能一次锁住很多数据、占满整个事务日志、耗尽系统资源、阻塞很多小的但重要的查询。 + +**分解大连接查询** + +将一个大连接查询分解成对每一个表进行一次单表查询,然后在应用程序中进行关联,这样做的好处有: -3. explain 进行分析,相应字段: +- 让缓存更高效。对于连接查询,如果其中一个表发生变化,那么整个查询缓存就无法使用。而分解后的多个查询,即使其中一个表发生变化,对其它表的查询缓存依然可以使用。 - - id:id 值越大,越先执行。无子查询时,id=1 - - type: - - NULL,执行时甚至不用访问表或索引 - - eq_ref,使用的是唯一索引 - - ref,使用的是非唯一索引或者是唯一索引的前缀 - - index,类似全表扫描,按照索引次序扫描表 - - rows:表扫描的行数 - - key:实际用到的索引,为空表没有用到索引 - - extra:十分重要的额外信息 - - using filesort - - using temporary +- 分解成多个单表查询,这些单表查询的缓存结果更可能被其它查询使用到,从而减少冗余记录的查询。 -4. 优化 SQL +- 减少锁竞争; - - 将子查询转换为 join 操作 +- 在应用层进行连接,可以更容易对数据库进行拆分,从而更容易做到高性能和可伸缩。 - - 使用 MAX(列),为该列建立索引 - - 数据值不要为 NULL - - 根据选择度建索引 - - like 进行模糊查询,第一个位置不要使用 '%' +- 查询本身效率也可能会有所提升。例如下面的例子中,使用 IN() 代替连接查询,可以让 MySQL 按照 ID 顺序进行查询,这可能比随机的连接要更高效。 + ```sql + SELECT * FROM tag + JOIN tag_post ON tag_post.tag_id=tag.id + JOIN post ON tag_post.post_id=post.id + WHERE tag.tag='mysql'; + ``` + ```sql + SELECT * FROM tag WHERE tag='mysql'; + SELECT * FROM tag_post WHERE tag_id=1234; + SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904); + ``` ### limit 优化 @@ -56,7 +90,7 @@ LIMIT 1000000,20 优化措施: -措施一:使用覆盖索引 +#### 1. 使用覆盖索引 ```sql SELECT id @@ -65,7 +99,7 @@ LIMIT 1000000,20 # id 是主键 ``` -措施二:使用 id 进行过滤 +#### 2. 使用 id 进行过滤 ```sql SELECT * @@ -74,7 +108,7 @@ WHERE id>=(SELECT id FROM product LIMIT 1000000,1) LIMIT 20; ``` -措施三:使用 jion +#### 3. 使用 jion ```sql SELECT * @@ -115,3 +149,14 @@ ON a.id=b.id; - 关闭一些不必要的二进制文件 - 增加 MySQL 的最大连接数 - 删除大量数据行后,使用 OPTIMIZE TABLE 命令进行**碎片整理** + + + +## 切分 + + + + + + + diff --git "a/docs/MySQL/6_MySQL\346\225\260\346\215\256\347\261\273\345\236\213.md" "b/docs/MySQL/6_MySQL\346\225\260\346\215\256\347\261\273\345\236\213.md" new file mode 100644 index 00000000..b15db3d3 --- /dev/null +++ "b/docs/MySQL/6_MySQL\346\225\260\346\215\256\347\261\273\345\236\213.md" @@ -0,0 +1,73 @@ +# 数据类型 + +## 整型 + +TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT 分别使用 8, 16, 24, 32, 64 位存储空间,一般情况下越小的列越好。 + +INT(11) 中的数字只是规定了交互工具显示字符的个数,对于存储和计算来说是没有意义的。 + +## 浮点数 + +FLOAT 和 DOUBLE 为浮点类型,DECIMAL 为高精度小数类型。CPU 原生支持浮点运算,但是不支持 DECIMAl 类型的计算,因此 DECIMAL 的计算比浮点类型需要更高的代价。 + +FLOAT、DOUBLE 和 DECIMAL 都可以指定列宽,例如 DECIMAL(18, 9) 表示总共 18 位,取 9 位存储小数部分,剩下 9 位存储整数部分。 + +## 字符串 + +主要有 CHAR 和 VARCHAR 两种类型,一种是定长的,一种是变长的。 + +VARCHAR 这种变长类型能够节省空间,因为只需要存储必要的内容。但是在执行 UPDATE 时可能会使行变得比原来长,当超出一个页所能容纳的大小时,就要执行额外的操作。MyISAM 会将行拆成不同的片段存储,而 InnoDB 则需要分裂页来使行放进页内。 + +在进行存储和检索时,会保留 VARCHAR 末尾的空格,而会删除 CHAR 末尾的空格。 + +## 时间和日期 + +MySQL 提供了两种相似的日期时间类型:DATETIME 和 TIMESTAMP。 + +### DATETIME + +能够保存从 1000 年到 9999 年的日期和时间,精度为秒,使用 8 字节的存储空间。 + +它与时区无关。 + +默认情况下,MySQL 以一种可排序的、无歧义的格式显示 DATETIME 值,例如“2008-01-16 22:37:08”,这是 ANSI 标准定义的日期和时间表示方法。 + +### TIMESTAMP + +和 UNIX 时间戳相同,保存从 1970 年 1 月 1 日午夜(格林威治时间)以来的秒数,使用 4 个字节,只能表示从 1970 年到 2038 年。 + +它和时区有关,也就是说一个时间戳在不同的时区所代表的具体时间是不同的。 + +MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提供了 UNIX_TIMESTAMP() 函数把日期转换为 UNIX 时间戳。 + +默认情况下,如果插入时没有指定 TIMESTAMP 列的值,会将这个值设置为当前时间。 + +**应该尽量使用 TIMESTAMP**: + +- DATETIME 类型与时区无关,即没有时区信息。当更换时区后,比如服务器更换地址或者更换客户端连接时区设置,就会导致从数据库中读出的时间错误。 + + TIMESTAMP 类型和时区有关。TIMESTAMP 类型字段的值会随着服务器时区的变化而变化,自动换算成相应的时间 + +- TIMESTAMP 只需要使用 4 个字节的存储空间,但 DATETIME 需要耗费 8 个字节的存储空间。但是,这样同样造成了一个问题,TIMESTAMP 表示的时间范围更小。 + + - DATETIME :`1000-01-01 00:00:00 ~ 9999-12-31 23:59:59` + - TIMESTAMP: `1970-01-01 00:00:00 ~ 2037-12-31 23:59:59` + + 注意 5.6.4 之后的 MySQL 多出了一个需要 0 ~ 3 字节的小数位。DATETIME 和 TIMESTAMP 会有几种不同的存储空间占用。 + +### 数值型时间戳 + +时间戳的定义是从一个基准时间 [ 1970-1-1 00:00:00 +0:00 ] 开始算起,用整数表示,以秒计时,随着时间的流逝这个时间整数不断增加。 + +一个数值,就可以完美地表示时间了,而且这个数值是一个绝对数值,即无论的身处地球的任何角落,这个表示时间的时间戳,都是一样的,生成的数值都是一样的,并且没有时区的概念。 + +MySQL 中可以使用 int 或者 bigint 类型的时间戳来表示时间。 + +小结: + +| 日期类型 | 存储空间 | 日期格式 | 日期范围 | 时区问题 | +| --------- | -------- | ------------------- | ---------------------------------------------- | -------- | +| DATETIME | 8 字节 | yyyy-MM-dd HH:mm:ss | 1000-01-01 00:00:00 ~
9999-12-31 23:59:59 | 存在 | +| TIMESTAMP | 4 字节 | yyyy-MM-dd HH:mm:ss | 1970-01-01 00:00:00 ~
2037-12-31 23:59:59 | 不存在 | +| 时间戳 | 4 字节 | 数值 | 1970-01-01 00:00:00 之后的时间 | 不存在 | + diff --git "a/docs/MySQL/6_\350\241\245\345\205\205\347\237\245\350\257\206.md" "b/docs/MySQL/6_\350\241\245\345\205\205\347\237\245\350\257\206.md" index 065d8d65..ae91f0e1 100644 --- "a/docs/MySQL/6_\350\241\245\345\205\205\347\237\245\350\257\206.md" +++ "b/docs/MySQL/6_\350\241\245\345\205\205\347\237\245\350\257\206.md" @@ -110,7 +110,13 @@ on 一般与 join 结合使用,使用 join 会产生临时表。 group by 在 order by 之前使用,即先进行分组,然后再进行排序。 +## drop、delete 与 truncate +- drop(丢弃数据): `drop table 表名` ,直接将表都删除掉,在删除表的时候使用。 +- truncate (清除数据) : `truncate table 表名` ,只删除表中的数据,再插入数据的时候自增长 id 又从 1 开始,在清空表中数据的时候使用。 +- delete(删除数据) : `delete from 表名 where 列名=值`,删除某一列的数据,如果不加 where 子句和`truncate table 表名`作用类似。 + +truncate 和不带 where 子句的 delete、以及 drop 都会删除表内的数据,但是 truncate 和 delete 只删除数据不删除表的结构(定义),执行 drop 语句,此表的结构也会删除,也就是执行 drop 之后对应的表不复存在。 ## 查询顺序 @@ -148,4 +154,4 @@ select > from > where > group by > having > order by > limit - ivm2 快照 + 复制 binlog -注:binlog 即二进制日志,记录对数据发生或者潜在发生更改的 SQL 语句,以二进制形式保存在文件中。 \ No newline at end of file +注:binlog 即二进制日志,记录对数据发生或者潜在发生更改的 SQL 语句,以二进制形式保存在文件中。 diff --git "a/docs/MySQL/RabbitMQ/1_\344\270\273\346\265\201\346\266\210\346\201\257\344\270\255\351\227\264\344\273\266.md" "b/docs/MySQL/RabbitMQ/1_\344\270\273\346\265\201\346\266\210\346\201\257\344\270\255\351\227\264\344\273\266.md" deleted file mode 100644 index c80a19fb..00000000 --- "a/docs/MySQL/RabbitMQ/1_\344\270\273\346\265\201\346\266\210\346\201\257\344\270\255\351\227\264\344\273\266.md" +++ /dev/null @@ -1,34 +0,0 @@ -# 主流消息中间件 - -## 1. ActiveMQ - -ActiveMQ 是 Apache 出品的最流行的、性能强劲的开源消息总线,并且其是一个完全支持 JMS 规范的消息中间件。其丰富的 API、多种集群构建模式使得它成为业界老牌消息中间件,广泛应用于中小型企业。 - -## 2. Kafka - -Kafka 是 LinkedIn 开源的分布式发布-订阅消息系统,目前属于 Apache 顶级项目。 - -Kafka 主要特点是基于 Pull 的模式来处理消息消费,追求高吞吐量,一开始的目的就是用于日志收集和传输。0.8 版本开始支持复制,不支持事务,对消息的重复、丢失、错误没有严格要求,适合产生大量数据的互联网服务的数据收集业务。 - -## 3. RocketMQ - -RocketMQ 是阿里开源的消息中间件,目前也已经孵化为 Apache 顶级项目,它是纯 Java 开发,具有高吞吐量、高可用性、适合大规模分布式系统应用的特点。 - -RocketMQ 思路起源于 Kafka,它对消息的可靠传输及事务性做了优化,目前在阿里集团被广泛应用于交易、充值、流计算、消息推送、日志流式处理、binglog 分发等场景。 - -## 4. RabbitMQ - -RabbitMQ 是使用 Erlang 语言开发的开源消息队列系统,基于 AMQP 协议来实现。 - -AMQP 的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。AMQP 协议更多用在企业系统内,对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求还在其次。 - -## 对比 - -| 消息中间件 | ActiveMQ | Kafka | RocketMQ | RabbitMQ | -| :----------------------: | :-----------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :------------------------------------------------: | -| *吞吐量* | 万数量级 | 十万级,高吞吐 | 十万级,高吞吐 | 万数量级 | -| *Topic 数量对吞吐量影响* | / | Topic 从几十到几百时,吞吐量会大幅度下降,
在同等机器下,Kafka 尽量保证 Topic 数量不要过多,如果要支撑大规模的 Topic,需要增加更多的机器 | Topic 可达百/千级,吞吐量下降幅度小
在同等机器下,可以支撑大量的 Topic | / | -| *时效性* | 毫秒级 | 毫秒级 | 毫秒级 | 微秒级 | -| *可用性* | 高
基于主从架构实现高可用高 | 非常高
分布式架构 | 非常高
分布式架构 | 高
基于主从架构实现高可用高 | -| *可靠性* | 丢失数据的概率低 | 优化参数配置,
可以做到零丢失 | 优化参数配置,
可以做到零丢失 | 基本不会丢失数据 | -| *功能* | 功能较完备 | 功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用 | MQ 功能较为完善,还是分布式的,扩展性好 | 基于 ErLang 开发,并发能力很强,性能极好,延时很低 | \ No newline at end of file diff --git "a/docs/MySQL/RabbitMQ/2_RabbitMQ\346\225\264\345\220\210SpringBoot.md" "b/docs/MySQL/RabbitMQ/2_RabbitMQ\346\225\264\345\220\210SpringBoot.md" deleted file mode 100644 index e69de29b..00000000 diff --git "a/docs/MySQL/RabbitMQ/2_RabbitMQ\346\246\202\350\277\260.md" "b/docs/MySQL/RabbitMQ/2_RabbitMQ\346\246\202\350\277\260.md" deleted file mode 100644 index 4945be10..00000000 --- "a/docs/MySQL/RabbitMQ/2_RabbitMQ\346\246\202\350\277\260.md" +++ /dev/null @@ -1,76 +0,0 @@ -# RabbitMQ 概述 - -## 简介 - -RabbitMQ 是一个开源的消息代理和队列服务器,用来通过普通协议在完全不同的应用中间共享数据,RabbitMQ 是使用 **Erlang 语言**来编写的,并且 RabbitMQ 是基于 AMQP 协议的。 - -特点: - -- 开源、性能优秀,稳定性好 -- 提供可靠性消息投递模式(confirm)、返回模式(return) -- 与 SpringAOP 完美的整合、API 丰富 -- 集群模式丰富,表达式配置,HA 模式,镜像队列模型 -- 保证数据不丢失的前提做到高可靠性、可用性 - -RabbitMQ 高性能的原因: - -**Erlang 语言**最初用在交换机的架构模式,这样使得 RabbitMQ 在 Broker 之间进行数据交互的性能时非常优秀的。Erlang 的优点:Erlang 有着和原生 Socket 一样的延迟。 - -## AMQP 协议 - -AMQP(Advanced Message Queuing Protocol)协议,即高级消息队列协议。 - -AMQP 是具有现在特征的二进制协议。是一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。 - -### 协议模型 - - - -### 核心概念 - - - - - -![1574044351197](C:\Users\DHA\AppData\Roaming\Typora\typora-user-images\1574044351197.png) - - - - - -![1574044966299](C:\Users\DHA\AppData\Roaming\Typora\typora-user-images\1574044966299.png) - - - - - -![1574045181833](C:\Users\DHA\AppData\Roaming\Typora\typora-user-images\1574045181833.png) - - - -![1574045266808](C:\Users\DHA\AppData\Roaming\Typora\typora-user-images\1574045266808.png) - -![1574045345331](C:\Users\DHA\AppData\Roaming\Typora\typora-user-images\1574045345331.png) - -![1574045528395](C:\Users\DHA\AppData\Roaming\Typora\typora-user-images\1574045528395.png) - -![1574045635960](C:\Users\DHA\AppData\Roaming\Typora\typora-user-images\1574045635960.png) - - - -![1574045733769](C:\Users\DHA\AppData\Roaming\Typora\typora-user-images\1574045733769.png) - - - -![1574044393028](C:\Users\DHA\AppData\Roaming\Typora\typora-user-images\1574044393028.png) - -![1574045701316](C:\Users\DHA\AppData\Roaming\Typora\typora-user-images\1574045701316.png) - - - -![1574045865612](C:\Users\DHA\AppData\Roaming\Typora\typora-user-images\1574045865612.png) - - - -![1574044458688](C:\Users\DHA\AppData\Roaming\Typora\typora-user-images\1574044458688.png) - diff --git "a/docs/MySQL/RabbitMQ/3_\346\266\210\346\201\257\345\217\257\351\235\240\346\200\247\346\212\225\351\200\222\347\232\204\350\247\243\345\206\263\346\226\271\346\241\210\345\256\236\347\216\260.md" "b/docs/MySQL/RabbitMQ/3_\346\266\210\346\201\257\345\217\257\351\235\240\346\200\247\346\212\225\351\200\222\347\232\204\350\247\243\345\206\263\346\226\271\346\241\210\345\256\236\347\216\260.md" deleted file mode 100644 index e69de29b..00000000 diff --git "a/docs/MySQL/\345\210\207\345\210\206\345\244\215\345\210\266\351\227\256\351\242\230.md" "b/docs/MySQL/\345\210\207\345\210\206\345\244\215\345\210\266\351\227\256\351\242\230.md" new file mode 100644 index 00000000..b8567dd9 --- /dev/null +++ "b/docs/MySQL/\345\210\207\345\210\206\345\244\215\345\210\266\351\227\256\351\242\230.md" @@ -0,0 +1,8 @@ +# 一、切分 + + + + + +# 二、复制 + diff --git "a/docs/JavaBasics/11_\346\212\275\350\261\241\347\261\273\345\222\214\346\216\245\345\217\243.md" "b/docs/OO/11_\346\212\275\350\261\241\347\261\273\345\222\214\346\216\245\345\217\243.md" similarity index 100% rename from "docs/JavaBasics/11_\346\212\275\350\261\241\347\261\273\345\222\214\346\216\245\345\217\243.md" rename to "docs/OO/11_\346\212\275\350\261\241\347\261\273\345\222\214\346\216\245\345\217\243.md" diff --git "a/docs/OO/2_\345\210\233\345\273\272\345\236\213.md" "b/docs/OO/2_\345\210\233\345\273\272\345\236\213.md" index 35a293b4..27f2ac57 100644 --- "a/docs/OO/2_\345\210\233\345\273\272\345\236\213.md" +++ "b/docs/OO/2_\345\210\233\345\273\272\345\236\213.md" @@ -12,7 +12,7 @@ 私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。 -

+

### Implementation @@ -279,7 +279,7 @@ public class Singleton { 客户类往往有多个,如果不使用简单工厂,那么所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,例如增加子类,那么所有的客户类都要进行修改。 -

+

### Implementation @@ -376,7 +376,7 @@ public class Client { 下图中,Factory 有一个 doSomething() 方法,这个方法需要用到一个产品对象,这个产品对象由 factoryMethod() 方法创建。该方法是抽象的,需要由子类去实现。 -

+

### Implementation @@ -440,7 +440,7 @@ public class ConcreteFactory2 extends Factory { 从高层次来看,抽象工厂使用了组合,即 Cilent 组合了 AbstractFactory,而工厂方法模式使用了继承。 -

+

### Implementation @@ -536,7 +536,7 @@ public class Client { 当然,光有指导者是不够的,必须要有能具体实现每步的对象,在生成器模式中称这些实现对象为**生成器**。 这样一来,**指导者就是可以重用的构建过程,而生成器是可以被切换的具体实现**。 -

+

### Implementation1 @@ -746,7 +746,7 @@ public class Client { 生成器的调用顺序: -

+

### Implementation2 @@ -836,7 +836,7 @@ abcdefghijklmnopqrstuvwxyz ### Class Diagram -

+

### Implementation diff --git "a/docs/OO/3_\350\241\214\344\270\272\345\236\213.md" "b/docs/OO/3_\350\241\214\344\270\272\345\236\213.md" index 9b5f8286..e91dd3cc 100644 --- "a/docs/OO/3_\350\241\214\344\270\272\345\236\213.md" +++ "b/docs/OO/3_\350\241\214\344\270\272\345\236\213.md" @@ -11,7 +11,7 @@ - Handler:定义处理请求的接口,并且实现后继链(successor) -

+

### Implementation @@ -158,7 +158,7 @@ request-2 is handle by ConcreteHandler2 - Invoker:通过它来调用命令 - Client:可以设置命令与命令的接收者 -

+

### Implementation1 ```java @@ -232,7 +232,7 @@ public class Client { 设计一个遥控器,可以控制电灯开关。 -

+

```java public interface Command { @@ -349,7 +349,7 @@ public class Client { - TerminalExpression:终结符表达式,每个终结符都需要一个 TerminalExpression。 - Context:上下文,包含解释器之外的一些全局信息。 -

+

### Implementation @@ -482,7 +482,7 @@ false - Iterator 主要定义了 hasNext() 和 next() 方法。 - Client 组合了 Aggregate,为了迭代遍历 Aggregate,也需要组合 Iterator。 -

+

### Implementation @@ -571,17 +571,17 @@ public class Client { - Mediator:中介者,定义一个接口用于与各同事(Colleague)对象通信。 - Colleague:同事,相关对象 -

+

### Implementation Alarm(闹钟)、CoffeePot(咖啡壶)、Calendar(日历)、Sprinkler(喷头)是一组相关的对象,在某个对象的事件产生时需要去操作其它对象,形成了下面这种依赖结构: -

+

使用中介者模式可以将复杂的依赖结构变成星形结构: -

+

```java public abstract class Colleague { @@ -744,7 +744,7 @@ doSprinkler() 备忘录实际上有两个接口,一个是提供给 Caretaker 的窄接口:它只能将备忘录传递给其它对象; 一个是提供给 Originator 的宽接口,允许它访问到先前状态所需的所有数据。理想情况是只允许 Originator 访问本备忘录的内部状态。 -

+

### Implementation @@ -916,7 +916,7 @@ public class Client { 主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。 -

+

### Class Diagram @@ -924,13 +924,13 @@ public class Client { 观察者(Observer)的注册功能需要调用主题的 registerObserver() 方法。 -

+

### Implementation 天气数据布告板会在天气信息发生改变时更新其内容,布告板有多个,并且在将来会继续增加。 -

+

```java public interface Subject { @@ -1059,7 +1059,7 @@ State:状态接口,用来封装与上下文的**一个特定状态所对应 ConcreteState:具体实现状态处理的类,每个类实现一个跟上下文相关的状态的具体处理。 -

+

### Implementation1 实现在线投票: @@ -1080,7 +1080,7 @@ ConcreteState:具体实现状态处理的类,每个类实现一个跟上下 程序结构如下图: -

+

```java /** @@ -1213,7 +1213,7 @@ public class Client { 糖果销售机有多种状态,每种状态下销售机有不同的行为,状态可以发生转移,使得销售机的行为也发生改变。 -

+

```java public interface State { @@ -1514,7 +1514,7 @@ No gumball dispensed - Strategy 接口定义了一个算法族,它们都实现了 behavior() 方法。 - Context 是使用到该算法族的类,其中的 doSomething() 方法会调用 behavior(),setStrategy(Strategy) 方法可以动态地改变 strategy 对象,也就是说能动态地改变 Context 所使用的算法。 -

+

### 与状态模式的比较 @@ -1535,7 +1535,7 @@ No gumball dispensed 2. 对老客户报的价格,根据客户年限,给予一定的折扣 3. 对大客户报的价格,根据大客户的累计消费金额,给予一定的折扣 -

+

```java /** @@ -1702,13 +1702,13 @@ squeak! ### Class Diagram -

+

### Implementation 冲咖啡和冲茶都有类似的流程,但是某些步骤会有点不一样,要求复用那些相同步骤的代码。 -

+

```java public abstract class CaffeineBeverage { @@ -1805,7 +1805,7 @@ Tea.addCondiments - ConcreteVisitor:具体访问者,存储遍历过程中的累计结果 - ObjectStructure:对象结构,可以是组合结构,或者是一个集合。 -

+

### Implementation @@ -2010,7 +2010,7 @@ Number of items: 6 ### Class Diagram -

+

### Implementation diff --git "a/docs/OO/4_\347\273\223\346\236\204\345\236\213.md" "b/docs/OO/4_\347\273\223\346\236\204\345\236\213.md" index 4839eb59..f3144f52 100644 --- "a/docs/OO/4_\347\273\223\346\236\204\345\236\213.md" +++ "b/docs/OO/4_\347\273\223\346\236\204\345\236\213.md" @@ -6,7 +6,7 @@ 把一个类接口转换成另一个用户需要的接口。 -

+

### Class Diagram Target:定义客户端需要的跟特定领域相关的接口。 @@ -16,7 +16,7 @@ Adaptee:已经存在的接口,通常能满足客户端的功能要求, Adapter:适配器,把Adaptee适配成为Client需要的Target。 -

+

### Implementation1 美国的电饭煲是在电压为 110V 下工作,而中国的电饭煲在电压 220V 下工作。要求将在美国使用的电饭煲适配成能在中国使用。 @@ -182,7 +182,7 @@ public class Client { - Abstraction:定义抽象类的接口 - Implementor:定义实现类接口 -

+

### Implementation @@ -343,7 +343,7 @@ Composite:组合对象,通常会存储子组件,定义包含子组件的 组合对象拥有一个或者多个组件对象,因此组合对象的操作可以委托给组件对象去处理,而组件对象可以是另一个组合对象或者叶子对象。 -

+

### Implementation1 商品类别树的管理,比如有如下所示的商品类别树: @@ -640,7 +640,7 @@ public class Client { 装饰者(Decorator)和具体组件(ConcreteComponent)都继承自组件(Component),具体组件的方法实现不需要依赖于其它对象,而装饰者组合了一个组件,这样它可以装饰其它装饰者或者具体组件。所谓装饰,就是把这个装饰者套在被装饰者之上,从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的,这属于它的功能,然后调用被装饰者的方法实现,从而也保留了被装饰者的功能。可以看到,具体组件应当是装饰层次的最低层,因为只有具体组件的方法实现不需要依赖于其它对象。 -

+

### Implementation1 给普通手机装饰上彩铃等功能。 @@ -778,7 +778,7 @@ IPhone打电话 下图表示在 DarkRoast 饮料上新增新添加 Mocha 配料,之后又添加了 Whip 配料。DarkRoast 被 Mocha 包裹,Mocha 又被 Whip 包裹。它们都继承自相同父类,都有 cost() 方法,外层类的 cost() 方法调用了内层类的 cost() 方法。 -

+

```java public interface Beverage { @@ -883,7 +883,7 @@ public class Client { ### Class Diagram -

+

### Implementation @@ -942,7 +942,7 @@ public class Client { - IntrinsicState:内部状态,享元对象共享内部状态 - ExtrinsicState:外部状态,每个享元对象的外部状态不同 -

+

### Implementation @@ -1042,7 +1042,7 @@ Java 利用缓存来加速大量小对象的访问时间。 - RealSubject:具体的目标对象,真正实现目标接口要求的功能。 -

+

### Implementation diff --git "a/docs/OO/6_\345\205\263\347\263\273\347\261\273\345\233\276.md" "b/docs/OO/6_\345\205\263\347\263\273\347\261\273\345\233\276.md" index f7c1fa10..a0532acf 100644 --- "a/docs/OO/6_\345\205\263\347\263\273\347\261\273\345\233\276.md" +++ "b/docs/OO/6_\345\205\263\347\263\273\347\261\273\345\233\276.md" @@ -6,7 +6,7 @@ 用来描述继承关系,在 Java 中使用 extends 关键字。 -

+

```text @startuml @@ -27,7 +27,7 @@ Vihical <|-- Trunck 用来实现一个接口,在 Java 中使用 implements 关键字。 -

+

```text @startuml @@ -48,7 +48,7 @@ MoveBehavior <|.. Run 表示整体由部分组成,但是整体和部分不是强依赖的,整体不存在了部分还是会存在。 -

+

```text @startuml @@ -71,7 +71,7 @@ Computer o-- Screen 和聚合不同,组合中整体和部分是强依赖的,整体不存在了部分也不存在了。比如公司和部门,公司没了部门就不存在了。但是公司和员工就属于聚合关系了,因为公司没了员工还在。 -

+

```text @startuml @@ -92,7 +92,7 @@ Company *-- DepartmentB 表示不同类对象之间有关联,这是一种静态关系,与运行过程的状态无关,在最开始就可以确定。因此也可以用 1 对 1、多对 1、多对多这种关联关系来表示。比如学生和学校就是一种关联关系,一个学校可以有很多学生,但是一个学生只属于一个学校,因此这是一种多对一的关系,在运行开始之前就可以确定。 -

+

```text @startuml @@ -115,7 +115,7 @@ School "1" - "n" Student - A 类是 B 类方法当中的一个参数; - A 类向 B 类发送消息,从而影响 B 类发生变化。 -

+

```text @startuml diff --git a/docs/README.md b/docs/README.md index b592279a..03951a42 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,316 +1,223 @@ -# ✏️ 计算机基础 - -## 1. 计算机网络 - -- [第一节 概述](Net/1_概述.md) -- [第二节 物理层](Net/2_物理层.md) -- [第三节 数据链路层](Net/3_数据链路层.md) -- [第四节 网络层](Net/4_网络层.md) -- [第五节 运输层](Net/5_运输层.md) -- [第六节 应用层](Net/6_应用层.md) - -## 2. 操作系统 - -- [第一节 操作系统概述](OS/1_操作系统概述.md) -- [第二节 进程管理](OS/2_进程管理.md) -- [第三节 死锁](OS/3_死锁.md) -- [第四节 内存管理](OS/4_内存管理.md) -- [第五节 设备管理](OS/4_设备管理.md) -- [第六节 链接](OS/6_链接.md) - -## 3. 数据结构和算法 - -- [第一节 概述](data_structure/1_概述.md) -- [第二节 线性表](data_structure/2_线性表.md) -- [第三节 栈和队列](data_structure/3_栈和队列.md) -- [第四节 树](data_structure/4_树.md) -- [第五节 图](data_structure/5_图.md) -- [第六节 集合和映射](data_structure/6_集合和映射.md) -- [第七节 并查集](data_structure/7_并查集.md) -- [第八节 优先队列和堆](data_structure/8_优先队列和堆.md) -- [第九节 哈希表](data_structure/9_哈希表.md) -- [第十节 排序](data_structure/10_排序.md) -- [第十一节 线段树](data_structure/11_线段树.md) -- [第十二节 Trie树](data_structure/12_Trie树.md) -- [第十三节 AVL](data_structure/13_AVL.md) -- [第十四节 红黑树](data_structure/14_红黑树.md) - -## 4. HTTP - -- [第一节 HTTP概述](HTTP/1_HTTP概述.md) -- [第二节 HTTP状态码](HTTP/2_HTTP状态码.md) -- [第三节 具体应用](HTTP/3_具体应用.md) -- [第四节 HTTPS](HTTP/4_HTTPS.md) -- [第五节 get和post比较](HTTP/5_get和post比较.md) - -## 5. Linux - -- [第一节 Linux概论](Linux/1_Linux概论.md) -- [第二节 Linux文件系统](Linux/2_Linux文件系统.md) -- [第三节 Linux常用命令](Linux/3_Linux常用命令.md) -- [第四节 Liunx进程管理](Linux/4_Liunx进程管理.md) -- [第五节 Linux压缩与打包](Linux/5_Linux压缩与打包.md) - # ☕️ Java -## 1. Java 基础 - -- [第一节 数据类型](JavaBasics/1_数据类型.md) -- [第二节 String](JavaBasics/2_String.md) -- [第三节 运算](JavaBasics/3_运算.md) -- [第四节 Object通用方法](JavaBasics/4_Object通用方法.md) -- [第五节 关键字](JavaBasics/5_关键字.md) -- [第六节 反射](JavaBasics/6_反射.md) -- [第七节 异常](JavaBasics/7_异常.md) -- [第八节 泛型](JavaBasics/8_泛型.md) -- [第九节 注解](JavaBasics/9_注解.md) -- [第十节 Java常见对象](JavaBasics/10_Java常见对象.md) -- [第十一节 抽象类和接口](JavaBasics/11_抽象类和接口.md) -- [第十二节 其他](JavaBasics/12_其他.md) - -## 2. Java 容器 - -- [第一节 Java容器概览](JavaContainer/1_Java容器概览.md) -- [第二节 容器中的设计模式](JavaContainer/2_容器中的设计模式.md) -- [第三节 容器源码分析 - List](JavaContainer/3_容器源码分析%20-%20List.md) -- [第四节 容器源码分析 - Map](JavaContainer/4_容器源码分析%20-%20Map.md) -- [第五节 容器源码分析 - 并发容器](JavaContainer/5_容器源码分析%20-%20并发容器.md) - -## 3. Java 虚拟机 - -- [第一节 运行时数据区域](JVM/1_JVM.md) -- [第二节 HotSpot 虚拟机对象](JVM/2_JVM.md) -- [第三节 String 类和常量池](JVM/3_JVM.md) -- [第四节 8 种基本类型的包装类和常量池](JVM/4_JVM.md) -- [第五节 垃圾收集](JVM/5_JVM.md) -- [第六节 内存分配与回收策略](JVM/6_JVM.md) -- [第七节 类加载机制](JVM/7_JVM.md) - -## 4. Java 并发 - -- [第一节 基础知识](Java_Concurrency/1_基础知识.md) -- [第二节 并发理论](Java_Concurrency/2_并发理论.md) -- [第三节 并发关键字](Java_Concurrency/3_并发关键字.md) -- [第四节 Lock 体系](Java_Concurrency/4_Lock%20体系.md) -- [第五节 原子操作类](Java_Concurrency/5_原子操作类.md) -- [第六节 并发容器](Java_Concurrency/6_并发容器.md) -- [第七节 并发工具](Java_Concurrency/7_并发工具.md) -- [第八节 线程池](Java_Concurrency/8_线程池.md) -- [第九节 并发实践](Java_Concurrency/9_并发实践.md) - -## 5. JavaIO - -- [第一节 概览](JavaIO/1_概览.md) -- [第二节 磁盘操作](JavaIO/2_磁盘操作.md) -- [第三节 字节操作](JavaIO/3_字节操作.md) -- [第四节 字符操作](JavaIO/4_字符操作.md) -- [第五节 对象操作](JavaIO/5_对象操作.md) -- [第六节 网络操作](JavaIO/6_网络操作.md) -- [第七节 NIO](JavaIO/7_NIO.md) -- [第八节 JavaIO方式](JavaIO/8_JavaIO方式.md) - -## 6. 正则表达式 - -- [第一节 概述](Regex/1_概述.md) -- [第二节 应用](Regex/2_应用.md) +## Java 基础 + +- [数据类型](JavaBasics/1_数据类型.md) +- [常见运算](JavaBasics/3_运算.md) +- [final & static](JavaBasics/5_关键字.md) +- [Java 常见类 I ](JavaBasics/10_Java常见对象.md) +- [Java 常见类 II](JavaBasics/10_Java常见对象_2.md) +- [异常 & 反射](JavaBasics/6_反射.md) +- [泛型 & 注解](JavaBasics/8_泛型.md) +- [正则表达式](JavaBasics/正则表达式.md) +- [JDK8 新特性](JavaBasics/JDK8新特性.md) + +## Java 容器 + +- [容器概览](JavaContainer/1_容器概览.md) +- [容器中的设计模式](JavaContainer/2_容器中的设计模式.md) +- [容器源码分析 - List](JavaContainer/3_容器源码分析%20-%20List.md) +- [容器源码分析 - Map](JavaContainer/4_容器源码分析%20-%20Map.md) +- [容器源码分析 - 并发容器](JavaContainer/5_容器源码分析%20-%20并发容器.md) + +## Java 虚拟机 + +- [Java 内存区域](JVM/1_Java内存区域.md) +- [垃圾收集](JVM/2_垃圾收集.md) +- [内存分配与回收策略](JVM/3_内存分配与回收策略.md) +- [JVM 调优](JVM/4_JVM调优.md) +- [类文件结构](JVM/5_类文件结构.md) +- [类加载机制](JVM/6_类加载机制.md) +- [Java 程序编译和运行过程](JVM/7_Java程序编译和运行过程.md) + +## Java 并发 + +- [进程和线程](Java_Concurrency/1_基础知识.md) +- [并发理论](Java_Concurrency/2_并发理论.md) +- [并发关键字](Java_Concurrency/3_并发关键字.md) +- [Lock 体系](Java_Concurrency/4_Lock%20体系.md) +- [原子操作类](Java_Concurrency/5_原子操作类.md) +- [并发容器 & 并发工具](Java_Concurrency/7_并发工具.md) +- [线程池](Java_Concurrency/8_线程池.md) +- [并发实践](Java_Concurrency/9_并发实践.md) + +## Java I/O + +- [Java I/O 概览](JavaIO/1_概览.md) +- [磁盘操作](JavaIO/2_磁盘操作.md) +- [字节操作](JavaIO/3_字节操作.md) +- [字符操作](JavaIO/4_字符操作.md) +- [对象操作](JavaIO/5_对象操作.md) +- [网络操作](JavaIO/6_网络操作.md) +- [NIO & AIO](JavaIO/7_NIO.md) +- [Java I/O 方式](JavaIO/8_JavaIO方式.md) # 👫 面向对象 -## 1. 设计模式 +## 设计模式 -- [第一节 概述](OO/1_概述.md) -- [第二节 创建型](OO/2_创建型.md) -- [第三节 行为型](OO/3_行为型.md) -- [第四节 结构型](OO/4_结构型.md) +- [概述](OO/1_概述.md) +- [创建型](OO/2_创建型.md) +- [行为型](OO/3_行为型.md) +- [结构型](OO/4_结构型.md) -## 2. 面向对象思想 +## 面向对象思想 -- [第一节 面向对象三大特性](OO/5_面向对象三大特性.md) -- [第二节 关系类图](OO/6_关系类图.md) -- [第三节 面向对象设计原则](OO/7_面向对象设计原则.md) +- [面向对象三大特性](OO/5_面向对象三大特性.md) +- [关系类图](OO/6_关系类图.md) +- [面向对象设计原则](OO/7_面向对象设计原则.md) # 📝 编程题 -## 1. 剑指 Offer 编程题 - -> **数据结构相关** - -- [第一节 数组和矩阵](AimForOffer/数据结构相关/1_数组和矩阵.md) -- [第二节 字符串](AimForOffer/数据结构相关/2_字符串.md) -- [第三节 链表](AimForOffer/数据结构相关/3_链表.md) -- [第四节 树](AimForOffer/数据结构相关/4_树.md) -- [第五节 栈](AimForOffer/数据结构相关/5_栈.md) -- [第六节 队列](AimForOffer/数据结构相关/6_队列.md) -- [第七节 堆](AimForOffer/数据结构相关/7_堆.md) -- [第八节 哈希.](AimForOffer/数据结构相关/8_哈希.md) - -> **算法相关** - -- [第一节 查找](AimForOffer/算法思想相关/1_查找.md) -- [第二节 排序](AimForOffer/算法思想相关/2_排序.md) -- [第三节 动态规划](AimForOffer/算法思想相关/3_动态规划.md) -- [第四节 回溯](AimForOffer/算法思想相关/4_回溯.md) -- [第五节 深度优先](AimForOffer/算法思想相关/5_深度优先.md) -- [第六节 贪心](AimForOffer/算法思想相关/6_贪心.md) -- [第七节 数学运算](AimForOffer/算法思想相关/7_数学运算.md) -- [第八节 其他](AimForOffer/算法思想相关/8_其他.md) - -## 2. LeetCode 编程题 - -> **数据结构相关** - -- [第一节 数组问题](LeetCode/数据结构相关/1_数组问题.md) -- [第二节 链表问题](LeetCode/数据结构相关/2_链表问题.md) -- [第三节 栈和队列](LeetCode/数据结构相关/3_栈和队列.md) -- [第四节 二叉树](LeetCode/数据结构相关/4_二叉树.md) -- [第五节 字符串](LeetCode/数据结构相关/5_字符串.md) -- [第六节 哈希](LeetCode/数据结构相关/6_哈希.md) -- [第七节 图](LeetCode/数据结构相关/7_图.md) -- [第八节 数据结构设计](LeetCode/数据结构相关/8_数据结构设计.md) - -> **算法思想相关** - -- [第一节 排序](LeetCode/算法思想相关/1_排序.md) -- [第二节 分治思想](LeetCode/算法思想相关/2_分治思想.md) -- [第三节 贪心思想](LeetCode/算法思想相关/3_贪心思想.md) -- [第四节 LRU](LeetCode/算法思想相关/4_LRU.md) -- [第五节 DFS](LeetCode/算法思想相关/5_DFS.md) -- [第六节 回溯法](LeetCode/算法思想相关/6_回溯法.md) -- [第七节 动态规划](LeetCode/算法思想相关/7_动态规划.md) -- [第八节 数学问题](LeetCode/算法思想相关/8_数学问题.md) +## 数据结构系列 + +- [数组 & 矩阵](AimForOffer/数据结构相关/1_数组和矩阵.md) +- [字符串](AimForOffer/数据结构相关/2_字符串.md) +- [链表](AimForOffer/数据结构相关/3_链表.md) +- [树](AimForOffer/数据结构相关/4_树.md) +- [栈](AimForOffer/数据结构相关/5_栈.md) +- [队列](AimForOffer/数据结构相关/6_队列.md) +- [堆](AimForOffer/数据结构相关/7_堆.md) +- [ 哈希.](AimForOffer/数据结构相关/8_哈希.md) + +## 算法思维系列 + +- [查找](AimForOffer/算法思想相关/1_查找.md) +- [排序](AimForOffer/算法思想相关/2_排序.md) +- [动态规划](AimForOffer/算法思想相关/3_动态规划.md) +- [搜索](AimForOffer/算法思想相关/4_搜索.md) +- [排列组合](AimForOffer/算法思想相关/5_排列组合.md) +- [贪心](AimForOffer/算法思想相关/6_贪心.md) +- [数学运算](AimForOffer/算法思想相关/7_数学运算.md) +- [其他](AimForOffer/算法思想相关/8_其他.md) # 💾 数据库 -## 1. DataBase - -- [第一节 数据库系统原理](DataBase/1_数据库系统原理.md) -- [第二节 关系数据库设计理论](DataBase/2_关系数据库设计理论.md) -- [第三节 设计关系型数据库](DataBase/3_设计关系型数据库.md) -- [第四节 SQL](DataBase/4_SQL.md) -- [第五节 LeetCode_Database题解](DataBase/5_LeetCode_Database题解.md) - -## 2. MySQL - -- [第一节 锁机制](MySQL/1_锁机制.md) -- [第二节 事务隔离级别实现](MySQL/2_事务隔离级别实现.md) -- [第三节 索引](MySQL/3_索引.md) -- [第四节 MySQL架构](MySQL/4_MySQL架构.md) -- [第五节 MySQL优化](MySQL/5_MySQL优化.md) -- [第六节 补充知识](MySQL/6_补充知识.md) - -## *3. Redis - -- [第一节 Redis初探](Redis/1_Redis初探.md) -- [第二节 Redis持久化](Redis/2_Redis持久化.md) -- [第三节 Redis复制](Redis/3_Redis复制.md) -- [第四节 处理系统故障](Redis/4_处理系统故障.md) -- [第五节 Redis事务](Redis/5_Redis事务.md) -- [第六节 Redis性能方面注意事项](Redis/6_Redis性能方面注意事项.md) -- [第七节 降低内存占用](Redis/7_降低内存占用.md) -- [第八节 简单点赞系统](Redis/8_简单点赞系统) - -## 4. 海量数据处理 - -- [第一节 概述](MassDataProcessing/1_概述.md) -- [第二节 哈希分治](MassDataProcessing/2_哈希分治.md) -- [第三节 位图](MassDataProcessing/3_位图.md) -- [第四节 布隆过滤器](MassDataProcessing/4_布隆过滤器.md) -- [第五节 Trie树](MassDataProcessing/5_Trie树.md) -- [第六节 数据库](MassDataProcessing/6_数据库.md) -- [第七节 倒排索引](MassDataProcessing/7_倒排索引.md) - -# 🔨 消息中间件 - -## 1. Kafka - -- [第一节 消息队列](Kafka/1_消息队列.md) -- [第二节 Kafka的架构](Kafka/2_Kafka的架构.md) -- [第三节 Kafka的高可用原理](Kafka/3_Kafka的高可用原理.md) -- [第四节 Kafka中一些常见问题](Kafka/4_Kafka中一些常见问题.md) -- [第五节 Kafka特点](Kafka/5_Kafka特点.md) - -## 2. RabbitMQ - -- [第一节 主流消息中间件](RabbitMQ/1_主流消息中间件.md) -- [第二节 RabbitMQ概述](RabbitMQ/2_RabbitMQ概述.md) -- [第三节 RabbitMQ入门](RabbitMQ/3_RabbitMQ入门.md) -- [第四节 RabbitMQ高级特性](RabbitMQ/4_RabbitMQ高级特性.md) -- [第五节 RabbitMQ整合SpringAMQP](RabbitMQ/5_RabbitMQ整合SpringAMQP.md) -- [第六节 RabbitMQ整合SpringBoot](RabbitMQ/6_RabbitMQ整合SpringBoot.md) -- [RabbitMQ 官网](https://www.rabbitmq.com/) - -# 📖 系统设计 - -## 1. 常用框架 - -- [第一节 SpringMVC](SSM/1_SpringMVC.md) -- [第二节 SpringIOC](SSM/2_SpringIOC.md) -- [第三节 SpringAOP](SSM/3_SpringAOP.md) -- [第四节 Spring事务管理](SSM/4_Spring事务管理.md) -- [第五节 Spring中Bean的作用域](SSM/5_Spring中Bean的作用域.md) -- [第六节 Spring中Bean的生命周期](SSM/6_Spring中Bean的生命周期.md) -- [第七节 Spring中常见注解](SSM/7_Spring中常见注解.md) -- [第八节 Spring中涉及到的设计模式](SSM/8_Spring中涉及到的设计模式.md) -- [第九节 MyBaits](SSM/9_MyBaits.md) - -## 2. Web 安全 - -- [第一节 常见安全问题](Safety/1_常见安全问题.md) -- [第二节 跨站脚本攻击](Safety/2_跨站脚本攻击.md) -- [第三节 跨站请求伪造](Safety/3_跨站请求伪造.md) -- [第四节 Cookies问题](Safety/4_Cookies问题.md) -- [第五节 点击劫持问题](Safety/5_点击劫持问题.md) -- [第六节 传输安全](Safety/6_传输安全.md) -- [第七节 密码安全](Safety/7_密码安全.md) -- [第八节 接入层注入问题](Safety/8_接入层注入问题.md) -- [第九节 接入层上传问题](Safety/9_接入层上传问题.md) -- [第十节 信息泄露](Safety/10_信息泄露.md) -- [第十一节 DoS攻击](Safety/11_DoS攻击.md) -- [第十二节 重放攻击](Safety/12_重放攻击.md) - -## 3. 分布式 - -- [第一节 分布式系统设计理念](distribution/1_分布式系统设计理念.md) -- [第二节 CAP理论](distribution/2_CAP理论.md) -- [第三节 BASE理论](distribution/3_BASE理论.md) -- [第四节 分布式锁](distribution/4_分布式锁.md) -- [第五节 分布式事务](distribution/5_分布式事务.md) -- [第六节 分布式缓存的一致性哈希算法](distribution/6_分布式缓存的一致性哈希算法.md) - -## *4. 微服务 - -## 5. 网站架构 - -- [网站架构](web_architecture/1_网站架构.md) -- [设计秒杀系统](web_architecture/2_设计秒杀系统.md) - -# 💻 工具 - -## 1. Git +## 数据库理论 + +- [数据库系统原理](DataBase/1_数据库系统原理.md) +- [关系数据库设计理论](DataBase/2_关系数据库设计理论.md) +- [如何设计关系型数据库?](DataBase/3_设计关系型数据库.md) + +## SQL 编程题 + +- [LeetCode SQL 练习](DataBase/5_LeetCode_Database题解.md) + +## MySQL + +- [锁机制](MySQL/1_锁机制.md) +- [事务隔离级别实现](MySQL/2_事务隔离级别实现.md) +- [索引](MySQL/3_索引.md) +- [MySQL架构](MySQL/4_MySQL架构.md) +- [MySQL优化](MySQL/5_MySQL优化.md) +- [MySQL数据类型](MySQL/6_MySQL数据类型.md) +- [切分 & 复制问题](MySQL/切分复制问题.md) +- [补充知识](MySQL/6_补充知识.md) + +## Redis + +- [Redis 概述](Redis/1_概述.md) +- [Redis 数据类型](Redis/2_数据类型.md) +- [Redis 单线程模型](Redis/3_单线程模型.md) +- [键的过期时间 & 内存淘汰机制](Redis/4_键的过期时间和内存淘汰机制.md) +- [Redis 持久化机制](Redis/5_持久化机制.md) +- [Redis 事务](Redis/6_事务.md) +- [缓存问题](Redis/7_缓存问题.md) +- [Redis 部署方式](Redis/8_部署方式.md) +- [Redis 实战](Redis/9_实战.md) + +# 🎓 系统设计 + +- [系统设计基础](1_基础.md) + +## 安全性 + +- [Cookie & Session &Token](Safety/Cookie_Session_Token.md) +- [常见攻击技术及防御](Safety/常见攻击技术及防御.md) + +## 分布式 + +- [分布式系统基本概念](distribution/1_分布式系统设计理念.md) +- [CAP 理论 & BASE 理论](distribution/2_CAP理论.md) +- [分布式锁](distribution/3_分布式锁.md) +- [分布式事务](distribution/4_分布式事务.md) +- [Paxos 算法 & Raft 算法](distribution/5_Paxos算法.md) + +## 集群 + +- [负载均衡](cluster/1_负载均衡.md) +- [集群下的 Session 管理](cluster/2_集群下的Session管理.md) + +## 缓存 + +- [缓存需要考虑的问题](cache/1_缓存需要考虑的问题.md) +- [缓存常见问题](cache/2_缓存常见问题.md) +- [数据分布](cache/3_数据分布.md) + +## 消息队列 + +- [消息队列](1_消息队列.md) +- [Kafka 原理及应用](Kafka&RabbitMQ/Kafka.md) +- [RabbitMQ 原理及应用](Kafka&RabbitMQ/RabbitMQ.md) +- [Kafka & RabbitMQ 比较](Kafka&RabbitMQ/Kafka_RabbitMQ.md) + +# ☎️ 常用框架 + +## Spring + +- [Spring 概述](Spring/1_Spring概述.md) +- [Spring IoC 原理](Spring/2_SpringIoC原理.md) +- [Spring AOP 原理](Spring/3_SpringAOP原理.md) +- [Spring MVC 原理](Spring/4_SpringMVC原理.md) +- [Spring 事务管理](Spring/5_Spring事务管理.md) +- [Spring 中用到的设计模式](Spring/6_Spring中用到的设计模式.md) +- [MyBatis](Spring/7_MyBatis.md) + +## SpringBoot + +- [SpringBoot 概述](SpringBoot/1_SpringBoot概述.md) +- [配置文件](SpringBoot/2_配置文件.md) +- [常用注解](SpringBoot/3_常用注解.md) +- [请求参数校验](SpringBoot/4_请求参数校验.md) +- [全局异常处理](SpringBoot/5_全局异常处理.md) +- [实现定时任务](SpringBoot/6_实现定时任务.md) + +## Zookeeper + +- [Zookeeper 概述](Zookeeper/1_概述.md) +- [Zookeeper 数据模型](Zookeeper/2_数据模型.md) +- [Zookeeper 的原理](Zookeeper/3_原理.md) +- [Zookeeper 的应用](Zookeeper/4_应用.md) + +# 📖 工具 + +## Git - [git - 简明指南](http://rogerdudler.github.io/git-guide/index.zh.html) - [git - 图解](http://marklodato.github.io/visual-git-guide/index-zh-cn.html) +- [github - 小技巧](https://snailclimb.gitee.io/javaguide/#/docs/tools/Github%E6%8A%80%E5%B7%A7) -## 2. Maven - -- [Maven](Maven/1_Maven.md) -- [Maven 下载](http://maven.apache.org/download.cgi) -- [Maven 官网](https://maven.apache.org/) +## Docker -## *3. Nginx +- [Docker 概述](https://snailclimb.gitee.io/javaguide/#/docs/tools/Docker) +- [Docker 实战](https://snailclimb.gitee.io/javaguide/#/docs/tools/Docker%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%AE%9E%E6%88%98) -## *4. Docker +# 📚 参考资料 +## 参考仓库 +- [Java 基础知识](reference/Java基础_1.md) +- [Java 进阶知识](reference/Java进阶_1.md) +- [编程题](reference/编程题_1.md) -# 🔧 进阶指南 +## 参考书籍 -- [后端面试进阶指南](https://xiaozhuanlan.com/CyC2018) -- [Java 面试进阶指南](https://xiaozhuanlan.com/javainterview) -- [编码规范指南](https://github.com/alibaba/p3c) +- [Java 基础](reference/Java基础_2.md) +- [编程题](reference/编程题_2.md) +- [数据库](reference/数据库.md) +- [常用框架](reference/常用框架.md) -# 🙊 参考资料 +## 小专栏 -- [参考仓库](reference/参考仓库.md) -- [参考书籍](reference/参考书籍.md) -- [慕课网](reference/慕课网.md) +- [后端面试进阶指南](https://xiaozhuanlan.com/CyC2018) +- [Java 面试进阶指南](https://xiaozhuanlan.com/javainterview) \ No newline at end of file diff --git "a/docs/RabbitMQ/1_\344\270\273\346\265\201\346\266\210\346\201\257\344\270\255\351\227\264\344\273\266.md" "b/docs/RabbitMQ/1_\344\270\273\346\265\201\346\266\210\346\201\257\344\270\255\351\227\264\344\273\266.md" deleted file mode 100644 index c80a19fb..00000000 --- "a/docs/RabbitMQ/1_\344\270\273\346\265\201\346\266\210\346\201\257\344\270\255\351\227\264\344\273\266.md" +++ /dev/null @@ -1,34 +0,0 @@ -# 主流消息中间件 - -## 1. ActiveMQ - -ActiveMQ 是 Apache 出品的最流行的、性能强劲的开源消息总线,并且其是一个完全支持 JMS 规范的消息中间件。其丰富的 API、多种集群构建模式使得它成为业界老牌消息中间件,广泛应用于中小型企业。 - -## 2. Kafka - -Kafka 是 LinkedIn 开源的分布式发布-订阅消息系统,目前属于 Apache 顶级项目。 - -Kafka 主要特点是基于 Pull 的模式来处理消息消费,追求高吞吐量,一开始的目的就是用于日志收集和传输。0.8 版本开始支持复制,不支持事务,对消息的重复、丢失、错误没有严格要求,适合产生大量数据的互联网服务的数据收集业务。 - -## 3. RocketMQ - -RocketMQ 是阿里开源的消息中间件,目前也已经孵化为 Apache 顶级项目,它是纯 Java 开发,具有高吞吐量、高可用性、适合大规模分布式系统应用的特点。 - -RocketMQ 思路起源于 Kafka,它对消息的可靠传输及事务性做了优化,目前在阿里集团被广泛应用于交易、充值、流计算、消息推送、日志流式处理、binglog 分发等场景。 - -## 4. RabbitMQ - -RabbitMQ 是使用 Erlang 语言开发的开源消息队列系统,基于 AMQP 协议来实现。 - -AMQP 的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。AMQP 协议更多用在企业系统内,对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求还在其次。 - -## 对比 - -| 消息中间件 | ActiveMQ | Kafka | RocketMQ | RabbitMQ | -| :----------------------: | :-----------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :------------------------------------------------: | -| *吞吐量* | 万数量级 | 十万级,高吞吐 | 十万级,高吞吐 | 万数量级 | -| *Topic 数量对吞吐量影响* | / | Topic 从几十到几百时,吞吐量会大幅度下降,
在同等机器下,Kafka 尽量保证 Topic 数量不要过多,如果要支撑大规模的 Topic,需要增加更多的机器 | Topic 可达百/千级,吞吐量下降幅度小
在同等机器下,可以支撑大量的 Topic | / | -| *时效性* | 毫秒级 | 毫秒级 | 毫秒级 | 微秒级 | -| *可用性* | 高
基于主从架构实现高可用高 | 非常高
分布式架构 | 非常高
分布式架构 | 高
基于主从架构实现高可用高 | -| *可靠性* | 丢失数据的概率低 | 优化参数配置,
可以做到零丢失 | 优化参数配置,
可以做到零丢失 | 基本不会丢失数据 | -| *功能* | 功能较完备 | 功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用 | MQ 功能较为完善,还是分布式的,扩展性好 | 基于 ErLang 开发,并发能力很强,性能极好,延时很低 | \ No newline at end of file diff --git "a/docs/RabbitMQ/2_RabbitMQ\346\246\202\350\277\260.md" "b/docs/RabbitMQ/2_RabbitMQ\346\246\202\350\277\260.md" deleted file mode 100644 index aeab0f78..00000000 --- "a/docs/RabbitMQ/2_RabbitMQ\346\246\202\350\277\260.md" +++ /dev/null @@ -1,256 +0,0 @@ -# RabbitMQ 概述 - -## 简介 - -RabbitMQ 是一个开源的消息代理和队列服务器,用来通过普通协议在完全不同的应用中间共享数据,RabbitMQ 是使用 **Erlang 语言**来编写的,并且 RabbitMQ 是基于 AMQP 协议的。 - -特点: - -- 开源、性能优秀,稳定性好 -- 提供可靠性消息投递模式(confirm)、返回模式(return) -- 与 SpringAOP 完美的整合、API 丰富 -- 集群模式丰富,表达式配置,HA 模式,镜像队列模型 -- 保证数据不丢失的前提做到高可靠性、可用性 - -RabbitMQ 高性能的原因: - -**Erlang 语言**最初用在交换机的架构模式,这样使得 RabbitMQ 在 Broker 之间进行数据交互的性能时非常优秀的。Erlang 的优点:Erlang 有着和原生 Socket 一样的延迟。 - -## AMQP 协议 - -AMQP(Advanced Message Queuing Protocol)协议,即高级消息队列协议。 - -AMQP 是具有现在特征的二进制协议。是一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。 - -### 协议模型 - -
- -### 核心概念 - -- Server - - 又称 Broker,接受客户端的连接,实现 AMQP 实体服务 - -- Connection - - 连接,应用程序与 Broker 的网络连接 - -- Channel - - 网络通信,几乎所有的操作都在 Channel 中进行,Channel 是进行消息读写的通道。客户端可建立多个 Channel,每个 Channel 代表一个会话任务。 - -- Message - - 消息,服务器和应用程序之间传送的数据,由 Properties 和 Body 组成。Properties 可以对消息进行设置,比如消息的优先级、延迟等高级特性;Body 则就是消息体内容。 - -- Virtual Host - - 虚拟主机,用于进行逻辑隔离,最上层的消息路由。 - - 一个 Virtual Host 里面可以有若干个 Exchange 和 Queue,同一个 Virtual Host 里面不能有相同名称的 Exchange 或 Queue。 - -- Exchange - - 交换机,接受消息,根据路由键转发消息到绑定的队列。 - -- Binding - - Exchange 和 Queue 之间的虚拟连接,Binding 中可以包含 Routing Key - -- Routing Key - - 一个路由规则,虚拟机可用它来确定如何路由一个特定消息 - -- Queue - - 也称为 Message Queue(消息队列),保存消息并将它们转发给消费者 - - - -## RabbitMQ 整体架构 - -
- - - -## RabbitMQ 消息流转 - -
- -## RabbitMQ 简单使用 - -### 1. 启动服务器 - -```html -rabbitmq-server start & -``` - -### 2. 停止服务器 - -```html -rabbitmqctl stop_app -``` - -### 3. 插件管理 - -```html -rabbitmq-plugins enable rabbitmq_management -``` - -### 4. 查看管控台 - -```html -http://localhost:15672/ - -# 用户名 guest -# 密码 guest -``` - - - -## 命令行基础操作 - -### 1. 应用 - -- 关闭应用 - - ```html - rabbitmqctl stop_app - ``` - -- 启动应用 - - ```html - rabbitmqctl start_app - ``` -- 查看节点状态 - - ```html - rabbitmqctl status - ``` - -### 2. 用户 - -- 添加用户 - - ```html - rabbitmqctl add_user username password - ``` - -- 删除用户 - - ```html - rabbitmqctl delete_user username - ``` - -- 列出所有用户 - - ```html - rabbitmqctl list_users - ``` - -- 清除用户权限 - - ```html - rabbitmqctl clear_permissions -p vhostpath username - ``` - -- 列出用户权限 - - ```html - rabbitmqctl list_user_permissions username - ``` - -- 修改密码 - - ```html - rabbitmqctl change_password username newpassword - ``` - -- 设置用户权限 - - ```html - rabbitmqctl set_permissions -p vhostpath username ".*" ".*" ".*" - ``` - -### 3. 虚拟主机 - -- 创建虚拟主机 - - ```html - rabbitmqctl add_vhost vhostpath - ``` - -- 删除虚拟主机 - - ```html - rabbitmqctl delete_vhost vhostpath - ``` - -- 列出所有虚拟主机 - - ```html - rabbitmqctl list_vhosts - ``` - -- 列出虚拟主机上所有权限 - - ```html - rabbitmqctl list_permissions -p vhostpath - ``` - -### 4. 队列 - -- 查看所有队列信息 - - ```html - rabbitmqctl list_queues - ``` - -- 清除队列里的消息 - - ```html - rabbitmqctl -p vhostpath purge_queue blue - ``` - - - -## 命令行高级操作 - -- 移除所有数据 - - ```html - rabbitmqctl reset - # 要在 rabbitmqctl stop_app 之后使用 - ``` - -- 组成集群命令 - - ```html - rabbitmqctl join_cluster [--ram] - ``` - -- 查看集群状态 - - ```html - rabbitmq cluster_status - ``` - -- 修改集群节点的存储形式 - - ```html - rabbitmqctl change_cluser_node_type disc | ram - ``` - -- 摘除节点(忘记节点) - - ```html - rabbitmqctl forget_cluster_node [--offline] - ``` - -- 修改节点名称 - - ```html - rabbitmqctl rename_cluster_node oldnode1 newnode1 [oldnode2] [newnode2] - ``` diff --git "a/docs/RabbitMQ/3_RabbitMQ\345\205\245\351\227\250.md" "b/docs/RabbitMQ/3_RabbitMQ\345\205\245\351\227\250.md" deleted file mode 100644 index 2f9b833d..00000000 --- "a/docs/RabbitMQ/3_RabbitMQ\345\205\245\351\227\250.md" +++ /dev/null @@ -1,653 +0,0 @@ -# RabbitMQ 入门 - -## 简单案例:消息生产与消费 - -pom.xml 配置 - -```html - - com.rabbitmq - amqp-client - - 3.6.5 - -``` - -### 生产者 - -```java -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; - -import java.io.IOException; -import java.util.concurrent.TimeoutException; - -/** - *

简单案例:消息生产与消费

- * 消息生产者 - * Created by DHA on 2019/11/18. - */ -public class Producer { - public static void main(String[] args) throws IOException, TimeoutException { - - //1 创建一个 Connectionfactory,并进行设置 - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.setHost("localhost"); - connectionFactory.setPort(5672); - connectionFactory.setVirtualHost("/"); - - //2 通过连接工厂创建连接 - Connection connection = connectionFactory.newConnection(); - - //3 通过 connecion 创建一个 Channel - Channel channel = connection.createChannel(); - - //4 通过 chanel 发送数据 - for(int i=0;i<10;i++){ - String data="Hello!"; - channel.basicPublish("","test001",null,data.getBytes()); - } - - //5 关闭相关连接 - channel.close(); - connection.close(); - } -} -``` - -### 消费者 - -```java -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; - -import java.io.IOException; -import java.util.concurrent.TimeoutException; - -/** - *

简单案例:消息生产与消费

- * 消息生产者 - * Created by DHA on 2019/11/18. - */ -public class Producer { - public static void main(String[] args) throws IOException, TimeoutException { - - //1 创建一个 Connectionfactory,并进行设置 - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.setHost("localhost"); - connectionFactory.setPort(5672); - connectionFactory.setVirtualHost("/"); - - //2 通过连接工厂创建连接 - Connection connection = connectionFactory.newConnection(); - - //3 通过 connecion 创建一个 Channel - Channel channel = connection.createChannel(); - - //4 通过 chanel 发送数据 - for(int i=0;i<10;i++){ - String data="Hello!"; - channel.basicPublish("","test001",null,data.getBytes()); - } - - //5 关闭相关连接 - channel.close(); - connection.close(); - } -} -``` - -## 交换机(Exchange) - -交换机:接收消息,并根据路由键转发消息所绑定的队列。 - -
- -### 交换机属性 - -- Name - - 交换机名称 - -- Type - - 交换机类型 direct、topic、fanout、headers - -- Durability - - 是否需要持久化,true 为持久化 - -- Auto Delete - - 当最后一个绑定到 Exchange 上的队列删除后,自动删除该 Exchange - -- Internal - - 当前 Exchange 是否用于 RabbitMQ 内部使用,默认为 false - -- Arguments - - 扩展参数,用于扩展 AMQP 协议 - -### 交换机类型 - -#### 1. Direct Exchange - -所有发送到 Direct Exchange 的消息被转发到 routing key 中指定的 Queue。 - -
- -注意:Direct 模式可以使用 RabbitMQ 自带的 Exchange:default Exchange,所以不需要将 Exchange 进行任何绑定(binding)操作,消息传递时,routing key 必须完全匹配才会被队列接收,否则该消息会被抛弃。 - -##### 生产者 - -```java -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; - -import java.io.IOException; -import java.util.concurrent.TimeoutException; - -/** - *

Direct Exchange

- * 所有发送到 Direct Exchange 的消息被转发到 routing key 中指定的 Queue。 - * 消息生产者 - * Created by DHA on 2019/11/19. - */ -public class Producer4DirectExchange { - public static void main(String[] args) throws IOException, TimeoutException { - - //1 创建一个 Connectionfactory,并进行设置 - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.setHost("localhost"); - connectionFactory.setPort(5672); - connectionFactory.setVirtualHost("/"); - - //2 通过连接工厂创建连接 - Connection connection = connectionFactory.newConnection(); - - //3 通过 connecion 创建一个 Channel - Channel channel = connection.createChannel(); - - //4 声明 - //声明 exchange 名称 - String exchangeName="test_direct_exchange"; - String routingKey = "test.direct"; - - //5 通过 chanel 发送数据 - String msg = "Hello World RabbitMQ 4 Direct Exchange Message ... "; - channel.basicPublish(exchangeName, routingKey , null , msg.getBytes()); - - //6 关闭相关连接 - channel.close(); - connection.close(); - } -} -``` - - - -##### 消费者 - -```java -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; -import com.rabbitmq.client.QueueingConsumer; - -import java.io.IOException; -import java.util.concurrent.TimeoutException; - -/** - *

Direct Exchange

- * 所有发送到 Direct Exchange 的消息被转发到 routing key 中指定的 Queue。 - * 消息消费者 - * Created by DHA on 2019/11/19. - */ -public class Consumer4DirectExchange { - public static void main(String[] args) throws IOException, TimeoutException, InterruptedException { - - //1 创建一个 Connectionfactory,并进行设置 - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.setHost("localhost"); - connectionFactory.setPort(5672); - connectionFactory.setVirtualHost("/"); - - //2 通过连接工厂创建连接 - Connection connection = connectionFactory.newConnection(); - - //3 通过 connecion 创建一个 Channel - Channel channel = connection.createChannel(); - - //4 声明 - String exchangeName="test_direct_exchange"; - String exchangeType="direct"; - String queueName="test_direct_queue"; - String routingKey="test.direct"; - - // 声明一个交换机 - channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null); - // 声明一个队列 - channel.queueDeclare(queueName,false,false,false,null); - // 绑定:将一个队列绑定到一个交换机上 - channel.queueBind(queueName,exchangeName,routingKey); - - //5 创建消费者 - QueueingConsumer queueingConsumer=new QueueingConsumer(channel); - - //6 设置 channel - channel.basicConsume(queueName,true,queueingConsumer); - - //7 获取数据 - while(true){ - QueueingConsumer.Delivery delivery=queueingConsumer.nextDelivery(); - String msg=new String(delivery.getBody()); - System.out.println("消费端:"+msg); - } - } -} -``` - - - -#### 2. Topic Exchange - -Topic Exchange 将 routing key 与某 Topic 进行模糊匹配,此时队列需要绑定一个 Topic。 - -可以使用**通配符**进行模糊匹配: - -- "#" 表示匹配一个或多个词。"log.#" 能够匹配到 "log.info.oa" -- "*" 表示只能匹配一个词。"log.\*" 值能够匹配到 "log.info" - -
- -##### 生产者 - -```java -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; - -import java.io.IOException; -import java.util.concurrent.TimeoutException; - -/** - *

Topic Exchange

- * Topic Exchange 将 routing key 与某 Topic 进行模糊匹配,此时队列需要绑定一个 Topic。 - * 消息生产者 - * Created by DHA on 2019/11/19. - */ -public class Producer4TopicExchange { - public static void main(String[] args) throws IOException, TimeoutException { - - //1 创建一个 Connectionfactory,并进行设置 - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.setHost("localhost"); - connectionFactory.setPort(5672); - connectionFactory.setVirtualHost("/"); - - //2 通过连接工厂创建连接 - Connection connection = connectionFactory.newConnection(); - - //3 通过 connecion 创建一个 Channel - Channel channel = connection.createChannel(); - - //4 声明 - //声明 exchange 名称 - String exchangeName="test_topic_exchange"; - String routingKey1 = "user.save"; - String routingKey2 = "user.update"; - String routingKey3 = "user.delete.abc"; - - //5 通过 chanel 发送数据 - String msg = "Hello World RabbitMQ 4 Topic Exchange Message ... "; - channel.basicPublish(exchangeName, routingKey1 , null , msg.getBytes()); - channel.basicPublish(exchangeName, routingKey2 , null , msg.getBytes()); - channel.basicPublish(exchangeName, routingKey3 , null , msg.getBytes()); - - //6 关闭相关连接 - channel.close(); - connection.close(); - } -} -``` - - - -##### 消费者 - -```java -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; -import com.rabbitmq.client.QueueingConsumer; - -import java.io.IOException; -import java.util.concurrent.TimeoutException; - -/** - *

Topic Exchange

- * Topic Exchange 将 routing key 与某 Topic 进行模糊匹配,此时队列需要绑定一个 Topic。 - * 消息消费者 - * Created by DHA on 2019/11/19. - */ -public class Consumer4TopicExchange { - public static void main(String[] args) throws IOException, TimeoutException, InterruptedException { - - //1 创建一个 Connectionfactory,并进行设置 - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.setHost("localhost"); - connectionFactory.setPort(5672); - connectionFactory.setVirtualHost("/"); - - //2 通过连接工厂创建连接 - Connection connection = connectionFactory.newConnection(); - - //3 通过 connecion 创建一个 Channel - Channel channel = connection.createChannel(); - - //4 声明 - String exchangeName="test_topic_exchange"; - String exchangeType="topic"; - String queueName="test_topic_queue"; - String routingKey="user.*"; - - // 声明一个交换机 - channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null); - // 声明一个队列 - channel.queueDeclare(queueName,false,false,false,null); - // 绑定:将一个队列绑定到一个交换机上 - channel.queueBind(queueName,exchangeName,routingKey); - - //5 创建消费者 - QueueingConsumer queueingConsumer=new QueueingConsumer(channel); - - //6 设置 channel - channel.basicConsume(queueName,true,queueingConsumer); - - //7 获取数据 - while(true){ - QueueingConsumer.Delivery delivery=queueingConsumer.nextDelivery(); - String msg=new String(delivery.getBody()); - System.out.println("消费端:"+msg); - } - } -} -``` - - - -#### 3. Fanout Exchange - -Fanout Exchange 不处理 routing key,只需要简单的将队列绑定到交换机上,发送到交换机的消息都会被转发到交换机绑定的所有队列上。 - -
- -##### 生产者 - -```java -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; - -import java.io.IOException; -import java.util.concurrent.TimeoutException; - -/** - *

Fanout Exchange

- * Fanout Exchange 不处理 routing key,只需要简单的将队列绑定到交换机上,发送到交换机的消息都会被转发到交换机绑定的所有队列上。 - * 消息生产者 - * Created by DHA on 2019/11/19. - */ -public class Producer4FanoutExchange { - public static void main(String[] args) throws IOException, TimeoutException { - - //1 创建一个 Connectionfactory,并进行设置 - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.setHost("localhost"); - connectionFactory.setPort(5672); - connectionFactory.setVirtualHost("/"); - - //2 通过连接工厂创建连接 - Connection connection = connectionFactory.newConnection(); - - //3 通过 connecion 创建一个 Channel - Channel channel = connection.createChannel(); - - //4 声明 - //声明 exchange 名称 - String exchangeName="test_fanout_exchange"; - - //5 通过 chanel 发送数据 - for(int i = 0; i < 10; i ++) { - String msg = "Hello World RabbitMQ 4 Fanout Exchange Message ..."; - channel.basicPublish(exchangeName, "", null , msg.getBytes()); - } - - //6 关闭相关连接 - channel.close(); - connection.close(); - } -} -``` - - - -##### 消费者 - -```java -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; -import com.rabbitmq.client.QueueingConsumer; - -import java.io.IOException; -import java.util.concurrent.TimeoutException; - -/** - *

Fanout Exchange

- * Fanout Exchange 不处理 routing key,只需要简单的将队列绑定到交换机上,发送到交换机的消息都会被转发到交换机绑定的所有队列上。 - * 消息消费者 - * Created by DHA on 2019/11/19. - */ -public class Consumer4FanoutExchange { - public static void main(String[] args) throws IOException, TimeoutException, InterruptedException { - - //1 创建一个 Connectionfactory,并进行设置 - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.setHost("localhost"); - connectionFactory.setPort(5672); - connectionFactory.setVirtualHost("/"); - - //2 通过连接工厂创建连接 - Connection connection = connectionFactory.newConnection(); - - //3 通过 connecion 创建一个 Channel - Channel channel = connection.createChannel(); - - //4 声明 - String exchangeName="test_fanout_exchange"; - String exchangeType="fanout"; - String queueName="test_fanout_queue"; - String routingKey=""; - - // 声明一个交换机 - channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null); - // 声明一个队列 - channel.queueDeclare(queueName,false,false,false,null); - // 绑定:将一个队列绑定到一个交换机上 - channel.queueBind(queueName,exchangeName,routingKey); - - //5 创建消费者 - QueueingConsumer queueingConsumer=new QueueingConsumer(channel); - - //6 设置 channel - channel.basicConsume(queueName,true,queueingConsumer); - - //7 获取数据 - while(true){ - QueueingConsumer.Delivery delivery=queueingConsumer.nextDelivery(); - String msg=new String(delivery.getBody()); - System.out.println("消费端:"+msg); - } - } -} -``` - - - -## 绑定(Binding) - -绑定是指 Exchange 和 Exchange、Queue 之间的连接关系。Binding 中可以包含 routing key 或者参数。 - -## 消息队列(Queue) - -消息队列实际用来存储消息数据。常用属性: - -- Durability - - 是否持久化,Durable:是,Transient:否 - -- Auto Delete - - yes 表示最后一个监听被移除之后,会自动删除该 Queue - -## 虚拟主机(Virtual Host) - -虚拟主机用于进行逻辑隔离,是最长层的消息路由。 - -- 一个虚拟主机中可以有若干个 Exchange 和 Queue -- 同一个虚拟主机中不能有相同名称的 Exchange 或 Queue - -## 消息(Message) - -消息即服务器和应用程序之间传送的数据。本质上就是一段数据,由 Properties 和 Body(Properties)组成。 - -常用属性: - -- Delivery Mode - -- Headers - - 自定义属性 - -### 生产者 - -```java -import com.rabbitmq.client.AMQP; -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeoutException; - -/** - *

消息属性设置

- * 消息生产者 - * Created by DHA on 2019/11/18. - */ -public class Producer { - public static void main(String[] args) throws IOException, TimeoutException { - - //1 创建一个 Connectionfactory,并进行设置 - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.setHost("localhost"); - connectionFactory.setPort(5672); - connectionFactory.setVirtualHost("/"); - - //2 通过连接工厂创建连接 - Connection connection = connectionFactory.newConnection(); - - //3 通过 connecion 创建一个 Channel - Channel channel = connection.createChannel(); - - // 设置自定义属性 - Map headers = new HashMap<>(); - headers.put("attr1", "111"); - headers.put("attr2", "222"); - - //4 设置消息属性 - AMQP.BasicProperties properties=new AMQP.BasicProperties.Builder() - .deliveryMode(2) // 2 表示持久化的投递 - .contentEncoding("UTF-8") // 设置内容编码 - .expiration("10000") // 设置过期时间为 10 秒 - .headers(headers) // 自定义属性 - .build(); - - //5 通过 chanel 发送数据 - for(int i=0;i<5;i++){ - String data="Hello!"; - channel.basicPublish("","test001",properties,data.getBytes()); - } - - //6 关闭相关连接 - channel.close(); - connection.close(); - } -} -``` - - - -### 消费者 - -```java -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; -import com.rabbitmq.client.QueueingConsumer; -import com.rabbitmq.client.QueueingConsumer.Delivery; - -import java.io.IOException; -import java.util.Map; -import java.util.concurrent.TimeoutException; - -/** - *

消息属性设置

- * 消息消费者 - * Created by DHA on 2019/11/18. - */ -public class Consumer { - public static void main(String[] args) throws IOException, TimeoutException, InterruptedException { - - //1 创建一个 Connectionfactory,并进行设置 - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.setHost("localhost"); - connectionFactory.setPort(5672); - connectionFactory.setVirtualHost("/"); - - //2 通过连接工厂创建连接 - Connection connection = connectionFactory.newConnection(); - - //3 通过 connecion 创建一个 Channel - Channel channel = connection.createChannel(); - - //4 声明一个队列 - String queueName="test001"; - channel.queueDeclare(queueName,true,false,false,null); - - //5 创建消费者 - QueueingConsumer queueingConsumer=new QueueingConsumer(channel); - - //6 设置 channel - channel.basicConsume(queueName,true,queueingConsumer); - - //7 获取数据 - while(true){ - Delivery delivery=queueingConsumer.nextDelivery(); - String msg=new String(delivery.getBody()); - System.out.println("消费端:"+msg); - // 获取自定义属性数据 - Map headers=delivery.getProperties().getHeaders(); - System.err.println("headers get attribute attr1 value: " + headers.get("attr1")); - } - } -} -``` - diff --git "a/docs/RabbitMQ/4_RabbitMQ\351\253\230\347\272\247\347\211\271\346\200\247.md" "b/docs/RabbitMQ/4_RabbitMQ\351\253\230\347\272\247\347\211\271\346\200\247.md" deleted file mode 100644 index defefa14..00000000 --- "a/docs/RabbitMQ/4_RabbitMQ\351\253\230\347\272\247\347\211\271\346\200\247.md" +++ /dev/null @@ -1,1011 +0,0 @@ -# RabbitMQ 高级特性 - -## 消息100%可靠性投递的解决方案 - -### 生产端可靠性投递 - -- 保障消息成功发出 -- 保障 MQ 节点的成功接收 -- 发送端收到 MQ 节点(Broker)确认应答 -- **完善的消息补偿机制** - -### 解决方案1:消息落库 - -消息落库,对消息状态进行打标。 - -
- - - -### 解决方案2:二次确认,回调检查 - -消息的延迟投递,做二次确认,回调检查。 - -
- - - -## 消费端幂等性操作 - -- 唯一 ID + 指纹码 机制,利用数据库主键去重 - - 优点:实现简单 - - 缺点:高并罚下有数据库写入的性能瓶颈 - - 解决方案:根据 ID 进行分库分表进行算法路由 - -- 利用 Redis 原子特性实现 - -## Confirm 消息机制 - -消息的确认是指生产者投递消息后,如果 Broker 收到消息,则会给生产者一个应答,生产者进行接收应答,用来确定这条消息是否正常地发送到 Broker。 - -
- -实现机制: - -- 第一步:在 channel 上开启确认模式 - - ```java - channel.confirmSelect() - ``` - -- 第二步:在 channel 上添加监听 - - ```java - channel.addConfirmListener() - ``` - - 监听成功和失败的返回结果,根据具体的结果对消息进行重新发送或记录日志等后续处理。 - -### 生产者 - -```java -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.ConfirmListener; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; - -import java.io.IOException; -import java.util.concurrent.TimeoutException; - -/** - *

Confirm 消息机制

- * 消息的确认是指生产者投递消息后,如果 Broker 收到消息,则会给生产者一个应答,生产者进行接收应答,用来确定这条消息是否正常地发送到 Broker。 - * 消息生产者 - * Created by DHA on 2019/11/18. - */ -public class Producer { - public static void main(String[] args) throws IOException, TimeoutException { - - //1 创建一个 Connectionfactory,并进行设置 - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.setHost("localhost"); - connectionFactory.setPort(5672); - connectionFactory.setVirtualHost("/"); - - //2 通过连接工厂创建连接 - Connection connection = connectionFactory.newConnection(); - - //3 通过 connecion 创建一个 Channel - Channel channel = connection.createChannel(); - - //4 指定消息投递模式:confirm 模式 - channel.confirmSelect(); - - String exchangeName = "test_confirm_exchange"; - String routingKey = "confirm.save"; - - //5 通过 chanel 发送数据 - String msg="Hello!"; - channel.basicPublish(exchangeName,routingKey,null,msg.getBytes()); - - //6 添加一个确认监听 - channel.addConfirmListener(new ConfirmListener() { - @Override - public void handleAck(long l, boolean b) throws IOException { - System.out.println("------ack!-------"); - } - - @Override - public void handleNack(long l, boolean b) throws IOException { - System.out.println("------Nack!-------"); - } - }); - } -} -``` - - - -### 消费者 - -```java -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; -import com.rabbitmq.client.QueueingConsumer; -import com.rabbitmq.client.QueueingConsumer.Delivery; - -import java.io.IOException; -import java.util.concurrent.TimeoutException; - -/** - *

Confirm 消息机制

- * 消息的确认是指生产者投递消息后,如果 Broker 收到消息,则会给生产者一个应答,生产者进行接收应答,用来确定这条消息是否正常地发送到 Broker。 - * 消息消费者 - * Created by DHA on 2019/11/18. - */ -public class Consumer { - public static void main(String[] args) throws IOException, TimeoutException, InterruptedException { - - //1 创建一个 Connectionfactory,并进行设置 - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.setHost("localhost"); - connectionFactory.setPort(5672); - connectionFactory.setVirtualHost("/"); - - //2 通过连接工厂创建连接 - Connection connection = connectionFactory.newConnection(); - - //3 通过 connecion 创建一个 Channel - Channel channel = connection.createChannel(); - - //4 声明 - String exchangeName="test_confirm_exchange"; - String exchangeType="topic"; - String queueName="test_confirm_queue"; - String routingKey="confirm.#"; - - // 声明一个交换机 - channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null); - // 声明一个队列 - channel.queueDeclare(queueName,false,false,false,null); - // 绑定:将一个队列绑定到一个交换机上 - channel.queueBind(queueName,exchangeName,routingKey); - - //5 创建消费者 - QueueingConsumer queueingConsumer=new QueueingConsumer(channel); - - //6 设置 channel - channel.basicConsume(queueName,queueingConsumer); - - //7 获取数据 - while(true){ - Delivery delivery=queueingConsumer.nextDelivery(); - String msg=new String(delivery.getBody()); - System.out.println("消费端:"+msg); - } - } -} -``` - - - -## Return 消息机制 - -消息生产者通过制动一个 Exchange 和 routing key,把消息送达到某一个队列中去,然后消费者监听队列,进行消费处理操作。 - -在某些情况下,如果我们在发送消息的时候,当前的 **Exchange 不存在**或者指定的 **routing key路由不到**,此时我们需要监听这种不可达的消息,就要使用 Return Listener。 - -基础 API 有一个配置项 mandatory - -- 如果为 true,那么监听器会接收到路由不可达的消息,然后进行后续处理 -- 如果为 false, 那么 Broker 端自动删除该消息 - - - -
- - - -### 生产者 - -```java -import com.rabbitmq.client.*; - -import java.io.IOException; -import java.util.concurrent.TimeoutException; - -/** - *

Return 消息机制

- * 消息生产者通过制动一个 Exchange 和 routing key,把消息送达到某一个队列中去,然后消费者监听队列,进行消费处理操作。 - * 在某些情况下,如果我们在发送消息的时候,当前的 Exchange 不存在或者指定的 routing key路由不到,此时我们需要监听这种不可达的消息,就要使用 Return Listener。 - * - * 消息生产者 - * Created by DHA on 2019/11/18. - */ -public class Producer { - public static void main(String[] args) throws IOException, TimeoutException { - - //1 创建一个 Connectionfactory,并进行设置 - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.setHost("localhost"); - connectionFactory.setPort(5672); - connectionFactory.setVirtualHost("/"); - - //2 通过连接工厂创建连接 - Connection connection = connectionFactory.newConnection(); - - //3 通过 connecion 创建一个 Channel - Channel channel = connection.createChannel(); - - //4 指定消息投递模式:confirmListener 模式 - channel.confirmSelect(); - - String exchangeName = "test_return_exchange"; - String routingKey = "returnListener.save"; - String routingKeyError = "return.save"; - - //5 通过 chanel 发送数据 - String msg="Hello!"; - // mandatory 如果为 true,那么监听器会接收到路由不可达的消息,然后进行后续处理 - // mandatory 如果为 false, 那么 Broker 端自动删除该消息 - channel.basicPublish(exchangeName,routingKeyError,true,null,msg.getBytes()); - - //6 添加一个监听 - channel.addReturnListener(new ReturnListener() { - @Override - public void handleReturn(int replyCode, String replyText, String exchange, - String routingKey, AMQP.BasicProperties properties, byte[] body) - throws IOException { - System.err.println("---------handle return----------"); - System.err.println("replyCode: " + replyCode); - System.err.println("replyText: " + replyText); - System.err.println("exchange: " + exchange); - System.err.println("routingKey: " + routingKey); - System.err.println("properties: " + properties); - System.err.println("body: " + new String(body)); - } - }); - - } -} -``` - - - -### 消费者 - -```java -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; -import com.rabbitmq.client.QueueingConsumer; -import com.rabbitmq.client.QueueingConsumer.Delivery; - -import java.io.IOException; -import java.util.concurrent.TimeoutException; - -/** - *

Return 消息机制

- * 消息生产者通过制动一个 Exchange 和 routing key,把消息送达到某一个队列中去,然后消费者监听队列,进行消费处理操作。 - * 在某些情况下,如果我们在发送消息的时候,当前的 Exchange 不存在或者指定的 routing key路由不到,此时我们需要监听这种不可达的消息,就要使用 Return Listener。 - * - * 消息消费者 - * Created by DHA on 2019/11/18. - */ -public class Consumer { - public static void main(String[] args) throws IOException, TimeoutException, InterruptedException { - - //1 创建一个 Connectionfactory,并进行设置 - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.setHost("localhost"); - connectionFactory.setPort(5672); - connectionFactory.setVirtualHost("/"); - - //2 通过连接工厂创建连接 - Connection connection = connectionFactory.newConnection(); - - //3 通过 connecion 创建一个 Channel - Channel channel = connection.createChannel(); - - //4 声明 - String exchangeName="test_return_exchange"; - String exchangeType="topic"; - String queueName="test_return_queue"; - String routingKey="returnListener.#"; - - // 声明一个交换机 - channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null); - // 声明一个队列 - channel.queueDeclare(queueName,false,false,false,null); - // 绑定:将一个队列绑定到一个交换机上 - channel.queueBind(queueName,exchangeName,routingKey); - - //5 创建消费者 - QueueingConsumer queueingConsumer=new QueueingConsumer(channel); - - //6 设置 channel - channel.basicConsume(queueName,queueingConsumer); - - //7 获取数据 - while(true){ - Delivery delivery=queueingConsumer.nextDelivery(); - String msg=new String(delivery.getBody()); - System.out.println("消费端:"+msg); - } - } -} -``` - - - -## 消费端自定义监听 - -我们一般在代码中编写 while 循环,进行 consumer.nextDelivery 方法获取下一条消息,然后进行消费处理! - -但是,我们使用自定义的 Counsumer 更加方便,解耦性更强,在实际工作中广泛使用。 - -### 自定义消费者 - -实现步骤: - -- 先继承 `com.rabbitmq.client.DefaultConsumer` -- 再重写 `handleDelivery()` 方法 - -```java -import com.rabbitmq.client.AMQP; -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.DefaultConsumer; -import com.rabbitmq.client.Envelope; - -import java.io.IOException; - -/** - *

自定义消费者

- * 1 先继承 DefaultConsumer - * 2 然后重写 handleDelivery() 方法 - * - * Created by DHA on 2019/11/20. - */ -public class MyConsumer extends DefaultConsumer{ - public MyConsumer(Channel channel) { - super(channel); - } - - @Override - public void handleDelivery(String consumerTag, Envelope envelope, - AMQP.BasicProperties properties, byte[] body) - throws IOException { - System.err.println("----------consumer message-----------"); - System.err.println("consumerTag:"+consumerTag); - System.err.println("envelope:"+envelope); - System.err.println("properties:"+properties); - System.err.println("body:"+new String(body)); - } -} -``` - - - -### 生产者 - -```java -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; - -import java.io.IOException; -import java.util.concurrent.TimeoutException; - -/** - *

消费端自定义监听

- * 消息生产者 - * Created by DHA on 2019/11/19. - */ -public class Producer { - public static void main(String[] args) throws IOException, TimeoutException { - - //1 创建一个 Connectionfactory,并进行设置 - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.setHost("localhost"); - connectionFactory.setPort(5672); - connectionFactory.setVirtualHost("/"); - - //2 通过连接工厂创建连接 - Connection connection = connectionFactory.newConnection(); - - //3 通过 connecion 创建一个 Channel - Channel channel = connection.createChannel(); - - //4 声明 - //声明 exchange 名称 - String exchangeName="test_consumer_exchange"; - String routingKey = "consumer.save"; - - //5 通过 chanel 发送数据 - String msg = "Hello World RabbitMQ 4 Consumer Exchange Message ... "; - channel.basicPublish(exchangeName, routingKey , true,null , msg.getBytes()); - } -} -``` - - - -### 消费者 - -```java -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; -import com.rabbitmq.client.QueueingConsumer; -import com.rabbitmq.client.QueueingConsumer.Delivery; - -public class Consumer { - - public static void main(String[] args) throws Exception { - //1 创建一个 Connectionfactory,并进行设置 - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.setHost("localhost"); - connectionFactory.setPort(5672); - connectionFactory.setVirtualHost("/"); - - //2 通过连接工厂创建连接 - Connection connection = connectionFactory.newConnection(); - - //3 通过 connecion 创建一个 Channel - Channel channel = connection.createChannel(); - - //4 声明 - String exchangeName = "test_consumer_exchange"; - String exchangeType= "topic"; - String routingKey = "consumer.#"; - String queueName = "test_consumer_queue"; - - // 声明一个交换机 - channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null); - // 声明一个队列 - channel.queueDeclare(queueName,false,false,false,null); - // 绑定:将一个队列绑定到一个交换机上 - channel.queueBind(queueName,exchangeName,routingKey); - - /* - //5 创建消费者 - QueueingConsumer queueingConsumer=new QueueingConsumer(channel); - - //6 设置 channel - channel.basicConsume(queueName,true,queueingConsumer); - - //7 获取数据 - while(true){ - QueueingConsumer.Delivery delivery=queueingConsumer.nextDelivery(); - String msg=new String(delivery.getBody()); - System.out.println("消费端:"+msg); - } - */ - - //5 消费端自定义监听 使用 MyConsumer 相应实例 - channel.basicConsume(queueName, true, new MyConsumer(channel)); - } -} -``` - - - -## 消费端限流 - -RabbitMQ 提供了一种 QoS(服务质量保证) 功能,**在非自动确认消息的前提下**,如果一定数目的消息(通过基于 Consume 或者 Channel 设置 QoS 值)未被确认前,不进行消费新的消息。 - -涉及到的方法: - -```erlang -void BasicQoS(unit prefetchSize,ushort prefetchCount,bool global) -``` - -- prefetchSize:0 -- prefetchCount:告知 RabbitMQ 不要同时给一个消费者推送多个 N 个消息,即一旦有 N 个消息还没有 ACK,则该 Consumer 将 block 掉,一直到有消息 ack -- golbal:true 表示将上面设置应用于 Channel;true 表示将上面设置应用于 Consumer。 - -注意: - -- prefetchSize 和 global 这两项,RabbitMQ 没有实现,暂且不研究 -- prefetchCount 在 no_ask-false 的情况下生效,即在自动应答的情况下是不生效的 - -### 生产者 - -```java -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; - -import java.io.IOException; -import java.util.concurrent.TimeoutException; - -/** - *

消费端限流

- * 消息生产者 - * Created by DHA on 2019/11/19. - */ -public class Producer { - public static void main(String[] args) throws IOException, TimeoutException { - - //1 创建一个 Connectionfactory,并进行设置 - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.setHost("localhost"); - connectionFactory.setPort(5672); - connectionFactory.setVirtualHost("/"); - - //2 通过连接工厂创建连接 - Connection connection = connectionFactory.newConnection(); - - //3 通过 connecion 创建一个 Channel - Channel channel = connection.createChannel(); - - //4 声明 - //声明 exchange 名称 - String exchangeName="test_qos_exchange"; - String routingKey = "qos.save"; - - //5 通过 chanel 发送数据 - for(int i=0;i<5;i++){ - String msg = "Hello World RabbitMQ 4 Qos Message ... "; - channel.basicPublish(exchangeName, routingKey , true,null , msg.getBytes()); - } - } -} -``` - - - -### 消费者 - -```java -import com.rabbitmq.client.AMQP; -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.DefaultConsumer; -import com.rabbitmq.client.Envelope; - -import java.io.IOException; - -/** - *

自定义消费者

- * 1 先继承 DefaultConsumer - * 2 然后重写 handleDelivery() 方法 - * - * Created by DHA on 2019/11/20. - */ -public class MyConsumer extends DefaultConsumer{ - - // channel 进行签收 - private Channel channel; - - public MyConsumer(Channel channel) { - super(channel); - this.channel=channel; - } - - @Override - public void handleDelivery(String consumerTag, Envelope envelope, - AMQP.BasicProperties properties, byte[] body) - throws IOException { - System.err.println("----------consumer message-----------"); - System.err.println("consumerTag:"+consumerTag); - System.err.println("envelope:"+envelope); - System.err.println("properties:"+properties); - System.err.println("body:"+new String(body)); - - // false 表示不支持批量签收 - channel.basicAck(envelope.getDeliveryTag(),false); - } -} -``` - - - -```java -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; - -/** - *

消费端限流

- * 消息消费者 - * - * basicQoS(prefetchSize,refetchCount,global) - * - prefetchSize:0 - * - prefetchCount:告知 RabbitMQ 不要同时给一个消费者推送多个 N 个消息,即一旦有 N 个消息还没有 ACK, - * 则该 Consumer 将 block 掉,一直到有消息 ack - * - golbal:true 表示将上面设置应用于 Channel;true 表示将上面设置应用于 Consumer。 - * - * Created by DHA on 2019/11/19. - */ -public class Consumer { - - public static void main(String[] args) throws Exception { - //1 创建一个 Connectionfactory,并进行设置 - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.setHost("localhost"); - connectionFactory.setPort(5672); - connectionFactory.setVirtualHost("/"); - - //2 通过连接工厂创建连接 - Connection connection = connectionFactory.newConnection(); - - //3 通过 connecion 创建一个 Channel - Channel channel = connection.createChannel(); - - //4 声明 - String exchangeName = "test_qos_exchange"; - String exchangeType= "topic"; - String routingKey = "qos.#"; - String queueName = "test_qos_queue"; - - // 声明一个交换机 - channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null); - // 声明一个队列 - channel.queueDeclare(queueName,false,false,false,null); - // 绑定:将一个队列绑定到一个交换机上 - channel.queueBind(queueName,exchangeName,routingKey); - - // 第二个参数为 1,表示一次处理一条消息 - // 第三个参数为 false,表示应用到 Consumer 级别 - channel.basicQos(0,1,false); - - //5 消费端自定义监听 - // 首先将第二个参数设置为 false,进行手动签收 - channel.basicConsume(queueName, false, new MyConsumer(channel)); - } -} -``` - - - -## 消费端 ACK 与重回队列 - -- **消费端的手工 ACK 和 NACK** - - 消费端进行消费时: - - 如果由于业务异常,我们可以进行日志的记录,然后进行补偿; - - 如果由于服务器宕机等严重问题,那么需要手工进行 ACK 保障消费端消费成功 - -- **消费端的重回队列** - - 消费端重回队列是为了对没有成功的消息, 消息会被重新投递给 Broker。一般在使用应用中,都会关闭重回队列,即设置为 false。 - -### 生产者 - -```java -import com.rabbitmq.client.AMQP; -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeoutException; - -/** - *

消费端的手工 ACK 和 NACK

- * 消息生产者 - * - * Created by DHA on 2019/11/19. - */ -public class Producer { - public static void main(String[] args) throws IOException, TimeoutException { - - //1 创建一个 Connectionfactory,并进行设置 - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.setHost("localhost"); - connectionFactory.setPort(5672); - connectionFactory.setVirtualHost("/"); - - //2 通过连接工厂创建连接 - Connection connection = connectionFactory.newConnection(); - - //3 通过 connecion 创建一个 Channel - Channel channel = connection.createChannel(); - - //4 声明 - //声明 exchange 名称 - String exchangeName="test_ack_exchange"; - String routingKey = "ack.save"; - - //5 通过 chanel 发送数据 - for(int i =0; i<5; i ++){ - - Map headers = new HashMap(); - headers.put("num", i); - - AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder() - .deliveryMode(2) - .contentEncoding("UTF-8") - .headers(headers) - .build(); - String msg = "Hello RabbitMQ ACK Message " + i; - channel.basicPublish(exchangeName, routingKey, true, properties, msg.getBytes()); - } - } -} -``` - - - -### 消费者 - -```java -import com.rabbitmq.client.AMQP; -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.DefaultConsumer; -import com.rabbitmq.client.Envelope; - -import java.io.IOException; - -/** - *

消费端的重回队列

- * 消费端重回队列是为了对没有成功的消息, 消息会被重新投递给 Broker。 - * 一般在使用应用中,都会关闭重回队列,即设置为 false。 - * - * Created by DHA on 2019/11/20. - */ -public class MyConsumer extends DefaultConsumer{ - - // channel 进行签收 - private Channel channel; - - public MyConsumer(Channel channel) { - super(channel); - this.channel=channel; - } - - @Override - public void handleDelivery(String consumerTag, Envelope envelope, - AMQP.BasicProperties properties, byte[] body) - throws IOException { - System.err.println("-----------consume message----------"); - System.err.println("body: " + new String(body)); - - // 为了实验效果明显 - try { - Thread.sleep(2000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - Integer num=(Integer) properties.getHeaders().get("num"); - if(num==0){ - // 第二个参数表示是否支持批量签收,如果为 false,表示不支持批量签收 - // 第三个参数表示是否重回队列,如果为 true,表示支持重回队列,则会重回到队列的尾端 - channel.basicNack(envelope.getDeliveryTag(),false,true); - }else{ - // false 表示不支持批量签收 - channel.basicAck(envelope.getDeliveryTag(),false); - } - //channel.basicAck(envelope.getDeliveryTag(),false); - } -} -``` - - - -```java -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; - -/** - *

消费端的手工 ACK 和 NACK

- * 消息消费者 - * - * Created by DHA on 2019/11/19. - */ -public class Consumer { - - public static void main(String[] args) throws Exception { - //1 创建一个 Connectionfactory,并进行设置 - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.setHost("localhost"); - connectionFactory.setPort(5672); - connectionFactory.setVirtualHost("/"); - - //2 通过连接工厂创建连接 - Connection connection = connectionFactory.newConnection(); - - //3 通过 connecion 创建一个 Channel - Channel channel = connection.createChannel(); - - //4 声明 - String exchangeName = "test_ack_exchange"; - String exchangeType= "topic"; - String routingKey = "ack.#"; - String queueName = "test_ack_queue"; - - // 声明一个交换机 - channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null); - // 声明一个队列 - channel.queueDeclare(queueName,false,false,false,null); - // 绑定:将一个队列绑定到一个交换机上 - channel.queueBind(queueName,exchangeName,routingKey); - - //5 消费端自定义监听 - // 首先将第二个参数 autoACK 设置为 false,进行手动签收 - channel.basicConsume(queueName, false, new MyConsumer(channel)); - } -} -``` - - - -## TTL - -TTL(Time To Live)即生存时间。 - -- RabbitMQ 支持**消息**的过期时间,在消息发送时可以进行指定 - -- RabbitMQ 支持**队列**的过期时间,从消息如队列开始计算,只要超过了队列的超时时间配置,那么会自动清除消息 - -## 死信队列(DLX,Dead-Letter-Exchange ) - -利用 DLX,当消息在一个队列中变成死信(dead message)之后,其能被重新 publish 到另一个 Exchange,这个 Exchange 就是 DLX。 - -消息变成死信的几种情况: - -- 消息被拒绝(basic.reject / basic.nack),并且 requeue=false -- 消息 TTL 过期 -- 队列达到最大长度 - -注意: - -- DLX 也是一个正常的 Exchange,和一般的 Exchange 没有区别,它能在任何队列上被指定,实际上就是设置某个队列的属性。 - -- 当这个队列中有死信时,RabbitMQ 就会自动的将这个消息重新发布到设置的 Exchange 上去,进而被路由到另一个队列。 - -- 死信队列设置需要设置 Exchange 和 队列,然后绑定 - - ```java - channel.exchangeDeclare("dlx.exchange", "topic", true, false, null); - channel.queueDeclare("dlx.queue", true, false, false, null); - channel.queueBind("dlx.queue", "dlx.exchange", "#"); - ``` - - 然后我们进行正常声明 Exchange、队列和绑定,此时需要在队列上加上参数 arguments - - ```java - Map agruments = new HashMap(); - agruments.put("x-dead-letter-exchange", "dlx.exchange"); - //这个agruments属性,要设置到声明队列上 - channel.queueDeclare(queueName, true, false, false, agruments); - ``` - -### 生产者 - -```java -import com.rabbitmq.client.AMQP; -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; - -/** - *

死信队列

- * 利用 DLX,当消息在一个队列中变成死信(dead message)之后, - * 其能被重新 publish 到另一个 Exchange,这个 Exchange 就是 DLX。 - * - * 消息生产者 - * Created by DHA on 2019/11/20. - */ -public class Producer { - - public static void main(String[] args) throws Exception { - - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.setHost("localhost"); - connectionFactory.setPort(5672); - connectionFactory.setVirtualHost("/"); - - Connection connection = connectionFactory.newConnection(); - Channel channel = connection.createChannel(); - - String exchange = "test_dlx_exchange"; - String routingKey = "dlx.save"; - - String msg = "Hello RabbitMQ DLX Message"; - - AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder() - .deliveryMode(2) - .contentEncoding("UTF-8") - .expiration("10000") - .build(); - channel.basicPublish(exchange, routingKey, true, properties, msg.getBytes()); - } -} -``` - - - -### 消费者 - -```java -import com.rabbitmq.client.AMQP; -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.DefaultConsumer; -import com.rabbitmq.client.Envelope; - -import java.io.IOException; - -/** - *

自定义消费者

- * 1 先继承 DefaultConsumer - * 2 然后重写 handleDelivery() 方法 - * - * Created by DHA on 2019/11/20. - */ -public class MyConsumer extends DefaultConsumer{ - public MyConsumer(Channel channel) { - super(channel); - } - - @Override - public void handleDelivery(String consumerTag, Envelope envelope, - AMQP.BasicProperties properties, byte[] body) - throws IOException { - System.err.println("----------consumer message-----------"); - System.err.println("consumerTag:"+consumerTag); - System.err.println("envelope:"+envelope); - System.err.println("properties:"+properties); - System.err.println("body:"+new String(body)); - } -} -``` - - - -```java -import java.util.HashMap; -import java.util.Map; - -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.Connection; -import com.rabbitmq.client.ConnectionFactory; - -/** - *

死信队列

- * 利用 DLX,当消息在一个队列中变成死信(dead message)之后, - * 其能被重新 publish 到另一个 Exchange,这个 Exchange 就是 DLX。 - * - * 消息消费者 - * Created by DHA on 2019/11/20. - */ -public class Consumer { - - public static void main(String[] args) throws Exception { - - ConnectionFactory connectionFactory = new ConnectionFactory(); - connectionFactory.setHost("localhost"); - connectionFactory.setPort(5672); - connectionFactory.setVirtualHost("/"); - - Connection connection = connectionFactory.newConnection(); - Channel channel = connection.createChannel(); - - // 这就是一个普通的交换机 和 队列 以及路由 - String exchangeName = "test_dlx_exchange"; - String routingKey = "dlx.#"; - String queueName = "test_dlx_queue"; - - channel.exchangeDeclare(exchangeName, "topic", true, false, null); - Map agruments = new HashMap(); - agruments.put("x-dead-letter-exchange", "dlx.exchange"); - //这个agruments属性,要设置到声明队列上 - channel.queueDeclare(queueName, true, false,false,agruments); - channel.queueBind(queueName, exchangeName, routingKey); - - //要进行死信队列的声明: - channel.exchangeDeclare("dlx.exchange", "topic", true, false, null); - channel.queueDeclare("dlx.queue", true, false, false, null); - channel.queueBind("dlx.queue", "dlx.exchange", "#"); - - channel.basicConsume(queueName, true, new MyConsumer(channel)); - } -} -``` - - - - - - - diff --git "a/docs/RabbitMQ/5_RabbitMQ\346\225\264\345\220\210SpringAMQP.md" "b/docs/RabbitMQ/5_RabbitMQ\346\225\264\345\220\210SpringAMQP.md" deleted file mode 100644 index 3385a135..00000000 --- "a/docs/RabbitMQ/5_RabbitMQ\346\225\264\345\220\210SpringAMQP.md" +++ /dev/null @@ -1,737 +0,0 @@ -# RabbitMQ 整合 SpringAMQP - -## RabbitAdmin - -RabbitAdmin 类可以很好地操作 RabbitMQ,在 Spring 中直接进行注入即可。 - -### 配置 - -```java - - com.rabbitmq - amqp-client - 3.6.5 - - - - org.springframework.boot - spring-boot-starter-amqp - -``` - - - -### 注入 - -```java -import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; -import org.springframework.amqp.rabbit.connection.ConnectionFactory; -import org.springframework.amqp.rabbit.core.RabbitAdmin; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; - -@Configuration -@ComponentScan({"com.southeast.spring.*"}) -public class RabbitMQConfig { - - @Bean - public ConnectionFactory connectionFactory(){ - CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); - connectionFactory.setAddresses("localhost"); - connectionFactory.setUsername("guest"); - connectionFactory.setPassword("guest"); - connectionFactory.setVirtualHost("/"); - return connectionFactory; - } - - @Bean - public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) { - RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory); - rabbitAdmin.setAutoStartup(true); - return rabbitAdmin; - } -} -``` - -注意: - -- autoStartup 必须要设置为 true,否则容器不会加载 RabbitAdmin - -### 基础功能操作 - -```java -import java.util.HashMap; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.amqp.core.Binding; -import org.springframework.amqp.core.BindingBuilder; -import org.springframework.amqp.core.DirectExchange; -import org.springframework.amqp.core.FanoutExchange; -import org.springframework.amqp.core.Queue; -import org.springframework.amqp.core.TopicExchange; -import org.springframework.amqp.rabbit.core.RabbitAdmin; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -/** - *

RabbitAdmin 测试

- */ -@RunWith(SpringRunner.class) -@SpringBootTest -public class ApplicationTests { - - @Autowired - private RabbitAdmin rabbitAdmin; - - /** - *

测试 RabbitAdmin 的基本基本功能

- * @throws Exception - */ - @Test - public void testAdmin() throws Exception { - // 声明交换机 - rabbitAdmin.declareExchange(new DirectExchange("test.direct", false, false)); - rabbitAdmin.declareExchange(new TopicExchange("test.topic", false, false)); - rabbitAdmin.declareExchange(new FanoutExchange("test.fanout", false, false)); - - // 声明队列 - rabbitAdmin.declareQueue(new Queue("test.direct.queue", false)); - rabbitAdmin.declareQueue(new Queue("test.topic.queue", false)); - rabbitAdmin.declareQueue(new Queue("test.fanout.queue", false)); - - // 声明绑定:注意有 2 种方式 - rabbitAdmin.declareBinding(new Binding("test.direct.queue", - Binding.DestinationType.QUEUE, - "test.direct", "direct", new HashMap<>())); - - rabbitAdmin.declareBinding( - BindingBuilder - .bind(new Queue("test.topic.queue", false)) //直接创建队列 - .to(new TopicExchange("test.topic", false, false)) //直接创建交换机 建立关联关系 - .with("user.#")); //指定路由Key - - - rabbitAdmin.declareBinding( - BindingBuilder - .bind(new Queue("test.fanout.queue", false)) - .to(new FanoutExchange("test.fanout", false, false))); - - //清空队列数据 - rabbitAdmin.purgeQueue("test.topic.queue", false); - } -} -``` - -### RabbitAdmin 源码解析 - -RabbitMQAdmin 底层实现就是从 Spring 容器中获取 Exchange、Binding、routing key 以及 Queue 的 @Bean 声明,然后使用 RabbitTemplate 的 execute 方法执行对应的声明、修改、删除等一系列 RabbitMQ 基础功能操作。 - -```java -this.rabbitTemplate.execute( - new ChannelCallback() { - public Object doInRabbit(Channel channel) throws Exception { - RabbitAdmin.this.declareExchanges(channel, (Exchange[])exchanges.toArray(new Exchange[exchanges.size()])); - RabbitAdmin.this.declareQueues(channel, (Queue[])queues.toArray(new Queue[queues.size()])); - RabbitAdmin.this.declareBindings(channel, (Binding[])bindings.toArray(new Binding[bindings.size()])); - - return null; - } -}); -``` - - - -## SpringAMQP 声明 - -- RabbitMQ 基础 API 中声明一个 Exchange、一个队列、一个绑定: - - ```java - // 声明一个交换机 - channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null); - // 声明一个队列 - channel.queueDeclare(queueName,false,false,false,null); - // 绑定:将一个队列绑定到一个交换机上 - channel.queueBind(queueName,exchangeName,routingKey); - ``` - -- 使用 SpringAMQP 去声明,使用 @Bean 注解 - - ```java - /** - *

针对消费者声明交换机、队列、绑定

- * 1. 设置交换机类型 - * 2. 将队列绑定到交换机 - * - FanoutExchange: 将消息分发到所有的绑定队列,无routingkey的概念 - * - HeadersExchange :通过添加属性key-value匹配 - * - DirectExchange:按照routingkey分发到指定队列 - * - TopicExchange:多关键字匹配 - */ - // 声明 交换机、队列和绑定 - @Bean - public TopicExchange exchange001() { - return new TopicExchange("topic001", true, false); - } - - @Bean - public Queue queue001() { - return new Queue("queue001", true); //队列持久 - } - - @Bean - public Binding binding001() { - return BindingBuilder.bind(queue001()).to(exchange001()).with("spring.*"); - } - - @Bean - public TopicExchange exchange002() { - return new TopicExchange("topic002", true, false); - } - - @Bean - public Queue queue002() { - return new Queue("queue002", true); //队列持久 - } - - @Bean - public Binding binding002() { - return BindingBuilder.bind(queue002()).to(exchange002()).with("rabbit.*"); - } - - @Bean - public Queue queue003() { - return new Queue("queue003", true); //队列持久 - } - - @Bean - public Binding binding003() { - return BindingBuilder.bind(queue003()).to(exchange001()).with("mq.*"); - } - - @Bean - public Queue queue_image() { - return new Queue("image_queue", true); //队列持久 - } - - @Bean - public Queue queue_pdf() { - return new Queue("pdf_queue", true); //队列持久 - } - ``` - - -## RabbitTemplate - -RabbitTemplate 即消息模板,是在于 SpringAMQP 整合时进行发送消息的关键类,该类提供了丰富的发送消息方法,包括可靠性投递消息方法、回调监听消息接口 ConfirmCallback、返回值确认接口 ReturnCallback 等等。同样我们需要进行注入到 Spring 容器中,然后直接使用。 - -```java -@Bean -public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) { - RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); - return rabbitTemplate; -} -``` - -```java -@Autowired -private RabbitTemplate rabbitTemplate; - -/** - *

rabbitTemplate 测试发送消息

- * @throws Exception - */ -@Test -public void testSendMessage() throws Exception { - //1 创建消息 - MessageProperties messageProperties = new MessageProperties(); - messageProperties.getHeaders().put("desc", "信息描述.."); - messageProperties.getHeaders().put("type", "自定义消息类型.."); - Message message = new Message("Hello RabbitMQ".getBytes(), messageProperties); - - rabbitTemplate.convertAndSend("topic001", "spring.amqp", message, new MessagePostProcessor() { - @Override - public Message postProcessMessage(Message message) throws AmqpException { - System.err.println("------添加额外的设置---------"); - message.getMessageProperties().getHeaders().put("desc", "额外修改的信息描述"); - message.getMessageProperties().getHeaders().put("attr", "额外新加的属性"); - return message; - } - }); -} - -@Test -public void testSendMessage2() throws Exception { - //1 创建消息 - MessageProperties messageProperties = new MessageProperties(); - messageProperties.setContentType("text/plain"); - Message message = new Message("mq 消息1234".getBytes(), messageProperties); - - rabbitTemplate.send("topic001", "spring.abc", message); - - rabbitTemplate.convertAndSend("topic001", "spring.amqp", "hello object message send!"); - rabbitTemplate.convertAndSend("topic002", "rabbit.abc", "hello object message send!!!"); -} -``` - -## SimpleMessageListenerContainer - -SimpleMessageListenerContainer 即简单消息监听容器。 - -该类非常强大,我们可以对他进行很多设置,对于消费者的配置项,这个类都可以满足。 - -使用 SimpleMessageListenerContainer 可以监听多个队列、设置消费者数量、批量消费。 - -```java -/** - *

设置 简单消息监听容器

- * @param connectionFactory - * @return {@link SimpleMessageListenerContainer} - */ -@Bean -public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) { - - SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory); - - //监听多个队列 - container.setQueues(queue001(), queue002(), queue003(), queue_image(), queue_pdf()); - container.setConcurrentConsumers(1); - container.setMaxConcurrentConsumers(5); - container.setDefaultRequeueRejected(false); - container.setAcknowledgeMode(AcknowledgeMode.AUTO); - container.setConsumerTagStrategy(new ConsumerTagStrategy() { - @Override - public String createConsumerTag(String queue) { - return queue + "_" + UUID.randomUUID().toString(); - } - }); - - //设置监听器 - container.setMessageListener(new ChannelAwareMessageListener() { - @Override - public void onMessage(Message message, Channel channel) throws Exception { - String msg = new String(message.getBody()); - System.err.println("消费者: " + msg); - } - }); - - return container; -} -``` - - - -## MessageListenerAdapter - -MessageListenerAdapter 即消息监听适配器,通过 MessageListenerAdapter 的代码我们可以看出如下核心属性: - -- defaultListenerMethod:默认监听方法名称 - -- Delegate 委托对象:实际真实的委托对象,用于处理消息 - -- queueOrTagToMathodName:队列标识与方法名称组成的集合 - - 可以将队列和方法名称绑定,即指定队列里的消息会被绑定的方法所接受处理 - -### Adpater - -```java -/** - *

设置 简单消息监听容器

- * @param connectionFactory - * @return {@link SimpleMessageListenerContainer} - */ -@Bean -public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) { - - SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory); - - //监听多个队列 - container.setQueues(queue001(), queue002(), queue003(), queue_image(), queue_pdf()); - container.setConcurrentConsumers(1); - container.setMaxConcurrentConsumers(5); - container.setDefaultRequeueRejected(false); - container.setAcknowledgeMode(AcknowledgeMode.AUTO); - container.setConsumerTagStrategy(new ConsumerTagStrategy() { - @Override - public String createConsumerTag(String queue) { - return queue + "_" + UUID.randomUUID().toString(); - } - }); - - //设置监听器 - /*container.setMessageListener(new ChannelAwareMessageListener() { - @Override - public void onMessage(Message message, Channel channel) throws Exception { - String msg = new String(message.getBody()); - System.err.println("消费者: " + msg); - } - });*/ - - /** - * defaultListenerMethod 方式 - */ - /*// 设置适配器,作为参数 - MessageListenerAdapter adapter=new MessageListenerAdapter(new MessageDelegate()); - // 未指定监听方法,由默认的 handleMessage 处理 - container.setMessageListener(adapter);*/ - - /** - * 指定自定义监听方法方式 - */ - /*MessageListenerAdapter adapter=new MessageListenerAdapter(new MessageDelegate()); - // 指定 consumeMessage 监听方法,参数是字节数组 - adapter.setDefaultListenerMethod("consumeMessage"); - // 也可以添加一个转换器: 从字节数组转换为String - adapter.setMessageConverter(new TextMessageConverter()); - container.setMessageListener(adapter);*/ - - /** - * 将队列和方法名称绑定 - */ - MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate()); - - Map queueOrTagToMethodName = new HashMap<>(); - queueOrTagToMethodName.put("queue001", "method1"); - queueOrTagToMethodName.put("queue002", "method2"); - adapter.setQueueOrTagToMethodName(queueOrTagToMethodName); - - //自定义的方法参数是 String 类型 - adapter.setMessageConverter(new TextMessageConverter()); - container.setMessageListener(adapter); - - return container; -} -``` - - - -### Delegate - -```java -/** - *

消息委托

- * 默认方法是 handleMessage(byte[]) - * 自定义方法是 consumeMessage(byte[]) 和 consumeMessage(String ) - * Created by DHA on 2019/11/22. - */ -public class MessageDelegate { - - /** - *

默认方法

- * @param messageBody 消息体,注意是字节数组 - */ - public void handleMessage(byte[] messageBody) { - System.err.println("默认方法, 消息内容:" + new String(messageBody)); - } - - /** - *

自定义方法

- * @param messageBody 消息体,注意是字节数组 - */ - public void consumeMessage(byte[] messageBody) { - System.err.println("字节数组方法, 消息内容:" + new String(messageBody)); - } - - /** - *

自定义方法

- * @param messageBody 消息体,注意是字符串,需要自定义转换器 - */ - public void consumeMessage(String messageBody) { - System.err.println("字符串方法, 消息内容:" + messageBody); - } - - /** - *

自定义方法

- * method1 方法与 queue001 匹配 - * @param messageBody - */ - public void method1(String messageBody) { - System.err.println("method1 收到消息内容:" + new String(messageBody)); - } - - /** - *

自定义方法

- * method2 方法与 queue002 匹配 - * @param messageBody - */ - public void method2(String messageBody) { - System.err.println("method2 收到消息内容:" + new String(messageBody)); - } -} -``` - -### TextMessageConverter - -```java -import org.springframework.amqp.core.Message; -import org.springframework.amqp.core.MessageProperties; -import org.springframework.amqp.support.converter.MessageConversionException; -import org.springframework.amqp.support.converter.MessageConverter; - -public class TextMessageConverter implements MessageConverter { - - @Override - public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException { - return new Message(object.toString().getBytes(), messageProperties); - } - - @Override - public Object fromMessage(Message message) throws MessageConversionException { - String contentType = message.getMessageProperties().getContentType(); - if(null != contentType && contentType.contains("text")) { - return new String(message.getBody()); - } - return message.getBody(); - } -} -``` - - - -## MessageConverter - -MessageConverter 即消息转换器,在进行发送消息的时候,正常情况下消息体为二进制的数据方式进行传输,如果希望内部帮我们进行转换,或者指定自定义的转换器,就需要使用 MessageConverter。 - -### 消息委托 - -```java -import com.southeast.spring.entity.Order; -import com.southeast.spring.entity.Packaged; - -import java.io.File; -import java.util.Map; - -/** - *

消息委托

- */ -public class MyMessageDelegate { - - /** - *

支持 Json 格式

- * @param messageBody json 实际上就是 map - */ - public void consumeMessage(Map messageBody) { - System.err.println("map方法, 消息内容:" + messageBody); - } - - /** - *

支持 Java 对象转换

- * @param order {@link Order} - */ - public void consumeMessage(Order order) { - System.err.println("order对象, 消息内容, id: " + order.getId() + - ", name: " + order.getName() + - ", content: "+ order.getContent()); - } - - /** - *

支持 Java 对象多隐射转换

- * @param pack {@link Packaged} - */ - public void consumeMessage(Packaged pack) { - System.err.println("package对象, 消息内容, id: " + pack.getId() + - ", name: " + pack.getName() + - ", content: "+ pack.getDescription()); - } - - /** - *

支持自定义转换器

- * @param file - */ - public void consumeMessage(File file) { - System.err.println("文件对象 方法, 消息内容:" + file.getName()); - } -} -``` - - - -### Json 转换器 - -```java -/** - * 支持json格式的转换器 - */ -MessageListenerAdapter adapter = new MessageListenerAdapter(new MyMessageDelegate()); -adapter.setDefaultListenerMethod("consumeMessage"); - -Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter(); -adapter.setMessageConverter(jackson2JsonMessageConverter); - -container.setMessageListener(adapter); -``` - - - -### Java 对象转换 - -```java -/** - * 支持 java 对象转换 - * DefaultJackson2JavaTypeMapper & Jackson2JsonMessageConverter 支持java对象转换 - */ -MessageListenerAdapter adapter = new MessageListenerAdapter(new MyMessageDelegate()); -adapter.setDefaultListenerMethod("consumeMessage"); - -Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter(); - -DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper(); -jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper); - -adapter.setMessageConverter(jackson2JsonMessageConverter); -container.setMessageListener(adapter); -``` - - - -### Java 对象多映射转换 - -```java -/** - * 支持java对象多映射转换 - * DefaultJackson2JavaTypeMapper & Jackson2JsonMessageConverter 支持java对象多映射转换 - */ -MessageListenerAdapter adapter = new MessageListenerAdapter(new MyMessageDelegate()); -adapter.setDefaultListenerMethod("consumeMessage"); -Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter(); -DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper(); - -Map> idClassMapping = new HashMap>(); -idClassMapping.put("order", com.southeast.spring.entity.Order.class); -idClassMapping.put("packaged", com.southeast.spring.entity.Packaged.class); - -javaTypeMapper.setIdClassMapping(idClassMapping); - -jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper); -adapter.setMessageConverter(jackson2JsonMessageConverter); -container.setMessageListener(adapter); -``` - - - -### 自定义二进制转换器 - -```java -/** - * 自定义转换器 - */ -MessageListenerAdapter adapter = new MessageListenerAdapter(new MyMessageDelegate()); -adapter.setDefaultListenerMethod("consumeMessage"); - -//全局的转换器: -ContentTypeDelegatingMessageConverter convert = new ContentTypeDelegatingMessageConverter(); - -TextMessageConverter textConvert = new TextMessageConverter(); -convert.addDelegate("text", textConvert); -convert.addDelegate("html/text", textConvert); -convert.addDelegate("xml/text", textConvert); -convert.addDelegate("text/plain", textConvert); - -Jackson2JsonMessageConverter jsonConvert = new Jackson2JsonMessageConverter(); -convert.addDelegate("json", jsonConvert); -convert.addDelegate("application/json", jsonConvert); - -ImageMessageConverter imageConverter = new ImageMessageConverter(); -convert.addDelegate("image/png", imageConverter); -convert.addDelegate("image", imageConverter); - -PDFMessageConverter pdfConverter = new PDFMessageConverter(); -convert.addDelegate("application/pdf", pdfConverter); - - -adapter.setMessageConverter(convert); -container.setMessageListener(adapter); -``` - -自定义消息转换器,需要实现 MessageConverter 接口,然后重写: - -```java -toMessage() // Java 对象转换为 Message -fromMessage() // Message 对象转换为 Java 对象 -``` - - - -#### 1. pdf 转换器 - -```java -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.util.UUID; - -import org.springframework.amqp.core.Message; -import org.springframework.amqp.core.MessageProperties; -import org.springframework.amqp.support.converter.MessageConversionException; -import org.springframework.amqp.support.converter.MessageConverter; - -public class PDFMessageConverter implements MessageConverter { - - @Override - public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException { - throw new MessageConversionException(" convert error ! "); - } - - @Override - public Object fromMessage(Message message) throws MessageConversionException { - System.err.println("-----------PDF MessageConverter----------"); - - byte[] body = message.getBody(); - String fileName = UUID.randomUUID().toString(); - String path = "F:/Projects/RabbitMQ/rabbitmq-spring/testForConverterDest/" + fileName + ".pdf"; - File f = new File(path); - try { - Files.copy(new ByteArrayInputStream(body), f.toPath()); - } catch (IOException e) { - e.printStackTrace(); - } - return f; - } - -} -``` - -#### 2. 图片转换器 - -```java -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.util.UUID; - -import org.springframework.amqp.core.Message; -import org.springframework.amqp.core.MessageProperties; -import org.springframework.amqp.support.converter.MessageConversionException; -import org.springframework.amqp.support.converter.MessageConverter; - -public class ImageMessageConverter implements MessageConverter { - - @Override - public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException { - throw new MessageConversionException(" convert error ! "); - } - - @Override - public Object fromMessage(Message message) throws MessageConversionException { - System.err.println("-----------Image MessageConverter----------"); - - Object _extName = message.getMessageProperties().getHeaders().get("extName"); - String extName = _extName == null ? "png" : _extName.toString(); - - byte[] body = message.getBody(); - String fileName = UUID.randomUUID().toString(); - String path = "F:/Projects/RabbitMQ/rabbitmq-spring/testForConverterDest/" + fileName + "." + extName; - File f = new File(path); - try { - Files.copy(new ByteArrayInputStream(body), f.toPath()); - } catch (IOException e) { - e.printStackTrace(); - } - return f; - } -} -``` \ No newline at end of file diff --git "a/docs/RabbitMQ/6_RabbitMQ\346\225\264\345\220\210SpringBoot.md" "b/docs/RabbitMQ/6_RabbitMQ\346\225\264\345\220\210SpringBoot.md" deleted file mode 100644 index 7539c854..00000000 --- "a/docs/RabbitMQ/6_RabbitMQ\346\225\264\345\220\210SpringBoot.md" +++ /dev/null @@ -1,302 +0,0 @@ -# RabbitMQ 整合 SpringBoot - -## 生产者 - -### application.properties 配置 - -```html -spring.rabbitmq.addresses=localhost:5672 -spring.rabbitmq.username=guest -spring.rabbitmq.password=guest -spring.rabbitmq.virtual-host=/ -spring.rabbitmq.connection-timeout=15000 - -spring.rabbitmq.publisher-confirms=true -spring.rabbitmq.publisher-returns=true -# 注意: -# mandatory 如果为 true,那么监听器会接收到路由不可达的消息,然后进行后续处理 -# mandatory 如果为 false, 那么 Broker 端自动删除该消息 -spring.rabbitmq.template.mandatory=true -``` - -### MainConfig - -```java -import org.springframework.amqp.core.Binding; -import org.springframework.amqp.core.BindingBuilder; -import org.springframework.amqp.core.Queue; -import org.springframework.amqp.core.TopicExchange; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; - -@Configuration -@ComponentScan({"com.southeast.springboot.*"}) -public class MainConfig { - - @Bean - public TopicExchange exchange_1(){ - return new TopicExchange("exchange-1",true,false); - } - - @Bean - public Queue queue_1(){ - return new Queue("queue-1",true); - } - - @Bean - public Binding binding_1(){ - return BindingBuilder.bind(queue_1()).to(exchange_1()).with("springboot.#"); - } - - @Bean - public TopicExchange exchange_2(){ - return new TopicExchange("exchange-2",true,false); - } - - @Bean - public Queue queue_2(){ - return new Queue("queue-2",true); - } - - @Bean - public Binding binding_2(){ - return BindingBuilder.bind(queue_2()).to(exchange_2()).with("springboot.*"); - } -} -``` - - - -### RabbitSender - -- publisher-confirms - - 实现一个监听器用于监听 Broker 端给我们返回的确认请求 - -- publisher-confirms - - 保证消息对 Broker 端是可达的,如果出现 routing key 不可达的情况,则使用监听器对不可达的消息进行后续的处理,保证消息的路由成功。 - -```java -import java.util.Map; - -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.amqp.rabbit.core.RabbitTemplate.ConfirmCallback; -import org.springframework.amqp.rabbit.core.RabbitTemplate.ReturnCallback; -import org.springframework.amqp.rabbit.support.CorrelationData; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.stereotype.Component; - -import com.southeast.springboot.entity.Order; -/** - *

消息发送者

- */ -@Component -public class RabbitSender { - - //自动注入RabbitTemplate模板类 - @Autowired - private RabbitTemplate rabbitTemplate; - - //回调函数: confirm确认 - final ConfirmCallback confirmCallback = new RabbitTemplate.ConfirmCallback() { - @Override - public void confirm(CorrelationData correlationData, boolean ack, String cause) { - System.err.println("correlationData: " + correlationData); - System.err.println("ack: " + ack); - if(!ack){ - System.err.println("异常处理...."); - } - } - }; - - //回调函数: return返回 - final ReturnCallback returnCallback = new RabbitTemplate.ReturnCallback() { - @Override - public void returnedMessage(org.springframework.amqp.core.Message message, int replyCode, String replyText, - String exchange, String routingKey) { - System.err.println("return exchange: " + exchange + ", routingKey: " - + routingKey + ", replyCode: " + replyCode + ", replyText: " + replyText); - } - }; - - /** - *

发送消息方法调用: 构建Message消息

- * @param message - * @param properties - * @throws Exception - */ - public void send(Object message, Map properties) throws Exception { - MessageHeaders mhs = new MessageHeaders(properties); - Message msg = MessageBuilder.createMessage(message, mhs); - rabbitTemplate.setConfirmCallback(confirmCallback); - rabbitTemplate.setReturnCallback(returnCallback); - - CorrelationData correlationData = new CorrelationData(); - //id + 时间戳 全局唯一 - correlationData.setId("123456"); - rabbitTemplate.convertAndSend("exchange-1", "springboot.abc", msg, correlationData); - } - - /** - *

发送消息方法调用: 构建自定义对象消息

- * @param order - * @throws Exception - */ - public void sendOrder(Order order) throws Exception { - rabbitTemplate.setConfirmCallback(confirmCallback); - rabbitTemplate.setReturnCallback(returnCallback); - //id + 时间戳 全局唯一 - CorrelationData correlationData = new CorrelationData(); - correlationData.setId("678910"); - rabbitTemplate.convertAndSend("exchange-2", "spring.def", order, correlationData); - } - -} -``` - - - -## 消费者 - -### 消费端核心配置 - -```html -# 配置为手工确认模式 -spring.rabbitmq.listener.simple.acknowledge-mode=manual -# 设置消费端的监听个数和最大个数,控制消费端的并发数量 -spring.rabbitmq.listener.simple.concurrency=5 -spring.rabbitmq.listener.simple.max-concurrency=10 - -# 交换机、队列、routing key 配置 -spring.rabbitmq.listener.order.queue.name=queue-2 -spring.rabbitmq.listener.order.queue.durable=true -spring.rabbitmq.listener.order.exchange.name=exchange-2 -spring.rabbitmq.listener.order.exchange.durable=true -spring.rabbitmq.listener.order.exchange.type=topic -spring.rabbitmq.listener.order.exchange.ignoreDeclarationExceptions=true -spring.rabbitmq.listener.order.key=springboot.* -``` - - - -### @RabbitListener 注解 - -@RabbitListener 是一个组合注解,里面可以配置注解 @QueueBinding、@Queue、@Exchange 直接通过这个注解一次性配置交换机、队列、绑定、 routing key。 - -```java -@RabbitListener(bindings = @QueueBinding( - value = @Queue(value = "queue-1", - durable="true"), - exchange = @Exchange(value = "exchange-1", - durable="true", - type= "topic", - ignoreDeclarationExceptions = "true"), - key = "springboot.*" - ) - ) -``` - -将配置写在代码中,不太灵活,所以使用配置文件改进:在 `application.peoperties` 文件中 - -```html -spring.rabbitmq.listener.msg.queue.name=queue-1 -spring.rabbitmq.listener.msg.queue.durable=true -spring.rabbitmq.listener.msg.exchange.name=exchange-1 -spring.rabbitmq.listener.msg.exchange.durable=true -spring.rabbitmq.listener.msg.exchange.type=topic -spring.rabbitmq.listener.msg.exchange.ignoreDeclarationExceptions=true -spring.rabbitmq.listener.msg.key=springboot.* -``` - - - -改进后的代码: - - - -```java -@RabbitListener(bindings = @QueueBinding( - value = @Queue(value = "${spring.rabbitmq.listener.msg.queue.name}", - durable="${spring.rabbitmq.listener.msg.queue.durable}"), - exchange = @Exchange(value = "${spring.rabbitmq.listener.msg.exchange.name}", - durable="${spring.rabbitmq.listener.msg.exchange.durable}", - type= "${spring.rabbitmq.listener.msg.exchange.type}", - ignoreDeclarationExceptions = "${spring.rabbitmq.listener.msg.exchange.ignoreDeclarationExceptions}"), - key = "${spring.rabbitmq.listener.msg.key}" - ) - ) -``` - - - -### RabbitReceiver - -```java -import java.util.Map; - -import com.southeast.springboot.entity.Order; -import org.springframework.amqp.rabbit.annotation.Exchange; -import org.springframework.amqp.rabbit.annotation.Queue; -import org.springframework.amqp.rabbit.annotation.QueueBinding; -import org.springframework.amqp.rabbit.annotation.RabbitHandler; -import org.springframework.amqp.rabbit.annotation.RabbitListener; -import org.springframework.amqp.support.AmqpHeaders; -import org.springframework.messaging.Message; -import org.springframework.messaging.handler.annotation.Headers; -import org.springframework.messaging.handler.annotation.Payload; -import org.springframework.stereotype.Component; - -import com.rabbitmq.client.Channel; - -@Component -public class RabbitReceiver { - - - @RabbitListener(bindings = @QueueBinding( - value = @Queue(value = "queue-1", - durable="true"), - exchange = @Exchange(value = "exchange-1", - durable="true", - type= "topic", - ignoreDeclarationExceptions = "true"), - key = "springboot.*" - ) - ) - @RabbitHandler - public void onMessage(Message message, Channel channel) throws Exception { - System.err.println("--------------------------------------"); - System.err.println("消费端Payload: " + message.getPayload()); - Long deliveryTag = (Long)message.getHeaders().get(AmqpHeaders.DELIVERY_TAG); - //手工ACK - channel.basicAck(deliveryTag, false); - } - - - // 使用 配置文件 - @RabbitListener(bindings = @QueueBinding( - value = @Queue(value = "${spring.rabbitmq.listener.order.queue.name}", - durable="${spring.rabbitmq.listener.order.queue.durable}"), - exchange = @Exchange(value = "${spring.rabbitmq.listener.order.exchange.name}", - durable="${spring.rabbitmq.listener.order.exchange.durable}", - type= "${spring.rabbitmq.listener.order.exchange.type}", - ignoreDeclarationExceptions = "${spring.rabbitmq.listener.order.exchange.ignoreDeclarationExceptions}"), - key = "${spring.rabbitmq.listener.order.key}" - ) - ) - @RabbitHandler - public void onOrderMessage(@Payload Order order, - Channel channel, - @Headers Map headers) throws Exception { - System.err.println("--------------------------------------"); - System.err.println("消费端order: " + order.getId()); - Long deliveryTag = (Long)headers.get(AmqpHeaders.DELIVERY_TAG); - //手工ACK - channel.basicAck(deliveryTag, false); - } -} -``` \ No newline at end of file diff --git "a/docs/Redis/1_Redis\345\210\235\346\216\242.md" "b/docs/Redis/1_Redis\345\210\235\346\216\242.md" deleted file mode 100644 index 85e48143..00000000 --- "a/docs/Redis/1_Redis\345\210\235\346\216\242.md" +++ /dev/null @@ -1,307 +0,0 @@ -# 初探 Redis - -## Redis 简介 - -Redis 是一个速度非常快的非关系型数据库(non-relational database)/ NoSQL 数据库。 - -Redis 不使用表,也不会预定义或者强制去要求用户对 Redis 存储不同的数据进行关联。 - -> **Redis 为什么速度非常快?** - -- **纯内存操作** - - 数据存在内存中,类似于 HashMap。HashMap 的优势就是查找和操作的时间复杂度都是 O(1) - -- **数据结构简单** - - 不仅数据结构简单,而且对数据操作也简单 - -- **单线程** - - 避免了频繁的上下文切换,也不存在多进程或者多线程导致的切换而消耗 CPU 资源,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗 - -## Redis 数据结构 - -Redis 存储键(key)和 5 种不同类型的值(value)之间的映射。这 5 中类型分别为: - -STRING(字符串)、LIST(列表)、HASH(散列)、SET(集合)和 ZSET(有序集合)。 - -| 结构类型 | 结构存储的值 | -| :------: | :----------------------------------------------------------: | -| STRING | 字符串、整数或者浮点数 | -| LIST | 一个链表,链表上的每个节点都包含了一个字符串 | -| HASH | 包含键值对的无序散列表 | -| SET | 包含字符串的无序收集器 | -| ZSET | 字符串成员(member)与浮点数(score)分值之间的有序映射,
元素的排列顺序由分值大小决定 | - - - -### 1. STRING(字符串) - -字符串示例,键为 hello,值为 world: - -
- -- 字符串命令: - - | 命令 | 行为 | 时间复杂度 | - | :--: | :------------------------------------------------: | :--------: | - | GET | 获取存储在给定键中的值 | O(1) | - | SET | 设置存储在给定键中的值 | O(1) | - | DEL | 删除存储在给定键中的值(这个命令可以用于所有类型) | O(1) | - | INCR | 自增 | O(1) | - | DECR | 自减 | O(1) | - -- 使用 - - ```html - 127.0.0.1:6379> set hello world - OK - 127.0.0.1:6379> get hello - "world" - 127.0.0.1:6379> del hello - (integer) 1 - 127.0.0.1:6379> get hello - (nil) - ``` - - ```html - 127.0.0.1:6379> set num 1 - OK - 127.0.0.1:6379> incr num - (integer) 2 - 127.0.0.1:6379> get num - "2" - ``` - - -### 2. LIST(列表) - -list-key 是一个包含 3 个元素的列表键,列表中的元素是可以重复的: - -
- -- 列表命令: - - | 命令 | 行为 | 时间复杂度 | - | :----: | :--------------------------------------: | :--------: | - | LPUSH | 将给定值推入链表的左端 | O(N) | - | RPUSH | 将给定值推入链表的右端 | O(N) | - | LRANGE | 获取列表在给定范围上的所有值 | O(N) | - | LINDEX | 获取列表在给定位置上的单个元素 | O(N) | - | LPOP | 从链表的左端弹出一个值,并返回被弹出的值 | O(1) | - -- 使用 - - ```html - 127.0.0.1:6379> rpush list-key item - (integer) 1 - 127.0.0.1:6379> rpush list-key item2 - (integer) 2 - 127.0.0.1:6379> rpush list-key item - (integer) 3 # 返回的列表长度 - ``` - - ```html - 127.0.0.1:6379> lrange list-key 0 -1 - 1) "item" - 2) "item2" - 3) "item" - # 使用 0 为范围的开始索引,-1 为范围索引的结束索引,可以去除列表包含的所有元素 - 127.0.0.1:6379> lindex list-key 1 - "item2" - ``` - - ```html - 127.0.0.1:6379> lpop list-key - "item" - 127.0.0.1:6379> lrange list-key 0 -1 - 1) "item2" - 2) "item" - ``` - - -### 3. HASH(散列) - -hash-key 是一个包含两个键值对的散列键: - -
- -- 散列命令: - - | 命令 | 行为 | 时间复杂度 | - | :-----: | :--------------------------------------: | :--------: | - | HSET | 在散列表中关联其给定的键值对 | O(1) | - | HGET | 获取指定散列键的值 | O(1) | - | HGETALL | 获取散列包含的所有键值对 | O(N) | - | HDEL | 如果给定键存在于散列表中,那么移除这个键 | O(N) | - -- 使用 - - ```html - 127.0.0.1:6379> hset hash-key sub-key1 value1 - (integer) 1 - 127.0.0.1:6379> hset hash-key sub-key2 value2 - (integer) 1 - 127.0.0.1:6379> hset hash-key sub-key1 value2 - (integer) 0 - 127.0.0.1:6379> hgetall hash-key - 1) "sub-key1" - 2) "value2" - 3) "sub-key2" - 4) "value2" - ``` - - ```html - 127.0.0.1:6379> hdel hash-key sub-key2 - (integer) 1 - 127.0.0.1:6379> hdel hash-key sub-key2 - (integer) 0 - 127.0.0.1:6379> hget hash-key sub-key1 - "value2" - 127.0.0.1:6379> hgetall hash-key - 1) "sub-key1" - 2) "value2" - ``` - - -### 4. SET(集合) - -set-key 是一个包含 3 个元素的集合键: - -
- - - -- 集合命令: - - | 命令 | 行为 | 时间复杂度 | - | :-------: | :------------------------------------------: | :--------: | - | SADD | 将给定元素添加到集合 | O(N) | - | SMEMBERS | 返回集合包含的所有元素 | O(N) | - | SREM | 如果给定的元素存在于集合中,那么一处这个元素 | O(N) | - | SISMEMBER | 检查给定元素是否存在于集合中 | O(1) | - -- 使用 - - ```html - 127.0.0.1:6379> sadd set-key item - (integer) 1 - 127.0.0.1:6379> sadd set-key item2 - (integer) 1 - 127.0.0.1:6379> sadd set-key item3 - (integer) 1 - 127.0.0.1:6379> sadd set-key item - (integer) 0 - 127.0.0.1:6379> smembers set-key - 1) "item" - 2) "item3" - 3) "item2" - ``` - - ```html - 127.0.0.1:6379> sismember set-key item4 - (integer) 0 - 127.0.0.1:6379> sismember set-key item - (integer) 1 - ``` - - ```html - 127.0.0.1:6379> srem set-key item - (integer) 1 - 127.0.0.1:6379> srem set-key item - (integer) 0 - 127.0.0.1:6379> smembers set-key - 1) "item3" - 2) "item2" - ``` - - - -### 5. ZSET(有序集合) - -zset-key 是已一个包含 2 个元素的有序集合键: - -
- -- 有序集合命令: - - | 命令 | 行为 | 时间复杂度 | - | :-----------: | :------------------------------------------------------: | :---------: | - | ZADD | 将一个带有给定分值的成员添加到有序集合里面 | O(Mlog(N)) | - | ZRANGE | 根据元素在有序排列中所处的位置,从有序集合中获取多个元素 | O(log(N)+M) | - | ZRANGEBYSCORE | 获取有序集合在给定范围内的所有元素 | O(log(N)+M) | - | ZREM | 如果给定成员存在于有序集合,那么移除这个成员 | O(Mlog(N)) | - -- 使用 - - ```html - 127.0.0.1:6379> zadd zset-key 728 member1 - (integer) 1 - 127.0.0.1:6379> zadd zset-key 982 member2 - (integer) 1 - 127.0.0.1:6379> zadd zset-key 982 member2 - (integer) 0 - 127.0.0.1:6379> zrange zset-key 0 -1 withscores - 1) "member1" - 2) "728" - 3) "member2" - 4) "982" - ``` - - ```html - 127.0.0.1:6379> zrangebyscore zset-key 0 800 withscores - 1) "member1" - 2) "728" - ``` - - ```html - 127.0.0.1:6379> zrem zset-key member1 - (integer) 1 - 127.0.0.1:6379> zrem zset-key member1 - (integer) 0 - 127.0.0.1:6379> zrange zset-key 0 -1 withscores - 1) "member2" - 2) "982" - ``` - - -> **扩展** - -- [Redis 常用命令时间复杂度](https://blog.csdn.net/zzm848166546/article/details/80360665) -- [Redis 命令大全](https://redis.io/commands) - - - -## Redis 与 Memcached - -两者都是非关系型内存键值数据库,主要有以下不同: - -### 数据类型 - -- Redis 支持五种不同的数据类型,可以更灵活地解决问题 - -- Memcached 仅支持字符串类型 - -### 数据持久化 - -- Redis 支持两种持久化策略:RDB 快照和 AOF 日志 - -- Memcached 不支持持久化。 - -### 分布式 - -- Redis Cluster 实现了分布式的支持 - -- Memcached 不支持分布式,只能通过在客户端使用一致性哈希来实现分布式存储,这种方式在存储和查询时都需要先在客户端计算一次数据所在的节点。 - -### 内存管理机制 - -- 在 Redis 中,并不是所有数据都一直存储在内存中,可以将一些很久没用的 value 交换到磁盘,而 Memcached 的数据则会一直在内存中。 -- Memcached 将内存分割成特定长度的块来存储数据,以完全解决内存碎片的问题。但是这种方式会使得内存的利用率不高,例如块的大小为 128 bytes,只存储 100 bytes 的数据,那么剩下的 28 bytes 就浪费掉了。 - -### 线程模型 - -- Redis 使用单线程的多路 IO 复用模型 - -- Memcached 是多线程,非阻塞IO复用的网络模型 \ No newline at end of file diff --git "a/docs/Redis/1_\346\246\202\350\277\260.md" "b/docs/Redis/1_\346\246\202\350\277\260.md" new file mode 100644 index 00000000..92dabc04 --- /dev/null +++ "b/docs/Redis/1_\346\246\202\350\277\260.md" @@ -0,0 +1,98 @@ +# Redis 概述 + +Redis 是速度非常快的**非关系型(NoSQL)**基于**内存**的**键值**数据库。 + +键的类型只能为字符串,值可以支持五种数据类型:字符串(stirng)、列表(list)、集合(set)、散列表(hash)、有序集合(zset)。 + +Redis 支持很多特性,例如将内存中的数据持久化到硬盘中,使用复制来扩展**读性能**,使用分片来扩展**写性能**。 + +Redis 除了做缓存之外,也经常用来做分布式锁,甚至是消息队列。 + +## Redis & Memcached + +分布式缓存主要解决的是单机缓存的容量受服务器限制并且无法保存通用信息的问题。 + +分布式缓存兴起时,比较常用的是 Memcached。随着 Redis 的发展,大家慢慢都转而使用更加强大的 Redis 了。现在基本没有看过还有项目使用 Memcached 来做缓存了。 + +两者相同点: + +- 都是基于内存的非关系型内存键值数据库,一般都用来当做缓存使用。 +- 都有过期策略 +- 性能都非常高 + +主要有以下不同: + +- 数据类型: + + Memcached 仅支持字符串类型,而 Redis 支持五种不同的数据类型,可以更灵活地解决问题。 + +- 线程模型: + + Memcached 是多线程,非阻塞 IO 复用的网络模型,而 Redis 使用单线程的多路 IO 复用模型。 + +- 删除策略: + + Memcached 过期数据的删除策略只用了惰性删除,而 Redis 同时使用了惰性删除与定期删除。 + +- 数据持久化: + + Redis 支持两种持久化策略:RDB 快照和 AOF 日志,而 Memcached 不支持持久化。 + +- 分布式: + + Memcached 不支持分布式,只能通过在客户端使用一致性哈希来实现分布式存储,这种方式在存储和查询时都需要先在客户端计算一次数据所在的节点。 + + Redis Cluster 实现了分布式的支持。 + +- 内存管理机制 + + - 在 Redis 中,并不是所有数据都一直存储在内存中,可以将一些很久没用的 value 交换到磁盘,而 Memcached 的数据则会一直在内存中。 + - Memcached 将内存分割成特定长度的块来存储数据,以完全解决内存碎片的问题。但是这种方式会使得内存的利用率不高,例如块的大小为 128 bytes,只存储 100 bytes 的数据,那么剩下的 28 bytes 就浪费掉了。 + +## 使用场景 + +### 计数器 + +可以对 String 进行自增自减运算,从而实现计数器功能。 + +Redis 这种内存型数据库的读写性能非常高,很适合存储频繁读写的计数量。 + +### 缓存 + +将热点数据放到内存中,设置内存的最大使用量以及淘汰策略来保证缓存的命中率。 + +### 查找表 + +例如 DNS 记录就很适合使用 Redis 进行存储。 + +查找表和缓存类似,也是利用了 Redis 快速的查找特性。但是查找表的内容不能失效,而缓存的内容可以失效,因为缓存不作为可靠的数据来源。 + +### 分布式锁实现 + +在分布式场景下,无法使用单机环境下的锁来对多个节点上的进程进行同步。 + +可以使用 Redis 自带的 SETNX 命令实现分布式锁,除此之外,还可以使用官方提供的 RedLock 分布式锁实现。 + +### 其它业务场景 + +Bitmap 可以实现统计统计活跃用户等功能。 + +Set 可以实现交集、并集等操作,从而实现共同好友等功能。 + +ZSet 可以实现有序性操作,从而实现排行榜等功能。 + + + +## 使用 Redis 缓存的好处 + +**高性能**: + +假如用户第一次访问数据库中的某些数据的话,这个过程是比较慢,毕竟是从硬盘中读取的。但如果用户访问的数据属于高频数据并且不会经常改变的话,那么我们就可以很放心地将该用户访问的数据存在缓存中。 + +可以保证用户下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。不过,要保持数据库和缓存中的数据的一致性。 如果数据库中的对应数据改变的之后,需要同步改变缓存中相应的数据。 + +**高并发**: + +一般像 MySQL 这类的数据库的 QPS(Query Per Second,服务器每秒可以执行的查询次数)大概都在 1w 左右(4 核 8g) ,使用 Redis 缓存之后很容易达到 10 w+,甚至最高能达到 30 w+(单机 Redis 的情况,Redis 集群的话会更高)。 + +由此可见,直接操作缓存能够承受的数据库请求数量是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库,也就提高了系统整体的并发。 diff --git "a/docs/Redis/2_Redis\346\214\201\344\271\205\345\214\226.md" "b/docs/Redis/2_Redis\346\214\201\344\271\205\345\214\226.md" deleted file mode 100644 index 2f822159..00000000 --- "a/docs/Redis/2_Redis\346\214\201\344\271\205\345\214\226.md" +++ /dev/null @@ -1,145 +0,0 @@ -# Redis 持久化 - -Redis 是内存型数据库,为了保证数据断电后不丢失,需要将内存中的数据持久化到磁盘上;另一方面,保存中间结果,不必重新计算。 - -持久化有 2 种: - -- 快照持久化:将存在某一时刻的所有数据都写入磁盘中 -- AOF 持久化:在执行写命令时,将被执行的写命令复制到文件中 - -Redis 提供的持久化配置选项: - -```html -# 快照持久化 -save 60 1000 -stop-writes-on-bgsave-error yes -rdbcompression no - -# AOF 持久化 -appendonly yes -appendfsync always # 同步选项 -auto-aof-rewrite-percentage 100 -auto-aof-rewrite-min-size 64mb - -# 共享选项,决定了快照文件和 AOF 文件的保存位置 -dir ./ -``` - - - -## 快照持久化 - -快照持久化是通过创建快照来获得 Redis 存储在内存中数据在某个时间点上的副本。在创建快照后,用户可对快照备份,可将快照留在原地以便重启 Redis 时使用。 - -### 快照持久化配置 - -```html -save 60 10000 -# 在60秒(1分钟)之后,如果至少有10000个键发生变化,Redis 就会自动触发 BGSAVE 命令创建快照。 - -stop-writes-on-bgsave-error yes -# 表示备份进程出错的时候,主进程就停止接收新的写入操作,是为了保护持久化数据的一致性。 - -rdbcompression no -# RDB 的压缩设置为 no,因为压缩会占用更多的 CPU 资源。 -``` - - - -### 创建快照的方法 - -- BGAVSE 命令 - - 客户端向 Redis 发送 BGSAVE 命令来创建一个快照。对于支持 BGSAVE 命令的平台来说(基本上所有平台支持,除了Windows平台),**Redis 会调用 fork 来创建一个子进程,然后子进程负责将快照写入硬盘,而主进程则继续处理命令请求**。 - -- 用户设置 save 配置选项 - - ```html - save 60 10000 - # 从 redis 最近一次创建快照之后算起,满足 “60 s 内 10000 次写入”,Redis 会自动触发 BGSAVE 命令 - ``` - - - -### 存在的问题 - -- 如果系统发生故障,将会丢失**最近一次创建快照之后的数据** -- 如果数据量很大,保存快照的时间会很长 - - - -## AOF 持久化 - -AOF 持久化将被执行的写命令写到 AOF 文件末尾,记录数据发生的变化。Redis 值要从头到尾重新执行一次 AOF - -文件包含的所有写命令,就可恢复 AOF 文件所记录的数据。 - -默认情况下,Redis 没有开启 AOF(append only file)方式的持久化,可以通过 appendonly 参数开启: - -```html -appendonly yes -``` - - - -### 快照持久化配置 - -```java -appendonly yes - -appendfsync always # 同步选项 - -auto-aof-rewrite-percentage 100 - -auto-aof-rewrite-min-size 64mb -``` - - - -### 同步选项 - -使用 AOF 持久化需要设置同步选项,从而确保写命令什么时候会同步到磁盘文件上。 - -| 同步选项 | 同步频率 | 说明 | -| :------: | :--------------------------------------------: | :----------------------------------------------------------: | -| always | 每个 Redis 写命令都要同步写入磁盘 | 当系统发生崩溃时,丢失的数据减到最少;
需要对磁盘大量写入,速度会受到磁盘限制 | -| everysec | 每秒执行一次同步,显示地将多个写命令同步到磁盘 | 性能上和不使用任何持久化特性时相差无几,
可保证数据即使出现系统崩溃,用户也最多只会丢失 1 秒内产生的数据 | -| no | 由操作系统来决定何时进行同步 | 不会对 Redis 性能产生影响,但系统崩溃将导致 Redis 丢失不定量的数据,
若用户写入磁盘处理写入速度不够快,当缓冲区被等待写入磁盘的数据填满时,Redis 写入操作会被阻塞,并导致 Redis 处理命令请求变慢 | - - - -## 重写 / 压缩 AOF 文件 - -AOF 持久化存在的问题: - -- 随着 Redis 不断运行,AOF 文件的体积会不断增长,占用更多的磁盘空间 -- 若 AOF 文件体积非常大,则恢复的时间可能会比较长 - -为了解决 AOF 文件不断增大的问题,我们采用重写 / 压缩 AOF 文件的方式: - -用户 Redis 发送 BGREWRITEAOF 命令,通过移除 AOF 文件中冗余命令来重写 AOF 文件来减小 AOF 文件的体积。 - -(新的 AOF 文件和原有的 AOF 文件所保存的数据状态是一致的,但是体积更小)。 - -BGREWRITEAOF 原理: - -
- -Redis 维护 AOF 重写缓冲区和 AOF 缓冲区。子进程创建新的 AOF 文件期间,记录服务器执行的所有写命令会同时写入 2 个缓冲区中,使得新旧两个 AOF 文件中所保存的数据状态一致。 - - - -## 对比 - -| 持久化方式 | 优点 | 缺点 | -| :--------: | :--------------------------------------------------: | :----------------------------------------------------------: | -| 快照持久化 | 文件小,恢复快 | 会丢失最近一次生成快照后写入的数据;
是压缩文件,可读性较差 | -| AOF 持久化 | 可读性好;
数据不易丢失(丢失 1 秒内产生的数据) | 文件体积大,占用磁盘空间;
恢复时间长 | - - - -## 优化 - -Redis 4.0 以后将快照和 AOF 混合持久化(默认关闭,使用 `aof-user-rdb-preamble`开启),AOF 重写时将快照写入 AOF 文件开头。 - -这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据,当然缺点也是有的, AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。 \ No newline at end of file diff --git "a/docs/Redis/2_\346\225\260\346\215\256\347\261\273\345\236\213.md" "b/docs/Redis/2_\346\225\260\346\215\256\347\261\273\345\236\213.md" new file mode 100644 index 00000000..79b0791d --- /dev/null +++ "b/docs/Redis/2_\346\225\260\346\215\256\347\261\273\345\236\213.md" @@ -0,0 +1,319 @@ +# Redis 数据类型 + +## STRING + +### 简介 + +STRING 可以存储字符串、整数或者浮点数, 既可以对整个字符串或者字符串的其中一部分执行操作,也可以对整数和浮点数执行自增或者自减操作。 + +Redis 底层构建了一种**简单动态字符串(Simple Dynamic String,SDS)**。相比于 C 的原生字符串,SDS 不光可以保存文本数据还可以保存二进制数据,并且获取字符串长度时间复杂度为 O(1)(C 字符串为 O(N)),除此之外,SDS 的 API 是安全的,不会造成缓冲区溢出。 + +### 常用命令 + +- 普通字符串的基本操作: + + ```html + > set hello world + OK + > get hello + "world" + > exists hello + (integer) 1 + > del hello + (integer) 1 + > get hello + (nil)s + ``` + +- 批量设置: + + ```html + > mset key1 value1 key2 value2 + OK + > mget key1 key2 + 1) "value1" + 2) "value2" + ``` + +- 计数器: + + ```html + > set number 1 + OK + > incr number + (integer) 2 + 127.0.0.1:6379> get number + "2" + 127.0.0.1:6379> decr number + (integer) 1 + 127.0.0.1:6379> get number + "1" + ``` + +- 过期(默认永不过期): + + ```html + > expire key 60 # 设置 key 在 60s 后过期 + (integer) 1 + > setex key 60 value + # 设置 key-value 值且 key 在 60s 后过期 (setex:[set] + [ex]pire) + OK + > ttl key # 查看数据还有多久过期 + (integer) 56 + ``` + +### 应用场景 + +一般常用在需要计数的场景,比如用户的访问次数、热点文章的点赞转发数量等等。 + + + +## LIST + +### 简介 + +LIST 即列表,可以从两端压入或者弹出元素,还可以对单个或者多个元素进行修剪,只保留一个范围内的元素。 + +Redis 的 LIST 的底层实现为一个**双向链表**,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。 + +### 常用命令 + +- rpush/lpop 实现队列: + + ```html + > rpush myList value1 + (integer) 1 + > rpush myList value2 value3 + (integer) 3 + > lpop myList + "value1" + > lrange myList 0 1 + 1) "value2" + 2) "value3" + > lrange myList 0 -1 # 查看列表中的所有元素,-1表示倒数第一 + 1) "value2" + 2) "value3" + > llen myList # 查看列表长度 + (integer) 2 + ``` + +- rpush/rpop 实现栈: + + ```html + > rpush myList2 value1 value2 value3 + (integer) 3 + > rpop myList2 + "value3" + ``` + +### 应用场景 + +发布与订阅或者说消息队列、慢查询。 + + + +## HASH + +### 简介 + +HASH 即包含键值对的无序散列表,可以添加、获取、移除单个键值对;获取所有键值对;检查某个键是否存在。 + +Redis 底层实现类 JDK1.8 前的 HashMap(数组+链表)。 + +### 常用命令 + +```html +> hmset user name "a" description "student" age "12" +OK +> hexists user name # 查看 key 对应的value 中指定的字段是否存在 +(integer) 1 +> hget user name +"a" +> hget user age +"12" +127.0.0.1:6379> hgetall user +1) "name" +2) "a" +3) "description" +4) "student" +5) "age" +6) "12" +> hkeys user +1) "name" +2) "description" +3) "age" +127.0.0.1:6379> hvals user +1) "a" +2) "student" +3) "12" +> hset user name "b" +> hget user name +"b" +``` + +### 应用场景 + +系统中对象数据的存储。 + + + +## SET + +### 简介 + +SET 即无序集合。当需要存储一个列表数据,又不希望出现重复数据时,SET 是一个很好的选择。基于 SET 可以轻易实现交集、并集、差集的操作。比如:将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis 可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能,求交集即可。 + +### 常用命令 + +```html +> sadd mySet value1 value2 +(integer) 2 +> sadd mySet value1 +(integer) 0 +> smembers mySet +1) "value1" +2) "value2" +> scard mySet # 查看 set 的长度 +(integer) 2 +> sismember mySet value1 # 检查某个元素是否存在 set 中,只能接收单个元素 +(integer) 1 +> sadd mySet2 value2 value3 +(integer) 2 +> sinterstore mySet3 mySet mySet2 # 获取 mySet 和 mySet2 的交集并存放在 mySet3 中 +(integer) 1 +127.0.0.1:6379> smembers mySet3 +1) "value2" +``` + +### 应用场景 + +存放不能重复的数据以及需要获取多个数据源交集和并集等场景 + + + +## ZSET + +### 简介 + +ZSET 即有序集合 (sorted set) ,增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列,还可以通过 score 的范围来获取元素的列表。 + +### 常用命令 + +```html +> zadd myZset 3.0 value1 # 添加元素到 sorted set 中 3.0 为权重 +(integer) 1 +> zadd myZset 2.0 value2 1.0 value3 +(integer) 2 +127.0.0.1:6379> zcard myZset # 查看 sorted set 中的元素数量 +(integer) 3 +> zscore myZset value1 # 查看某个 value 的权重 +"3" +> zrange myZset 0 -1 # 顺序输出某个范围区间的元素,0 -1 表示输出所有元素 +1) "value3" +2) "value2" +3) "value1" +> zrange myZset 0 1 # 顺序输出某个范围区间的元素,0 为 start 1 为 stop +1) "value3" +2) "value2" +> zrevrange myZset 0 1 # 逆序输出某个范围区间的元素,0 为 start 1 为 stop +1) "value1" +2) "value2" +``` + +### 应用场景 + +需要对数据根据某个权重进行排序的场景。比如在直播系统中,在线用户列表,各种礼物排行榜等。 + + + +## BITMAP + +### 简介 + +BITMAP 即位图。用一个 bit 位来标记某个元素对应的 value,而 key 即是该元素。我们知道 8 个 bit 可以组成一个 byte,所以 BITMAP 本身会极大的节省储存空间: + +假设在 32 位操作系统中,从 20 亿个随机整数中找出某个数 m。 + + Java 中,如果每个数字用 int 存储,占用的空间约为 + +(2000000000*4/1024/1024/1024) ≈ 7.45 G + +如果按位存储,20亿个数就是 20 亿位,占用空间约为 + +(2000000000/8/1024/1024/1024) ≈ 0.23 G + +### 常用命令 + +```html +> setbit mykey 7 1 # setbit 会返回之前位的值(默认是 0) +(integer) 0 +> setbit mykey 7 0 +(integer) 1 +> getbit mykey 7 +(integer) 0 +> setbit mykey 6 1 +(integer) 0 +> setbit mykey 8 1 +(integer) 0 +> bitcount mykey # 通过 bitcount 统计被被设置为 1 的位的数量 +(integer) 2 +``` + +### 应用场景 + +适合需要**保存状态信息**(比如是否签到、是否登录 ...)并需要进一步对这些信息进行分析的场景。 + +#### 场景 1:用户行为分析 + +很多网站会对用户进行行为分析,比如记录用户是否点赞过某个视频。 + +```html +> setbit AoTeMan_mp4 用户id 1 +# 用户id 为 AoTeMan_mp4 视频点赞 +``` + +#### 场景 2:统计活跃用户 + +可以使用时间作为 key,用户 id 为 offset,如果用户当日活跃过就设置为 1。 + +计算某几天/月/年的活跃用户(我们约定:统计时间内只要有一天在线就称为活跃) + +举个例子: + +- 初始化数据: + + 用户 1 在 20210908、20210909 两天活跃过,用户 2 在 20210908 这一天活跃过 + + ```html + > setbit 20210908 1 1 + (integer) 0 + > setbit 20210908 2 1 + (integer) 0 + > setbit 20210909 1 1 + (integer) 0 + ``` + +- 统计 20210308~20210309 每天都活跃用户数: + + ```html + > bitop and active1 20210308 20210309 + # bittop 命令支持 and、or、not 、xor 这四种操作 + (integer) 1 + > bitcount active1 + (integer) 1 + ``` + +- 统计 20210308~20210309 活跃用户数: + + ```html + 127.0.0.1:6379> bitop or active2 20210308 20210309 + (integer) 1 + 127.0.0.1:6379> bitcount active2 + (integer) 2 + ``` + +#### 场景 3:用户在线情况 + +对于获取或者统计用户在线状态,使用 BITMAP 是一个节约空间且效率又高的一种方法。 + +只需要一个 key,然后用户 id 为 offset,如果在线就设置为 1,不在线就设置为 0。 diff --git "a/docs/Redis/3_Redis\345\244\215\345\210\266.md" "b/docs/Redis/3_Redis\345\244\215\345\210\266.md" deleted file mode 100644 index 7198853f..00000000 --- "a/docs/Redis/3_Redis\345\244\215\345\210\266.md" +++ /dev/null @@ -1,5 +0,0 @@ -# Redis 复制 - -## 配置 - -## Redis 复制启动过程 diff --git "a/docs/Redis/3_\345\215\225\347\272\277\347\250\213\346\250\241\345\236\213.md" "b/docs/Redis/3_\345\215\225\347\272\277\347\250\213\346\250\241\345\236\213.md" new file mode 100644 index 00000000..50cd7fd4 --- /dev/null +++ "b/docs/Redis/3_\345\215\225\347\272\277\347\250\213\346\250\241\345\236\213.md" @@ -0,0 +1,101 @@ +# Redis 单线程模型 + +## Redis 单线程模型 + +Redis 基于 Reactor 模式开发了自己的网络事件处理器,这个处理器被称为文件事件处理器(file event handler)。由于文件事件处理器(file event handler)是单线程方式运行的,所以说 Redis 是单线程模型。 + +虽然 Redis 是单线程的,但仍然可以监听大量的客户端连接,原因在于文件事件处理器使用 **I/O 多路复用(multiplexing)程序**来同时监听多个客户端连接(即多个套接字),并根据套接字目前执行的任务来为套接字关联不同的事件处理器。也就是说,**I/O 多路复用技术使得 Redis 不需要额外创建多余的线程来监听客户端的大量连接,降低了资源的消耗**。 + +### I/O 多路复用 + +多路指的是多个 Socke t连接,复用指的是复用一个线程。 + +多路复用主要有三种技术:select,poll,epoll。epoll 是最新的也是目前最好的多路复用技术。 + +
+ +### 文件事件处理器 + +Redis 服务器是一个事件驱动程序。服务器需要处理两类事件: + +- 文件事件:服务器通过套接字与客户端或者其它服务器进行通信,**文件事件就是对套接字操作的抽象**。 +- 时间事件:服务器有一些操作需要在给定的时间点执行,时间事件是对这类定时操作的抽象。 + +文件事件处理器主要是包含 4 个部分: + +- 多个 Socket(客户端连接) +- IO 多路复用程序(支持多个客户端连接) +- 文件事件分派器(将 Socket 关联到相应的事件处理器) +- 事件处理器(比如命令请求处理器、命令回复处理器、连接应答处理器) + +Redis 选择使用单线程模型处理来自客户端的绝大多数网络请求,主要有以下 3 个原因: + +- 几乎不存在 CPU 成为瓶颈的情况, Redis 主要受限于内存和网络。 + + Redis 并不是 CPU 密集型的服务,如果不开启 AOF 备份,所有 Redis 的操作都会在内存中完成不会涉及任何的 I/O 操作,这些**数据的读写由于只发生在内存中**,所以处理速度是非常快的; + + 整个服务的瓶颈在于**网络传输带来的延迟**和**等待客户端的数据传输**,也就是网络 I/O,所以使用多线程模型处理全部的外部请求可能不是一个好的方案。 + +- 使用单线程模型能带来更好的可维护性,方便开发和调试 + + 单线程不需要并发控制。 + +- 使用单线程模型也能并发的处理客户端的请求 + + I/O 多路复用机制。 + + + +## Redis 4.0 后引入多线程 + +在 Redis 4.0 之后的版本,Redis 服务在执行一些命令时就会使用 ”主处理线程“ 之外的其他线程,例如 `UNLINK`、`FLUSHALL ASYNC`、`FLUSHDB ASYNC` 等非阻塞的删除操作。 + +对于 Redis 中的一些超大键值对,几十 MB 或者几百 MB 的数据并不能在几毫秒的时间内处理完,Redis 可能会需要在释放内存空间上消耗较多的时间。其实释放内存空间的工作可以由后台线程异步进行处理,只需要将键从元数据中删除,而真正的删除操作会在后台异步执行。Redis 引入多线程后,对于一些**大键值对的删除操作**,可以通过**多线程非阻塞地释放内存空间**减少对 Redis 主线程阻塞的时间,提高执行的效率。 + +总的来说,**Redis 6.0 之前主要还是单线程处理**。 + + + +## Redis 6.0 引入多线程 + +Redis6.0 引入多线程主要是为了提高网络 IO 读写性能。 + +虽然 Redis6.0 引入了多线程,但是 Redis 的多线程只是在**网络数据的读写这类耗时操作上使用**,执行命令仍然是单线程顺序执行。所以仍然是线程安全的。 + +Redis 6.0 的多线程默认是禁用的,只使用主线程。如需开启需要修改 redis.conf 配置文件: + +```html +io-threads-do-reads yes # 开启多线程 +``` + +开启多线程后,还需要设置线程数,否则是不生效的。同样需要修改 redis.conf 配置文件: + +```html +io-threads 2 # 官网建议 4 核的机器建议设置为2或3个线程,8 核的建议设置为6个线程 +``` + +关于线程数,官方有一个建议:4 核的机器建议设置为 2 或 3 个线程,8 核的建议设置为 6个线程,线程数一定要小于机器核数。 + +### Redis 6.0 多线程的实现机制 + +实现机制流程如下: + +- 主线程负责接收建立连接请求,获取 Socket 放入等待队列 +- 主线程处理完读事件之后,通过轮询调度方法将这些连接分配给这些 IO 线程 +- 主线程阻塞等待 IO 线程读取 Socket 完毕 +- 主线程执行请求命令,请求数据读取并解析完成 +- 主线程阻塞等待 IO 线程将数据回写 Socket 完毕 +- 解除绑定,清空等待队列 + +其中 IO 线程: + +- 要么同时在读 Socket,要么同时在写,不会同时读或写 +- 只负责读写 Socket 解析命令,不负责命令处理 + +
+ +### Redis 6.0 多线程和 Memcached 多线程模型对比 + +相同点:都采用了 Master 线程-Worker 线程模型 + +不同点:Memcached 执行主逻辑也是在 Worker 线程里,模型更加简单,实现了真正的线程隔离,符合我们对线程隔离的常规理解。而 Redis 把处理逻辑交还给 Master 线程,虽然一定程度上增加了模型复杂度,但也解决了线程并发安全等问题。 \ No newline at end of file diff --git "a/docs/Redis/4_\345\244\204\347\220\206\347\263\273\347\273\237\346\225\205\351\232\234.md" "b/docs/Redis/4_\345\244\204\347\220\206\347\263\273\347\273\237\346\225\205\351\232\234.md" deleted file mode 100644 index 29d25f59..00000000 --- "a/docs/Redis/4_\345\244\204\347\220\206\347\263\273\347\273\237\346\225\205\351\232\234.md" +++ /dev/null @@ -1,2 +0,0 @@ -# 处理系统故障 - diff --git "a/docs/Redis/4_\351\224\256\347\232\204\350\277\207\346\234\237\346\227\266\351\227\264\345\222\214\345\206\205\345\255\230\346\267\230\346\261\260\346\234\272\345\210\266.md" "b/docs/Redis/4_\351\224\256\347\232\204\350\277\207\346\234\237\346\227\266\351\227\264\345\222\214\345\206\205\345\255\230\346\267\230\346\261\260\346\234\272\345\210\266.md" new file mode 100644 index 00000000..8b219d63 --- /dev/null +++ "b/docs/Redis/4_\351\224\256\347\232\204\350\277\207\346\234\237\346\227\266\351\227\264\345\222\214\345\206\205\345\255\230\346\267\230\346\261\260\346\234\272\345\210\266.md" @@ -0,0 +1,240 @@ +# 键的过期时间 & 内存淘汰机制 + +## 键的过期时间 + +Redis 可以为每个键设置过期时间,当键过期时,会自动删除该键。 + +对于散列表这种容器,只能为整个键设置过期时间(整个散列表),而不能为键里面的单个元素设置过期时间。 + +### 设置键的过期时间 + +内存是有限的,所以缓存中的数据不能一直保存在内存中,通过为键设置过期时间的方式有助于缓解内存的消耗。此外,在有些业务场景中要求数据只能在某一时间段内有效,比如要求用户登录的 token 只在当天有效,我们只需要将 token 的过期时间设置为 1 天后过期。 + +Redis 中 STRING 类型可以通过 `setex` 命令设置键的过期时间,其他类型需要使用 `expire` 命令来设置键的过期时间。 + +```html +> setex key 60 value # 设置 key 在 60s 后过期(setex:[set] + [ex]pire) +OK +> expire key 60 +(integer) 1 +127.0.0.1:6379> ttl key # ttl 即 time to live,用于查看数据还有多久过期 +(integer) 56 +``` + +### 过期数据删除策略 + +#### 定期删除 + +Redis 默认是每隔 100ms 就**随机抽取一些**设置了过期时间的 key,检查其是否过期,如果过期就删除。 + +Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。但是定期删除可能会导致很多过期 key 到了时间并没有被删除掉。 + +#### 惰性删除 + +获取某个 key 时,Redis 进行过期检查,key 已经过期,就删除。 + +这样对 CPU 最友好,但是也有可能会造成太多过期 key 没有被删除。 + +定期删除对内存更加友好,惰性删除对 CPU 更加友好,所以 Redis 采用的是定期删除+惰性删除。实际上这还是有问题的,如果定期删除漏掉了很多过期 key,又没有及时去获取 key 值,也就没走惰性删除,大量过期 key 堆积在内存里,导致 Redis 内存块耗尽了。为了解决这个问题,Redis 引入**内存淘汰机制**。 + + + +## 内存淘汰机制 + +### LRU(Least Recently Used) + +LRU(Least Recently Used)即最近最久未使用策略,优先淘汰最久未使用的数据,也就是上次被访问时间距离现在最久的数据。该策略可以**保证内存中的数据都是热点数据,也就是经常被访问的数据,从而保证缓存命中率**。 + +#### 双向链表 + HashMap 实现 LRU 算法 + +以下是基于 `双向链表 + HashMap` 的 LRU 算法实现,对算法的解释如下: + +- 访问某个节点时,将其从原来的位置删除,并重新插入到链表头部。这样就能保证链表尾部存储的就是最近最久未使用的节点,当节点数量大于缓存最大空间时就淘汰链表尾部的节点。 +- 为了使删除操作时间复杂度为 O(1),就不能采用遍历的方式找到某个节点。HashMap 存储着 Key 到节点的映射,通过 Key 就能以 O(1) 的时间得到节点,然后再以 O(1) 的时间将其从双向队列中删除。 + +```java +public class LRU implements Iterable { + + private Node head; + private Node tail; + private HashMap map; + private int maxSize; + + private class Node { + + Node pre; + Node next; + K k; + V v; + + public Node(K k, V v) { + this.k = k; + this.v = v; + } + } + + + public LRU(int maxSize) { + + this.maxSize = maxSize; + this.map = new HashMap<>(maxSize * 4 / 3); + + head = new Node(null, null); + tail = new Node(null, null); + + head.next = tail; + tail.pre = head; + } + + + public V get(K key) { + + if (!map.containsKey(key)) { + return null; + } + + Node node = map.get(key); + unlink(node); + appendHead(node); + + return node.v; + } + + + public void put(K key, V value) { + + if (map.containsKey(key)) { + Node node = map.get(key); + unlink(node); + } + + Node node = new Node(key, value); + map.put(key, node); + appendHead(node); + + if (map.size() > maxSize) { + Node toRemove = removeTail(); + map.remove(toRemove.k); + } + } + + + private void unlink(Node node) { + + Node pre = node.pre; + Node next = node.next; + + pre.next = next; + next.pre = pre; + + node.pre = null; + node.next = null; + } + + + private void appendHead(Node node) { + Node next = head.next; + node.next = next; + next.pre = node; + node.pre = head; + head.next = node; + } + + + private Node removeTail() { + + Node node = tail.pre; + + Node pre = node.pre; + tail.pre = pre; + pre.next = tail; + + node.pre = null; + node.next = null; + + return node; + } + + + @Override + public Iterator iterator() { + + return new Iterator() { + private Node cur = head.next; + + @Override + public boolean hasNext() { + return cur != tail; + } + + @Override + public K next() { + Node node = cur; + cur = cur.next; + return node.k; + } + }; + } +} +``` + +#### LinkedHashMap 实现 LRU 算法 + +基于 `LinkedHashMap` ,因为 `LinkedHashMap` 的底层就是双向链表,只需要重写 removeEldestEntry 方法即可。 + +```java +public class LRUCache extends LinkedHashMap { + + private int maxSize; + + /** + * 传递进来最多能缓存多少数据 + * + * @param maxSize 缓存大小 + */ + public LRUCache(int maxSize) { + super(maxSize, 0.75f, true); + this.maxSize = maxSize; + } + + /** + * 如果 map 中的数据量大于设定的最大容量,返回true, + * 再新加入对象时删除最老的数据 + * + * @param eldest 最老的数据项 + * @return true-移除最老的数据 + */ + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + // 当 map中的数据量大于指定的缓存个数的时候,自动移除最老的数据 + return size() > capacity; + } +} +``` + + + +### LFU(Least Frequently Used) + +LFU(Least Frequently Used)即最不经常使用策略,优先淘汰一段时间内使用次数最少的数据。 + + + +### Redis 数据淘汰策略 + +当内存使用量超出设置时,会施行数据淘汰策略。Redis 具体有 6 种数据淘汰策略: + +| 策略 | 描述 | +| :-------------- | :--------------------------------------------------- | +| volatile-lru | 从已设置过期时间的数据集中挑选最近最少使用的数据淘汰 | +| volatile-ttl | 从已设置过期时间的数据集中挑选将要过期的数据淘汰 | +| volatile-random | 从已设置过期时间的数据集中任意选择数据淘汰 | +| allkeys-lru | 从所有数据集中挑选最近最少使用的数据淘汰 | +| allkeys-random | 从所有数据集中任意选择数据进行淘汰 | +| noeviction | 禁止驱逐数据 | + +作为内存数据库,出于对性能和内存消耗的考虑,Redis 的淘汰算法实际实现上并非针对所有 key,而是抽样一小部分并且从中选出被淘汰的 key。 + +使用 Redis 缓存数据时,为了提高缓存命中率,需要保证缓存数据都是**热点数据**。可以将内存最大使用量设置为热点数据占用的内存量,然后启用 **allkeys-lru** 淘汰策略,将最近最少使用的数据淘汰。 + +Redis 4.0 引入了 volatile-lfu 和 allkeys-lfu 淘汰策略,LFU 策略通过统计访问频率,将访问频率最少的键值对淘汰。 diff --git "a/docs/Redis/5_\346\214\201\344\271\205\345\214\226\346\234\272\345\210\266.md" "b/docs/Redis/5_\346\214\201\344\271\205\345\214\226\346\234\272\345\210\266.md" new file mode 100644 index 00000000..ded71902 --- /dev/null +++ "b/docs/Redis/5_\346\214\201\344\271\205\345\214\226\346\234\272\345\210\266.md" @@ -0,0 +1,113 @@ +# Redis 持久化机制 + +持久化数据是将内存中的数据写入到硬盘里面,大部分原因是为了之后重用数据(比如重启机器、机器故障之后恢复数据),或者是为了防止系统故障而将数据备份到一个远程位置 + +Redis 是内存型数据库,支持将内存中数据持久化到磁盘。一方面,可保证数据断电后不丢失,另一方面,保存中间结果,不必重新计算。 + +Redis 支持 2 种持久化: + +- RDB (redis database) 持久化 +- AOF (append only file) 持久化 + +## RDB 持久化 + +RDB 持久化即快照持久化,通过创建**快照**来获得 Redis 存储在内存中数据在某个时间点上的副本。在创建快照后,用户可对快照备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本,还可以将快照留在原地以便重启服务器的时候使用。 + +### RDB 持久化配置 + +快照持久化是 Redis 默认采用的持久化方式,可在 Redis.conf 文件中进行配置: + +```html +save 60 1 +# 在60秒(1分钟)之后,如果至少有1个键发生变化,Redis 就会自动触发 BGSAVE 命令创建快照。 + +stop-writes-on-bgsave-error yes +# 表示备份进程出错的时候,主进程就停止接收新的写入操作,是为了保护持久化数据的一致性。 + +rdbcompression no +# RDB 的压缩设置为 no,因为压缩会占用更多的 CPU 资源 +``` + +### BGAVSE 命令 + +客户端向 Redis 发送 BGSAVE 命令来**创建一个快照**。对于支持 BGSAVE 命令的平台来说(基本上所有平台支持,除了 Windows 平台),**Redis 会调用 fork 来创建一个子进程,然后子进程负责将快照写入硬盘,而主进程则继续处理命令请求**。 + +对于如下命令: + +```html +save 60 10000 +``` + +表示从 **Redis 最近一次创建快照之后**算起,满足 “60 s 内 10000 次写入”,Redis 会自动触发 BGSAVE 命令。 + + + +## AOF 持久化 + +AOF 持久化将被执行的写命令写到 AOF 文件末尾,记录数据发生的变化。 + +Redis 值只要从头到尾重新执行一次 AOF 文件包含的所有写命令,就可恢复 AOF 文件所记录的数据。 + +默认情况下,Redis 没有开启 AOF(append only file)方式的持久化,可以通过 appendonly 参数开启: + +```html +appendonly yes +``` + +### AOF 持久化配置 + +```html +appendfsync always # 同步选项 + +auto-aof-rewrite-percentage 100 + +auto-aof-rewrite-min-size 64mb +``` + +### 同步选项 + +使用 AOF 持久化需要设置同步选项,从而确保写命令同步到磁盘文件上的时机。 + +这是因为对文件进行写入并不会马上将内容同步到磁盘上,而是先存储到缓冲区,然后由操作系统决定什么时候同步到磁盘。有以下同步选项: + +| 同步选项 | 同步频率 | 说明 | +| :------: | :--------------------------------------------: | :----------------------------------------------------------: | +| always | 每个 Redis 写命令都要同步写入磁盘 | 当系统发生崩溃时,丢失的数据减到最少;
需要对磁盘大量写入,速度会受到磁盘限制 | +| everysec | 每秒执行一次同步,显示地将多个写命令同步到磁盘 | 性能上和不使用任何持久化特性时相差无几,
可保证数据即使出现系统崩溃,用户也最多只会丢失 1 秒内产生的数据 | +| no | 由操作系统来决定何时进行同步 | 不会对 Redis 性能产生影响,但系统崩溃将导致 Redis 丢失不定量的数据,
若用户写入磁盘处理写入速度不够快,当缓冲区被等待写入磁盘的数据填满时,Redis 写入操作会被阻塞,并导致 Redis 处理命令请求变慢 | + +### 重写/压缩 AOF + +AOF 持久化存在的问题: + +- 随着 Redis 不断运行,AOF 文件的体积会不断增长,占用更多的磁盘空间 +- 若 AOF 文件体积非常大,则恢复的时间可能会比较长 + +为了解决 AOF 文件不断增大的问题,我们采用重写/压缩 AOF 文件的方式: + +用户发送 BGREWRITEAOF 命令,通过**移除 AOF 文件中冗余命令**来重写 AOF 文件来减小 AOF 文件的体积。也就是说,AOF 重写产生了一个新的 AOF 文件,这个新的 AOF 文件和原有的 AOF 文件所保存的数据库状态一样,但体积更小。 + +BGREWRITEAOF 原理: + +
+ +Redis 维护一个 AOF 重写缓冲区(aof_rewrite_buf)。在子进程创建新的 AOF 文件期间,记录服务器执行的所有写命令。当子进程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新旧两个 AOF 文件所保存的数据库状态一致。最后,服务器用新的 AOF 文件替换旧的 AOF 文件,以此来完成 AOF 文件重写操作。 + +## RDB 和 AOF 对比 + +| 持久化方式 | 优点 | 缺点 | +| :--------: | :--------------------------------------------------: | :----------------------------------------------------------: | +| RDB | 文件小,恢复快 | 如果系统发生故障,会丢失最近一次生成快照后写入的数据;
是压缩文件,可读性较差 | +| AOF | 可读性好;
数据不易丢失(丢失 1 秒内产生的数据) | 文件体积大,占用磁盘空间;
恢复时间长 | + + + +## Redis 4.0 优化 + +Redis 4.0 开始支持 RDB和 AOF 的混合持久化。默认是关闭的,需要配置: + +```html +aof-user-rdb-preamble yes +``` + +如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。 diff --git "a/docs/Redis/6_Redis\346\200\247\350\203\275\346\226\271\351\235\242\346\263\250\346\204\217\344\272\213\351\241\271.md" "b/docs/Redis/6_Redis\346\200\247\350\203\275\346\226\271\351\235\242\346\263\250\346\204\217\344\272\213\351\241\271.md" deleted file mode 100644 index 7e7be7de..00000000 --- "a/docs/Redis/6_Redis\346\200\247\350\203\275\346\226\271\351\235\242\346\263\250\346\204\217\344\272\213\351\241\271.md" +++ /dev/null @@ -1,2 +0,0 @@ -# Redis 性能方面注意事项 - diff --git "a/docs/Redis/5_Redis\344\272\213\345\212\241.md" "b/docs/Redis/6_\344\272\213\345\212\241.md" similarity index 54% rename from "docs/Redis/5_Redis\344\272\213\345\212\241.md" rename to "docs/Redis/6_\344\272\213\345\212\241.md" index 13ed34bc..81872e19 100644 --- "a/docs/Redis/5_Redis\344\272\213\345\212\241.md" +++ "b/docs/Redis/6_\344\272\213\345\212\241.md" @@ -1,33 +1,55 @@ -# Redis 事务 - -## 命令 - -- MULTI命令: - - 将客户端从非事务状态切换到事务状态,标志着事务的开始。 - -- EXEC命令: - - 客户端向服务端发送该命令后,服务器会遍历这个客户端的事务队列,并将所有命令的执行结果返回给客户端。 - -- WATCH命令: - - 乐观锁,可以在EXEC命令执行之前,监视任意数量的数据库键,并在EXEC命令执行的时候,检查被监视的键是否至少有一个已经被修改过了,如果有,服务器将拒绝执行该事务,并向客户端返回代表事务执行失败的空回复。 - -## 简述 - -**Redis 通过 MULTI、EXEC、WATCH 等命令来实现事务功能**。 - -事务提供了一种将多个命令请求打包,然后一次性、按顺序地执行多个命令的机制。服务器在执行事务期间,不会改去执行其它客户端的命令请求,它会将事务中的所有命令都执行完毕,然后才去处理其他客户端的命令请求。 - -事务中的多个命令被一次性发送给服务器,而不是一条一条发送,这种方式被称为**pipeline**。 - -注意: - -- Redis 最简单的事务实现方式是使用 MULTI 和 EXEC 命令将事务操作包围起来。 - -- 使用 pineline 的好处:Redis 使用的是客户端 / 服务器(C/S)模型和请求/响应协议的 TCP 服务器。Redis 客户端与 Redis 服务器之间使用 TCP 协议进行连接,一个客户端可以通过一个 Socket 连接发起多个请求命令。每个请求命令发出后客户端通常会阻塞并等待 Redis 服务器处理,Redis 处理完请求命令后会将结果通过响应报文返回给客户端,因此当执行多条命令的时候都需要等待上一条命令执行完毕才能执行。 - - pipeline 可以一次性发送多条命令并在执行完后一次性将结果返回,可以减少客户端与服务器之间的**网络通信次数**从而提升性能,并且 **pineline 基于队列**,而队列的特点是先进先出,这样就保证数据的**顺序性**。 - -- Redis 的事务和传统关系型数据库事务的最大区别在于,**Redis 不支持事务回滚机制(rollback)**,即使事务队列中的某个命令在执行期间出现了错误,整个事务也会继续执行下去,直到将事务队列中的所有命令都执行完毕为止。因为其作者认为,Redis 事务执行时错误通常都是编程错误产生的,这种错误通常只会出现在开发环境中,而很少会在实际的生产环境中出现,所以他任务没有必要开发 Redis 的回滚功能。 \ No newline at end of file +# Redis 事务 + +## MULTI & EXEC & WATCH + +- MULTI 命令:将客户端从非事务状态切换到事务状态,标志着事务的开始。 +- EXEC 命令:客户端向服务端发送该命令后,服务器会遍历这个客户端的事务队列,并将所有命令的执行结果返回给客户端。 +- WATCH 命令:乐观锁,可以在 EXEC 命令执行之前,监视任意数量的数据库键,并在EXEC 命令执行的时候,检查被监视的键是否至少有一个已经被修改过了,如果有,服务器将拒绝执行该事务,并向客户端返回代表事务执行失败的空回复。 + +```html +> MULTI # 事务开始 +OK +> SET USER "a" +QUEUED +> GET USER +QUEUED +> EXEC # 遍历客户端的事务队列,并将所有命令的执行结果返回给客户端 +1) OK # 设置 user 为 "a" 成功 +2) "a" # 获取 user 数据 +``` + +```html +> WATCH USER # 监视 user +OK +> MULTI +> SET USER "a" # 修改 user 数据 +OK +> GET USER +"a" +> EXEC # user 发生了修改,拒绝执行该事务 +ERR EXEC without MULTI +``` + +## Redis 事务 + +**Redis 通过 MULTI、EXEC、WATCH 等命令来实现事务功能**。 + +事务提供了一种将多个命令请求打包,然后一次性、按顺序地执行多个命令的机制。服务器在执行事务期间,不会改去执行其它客户端的命令请求,它会将事务中的所有命令都执行完毕,然后才去处理其他客户端的命令请求。 + +事务中的多个命令被一次性发送给服务器,而不是一条一条发送,这种方式被称为**流水线 (pipeline)**。 + +注意: + +- Redis 最简单的事务实现方式是使用 MULTI 和 EXEC 命令将事务操作包围起来。 + +- 使用 pineline 的好处:Redis 使用的是客户端 / 服务器(C/S)模型和请求/响应协议的 TCP 服务器。Redis 客户端与 Redis 服务器之间使用 TCP 协议进行连接,一个客户端可以通过一个 Socket 连接发起多个请求命令。每个请求命令发出后客户端通常会阻塞并等待 Redis 服务器处理,Redis 处理完请求命令后会将结果通过响应报文返回给客户端,因此当执行多条命令的时候都需要等待上一条命令执行完毕才能执行。 + + pipeline 可以一次性发送多条命令并在执行完后一次性将结果返回,可以减少客户端与服务器之间的**网络通信次数**从而提升性能,并且 **pineline 基于队列**,而队列的特点是先进先出,这样就保证数据的**顺序性**。 + +## 事务回滚 & 原子性 + +**Redis 是不支持事务回滚的,因而不满足数据库原子性的(而且不满足持久性)。** + +即使事务队列中的某个命令在执行期间出现了错误,整个事务也会继续执行下去,直到将事务队列中的所有命令都执行完毕为止。因为其作者认为,Redis 事务执行时错误通常都是编程错误产生的,这种错误通常只会出现在开发环境中,而很少会在实际的生产环境中出现,所以他任务没有必要开发 Redis 的回滚功能。 + +Redis 事务本质上提供了一种将多个命令请求打包功能,然后按顺序执行打包的所有命令,并且不会被中途打断。 diff --git "a/docs/Redis/7_\347\274\223\345\255\230\351\227\256\351\242\230.md" "b/docs/Redis/7_\347\274\223\345\255\230\351\227\256\351\242\230.md" new file mode 100644 index 00000000..509860c5 --- /dev/null +++ "b/docs/Redis/7_\347\274\223\345\255\230\351\227\256\351\242\230.md" @@ -0,0 +1,134 @@ +# 缓存问题 + +## 缓存雪崩 + +### 问题描述 + +缓存雪崩指的是**一些被大量访问数据(热点缓存)在某一时刻大面积失效**或者**缓存服务器宕机**,导致对应的请求直接到达数据库。 + +在有缓存的系统中,系统非常依赖于缓存,缓存分担了很大一部分的数据请求。当发生缓存雪崩时,数据库无法处理这么大的请求,导致数据库崩溃。 + +### 解决方案 + +针对热点缓存失效的情况: + +- 为了防止缓存在同一时间大面积过期导致的缓存雪崩,可以通过观察用户行为,合理设置缓存过期时间来实现; +- 热点缓存设置为永不失效。 + +针对缓存服务器宕机的情况: + +- 为了防止缓存服务器宕机出现的缓存雪崩,可以使用分布式缓存,分布式缓存中每一个节点只缓存部分的数据,当某个节点宕机时可以保证其它节点的缓存仍然可用; +- 限流,避免同时处理大量的请求。 + +此外,还可以通过 Redis 持久化机制保存的数据快速恢复缓存。 + +## 缓存穿透 + +### 问题描述 + +缓存穿透指的是对某个一定不存在的数据进行请求,该请求将会穿透缓存到达数据库。 + +举个例子:某个黑客故意制造我们缓存中不存在的键,并发起大量请求,这些请求就都落到数据库中,给数据库造成压力。 + +### 解决方案 + +- 参数校验: + + 将一些不合法的参数请求直接抛出异常信息返回给客户端。比如传入的邮箱格式、电话号码长度不对时直接返回错误消息给客户端。 + +- 缓存无效键: + + 如果一个查询返回的数据为空(不管数据是否存在,还是系统故障),我们仍然把这个**空结果进行缓存**,并且设置好过期时间,过期时间最长不超过 5 分钟。这种方式可以解决请求的键变化不频繁的情况。如果遭遇恶意攻击,每次构建不同的请求键,则会导致 Redis 中缓存大量的无效键 。如果要用这种方式来解决缓存穿透问题的话,那么需要尽量将无效的键的过期时间设置短一点比如 1 分钟。 + +- 使用布隆过滤器对这类请求进行过滤: + + 当用户请求过来,先判断用户发来的请求的值是否存在于布隆过滤器中。布隆过滤器能够以极小的空间开销解决海量数据判重问题。**一个一定不存在的数据会被该过滤器拦截掉**,从而避免了对底层存储系统的查询压力。 + +### 布隆过滤器 + +布隆过滤器能够以极小的空间开销解决海量数据判重问题,但是会有一定的误判概率:布隆过滤器判断某个元素存在,小概率会误判。布隆过滤器判断某个元素不存在,那么这个元素一定不在。 + +布隆过滤器也是使用 BitSet 存储数据(m 位),但是它进行了一定的改进,从而解除了 BitSet 要求数据的范围不大的限制。在存储时,它要求数据先经过 k 个哈希函数得到 k 个位置,并将 BitSet 中对应位置设置为 1。在查找时,也需要先经过 k 个哈希函数得到 k 个位置,如果所有位置上都为 1,那么表示这个数据存在。 + +由于哈希函数的特点,两个不同的数通过哈希函数得到的值可能相同。如果两个数通过 k 个哈希函数得到的值都相同,那么使用布隆过滤器会将这两个数判为相同。 + +可以令 k 和 m 都大一些会使得误判率降低,但是这会带来更高的时间和空间开销。 + +
+ +补充:[布隆过滤器详解](https://github.com/Snailclimb/JavaGuide/blob/master/docs/cs-basics/data-structure/bloom-filter.md) + +## 缓存击穿 + +### 问题描述 + +缓存击穿是指某个键非常热点,访问非常频繁,处于集中式高并发访问的情况。当缓存的键在某个时间点过期的时,大量的请求就 ”击穿“ 了缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。 + +### 解决方案 + +- 将热点数据设置为**永远不过期**,这样不会出现热点键过期问题。 +- 基于 Redis 或 Zookeeper 实现互斥锁。当键获得的值为空时,先加锁,然后从数据库加载数据,加载完毕后再释放锁。其他线程发现获取锁失败,等待一段时间后重试。 + +## 双写一致性问题 + +一致性问题分为**最终一致性**和**强一致性**。数据库和缓存双写,就必然会存在不一致的问题。对数据有强一致性要求,则数据不能放入缓存。我们所做的一切,只能保证最终一致性。另外,我们所做的方案从根本上来说,只是降低双写不一致发生的概率,无法完全避免。 + +### 方案1:先删除缓存,后更新数据库 + +#### 可能存在的问题 + +数据更新,先删除了缓存,然后要去修改数据库数据,此时还没修改。另一个查询请求去读缓存,发现缓存数据为空,则去查询数据库,查到了修改前的旧数据,并放到缓存中。随后更新数据的程序完成了数据库的修改,此时,数据库和缓存中的数据又不一致了。 + +举个例子,线程 A 更新数据 ,线程 B 读取数据,则并发场景下,可能存在如下执行过程: + +- 线程 A 删除了缓存 +- 线程 B 查询,发现缓存数据为空 +- 线程 B 去数据库查询得到旧值 +- 线程 B 将旧值写入缓存 +- 线程 A 将新值写入数据库 + +显然缓存中是旧值,但是数据库中已经是新值了。 + +#### 解决方案 + +将读请求和写请求串行化,串到一个**内存队列**里去,这样就可以保证一定不会出现不一致的情况。串行化之后,就会导致系统的**吞吐量会大幅度的降低**,往往需要使用比正常情况下多几倍的机器去支撑线上的请求。 + +> **问题:为什么是删除缓存,而不是更新缓存?** + +原因:举个例子,一个缓存涉及的表的字段,在 1 分钟内就修改了 100 次,那么缓存更新 100 次;但是这个缓存在 1 分钟内只被**读取**了 1 次,有**大量的冷数据**。实际上,如果只是删除缓存的话,那么在 1 分钟内,这个缓存不过就重新计算一次而已,开销大幅度降低。因此对于频繁更新的场景,如果每次更新数据库,都要更新缓存,倒不如直接删除掉缓存。其实删除缓存,而不是更新缓存,就是一个 lazy 计算的思想:**不要每次都重新做复杂的计算,不管它会不会用到,而是让它在需要被使用时才重新计算**。 + + + +### 方案2:旁路缓存模式 + +旁路缓存模式 (Cache Aside Pattern): + +- 读的时候,先读缓存,缓存中没有数据的话,就读数据库中数据,然后取出数据库中数据放入缓存,同时返回响应。 +- 更新时,先更新数据库,然后再删除缓存。 + +#### 可能存在的问题 + +先修改数据库,再删除缓存。 + +如果**删除缓存失败**,那么会导致数据库中是新数据,缓存中是旧数据,数据就出现不一致。 + +如果在高并发的场景下,也有可能会出现数据库与缓存数据不一致的情况: + +- 缓存**刚好**失效 +- 线程 A 查询数据库,得一个旧值 +- 线程 B 将新值写入数据库 +- 线程 B 删除缓存 +- 线程 A 将查到的旧值写入缓存 + +上述情况,出现的**概率是特别低的**,因为这个条件需要发生在读缓存时缓存失效,而且并发着有一个写操作。实际上数据库的写操作会比读操作慢得多,而且还要锁表,**而读操作必需在写操作前进入数据库操作,而又要晚于写操作更新缓存**,所有的这些条件都具备的概率是非常小的。 + +#### 解决方案 + +如果更新数据库成功,而删除缓存失败,有以下两个解决方案: + +- 缩短缓存失效时间:将缓存数据的过期时间设置短一点,这样的话缓存就会从数据库中加载数据。 +- **增加缓存更新重试机制**:如果当前缓存服务不可用导致缓存删除失败的话,可以隔一段时间进行重试,自行设置重试次数。如果多次重试还是失败,则可以把当前更新失败的 键存入队列中,等缓存服务可用之后,再将缓存中对应的键删除即可。 + +### 方案3:读请求和写请求串行化 + +读请求和写请求串行化,串入到一个内存队列。读串行化可保证一定不会出现双写一致性的情况,但会导致系统吞吐量大幅降低,需要比正常情况下多几倍的机器去支持线上一个请求,若不是严格要求双写一致性,则最好不要使用。 diff --git "a/docs/Redis/7_\351\231\215\344\275\216\345\206\205\345\255\230\345\215\240\347\224\250.md" "b/docs/Redis/7_\351\231\215\344\275\216\345\206\205\345\255\230\345\215\240\347\224\250.md" deleted file mode 100644 index e99bc3f1..00000000 --- "a/docs/Redis/7_\351\231\215\344\275\216\345\206\205\345\255\230\345\215\240\347\224\250.md" +++ /dev/null @@ -1,2 +0,0 @@ -# 降低内存占用 - diff --git "a/docs/Redis/8_\351\203\250\347\275\262\346\226\271\345\274\217.md" "b/docs/Redis/8_\351\203\250\347\275\262\346\226\271\345\274\217.md" new file mode 100644 index 00000000..744745c6 --- /dev/null +++ "b/docs/Redis/8_\351\203\250\347\275\262\346\226\271\345\274\217.md" @@ -0,0 +1,350 @@ +Redis 有以下 4 种部署方式: + +# 一、单机模式 + +最基本的部署方式,只需要一台机器负责读写,一般只用于开发人员开发自测。 + +# 二、主从复制模式 + +对于缓存来说,一般都是用来支撑**读高并发**的,主从(Master-Slave)模式架构,一主多从,其中 Master 负责写,并且将数据复制到其它的 Slave 节点,而 Slave 节点负责读。所有的**读请求全部访问 Slave节点**,这样也可以很轻松实现水平扩容,支撑读高并发。 + +单机的 Redis,能够承载的 QPS 大概就在上万到几万不等。对于主从架构,增加 Slave 节点的数量可以使得 Redis的 QPS 达到 10 万以上。 + +
+ +## 主从同步方法 + +Master 节点接收到写请求并处理后,需要告知 Slave 节点数据发生变化,保证主从节点数据一致即为主从同步。主从同步方法有:增量同步、快照同步和无盘复制。 + +### 增量同步 + +Redis 同步的是指令流,Mater 节点会将对自己的状态产生修改的指令记录在本地 buffer 中,然后异步将 buffer 中的指令同步到 Slave 节点,Slave 节点一边执行同步的指令流来达到和 Master 节点一样的状态,一边向主节点反馈同步偏移量。 + +Slave 节点同步数据的时候不会影响 Master 节点的正常工作,也不会影响自己对外提供读服务的功能,Slave 节点会用旧的数据来提供服务,当同步完成后,需要删除旧数据集,加载新数据,此时会暂停对外服务。 + +因为内存的 buffer 是有限的,所以 Master 节点不能将所有的指令都记录在内存 buffer 中。Redis 的复制内存 buffer 是一个定长的环形数组,如果数组内容满了,就会从头开始覆盖前面的内容。 + +### 快照同步 + +节点间网络通信不好,当 Slave 节点同步的速度不如 Master 节点接收新写请求的速度时,buffer 中会丢失一部分指令,Slave 节点中的数据将与 Master 节点中的数据不一致,此时将会触发快照同步。 + +快照同步先在 Master 节点执行 bgsave 生成 rdb 快照文件,然后将快照文件的内容全部传送到 Slave 节点。Slave 节点将快照文件接受完毕后,立即执行一次**全量加载**,加载前会将当前内存数据清空。加载完毕后通知 Master 节点继续进行增量同步。 + +在整个快照同步进行的过程中,Master 节点的 buffer 仍在往前移动,如果快照同步的时间过长或者 buffer 太小,同步期间的增量指令在 buffer 中被覆盖,这样就会导致快照同步完成后无法进行增量复制,然后会再次发起快照同步,如此极有可能会陷入快照同步的死循环。所以需要配置一个合适的 buffer 大小参数,避免快照复制的死循环。 + +
+ +### 无盘复制 + +在进行快照同步时,存在大量的文件 IO 操作,特别是对于非 SSD 磁盘存储时,快照会对系统的负载产生较大影响。特别是当系统正在进行 AOF 的 fsync 操作时如果发生快照复制,fsync 将会被推迟执行,这就会严重影响 Master 节点的服务效率。 + +从 Redis 2.8.18 版开始支持无盘复制。无盘复制指的是 Master 节点会**一边遍历内存,一遍将序列化的内容发送给 Slave 节点,而不是生成完整的 rdb 快照文件后才进行 IO 传输**。Slave 节点还是跟之前一样,先将接收到的内容存储到磁盘文件中,再进行一次性加载。 + +## 主从同步完整流程 + +### 1. 建立连接 + +- Slave 节点配置文件中的 slaveof 配置项中配置 Master 节点的 IP 和 port 后, Slave 节点就知道自己要和那个 Master 节点进行连接。 +- Slave 节点内部有个定时任务,会每秒检查自己要连接的 Master 节点是否上线,如果发现 Master 节点上线,就跟 Master 节点进行网络连接,此时还没有进行主从同步。 +- Slave 节点发送 ping 命令给 Master 节点进行连接,如果设置了口令认证(Master 节点设置了requirepass),那么 Slave 节点必须发送正确的口令(masterauth)进行认证。 + +### 2. 快照同步 + +- Master 和 Slave 节点连接成功后,判断是否进行快照同步:如果 Slave 节点发现已经连接过某个 `run id` 的 Master 节点,则此次连接为重新连接,不会进行快照同步。相同 IP 和 port 的 Master 节点每次重启服务都会生成一个新的 `run id`,所以每次 Master 节点重启服务都会进行一次快照同步,如果想重启主节点服务而不改变 `run id`,使用 `redis-cli debug reload` 命令。 + +- 进行快照同步开始后, Master 节点在本地生成一份 rdb 快照文件,并将这个 rdb 文件发送给 Slave 节点,如果复制时间超过 60 秒(配置项:`repl-timeout`),那么就会认为复制失败,所以数据量比较大,要适当调大这个参数的值。 + + 进行快照同步时,Master 节点会把接收到的新请求命令写到 buffer 中,当快照同步完成后,再把 buffer 中的指令增量同步到 Slave 节点。如果在快照同步期间,内存缓冲区大小超过 64MB / 256MB 的状态持续时间超过60s(配置项:`client-output-buffer-limit slave 256MB 64MB 60`),那么快照同步失败。 + +- Slave 节点接收到 rdb 快照文件后,清空自己的旧数据,然后重新加载 rdb 到自己的内存中,在这个过程中基于旧的数据对外提供服务。如果 Master 节点开启了AOF,那么在快照同步结束后会立即执行 BGREWRITEAOF 命令,重写 AOF 文件。 + +- Master 节点维护了一个 backlog 文件,默认大小是 1MB 。Master 节点向 Slave 节点发送全量数据(即 rdb 快照文件)时,也会同步到 backlog 中,这样当发送全量数据这个过程意外中断后,从 backlog 文件中可以得知数据有哪些是发送成功了,哪些还没有发送,然后当主从节点再次连接后,从失败的地方开始增量同步。**当快照同步连接中断后,主从节点再次连接并非是第一次连接,进行增量同步,而不是继续进行快照同步**。 + +### 3. 增量同步 + +- 快照同步完成后,Master 节点后续接收到写请求导致数据变化后,将和 Slave 节点进行增量同步,遇到 buffer 溢出则会再触发快照同步。 + +## 主从同步补充 + +- 主从节点都会维护一个 offset,随着 Master 节点的数据变化以及主从同步的进行,主从节点会不断累加自己维护的 offset,Slave 节点每秒都会上报自己的 offset 给 Master 节点,Master 节点也会保存每个 Slave 节点的 offset,这样主从节点就能知道互相之间的数据一致性情况。 + + Slave 节点发送`psync runid offset`命令给 Master 节点从而开始主从同步,Master 节点会根据自身的情况返回响应信息,可能是`FULLRESYNC runid offset`触发快照复制,也可能是 `CONTINUE` 触发增量复制。 + +- 主从节点因为网络原因导致断开,当网络接通后,不需要手工干预,可以自动重新连接。 + +- Master 节点如果发现有多个 Slave 节点连接,在快照同步过程中仅仅会生成一个 red 快照文件,用一份数据服务所有 Slave 节点进行快照同步。 + +- Slave 节点不会处理过期 key,当 Slave 节点处理了一个过期 key,会模拟一条 del 命令发送给 Slave 节点。 + +- 主从节点会保持心跳(heartbeat)来检测对方是否在线,Master 节点默认每隔 10 秒发送一次 heartbeat,Slave 节点默认每隔 1 秒发送一个heartbeat。 + +- 建议 Master 节点使用 AOF+RDB 的持久化方式,并且在 Master 节点定期备份 red 快照文件,而 Slave 节点不要开启 AOF 机制,原因有两个: + + - Slave 节点开启 AOF 会降低性能 + - 如果 Master 节点数据丢失,数据同步给 Slave 节点后,Slave 节点会收到空数据,如果开启了 AOF,会生成空的 AOF 文件,基于 AOF 恢复数据后,全部数据就都丢失了,而如果不开启 AOF 机制, Slave 节点启动后,基于自身的 rdb 快照文件恢复数据,不至于丢失全部数据。 + +扩展:[主从模式实战](https://www.jianshu.com/p/f0e042b95249) + + + +# 三、哨兵模式 + +在 Redis 主从复制模式下,一旦 Master 节点由于故障不能提供服务,需要**人工**将 Slave 节点晋升为 Master 节点,再通知所有的程序把 Master 地址统统改一遍,然后重新上线。 + +Redis 从 2.8 开始正式提供了 **哨兵 (sentinel) 架构**来解决这个问题。 + +Redis 中多个 sentinel 组成**分布式架构**,持续监控主从节点的健康状况,当 Master 节点挂掉时,自动选择一个最优的 Slave 节点切换为 Master 节点。客户端在连接集群时,会先连接 sentinel,通过 sentinel 来查询 Master 节点的地址,然后再去连接 Master 节点进行数据交互。当 Master 节点发生故障时,客户端会重新向 sentinel 获取地址,sentinel 会将最新的 Master 节点地址告诉客户端,这样应用程序将无需重启即可自动完成节点切换。 + +
+ +sentinel 集群在 Redis 主从架构高可用中起到的 4 个作用: + +- 集群监控 + sentinel 节点会定期检测 Redis 数据节点、其余 sentinel 节点是否故障。 +- 故障转移 + 实现 Slave 节点晋升为 Master 节点并维护后续正确的主从关系。 +- 配置中心 + sentinel 架构中,客户端在初始化的时候连接的是 sentinel 集群,从中获取 Master 节点信息。 +- 消息通知 + sentinel 节点会将故障转移的结果通知给客户端。 + +注意:使用 sentinel 集群而不是单个 sentinel 节点去监控 Redis 主从架构,这是因为: + +- 对于节点的故障判断由多个 sentinel 节点共同完成,这样可**有效防止误判**。 +- sentinel 集群可以保证自身的**高可用性**,即某个 sentinel 节点自身故障也不会影响 sentinel 集群的健壮性。 + +## 监控功能 + +sentinel 集群通过 3 个定时监控任务完成对各个节点发现和监控: + +- 每隔10 秒,每个 sentinel 节点会向 Master 节点和 Slave 节点发送 info 命令获取 Redis 主从架构的最新情况。 +- 每隔 2 秒,每个 sentinel 节点会向 Redis 数据节点的 `__sentinel__:hello` 这个channel(频道)发送一条消息,该定时任务可以完成以下 2 个工作: + - 发现新的 sentinel节点 + - sentinel 节点之间交换 Master 节点的状态,用于确认 Master 下线和故障处理的 Leader 选举。 +- 每隔 1 秒,每个 sentinel 节点会向 Master 节点、Slave 节点、其余 sentinel 节点发送一条 ping 命令做一次心跳检测,来确认这些节点是否可达。 + +## sdown & odown + +sdown 即主观下线 (subjective down) :一个 sentinel 节点每隔 1 秒对 Master 节点、Slave 节点、其他 sentinel 节点发送 ping 命令做心跳检测,当某个节点超过 + `down-after-milliseconds` 没有进行有效回复,该 sentinel 节点就会认为该节点下线,这就是主观下线。 + +odown 即客观下线 (objective down) :超过 quorum 数量(quorum可配置)的 sentinel 节点认为 Master 节点确实有问题,即大部分是 sentinel 节点都对 Master 节点的下线做了同意的判定,这就是客观下线。 + +## quorum & majority + +只有大于等于 quorum 数量的 sentinel 节点都认为 Master 主观下线,sentinel 集群才会认为 Master 客观下线。 + +quorum 可在 sentinel.conf 中手动配置,默认值为 2: + +```conf +# sentinel monitor [master-name] [master-ip] [master-port] [quorum] +sentinel monitor testmaster 127.0.0.1 6379 2 +# quorum 默认值是 2 +``` + +majority 代表 sentinel 集群中大部分 sentinel 节点数,majority 的计算方法为:majority = num(sentinels) / 2 + 1。例如 2 个节点的 sentinel 集群的 majority 为 2 + 3 个节点的 sentinel 集群的 majority 为 2;4 个节点的 sentinel 集群的 majority为 3。 + +只有大于等于 max(quorum, majority) 个节点给某个 sentinel 节点投票,该 sentinel 节点才能被选为 Leader。 + +注意:sentinel 集群的节点个数至少为 3 个。 + +原因如下:假设当节点数为 2 时,此时一个 sentinel 节点宕机,那么剩余一个节点是无法让自己成为 Leader 的,因为 2 个节点的 sentinel 集群的 majority 是 2,此时没有 2 个节点都给剩余的节点投票,也就无法选择出 Leader,从而无法进行故障转移。 + +此外最好把 quorum 的值设置为小于等于 majority,否则即使 sentinel 集群剩余的节点满足 majority 数,但是有可能不能满足 quorum 数,那还是无法选举 Leader,也就不能进行故障转移。 + +## Leader 选举 + +当 sentinel 集群确认某个 Master 是客观下线了,需要选举出一个 **Leader 节点来进行故障转移**,选举过程如下: + +- 每个在线的 sentinel 节点都有资格成为 Leader,当某个 sentinel 节点确认 Master 节点 odown 时,会向其他 sentinel 节点发送 `sentinel is-master-down-by-addr`命令,要求将自己设置为 Leader。 +- 每个 sentinel 节点都只能投出一票,如果该 sentinel 节点得到的**票数大于 max(quorum, num(sentinels) / 2 + 1)** ,那么该 sentinel 节点成为 Leader。 +- 如果一次选举没有选举出 Leader,那么会进行下一次选举。 + +一般情况下,哪个 sentinel 节点最先确认 Master 客观下线,该 sentinel 节点就会成为执行故障转移的 Leader。 + +## 新 Master 选择 + +要执行故障转移,首先要从 Slave 中选择一个作为新的 Master。 + +不选择不健康的 Slave,以下状态的 slave 是不健康的: + +- 主观下线的 Slave +- 与 Master 失联超过`down-after-milliseconds * 10`秒的 Slave +- 大于等于 5 秒没有回复过 sentinel 节点 ping 响应的 Slave + +对健康的 slave 进行排序: + +- 选择 priority(可配置,默认100)最低的 Slave 节点,如果有优先级相同的节点,进行下一步。注意如果 priority=0,那么禁止该节点成为 Master +- 选择复制偏移量最大的 Slave 节点(复制的最完整),如果有复制偏移量相等的节点,进行下一步 +- 选择 runid 最小的 Slave 节点 + +## 故障转移 + +选举出 Leader 和新 Master 后,由 Leader 进行故障转移: + +- Leader 对选举出的新 Master(此时还是 Slave)执行 `slaveof no one` 命令让其成为 Master。 +- Leader 向剩余 Slave 发送命令,使它们称为新 Master 的 Slave。 +- Leader 会将原来的 Master 更新为 Slave,并保持着对其关注,当其恢复后命令它去复制新 Master。 + +## configuration epoch + +configuration epoch 是当前 Redis 主从架构的配置版本号,无论是 sentinel 集群选举 Leader 还是进行故障转移的时候,要求各 sentinel 节点得到的 configuration epoch 都是相同的,`sentinel is-master-down-by-addr` 命令中就必须有当前配置版本号这个参数,在选举 Leader 过程中,如果本次选举失败,那么进行下一次选举,就会更新配置版本号,也就是说,每次选举都对应一个新的 configuration epoch,在故障转移的过程中,也要求各个 sentinel 节点使用相同的 configuration epoch。 + +在故障转移成功之后,Leader 会更新生成最新的 master 配置,configuration epoch 也会更新,然后同步给其他的 sentinel 节点,这样保证 sentinel 集群中保存的 Master 和 Slave 配置都是最新的,当 Client 请求的时候就会拿到最新的配置信息。 + +## 数据丢失问题 + +### 数据丢失原因 + +Redis sentinel 可能会导致数据丢失: + +- 异步复制导致的数据丢失 + + Master 到 Slave 的复制是异步的,所以可能有部分数据还没复制到 Slave,Master 就宕机了,此时这部分数据就丢失了。 + +- 脑裂导致的数据丢失 + + 某个 Master 所在机器突然网络故障,跟其他 Slave 机器不能连接,但是实际上 Master 还运行着。sentinel 会认为该 Master 宕机了,然后开启选举,将其他 Slave 切换成了 Master,集群里就会有两个 Master,也就是所谓的脑裂。 + + 虽然某个 Slave 被切换成了 Master,但是 Client 还没来得及切换到新的 Master,还继续写向旧 Master 。旧 Master 再次恢复的时候,会被作为一个 Slave 挂到新的 Master 上去,自己的数据会清空,重新从新的 Master 复制数据,而新的 Master 并没有后来 Client 写入的数据,这部分数据就丢失了。 + +### 解决方案 + +Redis 提供了两个配置参数可以尽量丢失少的数据: + +```conf +min-slaves-to-write 1 +min-slaves-max-lag 10 +``` + +Master 至少有 1 个 slave,数据复制和同步的延迟不能超过 10 秒。如果说一旦所有的 Slave,数据复制和同步的延迟都超过了 10 秒钟,那么 Master 就不会再接收任何请求。 + +- 减少异步复制数据的丢失 + + `min-slaves-max-lag` 配置可确保一旦 Slave 复制数据和 ack 延时太长,就认为可能 Master 宕机后损失的数据太多了,那么就拒绝写请求,这样可以把 Master 宕机时由于部分数据未同步到 Slave 导致的数据丢失降低的可控范围内。 + +- 减少脑裂的数据丢失 + + 如果一个 Master 出现了脑裂,跟其他 Slave 丢了连接,那么上面两个配置可以确保如果不能继续给指定数量的 Slave 发送数据且 Slave 超过 10 秒没有给自己 ack 消息,那么就直接拒绝客户端的写请求。因此在脑裂场景下,最多就丢失 10 秒的数据。 + +注意:redis-2.6 版本提供的是 redis sentinel v1 版本,但是功能性和健壮性都有一些问题。如果想使用 redis sentinel 的话,建议使用 2.8 以上版本。 + +扩展:[哨兵模式实战](https://www.jianshu.com/p/9d873e7a205a) + + + +# 四、Cluster 集群模式 + +## Redis Cluster 架构 + +单 Master 架构中 Master 节点的数据和 Slave 节点的数据是一模一样的,Master 节点的最多能容纳多少数据量,Slave 节点也就只能容纳这么多数据。当数据量超过 Master 的内存,Redis 会使用 LRU 算法清除部分数据。如果确实要容纳更多的数据量,Redis 主从架构是无法解决这个问题的。 + +Redis Cluster 是 Redis 的**分布式解决方案**,Redis 3.0 正式推出,解决单Master 架构的内存、并发、流量等瓶颈,以达到负载均衡的目的。 + +
+ +Redis cluster 适用于海量数据、高并发、高可用场景,在 Redis Cluster 架构中: + +- 每个 Master 负责整个集群的一部分数据,每个节点负责数据量可能不一样 +- 每个 Master 的角色是对等的,每个 Master 节点都可以有多个 Slave 节点,当一个 Master 节点挂掉后,它的其中一个 Slave 节点升级为 Master +- Redis Cluster 已经自动具备了主从复制能力,直接集成了 replication 和 sentinel 的功能。 + +## 分布式寻址算法 + +### Hash 算法 + +#### 思路 + +根据 key 计算 Hash 值,然后对节点数取模,将 key 分配到对应的 Master 中。 + +#### 问题 + +一旦某个 Master 宕机,就需要调整 hash 算法,所有的数据都需要重新计算取模,然后重新分配数据。大部分的请求都无法正确的拿到数据,从而不得不去访问数据库,在高并发场景下,这样是不能接受的,所以目前分布式缓存不再使用此种算法分配数据。 + +### 一致性 Hash 算法 + +#### 思路 + +将哈希空间 [0, 2^n-1] 看成一个哈希环,每个服务器节点都配置到哈希环上。每个数据对象通过哈希取模得到哈希值之后,存放到哈希环中**顺时针方向**第一个大于等于该哈希值的节点上。 + +例如有 Object A、Object B、Object C、Object D 四个数据对象,经过哈希计算后,在哈希环上的位置如下: + +
+ +根据一致性 Hash 算法:Object A 会被定位到 Node A 上;Object B 会被定位到 Node B 上;Object C 会被定位到 Node C 上;Object D 会被定位到 Node D 上。 + +一致性哈希在增加或者删除节点时只会影响到哈希环中相邻的节点: + +例如删除节点 C,只需将 C 上的数据分布到节点 D 即可。 + +
+ +例如新增节点 X,只需要将它前一个节点 C 上的数据重新进行分布即可,对于节点 A、B、D 都没有影响。 + +
+ +综上所述,一致性 Hash 算法对于节点的增减都只需**重定位哈希环中的一小部分数据**,具有**较好的容错性和可扩展性**。 + +#### 问题 + +- 加减节点会造成哈希环中部分数据无法命中,当一个 Master 挂掉之后,例如节点 A 挂掉,那么当请求从 A 获取数据时,是获取不到的,于是继续顺时针去节点 B、节点 C、节点 D 获取数据,当然也是获取不到的,需要手动处理这些无法命中的数据 +- 节点数量越多,增减节点带来的影响越小,因此不适用与集群中只有少量节点的情况 +- 容易造成数据热点问题 + +#### 优化-虚拟节点 + +一致性哈希存在数据分布不均匀的问题,节点存储的数据量有可能会存在很大的不同。数据不均匀主要是因为节点在哈希环上分布的不均匀,这种情况在节点数量很少的情况下尤其明显。 + +
+ +解决方式是通过增加虚拟节点,然后将虚拟节点映射到真实节点上。虚拟节点的数量比真实节点来得多,那么虚拟节点在哈希环上分布的均匀性就会比原来的真实节点好,从而使得数据分布也更加均匀。 + +例如针对上面的情况,可以为每台服务器计算 3 个虚拟节点: + +- Node A 的 3 个虚拟节点:"Node A#1"、"Node A#2"、"Node A#3" +- Node B 的 3 个虚拟节点:"Node B#1"、"Node B#2"、"Node B#3" + +
+ +同时**数据定位算法不变**,只是多了一步虚拟节点到实际节点的映射过程,例如"Node A#1"、"Node A#2"、"Node A#3" 这 3 个虚拟节点的数据均定位到 Node A 上,解决了服务节点少时数据倾斜的问题。在实际应用中,通常将虚拟节点数设置为 32 甚至更大,因此即使很少的服务节点也能做到相对均匀的数据分布。 + +### hash slot 算法 + +#### 思路 + +Redis Cluster **使用 16384 个槽 (slot)** 来管理一段整数集合 (哈希值),slot 是集群内数据管理和迁移的基本单位。 + +每个 Master 节点负责管理一部分 slot,例如有 5 个节点,那个每个节点管理大约 3276 (16384 / 5 )个槽。对每个 key 使用 CRC32 算法进行哈希得到哈希值,使用哈希值对 16384 进行取模,得到此数据应该分配到的 slot 编号。每个 Master 管理的 slot 的信息就缓存在本地,客户端连接集群时,会获取集群 slot 配置信息,从而通过 key 精确找到 slot 所在的节点,此外还可以强制分配某个 key 到指定的 slot 上。 + +#### 优点 + +- 解耦数据和节点之间的关系,简化了节点扩容和收缩难度: + + 增加一个 Master,就让其他的 Master 分一部分 slot 给新的 Master 管理; + + 移除一个 Master,就把这个 Master 管理的 slot 分配给其他的 Master。 + +- 某个 Master 挂掉,不影响整个集群,因为请求是到 slot,而不是到 Master,但在 slot 迁移完成之前,请求到挂掉的节点也是不行的。 + +- slot 迁移的过程是很快的 + +- 节点自身维护 slot 的映射关系,无需人为管理 + +- 支持槽、节点、key之间的映射关系查询 + +### Redis 故障恢复 + +判断故障的逻辑其实与哨兵模式有点类似,在集群中,每个节点都会**定期的向其他节点发送 ping 命令**,通过有没有收到回复来判断其他节点是否已经下线。 + +如果**长时间没有回复,那么发起 ping 命令的节点就会认为目标节点疑似下线**,也可以和哨兵一样称作主观下线,当集群中超过半数的节点都认为该节点下线则判定该节点下线。 + +对宕机的 Master 节点,从其所有的 Slave 节点中选择一个切换成 Master,需要检查每个 Slave 节点与 Master 节点断开连接的时间,如果超过了 `cluster-node-timeout * cluster-slave-validity-factor` ,那么该 Slave 就没有资格切换成 Master。 + +每个 Slave 节点,都根据自己对 Master 复制数据的 offset,来设置一个选举时间,offset 越大(复制数据越多)的 Slave 节点,选举时间越靠前,优先进行选举。 + +所有的 Master 节点给要进行选举的 Slave 进行投票,如果超过半数的 Master 节点都投票给了某个 Slave 节点,那么选举通过,那个该 Slave 节点可以切换成 Master。 + +扩展:[Redis Cluster搭建及测试](https://www.jianshu.com/p/48713d6f35b8) + +补充:[Redis-简书](https://www.jianshu.com/nb/33672761) diff --git "a/docs/Redis/9_\344\275\277\347\224\250\347\274\223\345\255\230\345\270\270\350\247\201\351\227\256\351\242\230.md" "b/docs/Redis/9_\344\275\277\347\224\250\347\274\223\345\255\230\345\270\270\350\247\201\351\227\256\351\242\230.md" deleted file mode 100644 index d5000a0d..00000000 --- "a/docs/Redis/9_\344\275\277\347\224\250\347\274\223\345\255\230\345\270\270\350\247\201\351\227\256\351\242\230.md" +++ /dev/null @@ -1,106 +0,0 @@ -# 使用缓存常见问题 - -## 缓存雪崩 - -### 问题 - -缓存雪崩是指在我们设置缓存时采用了**相同的过期时间**,导致**缓存在某一时刻同时失效**,请求全部转发到数据库,数据库瞬时压力过重雪崩。 - -### 解决 - -缓存雪崩的事前事中事后的解决方案如下: - -- 事前: - - 尽量保证整个 Redis 集群的高可用性; - - 选择合适的内存淘汰策略。比如将缓存失效时间分散开。 - -- 事中:本地 ehcache 缓存 + hystrix 限流&降级,避免 MySQL 雪崩。 - -- 事后:利用 Redis 持久化机制保存的数据快速恢复缓存。 - -## 缓存穿透 - -### 问题 - -缓存穿透是指查询**一个一定不存在的数据**,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,数据库可能就挂掉了,有人利用不存在的键频繁攻击我们的应用,这就是漏洞。 - -### 解决 - -- 简单粗暴的方法:如果一个查询返回的数据为空(不管数据是否存在,还是系统故障),我们仍然把这个**空结果进行缓存**,但它的过期时间会很短,最长不超过 5 分钟。 -- **布隆过滤器**:布隆过滤器能够以极小的空间开销解决海量数据判重问题。**一个一定不存在的数据会被该过滤器拦截掉**,从而避免了对底层存储系统的查询压力。 - -## 缓存击穿 - -### 问题 - -缓存击穿是指某个键非常热点,访问非常频繁,处于集中式高并发访问的情况。当缓存在某个时间点过期的时,大量的请求就击穿了缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。 - -### 解决 - -- 将热点数据设置为**永远不过期**。不会出现热点键过期问题。 -- 基于 Redis 或 Zookeeper 实现互斥锁。根据键获得的值为空时,先加锁,然后从数据库加载数据,加载完毕后再释放锁。其他线程发现获取锁失败,等待一段时间后重试。 - -## 双写一致性问题 - -一致性问题是分布式常见问题,还可以再分为**最终一致性**和**强一致性**。 - -数据库和缓存双写,就必然会存在不一致的问题。答这个问题,如果对数据有强一致性要求,则数据不能放入缓存。我们所做的一切,只能保证最终一致性。另外,我们所做的方案其实从根本上来说,只能说降低不一致发生的概率,无法完全避免。 - -### 策略一:先删除缓存,再更新数据库 - -> **存在的问题** - -数据更新,先删除了缓存,然后要去修改数据库,此时还没修改。一个请求过来,去读缓存,发现缓存空了,去查询数据库,查到了修改前的旧数据,放到了缓存中。随后更新数据的程序完成了数据库的修改,此时,数据库和缓存中的数据又不一致了。 - -并发场景下分析: - -- 线程A删除了缓存 -- 线程B查询,发现缓存已不存在 -- 线程B去数据库查询得到旧值 -- 线程B将旧值写入缓存 -- 线程A将新值写入数据库 - -所以也会导致数据库和缓存不一致的问题。 - -> **解决方案** - -读请求和写请求串行化,串到一个**内存队列**里去,这样就可以保证一定不会出现不一致的情况。串行化之后,就会导致系统的**吞吐量会大幅度的降低**,用比正常情况下多几倍的机器去支撑线上的一个请求。 - -> **问题:为什么是删除缓存,而不是更新缓存?** - -原因:如果每次更新了数据库,都要更新缓存【这里指的是频繁更新的场景,这会耗费一定的性能】,倒不如直接删除掉。等再次读取时,缓存里没有,那再去数据库找,在数据库找到再写到缓存里边(体现**懒加载**)。 - -举个例子,一个缓存涉及的表的字段,在 1 分钟内就修改了 100 次,那么缓存更新 100 次;但是这个缓存在 1 分钟内只被**读取**了 1 次,有**大量的冷数据**。实际上,如果你只是删除缓存的话,那么在 1 分钟内,这个缓存不过就重新计算一次而已,开销大幅度降低。 - -其实删除缓存,而不是更新缓存,就是一个 lazy 计算的思想,**不要每次都重新做复杂的计算,不管它会不会用到,而是让它在需要被使用时才重新计算**。 - -### 策略二:Cache Aside Pattern - -Cache Aside Pattern 最经典的缓存结合数据库读写的模式。 - -- 读的时候,先读缓存,缓存中没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。 -- 更新的时候,先更新数据库,然后再删除缓存。 - -> **存在的问题** - -先修改数据库,再删除缓存。 - -如果删除缓存失败了,那么会导致数据库中是新数据,缓存中是旧数据,数据就出现不一致。 - -如果在高并发的场景下,出现数据库与缓存数据不一致的**概率特别低**,也不是没有: - -- 缓存**刚好**失效 -- 线程A查询数据库,得一个旧值 -- 线程B将新值写入数据库 -- 线程B删除缓存 -- 线程A将查到的旧值写入缓存 - -要达成上述情况,还是说一句**概率特别低**: - -> 因为这个条件需要发生在读缓存时缓存失效,而且并发着有一个写操作。而实际上数据库的写操作会比读操作慢得多,而且还要锁表,**而读操作必需在写操作前进入数据库操作,而又要晚于写操作更新缓存**,所有的这些条件都具备的概率基本并不大。 - -### 策略三:读请求和写请求串行化 - -读请求和写请求串行化,串入到一个内存队列。读串行化可保证一定不会出现双写一致性的情况,但会导致系统吞吐量大幅降低,需要比正常情况下多几倍的机器去支持线上一个请求,若不是严格要求双写一致性,则最好不要使用。 \ No newline at end of file diff --git "a/docs/Redis/8_\347\256\200\345\215\225\347\202\271\350\265\236\347\263\273\347\273\237.md" "b/docs/Redis/9_\345\256\236\346\210\230.md" similarity index 69% rename from "docs/Redis/8_\347\256\200\345\215\225\347\202\271\350\265\236\347\263\273\347\273\237.md" rename to "docs/Redis/9_\345\256\236\346\210\230.md" index 85ea9405..3759be90 100644 --- "a/docs/Redis/8_\347\256\200\345\215\225\347\202\271\350\265\236\347\263\273\347\273\237.md" +++ "b/docs/Redis/9_\345\256\236\346\210\230.md" @@ -1,4 +1,4 @@ -# 简单点赞系统 +# Redis 实战——实现简单点赞系统 该点赞系统功能如下: @@ -6,28 +6,27 @@ - 可以对文章进行点赞 - 在首页可以按文章的发布时间或者文章的点赞数进行排序显示 -## 文章信息 +## 文章信息存储 文章包括标题、作者、赞数等信息,在关系型数据库中很容易构建一张表来存储这些信息,在 Redis 中可以使用 HASH 来存储每种信息以及其对应的值的映射。 -
+
- - -## 点赞功能 +## 点赞功能实现 当有用户为一篇文章点赞时,除了要对该文章的 votes 字段进行加 1 操作,还必须记录该用户已经对该文章进行了点赞,防止用户点赞次数超过 1。可以建立文章的已投票用户集合来进行记录。 为了节约内存,规定一篇文章发布满一周之后,就不能再对它进行投票,而文章的已投票集合也会被删除,可以为文章的已投票集合设置一个一周的过期时间就能实现这个规定。 -
+
## 对文章进行排序 为了按发布时间和点赞数进行排序,可以建立一个文章发布时间的有序集合和一个文章点赞数的有序集合。(下图中的 score 就是这里所说的点赞数;下面所示的有序集合分值并不直接是时间和点赞数,而是根据时间和点赞数间接计算出来的) -
+
115423 号用户给 100408 号文章投票的时候,数据结构发生的变化: -
+
+ diff --git a/docs/SSM/2_SpringIOC.md b/docs/SSM/2_SpringIOC.md deleted file mode 100644 index be3c8721..00000000 --- a/docs/SSM/2_SpringIOC.md +++ /dev/null @@ -1,186 +0,0 @@ -# SpringIOC - -## IOC - -### IOC 概念 - -IOC,即控制反转(Iversion of Control)是一种**设计思想**。 - -- **控制** - - 控制对象的创建及销毁(生命周期)。 - -- **反转** - - 将对象的控制权交给 IoC 容器。 - -所有的类都会在 Spring 容器中注册,告诉 Spring 你是个什么东西,你需要什么东西,然后 Spring 会在系统运行到适当的时候,把你需要的东西主动给你 - -**所有类的创建、销毁都由 Spring 来控制,也就是说控制对象生命周期的不是引用它的对象,而是 Spring**。对于某个具体对象而言,以前是它控制其他对象,现在所有对象都被 Spring 控制。 - -### 依赖注入 - -说到控制反转,则不得不说依赖注入(Dependency Injection,DI) 。 - -所谓依赖注入就是**将底层类作为参数传递给上层类,实现上层对下层的控制**,**依赖注入实现控制反转**。 - -举例说明依赖注入:以生产行李箱为例。 - -> 传统设计思路: - -先设计轮子,然后根据轮子 size 来设计底盘,再根据底盘来设计箱体,最后设计好行李箱。 - -可这样表示: - -
- -相应的代码如下: - -
- -size 是固定值,可以进行相应的改进: - -
- -> 使用 DI 方式进行改进: - -先设计行李箱的大概样子,再根据行李箱的样子设计箱体,根据箱体去设计底盘,然后去设计轮子。 - -
- -改进后相应的代码如下: - -
- -不难理解,依赖注入就是**将底层类作为参数传递给上层类,实现上层对下层的控制**。 - -### IoC 和 DI 的关系 - -使用 DI 去实现 IoC。 - -DI 的 4 中方式: - -- setter 注入 -- 构造器注入 -- 注解注入 -- 接口注入 - -依赖倒置原则、IoC、DI 和 IoC 容器的关系: - -
- -使用 IoC 容器的好处: - -- 避免在各处使用 new 来创建类,并且可统一维护 -- 创建实例时,不需要了解其中的细节 - - - -## SpringIOC - -### IoC 容器的初始化过程 - -
- -- Resource 定位(即 BeanDefinition 的资源定位,Resource 为各种形式的 BeanDefinition 的使用都提供了统一的接口) -- BeanDefinition 的载入 -- 向 IoC 容器中注册 BeanDefinition (实际上 IoC 容器内部维护一个 HashMap,注册过程就是将 BeanDefinition 添加至 HashMap 中) - -## IoC 加载过程 - -
- -IoC 容器其实就是一个大工厂,它用来管理我们所有的对象以及依赖关系: - -- 根据 Bean 配置信息在容器内部创建 Bean 定义注册表 -- 根据注册表加载,实例化 Bean,**建立 Bean 与 Bean 之间的依赖关系** -- 将 Bean 实例放入 Spring IoC 容器中,等待应用程序调用 - - - -## BeanFactory 和 ApplicationContext - -### BeanFactory - -- IoC 容器要实现的最基础的接口 -- 采用**延迟初始化策略**(容器初始化完成后并不会创建 Bean 对象,只有当收到初始化请求时才进行初始化) -- 由于是延迟初始化策略,因此启动速度较快,占用资源较少 - -### ApplicationConext - -- 在 BeanFactory 基础上,增加了更为高级的特性:事件发布、国际化等。 -- 在容器启动时,完成所有 Bean 的创建 -- 启动时间较长,占用资源较多 - -> **注意:BeanFactory 和 FactoryBean 的区别** - -- BeanFactory 是 IoC 最基本的容器,负责生产和管理 Bean,为其他具体的 IoC 容器提供了最基本的规范。 - -- FactoryBean 是一个 Bean,是一个接口,当 IoC 容器中的 Bean 实现了 FactoryBean 后,通过 getBean(String beanName) 获取到的 Bean 对象并不是 FactoryBean 的实现类对象,而是这个实现类中的 getObject() 方法返回的对象。 - - 要想获取 FactoryBean 的实现类对象,就是在 beanName 前面加上 "&"。 - - - -## getBean 代码逻辑 - -- 获取参数 name 转化为 beanName -- 从缓存中加载实例 -- 实例化 Bean -- 检测 parentBeanFactory(若无缓存数据,直接到 parentBeanFactory 中去加载) -- 初始化依赖的 Bean -- 返回 Bean - - - -## Spring 中循环依赖 - -### 类的实例化和类的初始化 - -- 类的实例化是指创建一个类的实例(对象)的过程 -- 类的初始化是指为类中各个类成员(被static修饰的成员变量)赋初始值的过程,是**类生命周期中的一个阶段**。 - -在 Spring 容器中我们的类又是什么时候进行初始化和实例化的呢? - -- Spring 中**所有 Bean 默认都是单例模式**,所以 Bean 的初始化和实例化都是在加载进入 Bean 容器时做的 -- 如果想使用时再初始化,那么可以把类定义为原型模式 - -### 循环依赖 - -若 A 中有 B 的属性,B 中有 A 的属性,则当进行依赖注入时,就会产生 A 还未创建完,因为对 B 的创建再次返回创建 A。 - -### 解决循环依赖 - -单例对象,在 Spring IoC 容器中,有且仅有一个对象,将对象放入缓存中。Spring 中使用“三级缓存”: - -- SingletonObjects:单例对象的缓存(存储实例化完成的 Bean) -- earlySingletonObjects:提前曝光的单例对象的缓存(存储正在实例化的 Bean) -- SingletonFactories:单例 ObjectFactory 的缓存 - -举例说明解决循环依赖(A 中有B,B 中有 A)的具体过程: - -Spring 中单例对象的初始化主要分为 3 步: - -第一步:createBeanInstance - -第二步:populateBean 填充属性 - -第三步:intializeBean 初始化 - -在进行 createBeanInstance 后,该单例对象此时已被创建,Spring 将该对象**提前曝光到 SingeltonFacoties 中**。 - -- A 完成 createBeanInstance ,并且提前曝光到 SingeltonFacoties 中 -- A 进行第二步,发现需要依赖 B,尝试获取 B -- B 开始创建,B 完成 createBeanInstance,发现需要依赖 A,尝试获取 A:先尝试从 SingletonObjects 中获取,发现不存在,因为 A 未初始化完全;再尝试从 earlySingletonObjects 中获取;再去 SingeltonFacoties 中获取,此时 B 获取 A,并将 A 放入 earlySingletonObjects 中,再删除 A 在 SingeltonFacoties 中对应的 ObjectFactory。 -- B 获取 A,顺利完成第二、三步,并将初始化完成的 B 放入 SingletonObjects 中。 -- 此时返回创建 A,A 可获取 B,顺利完成第二、三步,A 初始化完成, 将 A 放入 SingletonObjects 中。 - -注意:Spring 中循环依赖有 2 种: - -- 构造器循环依赖:因为提前曝光到 SingletonFactories 中的前提是需要执行构造方法,所以使用 “三级缓存” 无法解决 -- setter 循环依赖 - -### 总结 - -- 尽量不要使用基于构造器的 DI,使用基于 setter 的 DI -- 使用 @Autowired 注解,让 Spring 决定合适的时机 \ No newline at end of file diff --git a/docs/SSM/3_SpringAOP.md b/docs/SSM/3_SpringAOP.md deleted file mode 100644 index 13e950f4..00000000 --- a/docs/SSM/3_SpringAOP.md +++ /dev/null @@ -1,46 +0,0 @@ -# SpringAOP - -## AOP - -AOP,即面向切面编程(Aspect Oriented Programing)。实际上是将一些通用的功能横向抽取出来:一方面,可减少系统的代码;另一方面,降低模块间的耦合度,比较好的维护和扩展。 - - - -## AOP 相关术语 - -| 术语 | 描述 | -| :----------------: | :----------------------------------------------------------: | -| Joinpoint(连接点) | 所谓连接点是指那些被拦截到的点。在Spring中,这些点指的是**方法**,因为Spring只支持**方法类型的连接点**。 | -| Pointcut(切入点) | 所谓切入点是指我们要**对哪些Joinpoint进行拦截**的定义。 | -| Advice(通知/增强) | 所谓通知是指拦截到Joinpoint之后所要做的事情就是**通知**。通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能) | -| Introduction(引介) | 引介是一种**特殊的通知**在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field | -| Target(目标对象) | 代理的目标对象 | -| Weaving(织入) | 是指把增强应用到目标对象来创建新的代理对象的过程。 有三种织入方式: Spring采用**动态代理织入**,而AspectJ采用**编译期织入**和**类装载期织入** | -| Proxy(代理) | 一个类被AOP织入增强后,就产生一个结果代理类 | -| Aspect(切面) | 是切入点和通知(/引介)的结合 | - -- 示例:在 IUserDao() 中 - - ```java - public interface IUserDao { - void add(); - void delete(); - void update(); - void search(); - } - // IUserDao 被增强的对象,就是 Target(目标对象) - // add()、delete()、update() 和 search() 都是 JoinPoint(连接点) - // 这里要对 add() 和 update() JoinPoint 进行拦截,则 add() 和 update() 就是 Pointcut(切入点) - // Advice 指的是要增强的代码,也就是代码的增强 - // Weaving:指的是把增强(Advice)应用到目标对象(Target)创建新的代理对象得人过程 - // Aspect:是切入点和通知的结合,在 add 或 delete 方法上应用增强 - ``` - - - -## AOP 底层原理 - -AOP 的底层原理是代理机制: - -- 类实现了接口,JDK 动态代理 -- 类未实现任何接口,Cglib 动态代理 \ No newline at end of file diff --git "a/docs/SSM/4_Spring\344\272\213\345\212\241\347\256\241\347\220\206.md" "b/docs/SSM/4_Spring\344\272\213\345\212\241\347\256\241\347\220\206.md" deleted file mode 100644 index c6284d5e..00000000 --- "a/docs/SSM/4_Spring\344\272\213\345\212\241\347\256\241\347\220\206.md" +++ /dev/null @@ -1,139 +0,0 @@ -# Spring 事务管理 - -## 编程式事务和声明式事务 - -### 编程式事务 - -编程式事务:指的是通过编码方式实现事务,类似 JDBC 编程实现事务管理,比如 jdbcTemplate、TransactionalTemplate。 - -### 声明式事务 - -声明式事务实现方式: - -- XML 实现 -- @Transactional 注解实现 - -### 比较 - -编程式事务: - -- 优点:显示调用,不易出错 -- 缺点:侵入式代码,编码量大 - -声明式事务: - -- 优点:简介,对代码入侵小 -- 缺点:隐藏实现细节,出错不易定位 - -## Spring 事务管理 - -Spring 事务的本质是**基于数据库对事务的支持**,没有数据库对事务的支持,Spring 本身无法提供事务管理功能。 - -Spring 事务通过 AOP 动态代理来是实现。 - -通常先在配置文件中**开启事务**,然后通过 xml 文件或者注解**配置执行方法**,在调用方法时,Spring 会自动生成代理,在调用前设置事务操作,调用方法后进行事务回滚或者提交操作。 - -## Spring 中事务隔离级别 - -事务隔离级别是指多个事务之间的隔离程度。 - -TransactionDefinition 接口中定义了五个表示隔离级别的常量: - -| 隔离级别 | 说明 | -| :------------------------------------: | :----------------------------------------------------------: | -| ISOLATION_DEFAULT(默认) | 这是默认值,表示使用底层数据库的默认隔离级别。 | -| ISOLATION_READ_UNCOMMITTED(读未提交) | 该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据,该级别不能防止脏读和不可重复读,因此很少使用该隔离级别 | -| ISOLATION_READ_COMMITTED(读可提交) | 该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值 | -| ISOLATION_REPEATABLE_READ(可重复读) | 该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读 | -| ISOLATION_SERIALIZABLE(可串行化) | 所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是,这将严重影响程序的性能,通常情况下也不会用到该级别 | - - - -## Spring 七种事务传播类型 - -- **PROPAGATION_REQUIRED(required)** - - 如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务。 - -- PROPAGATION_SUPPORTS(support) - - 表示支持当前事务,如果当前没有事务,就以无事务方式执行。 - -- PROPAGATION_MANDATORY(mandatory) - - 表示使用当前的事务,如果当前没有事务,就抛出异常。 - -- **PROPAGATION_REQUIRES_NEW(required_new)** - - 表示新建事务,如果当前存在事务,把当前事务挂起。 - -- **PROPAGATION_NOT_SUPPORTED(not_support)** - - 表示以无事务方式执行操作,如果当前存在事务,就把当前事务挂起。 - -- PROPAGATION_NEVER(never) - - 表示以无事务方式执行,如果当前存在事务,则抛出异常。 - -- PROPAGATION_NESTED(nested) - - 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 - -举例说明事务传播类型: - -- StudentServiceImplA - -```java -StudentServiceImplA implements StudentService{ - @Autowired - studentDao; - - @Autowired - studentService; - - @Transactional(required) //事务传播特性为 requried - insertA(){ - //...对数据库操作,插入 A 数据 - - studentService.insertB();// 插入 B 数据 - - //..i/o 异常 - } -} -``` - -- StudentServiceImplB - -```java -StudentServiceImplB implements StudentService{ - @Autowired - studentDao; - - @Transactional - insertB(){ - - //...对数据库操作,插入 B 数据 - - } -} -``` - -StudentServiceImplB 中 insertB() 中传播类型如果是 required,数据库中既没有 A 数据,也没有 B 数据; - -(insertA() 和 insertB() 属于同一个事务,发生异常,事务回滚) - -StudentServiceImplB 中 insertB() 中传播类型如果是 required_new,数据库中没有 A 数据,但是有 B 数据; - -(insertB() 创建了一个新事务,insertA() 中发生异常,事务回滚) - -StudentServiceImplB 中 insertB() 中传播类型如果是 not_supported,数据库中没有 A 数据,但是有 B 数据 - -(insertB() 以非事务方式只想,执行 insertA() 的事务回滚,insertB()中不会发生回滚) - - - -注意:insertB() 为何要放入 StudentServiceImplB 中? - -Spring 的事务机制是使用 AOP 代理实现的。 - -如果在 StudentServiceImplA 中使用 insertB() ,insertA() 中在调用 insertB() 是通过当前对象来调用 doB() 的,而不是通过代理来调用 doB() 的,此时 doB() 上加事务注解就失效了。 \ No newline at end of file diff --git "a/docs/SSM/5_Spring\344\270\255Bean\347\232\204\344\275\234\347\224\250\345\237\237.md" "b/docs/SSM/5_Spring\344\270\255Bean\347\232\204\344\275\234\347\224\250\345\237\237.md" deleted file mode 100644 index ca384c86..00000000 --- "a/docs/SSM/5_Spring\344\270\255Bean\347\232\204\344\275\234\347\224\250\345\237\237.md" +++ /dev/null @@ -1,124 +0,0 @@ -# Spring 中 Bean 的作用域 - -Spring Framework 支持五种作用域: - -| 类别 | 说明 | -| :-----------: | :----------------------------------------------------------: | -| singleton | 在 SpringIoC 容器中仅存在一个 Bean 实例,Bean 以单例方式存在 | -| prototype | 每次从容器中调用 Bean 时,都返回一个新的实例 | -| request | 每次HTTP请求都会创建一个新的 Bean,该作用域仅适用于 WebApplicationContext 环境 | -| session | 同一个 Http Session 共享一个 Bean,不同 Session 使用不同 Bean,仅适用于WebApplicationContext 环境 | -| globalSession | 一般同于 Portlet 应用环境,该作用域仅适用于 WebApplicationContext 环境 | - -注意:五种作用域中,request、session 和 global session 三种作用域仅在基于 web 的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于 web 的 Spring ApplicationContext 环境。 - -## 1. singleton - -当一个 Bean 的作用域为 singleton,那么Spring IoC容器中只会存在一个**共享的 Bean 实例**, 并且所有对 Bean 的请求,只要 id 与该 Bean 定义相匹配,则只会**返回 Bean 的同一实例**。 - -singleton 是单例类型(对应于单例模式),就是**在创建容器时就同时自动创建一个Bean对象**, 不管你是否使用,但我们可以指定Bean节点的 lazy-init="true" 来延迟初始化Bean, 这时候,只有在第一次获取Bean时才会初始化Bean,即第一次请求该bean时才初始化。 每次获取到的对象都是同一个对象。 注意,singleton 作用域是Spring中的**缺省作用域**。 - -- 配置文件XML中将 Bean 定义成 singleton : - - ```html - - ``` - -- @Scope 注解的方式: - -```java -@Service -@Scope("singleton") -public class ServiceImpl{ - -} -``` - - - -## 2. prototype - -当一个Bean的作用域为 prototype,表示一个 Bean 定义对应多个对象实例。 prototype 作用域的 Bean 会导致在每次对该 Bean 请求 (将其注入到另一个 Bean 中,或者以程序的方式调用容器的 getBean() 方法)时都会创建一个新的 bean 实例。 prototype 是原型类型,它在我们创建容器的时候并没有实例化, 而是当我们获取Bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。 - -根据经验,**对有状态的 Bean 应该使用 prototype 作用域,而对无状态的 Bean 则应该使用 singleton 作用域。** - -- 配置文件XML中将 Bean 定义成 prototype : - -```html - -``` - -或者 - -```html - -``` - -- @Scope 注解的方式: - -```java -@Service -@Scope("prototype") -public class ServiceImpl{ - -} -``` - - - -> **Spring 中线程安全问题** - -有状态 Bean 和 无状态 Bean: - -- **有状态 Bean** - - 对象中有实例变量(成员变量),可保存数据; - - 非线程安全。 - -- **无状态 Bean** - - 对象中午实例变量,不能保存数据,可在多线程环境下共享; - - 线程安全。 - -Spring 中有状态 Bean 如何保证线程安全? - -2 种方式保证线程安全: - -- 采用 ThreadLocal 进行处理 -- 采用原型模式,每次有 Bean 请求时,都会创建一个新的 Bean 实例 - - - -## 3. request - -request只适用于**Web程序**,每一次 HTTP 请求都会产生一个新的 Bean , 同时该 Bean 仅在当前HTTP request内有效,当请求结束后,该对象的生命周期即告结束。 - -在 XML 中将 bean 定义成 request ,可以这样配置: - -- 配置文件XML中将 Bean 定义成 prototype : - -```html - -``` - - - -## 4. session - -session只适用于**Web程序**, session 作用域表示该针对每一次 HTTP 请求都会产生一个新的 Bean, 同时**该 Bean 仅在当前 HTTP session 内有效**。 与request作用域一样,可以根据需要放心的更改所创建实例的内部状态, 而别的 HTTP session 中根据 userPreferences 创建的实例, 将不会看到这些特定于某个 HTTP session 的状态变化。 当HTTP session最终被废弃的时候,在该HTTP session作用域内的bean也会被废弃掉。 - -```html - -``` - - - -## 5. globalSession - -globalSession 作用域**类似于标准的 HTTP session** 作用域, 不过仅仅在基于 portlet 的 Web 应用中才有意义。 Portlet 规范定义了全局 Session 的概念, 它被所有构成某个 portlet web 应用的各种不同的 portlet所共享。 在globalSession 作用域中定义的 bean 被限定于全局portlet Session的生命周期范围内。 - -```html - -``` \ No newline at end of file diff --git "a/docs/SSM/6_Spring\344\270\255Bean\347\232\204\347\224\237\345\221\275\345\221\250\346\234\237.md" "b/docs/SSM/6_Spring\344\270\255Bean\347\232\204\347\224\237\345\221\275\345\221\250\346\234\237.md" deleted file mode 100644 index 31784113..00000000 --- "a/docs/SSM/6_Spring\344\270\255Bean\347\232\204\347\224\237\345\221\275\345\221\250\346\234\237.md" +++ /dev/null @@ -1,44 +0,0 @@ -# Spring中Bean的生命周期 - -## Bean 的创建过程 - -
- - - -## Bean 的销毁过程 - -- 若实现了 DisposableBean 接口,则会调用 destroy 方法 -- 若配置了 destroy-method 属性,则会调用其配置的销毁方法 - - - -## 详细过程 - -
- - - -1、Spring 对 Bean 进行实例化。 - -2、Spring 将值和 Bean 的引用注入到 Bean 对应的属性中。 - -3、如果 Bean 实现了 BeanNameAware 接口,Spring 将 bean 的 id 传递给 setBeanName() 接口方法。 - -4、如果 Bean 实现了 BeanFactoryAware 接口,Spring 将调用 setBeanFactory() 接口方法,将 BeanFactory 容器实例传入。 - -5、如果 Bean 实现了 ApplicationContextAware 接口,Spring 将调用 setApplicationContext() 接口方法,将应用上下文的引用传入。 - -6、如果 Bean 实现了BeanPostProcessor 接口,Spring 将调用 postProcessBeforeInitialization() 接口方法。 - -7、如果 Bean 实现了InitializingBean 接口,Spring 将调用他们的 afterPropertiesSet() 接口方法 - -8、如果Bean 实现了 init-method 声明了初始化方法,该方法也会被调用。 - -9、如果 Bean 实现了BeanPostProcessor 接口,Spring 将调用 postProcessAfterInitialization() 接口方法。 - -10、此时 Bean 已经准备就绪,可以被应用程序使用了,他们将一一直驻留在应用上下文中,一直到该应用上下文被销毁。 - -11、如果 Bean 实现了 DisposableBean 接口,Spring 将调用它的 destroy() 接口方法。 - -12、如果 Bean 使用 destroy-method 声明了销毁方法,方法也会被调用。 \ No newline at end of file diff --git "a/docs/SSM/7_Spring\344\270\255\345\270\270\350\247\201\346\263\250\350\247\243.md" "b/docs/SSM/7_Spring\344\270\255\345\270\270\350\247\201\346\263\250\350\247\243.md" deleted file mode 100644 index 7c0450db..00000000 --- "a/docs/SSM/7_Spring\344\270\255\345\270\270\350\247\201\346\263\250\350\247\243.md" +++ /dev/null @@ -1,99 +0,0 @@ -# Spring 中常见注解 - -## @Contoller - -SpringMVC 中,控制器 Controller 负责处理 DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个 Model,然后再把该 Model 返回给对应的 View 进行展示。 - -SpringMVC 提供了一个非常简便的定义 Controller 的方法,你无需继承特定的类或者接口,只需使用 @Controller 标记一个类是 Contoller。 - -## @RequestMapping - -使用 @RequestMapping 来映射 URL 到控制器,或者到 Controller 控制器的处理方法上。method 的值一旦指定,则处理方法只对指定的 HTTP method 类型请求处理。 - -可以为多个方法映射相同的 URI,不同的 HTTP method 类型,Spring MVC 根据请求的 method 类型是可以区分开这些方法的。 - -## @RequestParam 和 @PathVariable - -在 SpringMVC 中,两者的作用都是将 request 里的参数的值绑定到 Controller 里的方法参数中,区别在于 URL 的写法不同。 - -- 使用 @RequestParam 时,URL 是这样的: - -```html -http://host:port/path?参数名=参数值 -``` - -- 使用 @PathVariable 时,URL 是这样的: - -```html -http://host:port/path/参数值 -``` - -## @Autowired - -@Autowired 可以对成员变量、成员方法和构造函数进行标注,来完成自动装配工作。 - -## @Service、 @Contrller、 @Repository 和 @Component - - @Service、 @Contrller、 @Repository 其实这 3 个注解和 @Component 是等效的,用在实现类上: - -- @Service 用于标注业务层组件 -- @Controller 用于标注控制层组件 -- @Repository 用于编著数据访问组件 -- @Component 泛指组件,当组件不好归类时,可以使用这个注解进行标注 - -## @Value - -在 Spring 3.0 中,可以通过使用 @Value,对一些如 xxx.properties 文件中的文件,进行键值对的注入。 - -## @ResponseBody - -该注解用于将 Controller 中方法返回的对象,通过适当的 HttpMessageConverter 转换为指定的格式后,写入到 Response 对象的 bodys 数据区。 - - - -> **@Autowired 和 @Resource 的区别** - -@Autowired - -- 是 Spring 提供的注解 - -- 采用的策略是**按照类型**注入的 - - ```java - public class UserService{ - @Autowired - userDao; //在 Spring 容器中找到类型为 userDao 的类,将其注入 - } - ``` - - 存在问题:同一类型有多个 Bean,可以使用 @Qualifier 具体去装配哪个对象。 - - ```java - public class UserService{ - @Autowired - @Qualifier(name="userDao") - userDao; //在 Spring 容器中找到类型为 userDao 的类,将其注入 - } - ``` - - @Resource - -- J2EE 提供的注解 - -- 默认是**按照名称**注入的 - - ```java - Service{ - @Resource - userDao; //自动按名称进行装配 - - @Resource(name="studentDao") - studentDao; //按名称进行装配,找不到会抛出异常 - - @Resource(type="TeacherDao") - teacherDao; //按类型进行装配,找不到或者找到多个都会抛出异常 - - @Resource(name="manDao",type="ManDao") - manDao;//找唯一匹配的 Bean 进行装配,如果找不到则会抛出异常 - } - ``` diff --git "a/docs/SSM/8_Spring\344\270\255\346\266\211\345\217\212\345\210\260\347\232\204\350\256\276\350\256\241\346\250\241\345\274\217.md" "b/docs/SSM/8_Spring\344\270\255\346\266\211\345\217\212\345\210\260\347\232\204\350\256\276\350\256\241\346\250\241\345\274\217.md" deleted file mode 100644 index 0597d148..00000000 --- "a/docs/SSM/8_Spring\344\270\255\346\266\211\345\217\212\345\210\260\347\232\204\350\256\276\350\256\241\346\250\241\345\274\217.md" +++ /dev/null @@ -1,37 +0,0 @@ -# Spring中涉及到的设计模式 - -## 工厂模式 - -- [工厂模式](https://github.com/DuHouAn/Java/blob/master/docs/OO/2_%E5%88%9B%E5%BB%BA%E5%9E%8B.md#2-%E7%AE%80%E5%8D%95%E5%B7%A5%E5%8E%82simple-factory) - -BeanFactory 用来创建各种不同的 Bean。 - -## 代理模式 - -- [代理模式](https://github.com/DuHouAn/Java/blob/master/docs/OO/4_%E7%BB%93%E6%9E%84%E5%9E%8B.md#7-%E4%BB%A3%E7%90%86proxy) - -AOP、事务都大量运用了代理模式。 - -## 单例模式 - -- [单例模式](https://github.com/DuHouAn/Java/blob/master/docs/OO/2_%E5%88%9B%E5%BB%BA%E5%9E%8B.md#1-%E5%8D%95%E4%BE%8Bsingleton) - -在 Spring 配置文件中定义的 Bean 默认为单利模式。 - -## 原型模式 - -- [原型模式](https://github.com/DuHouAn/Java/blob/master/docs/OO/2_%E5%88%9B%E5%BB%BA%E5%9E%8B.md#6-%E5%8E%9F%E5%9E%8B%E6%A8%A1%E5%BC%8Fprototype) - -特点在于通过"复制"一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的"原型",这个原型是可定制的。 - -## 责任链模式 - -- [责任链模式](https://github.com/DuHouAn/Java/blob/master/docs/OO/3_%E8%A1%8C%E4%B8%BA%E5%9E%8B.md#1-%E8%B4%A3%E4%BB%BB%E9%93%BEchain-of-responsibility) - -在 SpringMVC 中,会经常使用一些拦截器(HandlerInterceptor),当存在多个拦截器的时候,所有的拦截器就构成了一条拦截器链。 - -## 观察者模式 - -- [观察者模式](https://github.com/DuHouAn/Java/blob/master/docs/OO/3_%E8%A1%8C%E4%B8%BA%E5%9E%8B.md#7-%E8%A7%82%E5%AF%9F%E8%80%85observer) - -Spring 中提供了一种监听机制,即 ApplicationListenber,可以实现 Spring 容器内的事件监听。 \ No newline at end of file diff --git a/docs/SSM/9_MyBaits.md b/docs/SSM/9_MyBaits.md deleted file mode 100644 index c91882c9..00000000 --- a/docs/SSM/9_MyBaits.md +++ /dev/null @@ -1,9 +0,0 @@ -# MyBatis - -> MyBatis 中使用 # 和使用 $ 的区别? - -- #{xxx} 语句拼接使用的是 PreparedStatement,会有类型转换,比较安全。 - - 简单讲,#{xxx} 是经过预编译的是安全的 - -- ${xxx}未经过预编译,仅仅是变量的值,是不安全的,可能会**存在 SQL 注入**的风险。 diff --git "a/docs/Safety/11_DoS\346\224\273\345\207\273.md" "b/docs/Safety/11_DoS\346\224\273\345\207\273.md" index 9f4597fb..6428706c 100644 --- "a/docs/Safety/11_DoS\346\224\273\345\207\273.md" +++ "b/docs/Safety/11_DoS\346\224\273\345\207\273.md" @@ -28,6 +28,3 @@ - 防雪崩机制 - 有损服务 - CDN - - - diff --git "a/docs/Safety/2_\350\267\250\347\253\231\350\204\232\346\234\254\346\224\273\345\207\273.md" "b/docs/Safety/2_\350\267\250\347\253\231\350\204\232\346\234\254\346\224\273\345\207\273.md" index 243430cf..2160320f 100644 --- "a/docs/Safety/2_\350\267\250\347\253\231\350\204\232\346\234\254\346\224\273\345\207\273.md" +++ "b/docs/Safety/2_\350\267\250\347\253\231\350\204\232\346\234\254\346\224\273\345\207\273.md" @@ -178,6 +178,4 @@ localhost:1521?from=Bai ### CSP -内容安全策略(Content Security Policy):用于指定那些内容可执行。 - -- [CSP简介](https://blog.csdn.net/Fly_hps/article/details/86466367) +[CSP](https://blog.csdn.net/Fly_hps/article/details/86466367) 即内容安全策略(Content Security Policy):用于指定哪些内容可执行。 \ No newline at end of file diff --git "a/docs/Safety/7_\345\257\206\347\240\201\345\256\211\345\205\250.md" "b/docs/Safety/7_\345\257\206\347\240\201\345\256\211\345\205\250.md" index f7572661..beab3301 100644 --- "a/docs/Safety/7_\345\257\206\347\240\201\345\256\211\345\205\250.md" +++ "b/docs/Safety/7_\345\257\206\347\240\201\345\256\211\345\205\250.md" @@ -36,7 +36,7 @@ - 彩虹表会失效(数量太大,无法建立通用性) - 时解密唱本增大 N 倍 -## 密码加固 +## 密码加盐 - 在用户表中加上 `salt`字段 - 如果用户没有 `salt` 值,则需要升级 diff --git a/docs/Safety/Cookie_Session_Token.md b/docs/Safety/Cookie_Session_Token.md new file mode 100644 index 00000000..617051a2 --- /dev/null +++ b/docs/Safety/Cookie_Session_Token.md @@ -0,0 +1,706 @@ +# 一、Cookie + +## Cookie 简介 + +Cookie 意为“甜饼”,是由 W3C 组织提出,最早由 Netscape 社区发展的一种机制。 +目前 Cookie 已经成为标准,所有的主流浏览器如 IE、Netscape、Firefox、Opera 等都支持 Cookie。 + +由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。 +怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证。 +这样服务器就能从通行证上确认客户身份了,这就是 Cookie 的工作原理。 + +Cookie 实际上是一小段的文本信息。 +客户端请求服务器,如果服务器需要记录该用户状态,就使用 response 向客户端浏览器颁发一个 Cookie。 +**客户端浏览器会把 Cookie 保存起来**。 +当浏览器再请求该网站时,浏览器把请求的网址连同该 Cookie 一同提交给服务器。 +服务器检查该 Cookie,以此来辨认用户状态。 +服务器还可以根据需要修改 Cookie 的内容。 + +## Cookie 机制 + +Cookie 技术是**客户端的解决方案**,Cookie 就是由**服务器发给客户端**的特殊信息,而这些信息以文本文件的方式**存放在客户端**, +然后客户端每次向服务器发送请求的时候都会带上这些特殊的信息。 + +具体过程如下: + +1. 用户使用浏览器访问一个支持Cookie的网站的时候,用户会提供包括用户名在内的个人信息并且提交至服务器; +2. 服务器在向客户端回传相应的超文本的同时也会发回这些个人信息,当然这些信息并不是存放在HTTP响应体 + (Response Body)中的,而是存放于HTTP响应头(Response Header) +3. 客户端浏览器接收到来自服务器的响应之后,浏览器会将这些信息存放在一个统一的位置。 + 对于Windows操作系统而言,我们可以从: [系统盘]:\Documents and Settings[用户名]\Cookies目录中找到存储的Cookie; +4. 客户端再次向服务器发送请求的时候,都会把相应的Cookie再次发回至服务器。 + 而这次,Cookie信息则存放在HTTP请求头(equest Header)了。 + +## HTTP 的 Cookie 机制 + +Web应用程序是使用HTTP协议传输数据的。HTTP协议是无状态的协议。 +一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接。 +这就意味着服务器无法从连接上**跟踪会话**。 +举个例子,用户A购买了一件商品放入购物车内, +当再次购买商品时服务器已经无法判断该购买行为是属于用户A的会话还是用户B的会话了。 +要跟踪该会话,必须引入一种机制。 +​ +Cookie就是这样的一种机制。它可以弥补HTTP协议无状态的不足。 +在Session出现之前,基本上所有的网站都**采用Cookie来跟踪会话**。 + +## Set-Cookie 和 Cookie + +两个Http头部和Cookie有关 : Set-Cookie和Cookie + +当服务器返回给客户端一个Http响应信息时,其中如果包含Set-Cookie这个头部,说明: + +1. 指示客户端建立一个cookie +2. 在后续的Http请求中自动发送这个cookie到服务器端,直到这个cookie过期。 +3. 如果cookie的生存时间是整个会话期间的话,那么浏览器会将 cookie 保存在内存中, + 浏览器关闭时就会自动清除这个cookie。 +4. 如果将 cookie 保存在客户端的硬盘中,浏览器关闭的话,该 cookie 也不会被清除, + 下次打开浏览器访问对应网站时,这个cookie就会自动再次发送到服务器端。 + +一个cookie的设置以及发送过程分为以下四步: + +- 1. **客户端发送一个http请求到服务器端** +- 1. **服务器端发送一个http响应到客户端,其中包含Set-Cookie头部** +- 1. **客户端发送一个http请求到服务器端,其中包含Cookie头部** +- 1. **服务器端发送一个http响应到客户端** + +
+ +在客户端的第二次请求中包含Cookie头部,提供给了服务器端可以用来唯一标识客户端身份的信息。 +这时,服务器端也就可以判断客户端是否启用了cookie。 +尽管,用户可能在和应用程序交互的过程中突然禁用cookie的使用, +但是,这个情况基本是不太可能发生的,所以可以不加以考虑,这在实践中也被证明是对的。 + +## Cookie 的不可跨域名性 + +很多网站都会使用Cookie。例如,Google会向客户端颁发Cookie,Baidu也会向客户端颁发Cookie。 +那浏览器访问Google会不会也携带上Baidu颁发的Cookie呢?或者Google能不能修改Baidu颁发的Cookie呢? + +答案是否定的。Cookie具有**不可跨域名性**。 +根据Cookie规范,浏览器访问Google只会携带Google的Cookie,而不会携带Baidu的Cookie。 +Google也只能操作Google的Cookie,而不能操作Baidu的Cookie。 + +Cookie在客户端是由**浏览器来管理**的。 +浏览器能够保证Google只会操作Google的Cookie而不会操作Baidu的Cookie,从而保证用户的隐私安全。 +浏览器判断一个网站是否能操作另一个网站Cookie的依据是**域名**。 +Google与Baidu的域名不一样,因此Google不能操作Baidu的Cookie。 + +- 注意: + +虽然网站images.google.com与网站www.google.com同属于Google, +但是域名不一样,二者同样不能互相操作彼此的Cookie。 + +用户登录网站www.google.com之后会发现访问images.google.com时登录信息仍然有效,而普通的Cookie是做不到的。 +这是因为Google做了特殊处理。 + +## 简单案例 : 记录上次访问时间 + +> cookie的API + +```java +new Cookie(String key,String value); +String getName();//获取cookie的key(名称) +String getValue();//获取cookie的值 +void setMaxAge(int);//设置cookie在浏览器存活时间,单位:秒 +//如果设置成0:表示删除高cookie(前提:路径必须一致) +void setPath(String path);//设置cookie的路径 +//当我们访问的路径中包含次cookie的path,才会携带cookie +//默认访问路径:访问Servlet的路径,从"/项目名称"开始,到最后一个"/"结束。比如:/demo/a/b,默认路径为/demo/a +//手动设置路径:以"/项目名称"开始,以"/"结尾 +``` + +> 写回浏览器 + +```java +response.addCookie(Cookie); +``` + +> 获取cookie + +```java +Cookie[] request.getCookies(); +``` + +- 核心代码: + +```java +/** + * 根据 cookie名称获取Cookie 的工具类 + */ +public class CookieUtils { + public static Cookie getCookieByName(String name,Cookie[] cookies){ + if(cookies != null){ + for(Cookie cookie : cookies){ + if(name.equals(cookie.getName())){ + return cookie; + } + } + } + return null; + } +} +``` + +```java +public class RecordServlet extends HttpServlet { + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + //1.设置编码 + response.setContentType("text/html;charset=utf-8"); + PrintWriter w = response.getWriter(); + + //2.获取指定名称的Cookie + Cookie cookie = CookieUtils.getCookieByName("record",request.getCookies()); + + //3.判断cookie是否为空; + // 若为null,则说明是第一次访问; + // 若不为 null,则根据cookie显示上一次的访问时间 + if(cookie == null){ + w.write("这是您第一次访问"); + }else{ + long lastTime= Long.parseLong(cookie.getValue()); + w.write("您上次访问的时间:"+ new Date(lastTime).toLocaleString()); + } + + //4.记录当前访问时间,并且该信息存入cookie中 + Cookie c = new Cookie("record",System.currentTimeMillis()+""); + //设置cookie的有效期是 1 小时 + c.setMaxAge(60*60); + response.addCookie(c); + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + doPost(request,response); + } +} +``` + +- [该案例完整代码](https://github.com/DuHouAn/Java/tree/master/JavaWeb/Login) + +## Cookie 案例 : 浏览记录 + +- 核心代码1:记录商品浏览记录 + +```java +/** + * 记录商品浏览记录,只展示3个商品 + */ +public class CategoryServlet extends HttpServlet { + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + //获取当前访问商品的id + String id = request.getParameter("id"); + + Cookie c = CookieUtils.getCookieByName("ids",request.getCookies()); + + //判断该 cookie 是否为空 + String ids=""; + if(c == null){ + //若为空,说明之前没有访问记录 + //将当前商品的id放入ids中 + ids = id; + }else{ + //若不为空,获取值。也就是之前浏览的商品编号,使用 "-"进行连接 + ids = c.getValue(); + + //将 ids 通过"-"进行分割,然后存入list中,方便后续的操作 + String[] categoryIds = ids.split("-"); + LinkedList categories = new LinkedList<>(); + if(categories != null){ + for(String categoryId : categoryIds){ + categories.add(categoryId); + } + } + //判断之前记录中有无该商品 + if(categories.contains(id)){ + //若有,删除原来的id,将当前的id放入前面 + categories.remove(id); + }else{ + // 若没有 + // 继续判断长度是否>=3 + // 若>=3,移除最后一个,将当前的id放入最前面 + // 若<3,直接将当前的id放入最前面. + if(categories.size() >= 3){ + categories.removeLast(); + } + } + //不管如何,id都是最新浏览的,直接加入到前面 + categories.addFirst(id); + + ids=""; + for(String categoryId : categories){ + ids += (categoryId + "-"); + } + ids = ids.substring(0,ids.length()-1); + } + + //创建cookie + c=new Cookie("ids",ids); + //设置访问路径 + c.setPath(request.getContextPath()+"/"); + //设置存活时间 + c.setMaxAge(60); + + //写回浏览器 + response.addCookie(c); + + //跳转到指定的商品页面上 + response.sendRedirect(request.getContextPath()+"/category_info"+id+".htm"); + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + doPost(request,response); + } +} +``` + +- 显示浏览记录 + +```html +
    + <% + //获取指定名称的cookie ids + Cookie c= CookieUtils.getCookieByName("ids", request.getCookies()); + + //判断ids是否为空 + if(c==null){ + %> +

    暂无浏览记录

    + <% + }else{//ids=3-2-1 + String[] arr=c.getValue().split("-"); + for(String id:arr){ + %> +
  • + <% + } + } + %> +
+``` + +- 核心代码2:清空浏览记录 + +```java +/** + * 清空浏览记录 + */ +public class ClearServlet extends HttpServlet { + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + Cookie c = new Cookie("ids",""); + + //cookie的路径与 CategoryServlet中的cookie中的路径要相同 + c.setPath(request.getContextPath()+"/"); + //直接将cookie设置成无效 + c.setMaxAge(0); + response.addCookie(c); + + //重定向 + response.sendRedirect(request.getContextPath()+"/category_list.jsp"); + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + doPost(request,response); + } +} +``` + +- 注意: + +1. cookie是不能跨浏览器的 +2. cookie不支持中文,需要编码 + + + +# 二、Session + +## Session 简介 + +Session是一种记录客户状态的机制,不同于Cookie的是Cookie保存在客户端浏览器中,而Session保存在服务器上。 +客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session。 +客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。 + +如果说Cookie机制是通过检查客户身上的"通行证"来确定客户身份的话, +那么Session机制就是通过检查服务器上的"客户明细表"来确认客户身份。 +Session相当于程序在服务器上建立的一份客户档案, +客户来访的时候只需要查询客户档案表就可以了。 + +## Session 机制 + +一方面,我们可以把**客户端浏览器与服务器之间一系列交互的动作**称为一个 Session。 +从这个语义出发,我们会提到Session持续的时间,会提到在Session过程中进行了什么操作等等。 + +另一方面,Session指的是**服务器端为客户端所开辟的存储空间**,该空间保存的信息就是用于保持状态。 +从这个语义出发,我们则会提到往Session中存放什么内容,如何根据键值从Session中获取匹配的内容等。 + +- 要使用Session,当然是先要创建Session。那么Session在何时创建呢? + +1. Session在服务器端程序运行的过程中创建的,不同语言实现的应用程序有不同创建Session的方法, + 在Java中是通过调用HttpServletRequest的getSession方法(使用true作为参数)创建的。 + 创建Session的同时,**服务器会为该Session生成唯一的session id**, + 这个session id在随后的请求中会被用来重新获得已经创建的Session +2. Session被创建之后,就可以调用Session相关的方法往Session中增加内容了, + 而这些内容只会保存在服务器中,发到客户端的只有session id + +1. 当客户端再次发送请求的时候,会将这个session id带上, + 服务器接受到请求之后就会依据session id找到相应的Session,从而再次使用Session。 + +## Session 的生命周期 + +Session保存在服务器端。为了获得更高的存取速度,服务器一般把Session放在**内存**中。 +**每个用户都会有一个独立的Session**。 +如果Session内容过于复杂,当大量客户访问服务器时可能会导致内存溢出。 +因此,Session里的信息应该尽量精简。 + +**Session在用户第一次访问服务器的时候自动创建**。 +需要注意只有访问JSP、Servlet等程序时才会创建Session, +只访问HTML、IMAGE等静态资源并不会创建Session。 +如果尚未生成Session,也可以使用request.getSession(true)强制生成Session。 + +Session生成后,只要用户继续访问,服务器就会更新Session的最后访问时间,并维护该Session。 +用户每访问服务器一次,无论是否读写Session,服务器都认为该用户的Session"活跃(active)"了一次。 + +## Session 的有效期 + +由于会有越来越多的用户访问服务器,因此Session也会越来越多。 +为防止内存溢出,服务器会把长时间内没有活跃的Session从内存删除。 +这个时间就是Session的超时时间。如果超过了超时时间没访问过服务器,Session就自动失效了。 + +Session的超时时间为maxInactiveInterval属性, +可以通过对应的getMaxInactiveInterval()获取,通过setMaxInactiveInterval(longinterval)修改。 + +Session的超时时间也可以在web.xml中修改。 +另外,通过调用Session的invalidate()方法可以使Session失效。 + +## Session 案例 : 购物车 + +> 获取Session + +```java +HttpSession getSession(); //request.getSession() +``` + +> 域对象 + +```java +xxxAttribute //存放私有数据 +``` + +> 域对象生命周期 + +- 创建:第一次调用request.getSession() +- 销毁: + +1. 服务器非正常关闭; +2. session超时; + +默认超时时间:30 min + +手动设置超时:setMaxInactiveInterval(int) (单位:秒) + +1. 手动设置; + +Session接口中的invalidate()方法 + +```java +public void invalidate() +``` + +- 核心代码1:将商品添加到购物车 + +```java +public class CartServlet extends HttpServlet { + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/html;charset=utf-8"); + PrintWriter out=response.getWriter(); + + //1.获取商品名称 + String name = request.getParameter("name"); + + //2.获取购物车,实际上就是存入session的map + HashMap map = (HashMap) request.getSession().getAttribute("cart"); + + Integer num = null; + + //3.判断购物车是否为空 + if(map==null){ + //3.1 购物车为空,说明是第一次将商品放入购物车 + //先创建购物车, + map = new HashMap<>(); + request.getSession().setAttribute("cart",map); + num = 1; + }else{ + //3.2 购物车不为空,判断该商品之前是否已经加入购物车 + num = map.get(name); + if(num == null){ + //num==null,说明该商品之前未加入购物车 + num = 1; + }else{ + num ++ ; + } + } + map.put(name,num); + + //4.提示信息 + out.print("
已经将"+name+"添加到购物车中
"); + out.print("
继续购物

"); + out.print("
查看购物车
"); + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + doPost(request,response); + } +} +``` + +- 显示购物车中信息 + +```java + +
+ 继续购物      + 清空购物车      +
+
+ 订单详情 + + + + + + + <% + HashMap map = (HashMap)request.getSession().getAttribute("cart"); + if(map==null){ + out.print(""); + }else{ + for(String name : map.keySet()){ + out.print(""); + + out.print(""); + out.print(""); + + out.print(""); + } + } + %> + +
商品数量
亲,购物车空空,先去逛逛~~
"); + out.print(name); + out.print(""); + out.print(map.get(name)); + out.print("
+
+
+
+ +``` + +- 核心代码2:清空购物车 + +```java +public class ClearCartServlet extends HttpServlet { + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + request.getSession().invalidate(); + + response.sendRedirect(request.getContextPath()+"/cart.jsp"); + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + doPost(request,response); + } +} +``` + +- [Coookie和Session案例的完整代码](https://github.com/DuHouAn/Java/tree/master/JavaWeb/Category) + +## 实现会话跟踪的技术 + +- **Cookie** + +向客户端发送Cookie : + +```java +Cookie c =new Cookie("name","value"); //创建Cookie +c.setMaxAge(60*60*24); //设置最大时效,此处设置的最大时效为一天 +response.addCookie(c); //把Cookie放入到HTTP响应中 +``` + +从客户端读取Cookie : + +```java +String name ="name"; +Cookie[]cookies =request.getCookies(); +if(cookies !=null){ + for(int i= 0;i +``` + +优点: Cookie被禁时可以使用 + +缺点: 所有页面必须是表单提交之后的结果。 + +- **session** + +当一个用户第一次访问某个网站时会自动创建 HttpSession,**每个用户可以访问他自己的HttpSession**。 + +可以通过HttpServletRequest对象的getSession方法获得HttpSession。 +通过HttpSession的setAttribute方法可以将一个值放在HttpSession中, +通过调用 HttpSession对象的getAttribute方法,同时传入属性名就可以获取保存在HttpSession中的对象。 + +与上面三种方式不同的是,**HttpSession放在服务器的内存中**,因此不要将过大的对象放在里面。 +即使目前的Servlet容器可以在内存将满时将 HttpSession 中的对象移到其他存储设备中,但是这样势必影响性能。 +添加到 HttpSession 中的值可以是任意Java对象,这个对象最好实现了 Serializable接口, +这样Servlet容器在必要的时候可以将其序列化到文件中,否则在序列化时就会出现异常。 + +# 三、Cookie 和 Session 区别 + +- HTTP协议是**无状态的协议**,服务端需要记录用户的状态,就需要用某种机制来**识别具体的用户**,这个机制就是Session。 + Session典型的应用场景就是购物车,当点击下单按钮时,由于HTTP协议无状态,所以并不知道是哪个用户操作的, + 所以服务端要为特定的用户创建了特定的Session,用于**标识这个用户,并且跟踪用户**,这样才知道购物车里面的商品情况。 + 这个Session是保存在服务端的,有一个唯一标识。在服务端保存Session的方法很多,内存、数据库、文件都有。 + 集群的时候也要考虑Session的转移,在大型的网站,一般会有专门的Session服务器集群, + 用来保存用户会话,这个时候 Session 信息都是放在内存的,此外,一些缓存服务比如Memcached之类的来放 Session。 +- 服务端使用Cookie来识别特定的客户。每次HTTP请求的时候,客户端都会发送相应的Cookie信息到服务端。 + 实际上大多数的应用都是用 Cookie 来实现Session跟踪的, + 第一次创建Session的时候,服务端会在HTTP协议中告诉客户端,需要在 Cookie 里面记录一个session id, + 以后每次请求把这个 session id发送到服务器,这样就可以使用对应的Seesion了。 + 如果客户端的浏览器禁用了 Cookie 怎么办? + 一般这种情况下,会使用一种叫做**URL重写的技术**来进行**会话跟踪**, + 即每次HTTP交互,URL后面都会被附加上一个诸如 sid=xxxxx 这样的参数,服务端据此来识别用户。 +- Cookie其实还可以用在一些方便用户的场景下, + 设想你某次登陆过一个网站,下次登录的时候不想再次输入账号了,怎么办? + 这个信息可以写到Cookie里面,访问网站的时候, + 网站页面的脚本可以读取这个信息,就自动帮你把用户名给填了, + 能够方便一下用户。这也是Cookie名称的由来,给用户的一点甜头。 + +总结: + +- Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中。 +- Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。 + +# 四、Token + +## 服务器验证方式存在的一些问题 + +- 内存开销 + + 每次认证用户发起请求时,服务器需要去创建一个记录来存储信息。当越来越多的用户发请求时,内存的开销也会不断增加。 + +- CSRF,跨站请求伪造 + + 进行 Session 认证的时候,我们一般使用 Cookie 来存储 sessionId,当我们登陆后后端生成一个 sessionId 放在 Cookie 中返回给客户端,服务端通过 Redis 或者其他存储工具记录保存着这个 sessionId,客户端登录以后每次请求都会带上这个 sessionId,服务端通过这个 sessionId 来标识用户。如果别人通过 Cookie 拿到了 sessionId 后就可以代替该用户访问系统了。 + +- 可扩展性 + + 由于sessions 存放在服务器内存中,伴随而来的是可扩展性问题。当我们想要增加服务器来解决负载问题时,session 里的关键性信息会限制我们的扩展。 + +## 基于 Token 的验证原理 + +基于 Token 的身份验证是**无状态的**,我们不用将用户信息存在服务器或 Session 中。没有 session 信息意味着你的程序可以根据需要去增减机器,而不用去担心用户是否登录和已经登录到了哪里。客户端每次请求都需要 Token。**Token 应该在 HTTP 的头部发送从而保证了 HTTP 请求无状态**。我们也需要设置服务器属性: + +```html +Access-Control-Allow-Origin: * +``` + +来让服务器能接受来是所有域的请求。 + +具体实现思路: + +- 客户端使用用户名跟密码请求登录 +- 服务端收到请求,去验证用户名与密码 +- 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端 +- 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里 +- 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token +- 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据 + +## Tokens 的优势 + +- 无状态、可扩展 + + 在客户端存储的 Token 是无状态的,并且能够被扩展。基于这种无状态和不存储 Session 信息,负载均衡服务器能够将用户的请求传递到任何一台服务器上,因为服务器与用户信息没有关联。相反在传统方式中,我们必须将请求发送到一台存储了该用户 Session 的服务器上(称为 Session 亲和性),因此当用户量大时,可能会造成 一些拥堵,使用 Token 完美解决了此问题。 + +- 安全性 + + 请求中发送 Token 而不是 Cookie,这能够防止 CSRF(跨站请求伪造)攻击。即使在客户端使用 Cookie 存储 Token,Cookie 也仅仅是一个存储机制而不是用于认证。 + +- 可扩展性 + + 使用 Tokens 能够与其它应用共享权限。例如,能将一个博客帐号和自己的 QQ 号关联起来。当通过一个第三方平台登录 QQ 时,我们可以将一个博客发到 QQ 平台中。使用 Token,可以给第三方应用程序提供自定义的权限限制。当用户想让一个第三方应用程序访问它们的数据时,我们可以通过建立自己的 API,给出具有特殊权限的 Tokens。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +可扩展性: +CORS (跨域资源共享):当我们扩展应用程序,让数据能够从不同设备上访问时,跨域资源的共享会是一个让人头疼的问题。在使用 Ajax 抓取另一个域的资源时(移动端访问我们的 API 服务器),可能会出现禁止请求的情况。 +:用户在访问银行网站时,他们很容易受到跨站请求伪造的攻击,并且能够被利用其访问其他的网站。 +在这些问题中,可扩展性是最突出的。因此我们有必要去寻求一种更有行之有效的方法。 +———————————————— +版权声明:本文为CSDN博主「wnvalentin」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 +原文链接:https://blog.csdn.net/wnvalentin/article/details/89854980 + diff --git "a/docs/Safety/\345\270\270\350\247\201\346\224\273\345\207\273\346\212\200\346\234\257\345\217\212\351\230\262\345\276\241.md" "b/docs/Safety/\345\270\270\350\247\201\346\224\273\345\207\273\346\212\200\346\234\257\345\217\212\351\230\262\345\276\241.md" new file mode 100644 index 00000000..c63adaf0 --- /dev/null +++ "b/docs/Safety/\345\270\270\350\247\201\346\224\273\345\207\273\346\212\200\346\234\257\345\217\212\351\230\262\345\276\241.md" @@ -0,0 +1,319 @@ +# 一、跨站脚本攻击(XSS) + +## 概念 + +跨站脚本攻击(Cross-Site Scripting, XSS),可以将代码注入到用户浏览的网页上,这种代码包括 HTML 和 JavaScript。 + + + +## 攻击原理 + +
+ + + +## 危害 + +- 偷取网站任意数据 +- 偷取用户资料 +- 劫持前端逻辑 +- 显示伪造的文章或者图片 + + + +## 攻击分类 + +### 1. 反射型 + +反射型指的是 url 参数直接注入。比如:在浏览器地址栏中 + +```html +localhost:1521?from=Bai +``` + + + +### 2. 存储型 + +存储到数据库后读取时注入。 + +一个攻击者在论坛的楼层中包含了一段 JavaScript 代码,并且服务器没有正确进行过滤,那就会造在浏览这个页面时会执行这段 JavaScript 代码。 + + + +## XSS 攻击注入点 + +### 1. HTML 节点内容 + +```html +
+ #{content} +
+``` + +注入点: + +```html +
+ +
+``` + +### 2. HTML 属性 + +```html + +``` + +注入点: + +```html + +``` + +其中 1" onerror="alert(1) 就是 #{image} 。这里可看出 ``多了一个属性 onerror。 + +### 3. JavaScript 代码 + +```html + +``` + +注入点: + +```html + +``` + +其中 hello";alert(1);" 就是 #{data}。这里可看出实际上是即为 data 赋值,也进行了 alert 操作。 + +### 4. 富文本 + +富文本需要保留 HTML,但 HTML 存在 XSS 攻击风险。 + + + +## 防御 + +### 浏览器自带防御 + +使用浏览器自带防御可防御一些 XSS。但是存在一些不足: + +- 只能对参数出现在 HMTL 内容或属性的反射型 XSS 进行防御 +- 并不是所有浏览器都支持对 XSS 的防御 + +### HTML 内容 + +```html +
+ +
+``` + +解决: + +- 将 `<` 转义为 `<` +- 将 `>` 转义为 `>` + +转义后: + +```html +<div> + <script> + </script> +</div> +``` + +### HTML 属性 + +```html + +``` + +解决: + +- 将 `"`转义为 `&quto;` +- 将 `'`转义为 `'` +- 将空格转义为 ` ` + +转义后: + +```html + +``` + +### JavaScript 代码 + +```java + +``` + +解决: + +- 将 `"`转义为 `\"` +- 将 `\`转义为 `\\` +- 或者转化为 json + +转义后: + +```html + +``` + +### 富文本 + +[按**白名单**保留部分标签和属性。](https://github.com/leizongmin/js-xss/blob/master/README.zh.md) + +### CSP + +[CSP](https://blog.csdn.net/Fly_hps/article/details/86466367) 即内容安全策略(Content Security Policy):用于指定哪些内容可执行。 + + + + + +# 二、跨站请求伪造(CSRF) + +## 概念 + +跨站请求伪造(Cross-site request forgery,CSRF),是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去执行。 + +XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户浏览器的信任。 + + + +## 攻击原理 + +
+ +- 用户登录 A 网站 +- A 网站确认身份 +- B 网站页面向 A 网站发起请求(带 A 网站身份) + - B 网站向 A 网站请求,带 A 网站 Cookies + - 不访问 A 网站前端 + - referer 为 B 网站 + + + +## 危害 + +- 利用用户登录态 +- 用户不知情 +- 完成业务请求 + + + +## 防御 + +### 带 A 网站 Cookies + +利用 Cookie 中新增**属性 same-site**,禁止第三方网站带 Cookies。但是这样适配性比较差,目前只有 Chrome 支持。 + +### 不访问 A 网站前端 + +在前端页面加入验证消息,有 2 种方式: + +- **验证码** + + 因为 CSRF 攻击是在用户无意识的情况下发生的,所以要求用户输入验证码可以让用户知道自己正在做的操作。 + +- **token** + + 例如服务器生成随机数并附加在表单中,并要求客户端传回这个随机数。 + +### referer 为 B 网站 + +**验证 referer**。判断请求的来源是否合法。 + + + +# 三、SQL 注入 + +## 概念 + +服务器上的数据库运行非法的 SQL 语句,主要通过拼接来完成。 + +## 攻击原理 + +例如一个网站登录验证的 SQL 查询代码为: + +```sql +SELECT * FROM users WHERE (name = '${userName}') and (pw = '${passWord}'); +``` + +如果填入以下内容: + +```sql +userName = 1' OR '1'='1 +passWord = 1' OR '1'='1 +``` + +那么 SQL 查询字符串为: + +```sql +SELECT * FROM users WHERE (name = '1' OR '1'='1') and (pw = '1' OR '1'='1'); +``` + +此时无需验证通过就能执行以下查询: + +```sql +SELECT * FROM users; +``` + +## 危害 + +- 猜解密码 +- 获取数据 +- 删库删表 +- 拖库 + +## 防御 + +- 关闭错误输出(只能在一定程度上增加 SQL 注入的难度,并不能解决 SQL 注入问题) +- 检查数据类型 +- 对数据进行转义 +- 使用**参数化查询**(推荐使用) +- 使用 [ORM](https://www.cnblogs.com/best/p/9711215.html#_label0)(对象关系映射) + + + +# 四、DoS 攻击 + +## 概念 + +拒绝服务攻击(denial-of-service attack,DoS),亦称洪水攻击,模拟正常用户使目标电脑的网络或系统资源耗尽,使服务暂时中断或停止,导致正常用户无法访问。 + +## 类型 + +- TCP 半连接 +- HTTP 连接 +- DNS + +## DDoS + +分布式拒绝服务攻击(distributed denial-of-service attack,DDoS),指攻击者使用两个或以上被攻陷的电脑作为“僵尸”向特定的目标发动“拒绝服务”式攻击。 + +## 防御 + +- 防火墙 +- 交换机、路由器 +- 流量清洗 +- 高仿 IP + +## 预防 + +- 避免重逻辑业务 +- 快速失败快速返回 +- 防雪崩机制 +- 有损服务 +- CDN \ No newline at end of file diff --git "a/docs/Spring/1_Spring\346\246\202\350\277\260.md" "b/docs/Spring/1_Spring\346\246\202\350\277\260.md" new file mode 100644 index 00000000..56b6d066 --- /dev/null +++ "b/docs/Spring/1_Spring\346\246\202\350\277\260.md" @@ -0,0 +1,187 @@ +# Spring 概述 + +## Spring 的特性 + +Spring 基于 J2EE 技术实现了一套**轻量级**的 Java Web Service 系统应用框架,旨在提高开发人员的开发效率以及系统的可维护性。Spring 的特性包括: + +- 轻量级 + + 从 Jar 包的大小上来说,核心 Jar 包 `spring-web-5.2.0.RELEASE.jar`和 `spring-core-5.2.0.RELEASE.jar`均为 1.4 M 左右; + + 从系统的资源使用上来说,Spring 运行期间只需要少量的操作系统资源(内存和 CPU)便能稳定运行。 + +- 面向容器 + + Spring 实现了对象的配置化生成和对象的生命周期管理,所以是面向容器的。 + +- 控制反转 + +- 面向切面 + +- 框架灵活 + +## Spring 的模块 + +Spring 为企业应用程序提供一站式服务。Spring 模块提供的常用模块有核心容器层(Core Container)、数据访问层(Data Access)、Web 应用层(Web Access)。 + +### 核心容器层 + +核心容器层包括 Spring-Beans、Spring-Core、Spring-Context 等模块。 + +- Spring-Beans + + 基于工厂模式实现对象的创建。Spring-Beans 通过 xml 配置文件实现了声明式的对象管理,将对象之间复杂的依赖关系从实际编码逻辑中解耦出来。 + +- Spring-Core + + Spring 的核心功能实现,提供 IoC 依赖注入功能的支持。 + +- Spring-Context + + 在 Spring-Beans 和 Spring-Core 模块的基础上构建起来的。Spring-Context 模块继承自 Spring-Beans 模块,并且添加了国际化、事件传播、资源加载和透明地创建上下文等功能。 + +### 数据访问层 + +数据访问层包括:JDBC、ORM、OXM、JMS 和 TX 模块。 + +- JDBC (Java Data Base Connectivity) + + 提供了对数据库访问的抽象 JDBC。不同的数据库都有自己独立的 API 用于操作数据库,而 Java 程序只需要和 JDBC API 交互,这样就屏蔽了数据库的影响。 + +- ORM (Object Relational Mapping) + + 提供对 Hibernate 等 ORM 框架的支持。 + +- OXM (Object XML Mapping) + + 提供对 Castor 等 OXM 框架的支持。 + +- JMS (Java Message Service) + + JMS 模块包括消息的生产和消费功能。从 Spring 4.1 开始,Spring 集成了 Spring-Messaging 模块,用于实现对消息队列的支持。 + +- TX + + 提供对事务的支持。 + +### Web 应用层 + +Web 应用层主要包括 Web 交互和数据传输等相关功能,包括 Web、Web-MVC、Web-Socket 和 Web-Flux。 + +- Web + + 提供了面向 Web 应用的基本功能。 + +- Web-MVC + + 提供对 Spring MVC 的实现。 + +- Web-Socket + + 提供了对 WebSocket 的支持,WebSocket 可以让客户端和服务端进行双向通信。 + +- Web-Flux + + 提供对 WebFlux 的支持。 + + 目前最新的 5.x 版本中 Web 模块的 Portlet 组件已经被废弃掉,同时增加了用于异步响应式处理的 WebFlux 组件。 + +### 其他重要模块 + +- Spring AOP + + 提供了面向切面的编程实现,允许应用程序通过定义方法拦截器和切入点来实现系统功能和业务功能之间的解耦。 + +- Spring Aspects + + 提供了 Spring 与 AspectJ 的集成,是一个面向切面编程的模块。 + +## Spring 的注解 + +### @Contoller + +SpringMVC 中,控制器 Controller 负责处理 DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个 Model,然后再把该 Model 返回给对应的 View 进行展示。 + +SpringMVC 提供了一个非常简便的定义 Controller 的方法,无需继承特定的类或者接口,只需使用 @Controller 标记一个类是 Contoller。 + +### @RequestMapping + +使用 @RequestMapping 来映射 URL 到 Controller,或者到 Controller 的处理方法上。method 的值一旦指定,则处理方法只对指定的 HTTP method 类型请求处理。 + +可以为多个方法映射相同的 URL 和不同的 HTTP method 类型,Spring MVC 根据请求的 method 类型是可以区分开这些方法的。 + +### @RequestParam & @PathVariable + +在 SpringMVC 中,两者的作用都是将 request 里的参数值绑定到 Controller 里的方法参数中,区别在于 URL 的写法不同。 + +- 使用 @RequestParam 时,URL 是这样的: + +```html +http://host:port/path?参数名=参数值 +``` + +- 使用 @PathVariable 时,URL 是这样的: + +```html +http://host:port/path/参数值 +``` + +### @ResponseBody + +该注解用于将 Controller 中方法返回的对象,通过适当的 HttpMessageConverter 转换为指定的格式后,写入到 Response 对象的 bodys 数据区。 + +### @Service & @Controller & @Repository & @Component + + @Service、 @Contrller、 @Repository 其实这 3 个注解和 @Component 是等效的,用在实现类上: + +- @Service 用于标注业务层组件 +- @Controller 用于标注控制层组件 +- @Repository 用于编著数据访问组件 +- @Component 泛指组件,当组件不好归类时,可以使用这个注解进行标注 + +### @Value + +在 Spring 3.0 中,可以通过使用 @Value,对一些如 xxx.properties 文件中的文件,进行键值对的注入。 + +### @Autowired + +@Autowired 可以对成员变量、成员方法和构造函数进行标注,来完成自动装配工作。 + +### @Autowired & @Resource + +@Autowired 是 Spring 提供的注解,采用的策略是**按照类型**注入的: + +```java +public class UserService{ + @Autowired + userDao; // 在 Spring 容器中找到类型为 UserDao 的对象,将其注入 +} +``` + +存在问题:同一类型有多个 Bean,可以使用 @Qualifier 具体去装配哪个对象。 + +```java +public class UserService{ + @Autowired + @Qualifier(name="userDao") + userDao; // 在 Spring 容器中找到类型为 UserDao,名称为 userDao 的对象,将其注入 +} +``` + + @Resource 是 J2EE 提供的注解,默认是**按照名称**注入的: + +```java +Service{ + @Resource + userDao; // 自动按名称进行装配 + + @Resource(name="studentDao") + studentDao; // 按名称进行装配,找不到会抛出异常 + + @Resource(type="TeacherDao") + teacherDao; // 按类型进行装配,找不到或者找到多个都会抛出异常 + + @Resource(name="manDao",type="ManDao") + manDao;// 找唯一匹配的 Bean 进行装配,如果找不到则会抛出异常 +} +``` diff --git "a/docs/Spring/2_SpringIoC\345\216\237\347\220\206.md" "b/docs/Spring/2_SpringIoC\345\216\237\347\220\206.md" new file mode 100644 index 00000000..875edb55 --- /dev/null +++ "b/docs/Spring/2_SpringIoC\345\216\237\347\220\206.md" @@ -0,0 +1,421 @@ +# Spring IoC 原理 + +## IoC (Inverse of Control) + +IOC,即控制反转(Inverse of Control)是一种**设计思想**,并不是一个具体的技术实现。 + +- 控制:控制对象的创建及销毁(生命周期)。 +- 反转:将对象的控制权交给 IoC 容器。 + +**所有类的创建、销毁都由 Spring 来控制,也就是说控制对象生命周期的不是引用它的对象,而是 Spring**。对于某个具体对象而言,以前是它控制其他对象,现在所有对象都被 Spring 控制。 + +## 依赖注入 (Dependency Injection) + +依赖注入就是**将底层类作为参数传递给上层类,实现上层对下层的控制**,**依赖注入实现控制反转**。 + +举例说明依赖注入:以生产行李箱为例。 + +**传统设计思路**:先设计轮子,然后根据轮子 size 来设计底盘,再根据底盘来设计箱体,最后设计好行李箱。 + +可这样表示: + +
+ +相应代码代码: + +
+ +size 是固定值,可以进行相应的改进: + +
+ +**依赖注入设计思路**: + +先设计行李箱的大概样子,再根据行李箱的样子设计箱体,根据箱体去设计底盘,然后去设计轮子。 + +
+ +相应代码如下: + +
+ +不难理解,依赖注入就是**将底层类作为参数传递给上层类,实现上层对下层的控制**。 + +Spring 支持 4 种依赖注入:setter 注入、构造器注入、注解注入和接口注入。 + +### setter 注入 + +```xml + + + + + + + + + + + + + +``` + +```java +public class ExampleBean { + + private AnotherBean beanOne; + + private YetAnotherBean beanTwo; + + private int i; + + public void setBeanOne(AnotherBean beanOne) { + this.beanOne = beanOne; + } + + public void setBeanTwo(YetAnotherBean beanTwo) { + this.beanTwo = beanTwo; + } + + public void setIntegerProperty(int i) { + this.i = i; + } +} +``` + +### 构造器注入 + +```xml + + + + + + + + +``` + +```java +public class ExampleBean { + + private AnotherBean beanOne; + + private YetAnotherBean beanTwo; + + private int i; + + public ExampleBean( + AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { + this.beanOne = anotherBean; + this.beanTwo = yetAnotherBean; + this.i = i; + } +} +``` + +### 注解注入 + +```java +public class ExampleBean { + + @Autowired + @Qualifier("anotherExampleBean") + private AnotherBean beanOne; + + @Autowired + @Qualifier("yetAnotherBean") + private YetAnotherBean beanTwo; + + @Value("1") + private int i; +} +``` + +### 接口注入 + +接口注入模式因为历史较为悠久,在很多容器中都已经得到应用。但由于其在灵活性、易用性上不如其他注入模式,因而在 IOC 的专题世界内并不被看好。 + + + +## IoC 和 DI 的关系 + +依赖注入实现控制反转。 + +依赖倒置原则、IoC、DI 和 IoC 容器的关系: + +
+ +补充:[控制反转-知乎解答](https://www.zhihu.com/question/23277575/answer/169698662) + + + +## IoC 容器 + +IoC 容器指具有依赖注入功能的容器。 + +IoC 容器负责创建对象,将对象连接在一起,配置这些对象,并从创建中处理这些对象的整个生命周期,直到它们被完全销毁。 + +Spring 通过**配置文件**描述 IoC 容器管理的对象。Spring IoC 容器通过读取配置文件中的配置元数据,通过元数据对应用中的各个对象进行实例化及装配。一般使用基于 xml 配置文件进行配置元数据,而且 Spring 与配置文件完全解耦的,可以使用其他任何可能的方式进行配置元数据,比如注解、基于 Java 文件的、基于属性文件的配置都可以。 + +Spring IoC 容器的代表就是 `org.springframework.beans` 包下的 **BeanFactory** 接口: + +- IoC 容器要实现的最基础的接口 +- 采用**延迟初始化策略**(容器初始化完成后并不会创建 Bean 对象,只有当收到初始化请求时才进行初始化) +- 由于是延迟初始化策略,因此启动速度较快,占用资源较少 + +`org.springframework.context` 包下的 **ApplicationContext** 接口扩展了 BeanFactory: + +- 在 BeanFactory 基础上,增加了更为高级的特性:事件发布、国际化等。 +- 在容器启动时,完成所有 Bean 的创建 +- 启动时间较长,占用资源较多 + +### IoC 容器初始化过程 + +
+ +- Resource 定位:即 BeanDefinition 的资源定位,Resource 为各种形式的 BeanDefinition 的使用都提供了统一的接口 +- BeanDefinition 的载入 +- 向 IoC 容器中注册 BeanDefinition:实际上 IoC 容器内部维护一个 HashMap,注册过程就是将 BeanDefinition 添加至 HashMap 中。 + +### Spring Bean 的装配流程 + +
+ +IoC 容器其实就是一个大工厂,它用来管理我们所有的对象以及依赖关系: + +- Spring 在启动时会从 xml 配置文件/注解中读取应用程序提供的 Bean 配置信息,并在 Spring 容器中生成一份相应的 Bean 定义注册表 +- 根据注册表实例化 Bean,装配好 Bean 之间的依赖关系 +- 将 Bean 实例放入 Spring IoC 容器中,等待应用程序调用 + +### getBean 方法 + +ApplicationContext 接口获取 Bean 的方法: + +| 方法 | 说明 | +| --------------------------------------------- | ------------------------------------------------------------ | +| Object getBean(String name) | 根据名称返回一个 Bean,**客户端需要自己进行类型转换** | +| T getBean(String name, Class requiredType) | 根据名称和指定的类型返回一个Bean,客户端无需自己进行类型转换,如果类型转换失败,容器抛出异常 | +| T getBean(Class requiredType) | 根据指定的类型返回一个Bean,客户端无需自己进行类型转换,如果没有或有多于一个Bean存在容器将抛出异常 | +| Map getBeansOfType(Class type) | 根据指定的类型返回一个键值为名字和值为 Bean 对象的Map,如果没有Bean对象存在则返回空的 Map | + +getBean(name) 代码逻辑: + +- 获取参数 name 转化为 beanName +- 从缓存中加载实例 +- 实例化 Bean +- 检测 parentBeanFactory(若无缓存数据,直接到 parentBeanFactory 中去加载) +- 初始化依赖的 Bean +- 返回 Bean + +> **注意:BeanFactory 和 FactoryBean 的区别** + +- BeanFactory 是 IoC 最基本的容器,负责生产和管理 Bean,为其他具体的 IoC 容器提供了最基本的规范。 +- FactoryBean 是一个 Bean,是一个接口,当 IoC 容器中的 Bean 实现了 FactoryBean 后,通过 getBean(String beanName) 获取到的 Bean 对象并不是 FactoryBean 的实现类对象,而是这个实现类中的 getObject() 方法返回的对象。**要想获取 FactoryBean 的实现类对象,就是在 beanName 前面加上 "&"**。 + + + +## Spring 中 Bean 的作用域 + +### singleton + +singleton 即单例模式。singleton 作用域是 Spring 中的**缺省作用域**。 + +当一个 Bean 的作用域为 singleton,那么 Spring IoC 容器中只会存在一个**共享的 Bean 实例**, 并且所有对 Bean 的请求,只要 id 与该 Bean 定义相匹配,则只会**返回 Bean 的同一实例**。 + +可以指定 Bean 节点的 lazy-init="true" 来延迟初始化 Bean, 此时只有在第一次获取 Bean 时才会初始化 Bean,即第一次请求该 Bean 时才初始化。 每次获取到的对象都是同一个对象。 + +配置文件 XML 中将 Bean 定义成 singleton : + +```html + +``` + +@Scope 注解的方式: + +```java +@Service +@Scope("singleton") +public class ServiceImpl{ +} +``` + +### prototype + +prototype 即原型模式。当一个 Bean 的作用域为 prototype,表示一个 Bean 定义对应多个对象实例。 prototype 作用域的 Bean 会导致在每次对该 Bean 请求(将其注入到另一个 Bean 中,或者以程序的方式调用容器的 getBean() 方法)时都会创建一个新的 Bean 实例。 + +在创建容器的时候并没有实例化, 而是当我们获取 Bean 的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。 + +配置文件 XML 中将 Bean 定义成 prototype : + +```html + +``` + +或者 + +```html + +``` + +@Scope 注解的方式: + +```java +@Service +@Scope("prototype") +public class ServiceImpl{ +} +``` + +> **Spring 中线程安全问题** + +有状态 Bean & 无状态 Bean: + +- **有状态 Bean** + + 对象中有实例变量(成员变量),可保存数据; + + **非线程安全**。 + +- **无状态 Bean** + + 对象中无实例变量,不能保存数据,可在多线程环境下共享; + + **线程安全**。 + +Spring 采用两种方式保证线程安全: + +- 采用 ThreadLocal 进行处理 +- 采用原型模式,每次有 Bean 请求时,都会创建一个新的 Bean 实例 + +所以根据经验,**对有状态的 Bean 应该使用 prototype 作用域,而对无状态的 Bean 则应该使用 singleton 作用域。** + +### request + +request 只适用于**Web程序**,每一次 HTTP 请求都会产生一个新的 Bean , 同时该 Bean 仅在当前HTTP request 内有效,当请求结束后,该对象的生命周期即告结束。 + +在 XML 中将 Bean 定义成 request ,可以这样配置: + +```html + +``` + +### session + +session 只适用于**Web程序**, session 作用域表示该针对每一次 HTTP 请求都会产生一个新的 Bean, 同时**该 Bean 仅在当前 HTTP session 内有效**。 与 request 作用域一样,可以根据需要放心的更改所创建实例的内部状态, 而别的 HTTP session 中根据 userPreferences 创建的实例, 将不会看到这些特定于某个 HTTP session 的状态变化。 当HTTP session最终被废弃的时候,在该 HTTP session 作用域内的 Bean 也会被废弃掉。 + +```html + +``` + +### globalSession + +globalSession 作用域**类似于标准的 HTTP session** 作用域, 不过仅仅在基于 portlet 的 Web 应用中才有意义。 Portlet 规范定义了全局 Session 的概念, 它被所有构成某个 portlet web 应用的各种不同的 portlet所共享。 在 globalSession 作用域中定义的 Bean 被限定于全局 portlet Session 的生命周期范围内。 + +```html + +``` + +注意:五种作用域中,request、session 和 globalSession 三种作用域仅在基于 Web 的应用中使用(不必关心你所采用的是什么 Web 应用框架),只能用在基于 Web 的 Spring ApplicationContext 环境。 + + + +## Spring 中 Bean 的生命周期 + +Spring bean 的生命周期执行如下图: + +
+ +1、Spring 对 Bean 进行实例化。 + +2、Spring 将值和 Bean 的引用注入到 Bean 对应的属性中。 + +3、如果 Bean 实现了 BeanNameAware 接口,则会调用其实现的 setBeanName() 方法, + +Spring 会将 bean 的 id 传递给 setBeanName() 接口方法。 + +4、如果 Bean 实现了 BeanFactoryAware 接口,则会调用其实现的 setBeanFactory() 方法,将 BeanFactory 容器实例作为传入参数。 + +5、如果 Bean 实现了 ApplicationContextAware 接口,则会调用其实现的 setApplicationContext() 方法,将应用上下文的引用作为传入参数。 + +6、如果 Bean 实现了 BeanPostProcessor 接口,Spring 将调用 postProcessBeforeInitialization() 接口方法。 + +7、如果 Bean 实现了InitializingBean 接口,Spring 将调用 afterPropertiesSet() 接口方法。 + +8、如果Bean 实现了 init-method 声明了初始化方法,该方法也会被调用。 + +9、如果 Bean 实现了 BeanPostProcessor 接口,Spring 将调用 postProcessAfterInitialization() 接口方法。 + +10、此时 Bean 已经准备就绪,可以被应用程序使用了,他们将会一直驻留在应用上下文中,一直到该应用上下文被销毁。 + +11、如果 Bean 实现了 DisposableBean 接口,Spring 将调用它的 destroy() 接口方法。 + +12、如果 Bean 使用 destroy-method 声明了销毁方法,方法也会被调用。 + + + +## 循环依赖问题 + +循环依赖指的是若 A 中有 B 的属性,B 中有 A 的属性,则当进行依赖注入时,就会产生 A 还未创建完,因为对 B 的创建再次返回创建 A。 + +### 类的实例化 & 类的初始化 + +类的实例化是指创建一个类的实例(对象)的过程。 + +类的初始化是指为类中各个类成员(被 static 修饰的成员变量)赋初始值的过程,是**类生命周期中的一个阶段**。 + +### Spring 中类的实例化 & 类的初始化 + +Spring 中**所有 Bean 默认都是单例模式**,所以 Bean 的初始化和实例化都是在加载进入 Bean 容器时进行的。如果想使用时再初始化,那么可以把类定义为原型模式。 + +### 三级缓存 + +单例对象,在 Spring IoC 容器中,有且仅有一个对象,将对象放入缓存中。 + +Spring 中使用 “三级缓存”: + +```java +// 一级缓存:单例对象的缓存(存储实例化完成的 Bean) +/** Cache of singleton objects: bean name --> bean instance */ +private final Map singletonObjects = new ConcurrentHashMap(256); + +// 三级缓存:单例 ObjectFactory 的缓存 +/** Cache of singleton factories: bean name --> ObjectFactory */ +private final Map> singletonFactories = new HashMap>(16); + +// 二级缓存:提前曝光的单例对象的缓存(存储正在实例化的 Bean) +/** Cache of early singleton objects: bean name --> bean instance */ +private final Map earlySingletonObjects = new HashMap(16); +``` + +举例说明解决循环依赖(A 中有B,B 中有 A)的具体过程: + +Spring 中单例对象的初始化主要分为 3 步: + +- 第一步:createBeanInstance +- 第二步:populateBean 填充属性 +- 第三步:intializeBean 初始化 + +在进行 createBeanInstance 后,该单例对象此时已被创建,Spring 将该对象**提前曝光到 singeltonFacoties 中**。 + +- A 完成 createBeanInstance ,并且**提前曝光到 singeltonFacoties 中** +- A 进行第二步,发现需要依赖 B,尝试获取 B +- B 开始创建,B 完成 createBeanInstance,发现需要依赖 A,尝试获取 A:先尝试从 singletonObjects 中获取,发现不存在,因为 A 未初始化完全;再尝试从 earlySingletonObjects 中获取;再去 singletonFactories 中获取,此时 B 获取 A,并将 A 放入 earlySingletonObjects 中,再删除 A 在singletonFactories 中对应的 ObjectFactory。 +- B 获取 A,顺利完成第二、三步,将初始化完成的 B 放入 singletonObjects 中。 +- 此时返回创建 A,A 可获取 B,顺利完成第二、三步,A 初始化完成, 将 A 放入 singletonObjects 中。 + +> **Spring 2 种循环依赖:构造器循环依赖 & setter 循环依赖** + +- 构造器循环依赖:因为提前曝光到 singletonFactories 中的前提是需要执行构造方法,所以使用 “三级缓存” 无法解决该种循环依赖。 +- setter 循环依赖 + +所以在使用 Spring 框架进行开发时: + +- 尽量不要使用基于构造器的依赖注入方式,使用基于 setter 的依赖注入方式 +- 使用 @Autowired 注解,让 Spring 决定合适的时机 + +# 补充 + +- [Spring 官网](https://docs.spring.io/spring-framework/docs/current/reference/html/index.html) +- [Spring 循环依赖及三级缓存](https://blog.csdn.net/u012098021/article/details/107352463?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.no_search_link) diff --git "a/docs/Spring/3_SpringAOP\345\216\237\347\220\206.md" "b/docs/Spring/3_SpringAOP\345\216\237\347\220\206.md" new file mode 100644 index 00000000..c6da0ca1 --- /dev/null +++ "b/docs/Spring/3_SpringAOP\345\216\237\347\220\206.md" @@ -0,0 +1,92 @@ +# Spring AOP 原理 + +AOP 即面向切面编程(Aspect Oriented Programing),实际上是将一些通用的功能横向抽取出来: + +- 一方面,减少系统的重复代码 +- 另一方面,降低模块间的耦合度,比较好维护和扩展 + +Spring AOP 将应用分为核心关注点和横切关注点。业务处理流程为核心关注点,被业务所依赖的公共部分为横切关注点。横切关注点的特点是其行为经常发生在核心关注点多出,而多处操作基本相似,比如权限认证、日志、事务等。AOP 的核心思想是将核心关注点和横切关注点分离开来,以降低模块耦合度。 + +Spring AOP 的应用场景主要有: + +- 权限统一管理授权 +- 缓存统一维护 +- 数据懒加载 +- 资源池统一申请和管理 +- 统一事务管理 + +## AOP 相关术语 + +| 术语 | 解释 | 描述 | +| :----------: | --------- | :----------------------------------------------------------: | +| Joinpoint | 连接点 | 所谓连接点是指那些被拦截到的点。在 Spring 中,这些点指的是**方法**,因为 Spring 只支持**方法类型的连接点**。 | +| Pointcut | 切入点 | 所谓切入点是指我们要**对哪些 Joinpoint 进行拦截**的定义。 | +| Advice | 通知/增强 | 所谓通知是指拦截到 Joinpoint 之后要执行的具体操作。通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能) | +| Introduction | 引介 | 引介是一种**特殊的通知**。在不修改类代码的前提下,可以在运行期为类动态地添加一些方法或字段 | +| Target | 目标对象 | 代理的目标对象 | +| Weaving | 织入 | 是指把增强应用到目标对象来创建新的代理对象的过程。 有三种织入方式:Spring 采用**动态代理织入**,而 AspectJ 采用**编译期织入**和**类装载期织入** | +| Proxy | 代理 | 一个类被 AOP 织入增强后,就产生一个结果代理类 | +| Aspect | 切面 | 是切入点和通知(/引介)的结合 | + +例如在 IUserDao 接口中: + +```java +public interface IUserDao { + void add(); + void delete(); + void update(); + void search(); +} +``` + +- IUserDao 被增强的对象,就是 Target(目标对象) +- add()、delete()、update() 和 search() 都是 JoinPoint(连接点) +- 这里要对 add() 和 update() JoinPoint 进行拦截,则 add() 和 update() 就是 Pointcut(切入点) +- Advice 指的是要增强的代码,也就是代码的增强 +- Weaving:指的是把增强(Advice)应用到目标对象(Target)创建新的代理对象的过程 +- Aspect:是切入点和通知的结合,在 add 或 update 方法上应用增强 + +## AOP 底层原理 + +AOP 的底层原理是动态代理机制: + +- 类实现了接口,JDK 动态代理 +- 类未实现任何接口,Cglib 动态代理 + +## AOP 的 5 种通知类型 + +- 前置通知 + + 在连接点前面执行,前置通知不会影响连接点的执行,除非此处抛出异常。 + +- 后置通知 + + 在连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容。 + +- 成功通知 + + 在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行。 + +- 异常通知 + + 在连接点抛出异常后执行。 + +- 环绕通知 + + 环绕通知围绕在连接点前后,比如一个方法调用的前后。这是最强大的通知类型,能在方法调用前后自定义一些操作。环绕通知还需要负责决定是继续处理连接点(调用ProceedingJoinPoint 的 proceed 方法)还是中断执行。 + +五种通知的执行顺序为:前置通知、环绕通知、成功通知/异常通知、后置通知。 + +## Spring AOP & AspectJ AOP + +- 增强时机 + + Spring AOP 是运行时增强;AspectJ 是编译时增强 + +- 底层原理 + + Spring AOP 基于代理;AspectJ 基于字节码操作 (Bytecode Manipulation) + +- 性能 + + AspectJ 相比于 Spring AOP 功能更加强大,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。Spring AOP 相对来说更简单,并且 Spring AOP 已集成了 AspectJ,如果切面较少,那么两者性能差异不大,当切面较多时,最好选择 AspectJ ,它比 Spring AOP 快很多。 diff --git a/docs/SSM/1_SpringMVC.md "b/docs/Spring/4_SpringMVC\345\216\237\347\220\206.md" similarity index 50% rename from docs/SSM/1_SpringMVC.md rename to "docs/Spring/4_SpringMVC\345\216\237\347\220\206.md" index ea890aa1..42723ef6 100644 --- a/docs/SSM/1_SpringMVC.md +++ "b/docs/Spring/4_SpringMVC\345\216\237\347\220\206.md" @@ -1,69 +1,81 @@ -# SpringMVC +# Spring MVC 原理 -## MVC +## MVC (Model-View-Controller) -即 Model-View-Controller,是一种复合模式。 +MVC 即 Model-View-Controller,是一种复合模式。 - Model:模型。封装数据源和所有基于这些数据的操作 - View:视图。用来显现模型 - Controller:控制器。封装外界作用于模型的操作 -其中,模型利用 “观察者模式” 让 Controller 和视图随最新的状态改变而更新;View 和 Controller 实现 “策略模式”,控制器是视图的行为,若希望有不同的行为,可直接换一个控制器;视图内部利用“组合模式”。 +模型利用 “观察者模式” 让控制器和视图随最新的状态改变而更新; -优点: +视图和控制器实现 “策略模式”,控制器是视图的行为,若希望有不同的行为,可直接换一个控制器; + +视图内部则是利用 “组合模式”。 + +MVC 具有以下优点: - 三个模块可共享一个模型,大大提高代码的可重用性 - 三个模块相互独立,耦合性较低 - Controller 提高了应用程序的灵活性,使用 Controller 可连接不同模型和视图去满足用户的需求 -## SpringMVC +## Spring MVC SpringMVC 是一种基于 Java,实现了 Web MVC 设计模式,请求驱动类型的轻量级 Web 框架。优点如下: - 基于组件技术。全部的应用对象,无论是控制器、视图,还是业务对象之类都是 Java 组件。并且和 Spring 提供的其他基础结构紧密集成; - 不依赖于 Servlert API; -- 可以任意使用各种视图技术,而不仅仅局限于jspl; +- 可以任意使用各种视图技术,而不仅仅局限于 jspl; - 支持各种请求资源的映射策略; - 易扩展。 ### 原理 -SpringMVC 框架是以请求为驱动,围绕 Servlet 设计,将请求发给控制器,然后通过模型对象,分派器来展示请求结果视图。 其中**核心类是 DispatcherServlet**,它是一个 Servlet,顶层实现 Servlet 接口。 +Spring MVC 框架是以请求为驱动,围绕 **DispatcherServlet** 设计,将请求发给控制器,然后通过模型对象,分派器来展示请求结果视图。 原理图如下: -
- +
+- 客户端发起 HTTP 请求,将请求提交到 DispatcherServlet。 +- 寻找处理器:由 DispatcherServlet 查询一个或多个 HandlerMapping,找到处理该请求的 Contoller。 +- 调用处理器:DispatcherServlet 将请求提交到 Controller。 +- 调用业务处理逻辑并返回结果:Controller 调用业务处理逻辑后,返回 ModelAndView。 +- 处理视图映射并返回模型:DispatcherServlet 查询一个或多个 ViewResolver 视图解析器,找到 ModelAndView 指定的视图。 +- HTTP 响应:视图负责将结果在客户端浏览器上渲染和展示。 ### 重要组件 -#### 1、DispatcherServlet +#### DispatcherServlet - 说明:前端控制器,不需要工程师开发,由 SpringMVC 框架提供。 - 作用:**Spring MVC 的入口。接收请求,响应结果,相当于转发器,中央处理器**。DispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,**DispatcherServlet 降低了组件之间的耦合度**。 -#### 2、HandlerMapping +#### HandlerMapping -- 说明:处理器映射器,不需要工程师开发,由 SpringMVC 框架提供。 -- 作用:**根据请求的 url 查找 Handler**。SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。 +- 说明:处理器映射器,不需要工程师开发,由 Spring MVC 框架提供。 +- 作用:**根据请求的 url 查找 Handler**。Spring MVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。 -#### 3、HandlerAdapter +#### HandlerAdapter - 说明:处理器适配器。 -- 作用:**按照特定规则(HandlerAdapter要求的规则)执行 Handler**。通过 HandlerAdapter 对处理器进行执行,这是[适配器模式](https://duhouan.github.io/Java/#/OO/4_%E7%BB%93%E6%9E%84%E5%9E%8B?id=_1-%e9%80%82%e9%85%8d%e5%99%a8%ef%bc%88adapter%ef%bc%89)的应用,通过扩展适配器可以对更多类型的处理器进行执行。 +- 作用:**按照特定规则(HandlerAdapter要求的规则)执行 Handler**。通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。 -#### 4、Handler +#### Handler - 说明:处理器,需要工程师开发。 - 注意:编写 Handler 时按照 HandlerAdapter 的要求的规则去做,这样适配器才可以去正确执行 Handler, Handler 是**后端控制器**,在 DispatcherServlet 的控制下 Handler 对具体的用户请求进行处理。 由于 Handler 涉及到具体的用户业务请求,所以一般情况需要工程师根据业务需求开发 Handler。 -#### 5、ViewResolver +#### ViewResolver + +- 说明:视图解析器,不需要工程师开发,由 Spring MVC 框架提供。 + +- 作用:**进行视图解析,根据逻辑视图名解析成真正的视图**。ViewResolver 负责将处理结果生成 View 视图, ViewResolver 首先根据逻辑视图名解析成物理视图名即具体的页面地址,再**生成 View 视图对象**。 -- 说明:视图解析器,不需要工程师开发,由 SpringMVC 框架提供。 -- 作用:**进行视图解析,根据逻辑视图名解析成真正的视图**。ViewResolver 负责将处理结果生成 View 视图, ViewResolver 首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成 View 视图对象,最后对View 进行渲染将处理结果通过页面展示给用户。 SpringMVC 框架提供了很多的 View 视图类型,包括:jstlView、freemarkerView、pdfView等。 一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要工程师根据业务需求开发具体的页面。 + Spring MVC 框架提供了很多的 View 视图类型,包括:jstlView、freemarkerView、pdfView等。 一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要工程师根据业务需求开发具体的页面。 -#### 6、View +#### View - 说明:视图 View,需要工程师开发。 -- 作用:**View 是一个接口,实现类支持不同的 View类型(jsp、freemarker、pdf…)**。 \ No newline at end of file +- 作用:**View 是一个接口,实现类支持不同的 View 类型(jsp、freemarker、pdf…)**。 diff --git "a/docs/Spring/5_Spring\344\272\213\345\212\241\347\256\241\347\220\206.md" "b/docs/Spring/5_Spring\344\272\213\345\212\241\347\256\241\347\220\206.md" new file mode 100644 index 00000000..1480f92a --- /dev/null +++ "b/docs/Spring/5_Spring\344\272\213\345\212\241\347\256\241\347\220\206.md" @@ -0,0 +1,406 @@ +# Spring 事务管理 + +## 编程式事务 & 声明式事务 + +### 编程式事务 + +编程式事务指的是通过编码方式实现事务,类似 JDBC 编程实现事务管理,比如 TransactionalTemplate 或者 TransactionManager。 + +```java +@Autowired +private TransactionTemplate transactionTemplate; + +public void testTransaction() { + + transactionTemplate.execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { + + try { + // 业务代码 + } catch (Exception e){ + // 回滚 + transactionStatus.setRollbackOnly(); + } + } + }); +} +``` + +```java +@Autowired +private PlatformTransactionManager transactionManager; + +public void testTransaction() { + + TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); + try { + // 业务代码 + transactionManager.commit(status); + } catch (Exception e) { + // 回滚 + transactionManager.rollback(status); + } +} +``` + +### 声明式事务 + +声明式事务在 **XML 配置文件中配置**或者直接基于**注解**,其中基于`@Transactional` 的全注解方式使用最多。 + +| 事务方式 | 优点 | 缺点 | +| :--------: | :----------------: | :------------------------: | +| 编程式事务 | 显示调用,不易出错 | 侵入式代码,编码量大 | +| 声明式事务 | 简单,对代码侵入小 | 隐藏实现细节,出错不易定位 | + + + +## Spring 事务管理接口 + +Spring 框架中,事务管理相关最重要的 3 个接口如下: + +- PlatformTransactionManager:(平台)事务管理器 +- TransactionDefinition:事务定义信息(隔离级别、传播行为、超时、只读、回滚) +- TransactionStatus:事务运行状态 + +**所谓事务管理,实质上就是按照给定的事务规则来执行提交或者回滚操作**。其中,“给定的事务规则”是用 TransactionDefinition 表示的,“按照……来执行提交或者回滚操作”是用 PlatformTransactionManager 表示的,而 TransactionStatus 可以看作代表事务本身。 + +### PlatformTransactionManager + +**Spring 并不直接管理事务,而是提供了多种事务管理器** 。Spring 事务管理是通过 PlatformTransactionManager 接口体现的,该接口是 Spring事务策略的核心。通过该接口,Spring 为各个平台如 JDBC (DataSourceTransactionManager)、Hibernate (HibernateTransactionManager)、JPA (JpaTransactionManager) 等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。 + +```java +public interface PlatformTransactionManager { + + //平台无关的获得事务的方法 + TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; + + //平台无关的事务提交方法 + void commit(TransactionStatus status) throws TransactionException; + + //平台无关的事务回滚方法 + void rollback(TransactionStatus status) throws TransactionException; +} +``` + +比如我们在使用 JDBC 进行数据持久化操作时,进行如下配置: + +```xml + + + + + +``` + +### TransactionDefinition + +TransactionDefinition 接口用于定义事务属性,事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。事务属性包含了以下 5 个方面: + +- 事务隔离级别 +- 事务传播行为 +- 回滚规则 +- 是否只读 +- 事务超时 + +### TransactionStatus + +TransactionStatus 接口用来记录事务的状态,该接口定义了一组方法,用来获取或判断事务的相应状态信息。 + +```java +public interface TransactionStatus{ + boolean isNewTransaction(); // 是否是新的事务 + boolean hasSavepoint(); // 是否有恢复点 + void setRollbackOnly(); // 设置为只回滚 + boolean isRollbackOnly(); // 是否为只回滚 + boolean isCompleted; // 是否已完成 +} +``` + + + +## Spring 事务属性 + +### 事务隔离级别 + +事务隔离级别是指多个事务之间的隔离程度。 + +TransactionDefinition 接口中定义了 5 个表示隔离级别的常量: + +```java +public interface TransactionDefinition { + ... + int ISOLATION_DEFAULT = -1; + int ISOLATION_READ_UNCOMMITTED = 1; + int ISOLATION_READ_COMMITTED = 2; + int ISOLATION_REPEATABLE_READ = 4; + int ISOLATION_SERIALIZABLE = 8; + ... +} +``` + +```java +// 事务隔离级别对应的枚举 +public enum Isolation { + DEFAULT(TransactionDefinition.ISOLATION_DEFAULT), READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED), + READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED), + REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ), + SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE); + + private final int value; + + Isolation(int value) { + this.value = value; + } + + public int value() { + return this.value; + } + +} +``` + +| 隔离级别 | 解释 | 说明 | +| :------------------------: | -------- | :----------------------------------------------------------: | +| ISOLATION_DEFAULT | 默认 | 这是默认值,表示使用底层数据库的默认隔离级别。 | +| ISOLATION_READ_UNCOMMITTED | 读未提交 | 该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据,该级别不能防止脏读和不可重复读,因此很少使用该隔离级别 | +| ISOLATION_READ_COMMITTED | 读可提交 | 该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下推荐的隔离级别 | +| ISOLATION_REPEATABLE_READ | 可重复读 | 该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读,但幻读仍有可能发生 | +| ISOLATION_SERIALIZABLE | 可串行化 | 所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是,这将严重影响程序的性能,通常情况下也不会用到该级别 | + +### 事务传播行为 + +**事务传播行为是为了解决业务层方法之间互相调用的事务问题**。 + +当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。 + +TransactionDefinition 接口中定义了 5t个表示传播行为的常量: + +```java +public interface TransactionDefinition { + int PROPAGATION_REQUIRED = 0; + int PROPAGATION_SUPPORTS = 1; + int PROPAGATION_MANDATORY = 2; + int PROPAGATION_REQUIRES_NEW = 3; + int PROPAGATION_NOT_SUPPORTED = 4; + int PROPAGATION_NEVER = 5; + int PROPAGATION_NESTED = 6; + ... +} +``` + +```java +// 事务传播行为对应的枚举 +public enum Propagation { + REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED), + SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS), + MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY), + REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW), + NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED), + NEVER(TransactionDefinition.PROPAGATION_NEVER), + NESTED(TransactionDefinition.PROPAGATION_NESTED); + + private final int value; + + Propagation(int value) { + this.value = value; + } + + public int value() { + return this.value; + } + +} +``` + +| 传播行为 | 简写 | 说明 | +| :-----------------------: | :-----------: | :----------------------------------------------------------: | +| PROPAGATION_REQUIRED | required | 如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务。 | +| PROPAGATION_SUPPORTS | supports | 表示支持当前事务,如果当前没有事务,就以无事务方式执行。 | +| PROPAGATION_MANDATORY | mandatory | 表示使用当前的事务,如果当前没有事务,就抛出异常。 | +| PROPAGATION_REQUIRES_NEW | required_new | 表示新建事务,如果当前存在事务,把当前事务挂起。 | +| PROPAGATION_NOT_SUPPORTED | not_supported | 表示以无事务方式执行操作,如果当前存在事务,就把当前事务挂起。 | +| PROPAGATION_NEVER | never | 表示以无事务方式执行,如果当前存在事务,则抛出异常。 | +| PROPAGATION_NESTED | nested | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 | + +举例说明事务传播类型: + +```java +StudentServiceImplA implements StudentService{ + @Autowired + studentDao; + + @Autowired + studentService; + + @Transactional(required) // 事务传播特性为 requried: 如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务。 + insertA(){ + // 对数据库操作,插入 A 数据 + studentService.insertB(); // 插入 B 数据 + // I/O 异常 + } +} +``` + +```java +StudentServiceImplB implements StudentService{ + @Autowired + studentDao; + + @Transactional + insertB(){ + // 对数据库操作,插入 B 数据 + } +} +``` + +分以下几种情况讨论: + +- 若 StudentServiceImplB 中 insertB() 中传播类型是 required,数据库中既没有 A 数据,也没有 B 数据,则 insertA() 和 insertB() 属于同一个事务,发生异常,事务回滚即可。 +- 若 StudentServiceImplB 中 insertB() 中传播类型是 required_new,数据库中没有 A 数据,但是有 B 数据,则 insertB() 创建了一个新事务,insertA() 中发生异常,该事务回滚。 +- 若 StudentServiceImplB 中 insertB() 中传播类型是 not_supported,数据库中没有 A 数据,但是有 B 数据,则 insertB() 以非事务方式执行,执行 insertA() 的事务回滚,insertB() 中不会发生回滚 + +**注意:insertB() 为何要放入 StudentServiceImplB 中?** + +Spring 的事务机制是基于 AOP 代理实现的。如果在 StudentServiceImplA 中使用 insertB() ,insertA() 中在调用 insertB() 是通过当前对象来调用 insertB() 的,而不是通过代理来调用 insertB() 的,此时 insertB() 上加**事务注解就失效了**。 + +### 事务超时属性 + +事务超时指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒,默认值为 -1。 + +### 事务只读属性 + +对于只有读取数据查询的事务,可以指定事务类型为 readonly,即只读事务。 + +只读事务不涉及数据的修改,数据库会提供一些优化手段,适合用在有多条数据库查询操作的方法中。 + +### 事务回滚规则 + +默认情况下,事务只有遇到运行期异常时才会回滚,Error 也会导致事务回滚,但遇到检查型异常时不会回滚。 + + + +## @Transactional 注解 + +@Transactional 注解可以使用在类上,表明该注解对该类中所有的 public 方法都生效。也可以使用在方法上,但是注解只能应用到 public 方法上,否则不生效。 + +### 源码解析 + +```java +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +public @interface Transactional { + + @AliasFor("transactionManager") + String value() default ""; + + @AliasFor("value") + String transactionManager() default ""; + + // 事务的传播行为,默认值为 REQUIRED + Propagation propagation() default Propagation.REQUIRED; + + // 事务隔离级别,默认值为 DEFAULT + Isolation isolation() default Isolation.DEFAULT; + + // 事务的超时时间,默认值为-1,即不会超时 + int timeout() default TransactionDefinition.TIMEOUT_DEFAULT; + + // 指定事务是否为只读事务,默认值为 false + boolean readOnly() default false; + + // 用于指定能够触发事务回滚的异常类型 + // Class[] 说明可以指定多个异常类型 + Class[] rollbackFor() default {}; + + String[] rollbackForClassName() default {}; + + Class[] noRollbackFor() default {}; + + String[] noRollbackForClassName() default {}; + +} +``` + +### 工作机制 + +@Transactional 的工作机制是基于 Spring AOP 实现的。 + +如果一个类或者一个类中的 public 方法被标注 @Transactional 注解的话,Spring 容器就会在启动的时候为其创建一个代理类,在调用被 @Transactional 注解的 public 方法的时,实际调用的是 TransactionInterceptor 类中的 invoke() 方法。invoke() 方法会在目标方法之前开启事务,方法执行过程中如果遇到异常的时候回滚事务,方法调用完成后提交事务。 + +### 事务失效问题 + +#### 1. 同一个类中方法调用 + +在同一个类中的其他没有被 @Transactional 注解的方法内部调用 @Transactional 注解的方法,则 @Transactional 注解的方法的事务会失效。 + +这是因为Spring AOP 代理造成的,因为只有当 @Transactional`注解的方法在类以外被调用的时候,Spring 事务管理才生效。 + +```java +@Service +public class StudentService { + + private void insertA() { + // insertA() 中调用 insertB() 是通过当前对象来调用 insertB() 的,而不是通过代理来调用 insertB() 的,此时insertB()上加事务注解就失效了。 + insertB(); + // ... + } + + // 事务注解失效 + @Transactional + public void insertB() { + // ... + } +}Copy to clipboardErrorCopied +``` + +可以采用以下 2 种方式解决事务失效问题: + +- 避免同一类中自调用 +- 使用 AspectJ 取代 Spring AOP 代理 + +#### 2. try-catch 导致事务失效 + +```java +StudentServiceImplA implements StudentService{ + @Autowired + studentDao; + + @Autowired + studentService; + + @Transactional + insertA(){ + try { + // 对数据库操作,插入 A 数据 + studentService.insertB(); // 插入 B 数据 + } catch (Exception e) { + e.printStackTrace(); + } + } +} +``` + +如果 insertB() 内部抛了异常,被 insertA() 方法 catch 了该异常,则该事务不能正常回滚。 + +这是因为当 ServiceB 中抛出异常后,ServiceB 标识当前事务需要 rollback 。但是ServiceA 中由于捕获这个异常并进行处理,ServiceA 认为当前事务应该正常commit ,会抛出UnexpectedRollbackException 异常。 + +Spring 的事务是在调用业务方法之前开始的,业务方法执行完毕之后才执行 commit 或者 rollback,事务是否执行取决于是否抛出运行时异常。如果抛出运行时异常并在业务方法中没有 catch 到的话,事务会回滚。 + +解决该事务失效问题: + +在业务方法中一般不需要 catch 异常,如果非要 catch 则一定要抛出运行时异常,或者注解中指定抛异常类 @Transactional(rollbackFor=Exception.class)。 + +#### 3. 数据库引擎不支持事务 + +事务能否生效数据库引擎是否支持事务是关键。 + +常用的 MySQL 数据库默认使用支持事务的InnoDB 存储引擎。如果数据库引擎切换成不支持事务的 MyIsam,那么事务就从根本上失效了。 + +### 使用注意事项 + +- 正确的设置 @Transactional 的 rollbackFor 和 propagation 属性,否则事务可能会回滚失败 +- @Transactional 注解只有作用到 public 方法上事务才生效,不推荐在接口上使用 +- 避免同一个类中调用 @Transactional 注解的方法,这样会导致事务失效 diff --git "a/docs/Spring/6_Spring\344\270\255\347\224\250\345\210\260\347\232\204\350\256\276\350\256\241\346\250\241\345\274\217.md" "b/docs/Spring/6_Spring\344\270\255\347\224\250\345\210\260\347\232\204\350\256\276\350\256\241\346\250\241\345\274\217.md" new file mode 100644 index 00000000..4eb9f6e4 --- /dev/null +++ "b/docs/Spring/6_Spring\344\270\255\347\224\250\345\210\260\347\232\204\350\256\276\350\256\241\346\250\241\345\274\217.md" @@ -0,0 +1,102 @@ +# Spring 中涉及到的设计模式 + +## 工厂模式 + +Spring 使用工厂模式可以通过 BeanFactory 或 ApplicationContext 创建 Bean。 + +```java +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.FileSystemXmlApplicationContext; + +public class App { + public static void main(String[] args) { + ApplicationContext context = new FileSystemXmlApplicationContext( + "xxxx"); + + context.getBean("xxx"); // 使用 ApplicationContext 创建 Bean + } +} +``` + +## 单例模式 + +Spring 通过 ConcurrentHashMap 实现单例注册表的特殊方式实现单例模式: + +```java +// 通过 ConcurrentHashMap(线程安全) 实现单例注册表 +private final Map singletonObjects = new ConcurrentHashMap(64); + +public Object getSingleton(String beanName, ObjectFactory singletonFactory) { + Assert.notNull(beanName, "'beanName' must not be null"); + synchronized (this.singletonObjects) { + // 检查缓存中是否存在实例 + Object singletonObject = this.singletonObjects.get(beanName); + if (singletonObject == null) { + //... + try { + singletonObject = singletonFactory.getObject(); + } + //... + // 如果实例对象不不存在,那么注册到单例注册表中。 + addSingleton(beanName, singletonObject); + } + return (singletonObject != NULL_OBJECT ? singletonObject : null); + } + } + // 将对象添加到单例注册表 + protected void addSingleton(String beanName, Object singletonObject) { + synchronized (this.singletonObjects) { + this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT)); + + } + } +} +``` + +Spring 实现单例的方式: + +- XML 配置方式: + + ```xml + + ``` + +- 注解方式: + + ```java + @Service + @Scope("singleton") + public class TestServiceImpl{ + } + ``` + +## 原型模式 + +Spring 实现原型方式: + +Spring 实现单例的方式: + +- XML 配置方式: + + ```xml + + ``` + +- 注解方式: + + ```java + @Service + @Scope("prototype") + public class TestServiceImpl{ + } + ``` + +## 代理模式 + +Spring AOP、Spring 事务管理都大量运用了代理模式。 + +## 适配器模式 + +Spring AOP 的增强或通知 (Advice) 使用到了适配器模式,与之相关的接口是 AdvisorAdapter 。Advice 常用的类型有:BeforeAdvice(前置通知)、AfterAdvice(后置通知)等,每个类型 Advice 都有对应的拦截器:MethodBeforeAdviceInterceptor、AfterReturningAdviceAdapter。Spring 预定义的 Advice 要通过对应的适配器,适配成 MethodInterceptor 接口(方法拦截器)类型的对象,例如 MethodBeforeAdviceInterceptor 负责适配 MethodBeforeAdvice。 + +Spring MVC 中的 DispatcherServlet 根据请求信息调用 HandlerMapping,解析请求对应的 Handler。解析到对应的 Handler 后,开始由 HandlerAdapter 处理。HandlerAdapter 作为期望接口,具体的适配器实现类用于对目标类进行适配,Controller 作为需要适配的类。 diff --git a/docs/Spring/7_MyBatis.md b/docs/Spring/7_MyBatis.md new file mode 100644 index 00000000..9c7e4777 --- /dev/null +++ b/docs/Spring/7_MyBatis.md @@ -0,0 +1,123 @@ +# Mybatis 基本原理 + +MyBatis 是 Apache 的一个 Java 开源项目,是一款优秀的持久层框架。 + +支持定制化 SQL、存储过程以及高级映射。Mybatis 可将 SQL 语句配置在 XML 文件中,避免将 SQL 语句硬编码在 Java 类中。MyBatis 具有如下特点: + +- 通过参数映射方式,可以将参数灵活的配置在 SQL 语句中的配置文件中,避免在 Java 类中配置参数(JDBC) +- 通过输出映射机制,将结果集的检索自动映射成相应的 Java 对象,避免对结果集手工检索(JDBC) +- Mybatis 通过 XML 配置文件对数据库连接进行管理 + +## 核心类 + +### SqlSession + +SqlSession 相当于一个会话,每次访问数据库都需要这样一个会话,类似 JDBC 中的 Connection,但是现在几乎所有的连接都是使用的连接池技术,用完后直接归还而不会像 SqlSession 一样销毁。 + +注意 SqlSession 不是线程安全的,应该设置为线程私有。每次创建的 SqlSession 都必须及时关闭它,如果 SQLSession 长期存在就会使数据库连接池的活动资源减少,对系统性能的影响很大,可以在 finally 代码块中将其关闭。此外 SqlSession 存活于一个应用的请求和操作,可以执行多条 SQL语句,保证事务的一致性。 + +SqlSession 在执行过程中,包括以下对象: + +- **Executor** + + 即执行器,负责调度 StatementHandler、ParameterHandler、ResultSetHandler 等来执行对应的 SQL 语句。 + +- **StatementHandler** + + 使用数据库的 Statement(PreparedStatement)执行操作。 + +- **ParamentHandler** + + 来处理 SQL 参数。 + +- **ResultSetHandler** + + 封装并处理数据集。 + +### SqlSessionFactory + +SqlSessionFactory 用于创建 SqlSession。每次应用程序访问数据库时, 需要通过 SqlSessionFactory 创建 SqlSession,显然 SqlSessionFactory 和整个 Mybatis 的生命周期是相同的。 + +SqlSessionFactory 是线程安全的,应用运行迁建不要重复创建多次,因为创建多个可能为消耗尽数据库的连接资源,建议使用单例模式。 + +### SqlSessionaFactoryBuilder + +SqlSessionaFactoryBuilder 用于创建 SqlSessionFactory,该类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域。 + +### Mapper + +Mapper 接口的实例是从 SqlSession 中获取,作用是发送 SQL,然后返回我们需要的结果,或者执行 SQL 从而更改数据库的数据,因此它应该在 SqlSession 的事务方法之内。 + +在 Spring 管理的 Bean 中, Mapper 是单例的。 + +## 功能架构 + +
+ +### 接口层 + +提供给外部使用的接口 API,开发人员通过这些本地 API 来操作数据库。 + +接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。 + +### 数据处理层 + +负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要功能是根据调用的请求完成一次数据库操作。 + +- 加载配置:配置来源有配置文件或者注解,将 SQL 的配置信息加载成为一个个 MappedStatement 对象(包括了传入参数映射配置、执行的 SQL 语句、结果映射配置),存储在内存中。 +- SQL解析:当 API 接口层接收到调用请求时,会接收到传入 SQL 的 ID 和传入对象(可以是 Map、JavaBean 或者基本数据类型),Mybatis 会根据 SQL 的 ID 找到对应的 MappedStatement,然后根据传入参数对象对MappedStatement 进行解析,解析后可以得到最终要执行的 SQL 语句和参数。 +- SQL执行:将最终得到的 SQL 和参数拿到数据库进行执行,得到操作数据库的结果。 +- 结果映射:将操作数据库的结果按照映射的配置进行转换,可以转换成HashMap、JavaBean 或者基本数据类型,并将最终结果返回。 + +### 基础支撑层 + +负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。 + +## 执行流程 + +- Mybatis 配置 + + sqlMapConfig.xml 文件是 Mybatis 的全局配置文件,配置了 Mybatis的运行环境等信息。Mapper.xml 文件即 SQL 映射文件,文件中配置了操作数据库的 SQL 语句,需要在 sqlMapConfig.xml 中加载。 + +- 通过 Mybatis 环境等配置信息构造 SqlSessionFactory + +- 通过 SqlSessionFactory 创建 SqlSession,操作数据库需要通过 SqlSession进行。 + +- Mybatis 自定义了 Executor 执行器操作数据库,Executor 接口有两个实现,一个是基本执行器、一个是缓存执行器。 + +- MappedStatement 是 Mybatis 一个底层封装对象,包装 Mybatis 配置信息及 SQL 映射信息等。Mapper.xml 文件中一个 SQL 对应一个 MappedStatement对象,SQL 的 id 即 MappedStatement 的 id。 + +- MappedStatement 对 SQL 执行输入参数进行定义,包括 HashMap、基本类型、pojo,Executor 通过MappedStatement 在执行 SQL 前将输入的 Java 对象映射至 SQL 中,输入参数映射就是 jdbc 编程中对preparedStatement 设置参数。 + +- MappedStatement 对 SQL 执行输出结果进行定义,包括 HashMap、基本类型、pojo,Executor 通过MappedStatement 在执行 SQL 后将输出结果映射至 Java对象中,输出结果映射过程相当于 jdbc 编程中对结果的解析处理过程。 + +
+ +## 缓存机制 + +MyBatis 的缓存分为一级缓存和二级缓存。默认情况下一级缓存是开启的。 + +
+ +### 一级缓存 + +一级缓存指 SqlSession 级别的缓存,在同一个 SqlSession 中执行相同的 SQL 语句查询时将查询结果集缓存。一级缓存最多能缓存 1024 条 SQL 语句。 + +当客户端第一次发出一个 SQL 查询语句时,MyBatis 执行 SQL 查询并将查询结果写入 SqlSession 的一级缓存,当第二次有相同的 SQL 查询语句时,则直接从缓存中获取。当同一个 SqlSession 多次发出相同的 SQL 查询语句时,MyBatis 直接从缓存中获取数据。如果两次查询中出现 commit 操作(新增、删除、修改),则认为数据发生了变化,Mybaits 会把该 SqlSession 中的一级缓存区域全部清空,当下次再到缓存中查找时将找不到对应的缓存数据,因此需要再次从数据库中查询数据并将查询的结果写入缓存。 + +### 二级缓存 + +二级缓存指跨 SqlSession 的缓存,即 Mapper 级别的缓存。在 Mapper 级别的缓存内,不同的 SqlSession 缓存可以共享。 + +Mapper 以命名空间为单位创建缓存数据结构,数据结构是 Map 类型,Map 中 Key 为`MapperId + Offset + Limit + SQL + 所有入参`。开启二级缓存后,会使用 CachingExecutor 装饰 Executor ,在进入一级缓存的查询流程前,先在 CachingExecutor 进行二级缓存的查询。 + +开启二级缓存需要做一下配置: + +- 在全局配置中启用二级缓存配置。 +- 在对应的 Mappper.xml 中配置 Cache 节点。 +- 在对应的 select 查询节点中添加 userCache=true。 + +# MyBatis 面试题 + +- [MyBatis 面试题集锦](https://snailclimb.gitee.io/javaguide/#/docs/system-design/framework/mybatis/mybatis-interview) +- [Mybatis 的常见面试题](https://blog.csdn.net/eaphyy/article/details/71190441) diff --git "a/docs/SpringBoot/1_SpringBoot\346\246\202\350\277\260.md" "b/docs/SpringBoot/1_SpringBoot\346\246\202\350\277\260.md" new file mode 100644 index 00000000..678f2770 --- /dev/null +++ "b/docs/SpringBoot/1_SpringBoot\346\246\202\350\277\260.md" @@ -0,0 +1,65 @@ +# Spring Boot 概述 + +## Spring Boot 特点 + +SpringBoot 是由 pivotal 团队开发的全新的Spring 框架,其设计初衷是**简化 Spring 应用复杂的搭建和开发过程**。该框架提供了一套简单的 Spring 模块依赖和管理工具,从而避免了开发人员处理复杂的模块依赖和版本冲突问题,同事提供打包即可用 Web 服务。 + +SpringBoot 特点如下: + +- 快速创建独立的 Spring 应用程序 +- 嵌入 Tomcat Web 容器,可快速部署 +- 自动配置 JAR 包依赖和版本控制,简化 Maven 配置 +- 自动装配 Spring 实例,不需要 XML 配置 +- 提供诸如性能指标、健康检查、外部配置等线上监控和配置功能。 + +## Spring Boot Application Starters + +Starters 是一组**资源依赖描述**,用于为不同的 SpringBoot 应用提供一站式服务,而不必像传统的 Spring 项目那样,需要开发人员处理服务和服务之间的复杂依赖关系。例如,如果需要开发 REST 服务或 Web 应用程序时,只需只需添加一个 **spring-boot-starter-web** 依赖就可以了,具体的依赖细节由 Starters 统一处理,不需要应用程序分别处理各个 Jar 包之间的依赖关系。 + +```html + + + org.springframework.boot + spring-boot-starter-web + +``` + +## Spring Boot 支持的内嵌 Servlet 容器 + +Spring Boot 支持的内嵌 Servlet 容器有: + +- Tomcat 9.0 +- Jetty 9.4 +- Undertow 2.0 + +Spring Boot ( spring-boot-starter-web) 使用 Tomcat 作为默认的嵌入式 Servlet 容器,如果需要使用 Jetty 的话需要修改 pom.xml (Maven) 或者 build.gradle (Gradle) 文件配置。 + +```html + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + + + org.springframework.boot + spring-boot-starter-jetty + +``` + +```html +// 修改 build.gradle 文件配置(看上去更加简洁) +compile("org.springframework.boot:spring-boot-starter-web") { + exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat' +} +compile("org.springframework.boot:spring-boot-starter-jetty") +``` + diff --git "a/docs/SpringBoot/2_\351\205\215\347\275\256\346\226\207\344\273\266.md" "b/docs/SpringBoot/2_\351\205\215\347\275\256\346\226\207\344\273\266.md" new file mode 100644 index 00000000..92f4732c --- /dev/null +++ "b/docs/SpringBoot/2_\351\205\215\347\275\256\346\226\207\344\273\266.md" @@ -0,0 +1,214 @@ +# 配置文件 + +## YAML 文件 + +我们可以通过 application.properties / application.yml 对 Spring Boot 程序进行简单的配置。 + +YAML 是 "YAML Ain't a Markup Language"(YAML 不是一种标记语言)的递归缩写,是一种人类可读的数据序列化语言。相比于 properties 文件,YAML 配置的方式更加直观清晰,简介明了,有层次感。 + +properties 配置方式: + +```html +server.port=8330 +spring.dubbo.applicaiton.name=dubbo-consumer +spring.dubbo.applicaiton.registry=zookeeper:127.0.0.1:2181 +``` + +yaml 配置方式: + +```html +server: + prot:8330 +spring: + dubbo: + application: + name:dubbo-consumer + registry:zookeeper:127.0.0.1:2181 +``` + +但是 YAML 配置的方式有一个缺点,那就是不支持 @PropertySource 注解导入自定义的 YAML 配置。 + +## 读取配置文件 + +### @Value 方式 + +```java +@Value("${xxx}") +String xxx; +``` + +根据传入的 xxx 获取相应的值,@Value 方式一般同于读取比较简单的配置信息,但是并不建议使用。 + +### @ConfigurationProperties 方式 + +定义一个 LibraryProperties 类: + +```java +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; + +import java.util.List; + +// 使用 @Component 注解声明,可以像使用普通 Bean 一样将其注入到类中使用。 +@Component +@ConfigurationProperties(prefix = "library") +@Setter +@Getter +@ToString +class LibraryProperties { + private String location; + private List books; + + @Setter + @Getter + @ToString + static class Book { + String name; + String description; + } +} +``` + + + +```java +package cn.javaguide.readconfigproperties; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ReadConfigPropertiesApplication implements InitializingBean { + + // 注入使用 + private final LibraryProperties library; + + public ReadConfigPropertiesApplication(LibraryProperties library) { + this.library = library; + } + + public static void main(String[] args) { + SpringApplication.run(ReadConfigPropertiesApplication.class, args); + } + + @Override + public void afterPropertiesSet() { + System.out.println(library.getLocation()); + System.out.println(library.getBooks()); + } +} +``` + +### @ConfigurationProperties 方式 + +@ConfigurationProperties 方式不仅可以读取数据,还可以对读取的数据进行校验。 + +```html +user: + name: xxx + email: 123455@ +``` + +```java +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; +import org.springframework.validation.annotation.Validated; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotEmpty; + +// 注意:这里 ProfileProperties 类没有加 @Component 注解。 +@Getter +@Setter +@ToString +@ConfigurationProperties("user") +@Validated +public class ProfileProperties { + @NotEmpty + private String name; + + @Email + @NotEmpty + private String email; + + // 配置文件中没有读取到的话就用默认值 + private Boolean handsome = Boolean.TRUE; +} +``` + +```java +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; + +@SpringBootApplication +// 使用 @EnableConfigurationProperties 注册我们的配置 Bean。 +@EnableConfigurationProperties(ProfileProperties.class) +public class ReadConfigPropertiesApplication implements InitializingBean { + private final ProfileProperties profileProperties; + + public ReadConfigPropertiesApplication(ProfileProperties profileProperties) { + this.profileProperties = profileProperties; + } + + public static void main(String[] args) { + SpringApplication.run(ReadConfigPropertiesApplication.class, args); + } + + @Override + public void afterPropertiesSet() { + System.out.println(profileProperties.toString()); + } +} +``` + +邮箱格式不正确,所以程序运行的时候就报错,也就保证了数据类型的安全性。 + +### @PropertySource 方式 + +```java +import lombok.Getter; +import lombok.Setter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Component; + +@Component +// @PropertySource 注解方式读取指定的 .properties 文件 +@PropertySource("classpath:website.properties") +@Getter +@Setter +class WebSite { + @Value("${url}") + private String url; +} +``` + +使用: + +```java +@Autowired +private WebSite webSite; + +System.out.println(webSite.getUrl()); +``` + +## Spring Boot 加载配置文件的优先级 + +Spring 读取配置文件也是有优先级的,其中: + +- 项目目录下的 config 目录下的 applicaiton.yml 的优先级最高; +- resources 目录下的 config 目录下的 application.yml 的优先级次之; +- resources 目录下的 application.yml 优先级最低。 + +
\ No newline at end of file diff --git "a/docs/SpringBoot/3_\345\270\270\347\224\250\346\263\250\350\247\243.md" "b/docs/SpringBoot/3_\345\270\270\347\224\250\346\263\250\350\247\243.md" new file mode 100644 index 00000000..aa907675 --- /dev/null +++ "b/docs/SpringBoot/3_\345\270\270\347\224\250\346\263\250\350\247\243.md" @@ -0,0 +1,102 @@ +# 常用注解 + +## @SpringBootApplication + +Spring Boot 项目的基石,创建 Spring Boot 项目之后会默认在主类加上。 + +@SpringBootApplication 可以认为是 **@Configuration** 、**@EnableAutoConfiguration** 和 **@ComponentScan** 注解的集合。这三个注解的具体作用如下: + +- @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制 +- @ComponentScan: 扫描被 @Component / @Service / @Controller 注解的 Bean,注解默认会扫描该类所在的包下所有的类。 +- @Configuration:允许在 Spring 上下文中注册额外的 Bean 或导入其他配置类 + +@EnableAutoConfiguration 注解是启动自动配置的关键,其源码如下: + +```java +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.springframework.context.annotation.Import; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@AutoConfigurationPackage +@Import({AutoConfigurationImportSelector.class}) +public @interface EnableAutoConfiguration { + String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; + + Class[] exclude() default {}; + + String[] excludeName() default {}; +} +``` + +@EnableAutoConfiguration 注解通过 Spring 提供的 @Import 注解导入 AutoConfigurationImportSelector 类。AutoConfigurationImportSelector 类中 getCandidateConfigurations 方法会将所有自动配置类的信息以 List 的形式返回,这些配置信息会被 Spring 容器当作 Bean 来进行管理。 + +```java + protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { + List configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), + getBeanClassLoader()); + Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + + "are using a custom packaging, make sure that file is correct."); + return configurations; + } +``` + +补充:[SpringBoot 自动装配原理](https://www.cnblogs.com/javaguide/p/springboot-auto-config.html) + + + +## Spring Bean 相关注解 + +- Autowired + + 自动导入对象到类中,被注入进的类同样要被 Spring 容器管理。 + +- @RestController + + @RestController 注解是 @Controller和 @ResponseBody 的合集,表示这是个控制器 Bean,并且是将函数的返回值直 接填入 HTTP 响应体中,是 REST 风格的控制器。 + +- @Component + + 可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用 @Component 注解标注。 + +- @Repository + + 对应持久层即 Dao 层,主要用于数据库相关操作。 + +- @Service + + 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。 + +- @Controller + + 对应 Spring MVC 控制层,主要用于接受用户请求并调用 Service 层返回数据给前端页面。 + + + +## 处理常见的 HTTP 请求类型 + +- @GetMapping:GET 请求 +- @PostMapping:POST 请求 +- @PutMapping:PUT 请求 +- @DeleteMapping:DELETE 请求 + + + +## 前后端传值 + +- @RequestParam & @Pathvairable + + @PathVariable用于获取路径参数; + + @RequestParam用于获取查询参数。 + +- @RequestBody + + 用于读取 request 请求(可能是 POST、PUT、DELETE、GET 请求)的 Body 部分并且 Content-Type 为 application/json 格式的数据,接收到数据之后会自动将数据绑定到 Java 对象上去。系统会使用HttpMessageConverter 或者自定义的 HttpMessageConverter 将请求的 Body 中的 json 字符串转换为 java 对象。 \ No newline at end of file diff --git "a/docs/SpringBoot/4_\350\257\267\346\261\202\345\217\202\346\225\260\346\240\241\351\252\214.md" "b/docs/SpringBoot/4_\350\257\267\346\261\202\345\217\202\346\225\260\346\240\241\351\252\214.md" new file mode 100644 index 00000000..10867edb --- /dev/null +++ "b/docs/SpringBoot/4_\350\257\267\346\261\202\345\217\202\346\225\260\346\240\241\351\252\214.md" @@ -0,0 +1,121 @@ +# 请求参数校验 + +我们需要对传入后端的数据再进行一遍校验,避免用户绕过浏览器直接通过一些 HTTP 工具直接向后端请求一些违法数据。Spring Boot 程序做请求参数校验,需要引入 **spring-boot-starter-web** 依赖。 + +## 常用校验注解 + +### JSR 校验注解 + +- @Null + + 被注释的元素必须为 null + +- @NotNull + + 被注释的元素必须不为 null + +- @AssertTrue + + 被注释的元素必须为 true + +- @AssertFalse + + 被注释的元素必须为 false + +- @Min(value) + + 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 + +- @Max(value) + + 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 + +- @DecimalMin(value) + + 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 + +- @DecimalMax(value) + + 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 + +- @Size(max=, min=) + + 被注释的元素的大小必须在指定的范围内 + +- @Digits (integer, fraction) + + 被注释的元素必须是一个数字,其值必须在可接受的范围内 + +- @Past + + 被注释的元素必须是一个过去的日期 + +- @Future + + 被注释的元素必须是一个将来的日期 + +- @Pattern(regex=,flag=) + + 被注释的元素必须符合指定的正则表达式 + +### Hibernate Validator 校验注解 + +- @NotBlank(message =) + + 验证字符串非 null,且长度必须大于 0 + +- @Email + + 被注释的元素必须是电子邮箱地址 + +- @Length(min=,max=) + + 被注释的字符串的大小必须在指定的范围内 + +- @NotEmpty + + 被注释的字符串的必须非空 + +- @Range(min=,max=,message=) + + 被注释的元素必须在合适的范围内 + +## 验证请求参数 Path Variables 和 Request Parameters + +```java +@RestController +@RequestMapping("/api") +// 使用 @Validated 注解,告诉 Spring 去校验方法参数。 +@Validated +public class PersonController { + + @GetMapping("/person/{id}") + // 校验请求参数 Path Variables + public ResponseEntity getPersonByID(@Valid @PathVariable("id") @Max(value = 5,message = "超过 id 的范围了") Integer id) { + return ResponseEntity.ok().body(id); + } + + @PutMapping("/person") + // 校验请求参数 Request Parameters + public ResponseEntity getPersonByName(@Valid @RequestParam("name") @Size(max = 6,message = "超过 name 的范围了") String name) { + return ResponseEntity.ok().body(name); + } +} +``` + +## 验证请求体 + +在需要验证的参数上加上了@Valid 注解,如果验证失败,它将抛出 MethodArgumentNotValidException。默认情况下,Spring 会将此异常转换为 HTTP Status 400(错误请求)。 + +```java +@RestController +@RequestMapping("/api") +public class PersonController { + + @PostMapping("/person") + public ResponseEntity getPerson(@RequestBody @Valid Person person) { + return ResponseEntity.ok().body(person); + } +} +``` + diff --git "a/docs/SpringBoot/5_\345\205\250\345\261\200\345\274\202\345\270\270\345\244\204\347\220\206.md" "b/docs/SpringBoot/5_\345\205\250\345\261\200\345\274\202\345\270\270\345\244\204\347\220\206.md" new file mode 100644 index 00000000..2fba31ac --- /dev/null +++ "b/docs/SpringBoot/5_\345\205\250\345\261\200\345\274\202\345\270\270\345\244\204\347\220\206.md" @@ -0,0 +1,3 @@ +# 全局异常处理 + +待补充。 \ No newline at end of file diff --git "a/docs/SpringBoot/6_\345\256\236\347\216\260\345\256\232\346\227\266\344\273\273\345\212\241.md" "b/docs/SpringBoot/6_\345\256\236\347\216\260\345\256\232\346\227\266\344\273\273\345\212\241.md" new file mode 100644 index 00000000..0d093d60 --- /dev/null +++ "b/docs/SpringBoot/6_\345\256\236\347\216\260\345\256\232\346\227\266\344\273\273\345\212\241.md" @@ -0,0 +1,22 @@ +# 实现定时任务 + +基于 SpringBoot 的 @Scheduled 注解就能很方便地创建一个定时任务。 + +```java +@Component +public class ScheduledTasks { + private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class); + private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss"); + + /** + * fixedRate:即固定速率执行,单位是毫秒,这里 5000 表示每 5 秒执行一次。 + */ + @Scheduled(fixedRate = 5000) + public void reportCurrentTimeWithFixedRate() { + log.info("Current Thread : {}", Thread.currentThread().getName()); + log.info("Fixed Rate Task : The time is now {}", dateFormat.format(new Date())); + } +} +``` + +然后需要在启动类中加上 @EnableScheduling 注解,这样才可以启动定时任务。@EnableScheduling 注解的作用是发现注解 @Scheduled 的任务并在后台执行该任务。 \ No newline at end of file diff --git a/docs/SpringBoot/8_SpringBoot.md b/docs/SpringBoot/8_SpringBoot.md new file mode 100644 index 00000000..8ccf0907 --- /dev/null +++ b/docs/SpringBoot/8_SpringBoot.md @@ -0,0 +1,51 @@ +# SpringBoot + +## 简介 + +SpringBoot 是由 pivotal 团队开发的全新的Spring 框架,其设计初衷是简化 Spring 应用复杂的搭建和开发过程。该框架提供了一套简单的 Spring 模块依赖和管理工具,从而避免了开发人员处理复杂的模块依赖和版本冲突问题,同事提供打包即可用 Web 服务。 + +SpringBoot 特点如下: + +- 快速创建独立的 Spring 应用程序 +- 嵌入 Tomcat Web 容器,可快速部署。 +- 自动配置 JAR 包依赖和版本控制,简化 Maven 配置 +- 自动装配 Spring 实例,不需要 XML 配置 +- 提供诸如性能指标、健康检查、外部配置等线上监控和配置功能。 + +## 常用注解 + +### @SpringBootApplication + +Spring Boot 项目的基石,创建 SpringBoot 项目之后会默认在主类加上。 + +@SpringBootApplication 可以认为是 @Configuration 、@EnableAutoConfiguration 和 @ComponentScan 注解的集合。其中 + +- @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制 +- @ComponentScan: 扫描被 @Component / @Service / @Controller 注解的 Bean,注解默认会扫描该类所在的包下所有的类。 +- @Configuration:允许在 Spring 上下文中注册额外的 Bean 或导入其他配置类 + +### @Value & @ ConfigurationProperties & @PropertySource + +通过 @Value("${property}") 读取比较简单的配置信息; + +通过 @ConfigurationProperties 读取配置信息并与 bean 绑定; + +通过 @PropertySource 读取指定 properties 文件。 + +### @ControllerAdvice & @ExceptionHandler + +@ControllerAdvice:注解定义全局异常处理类 + +@ExceptionHandler:注解声明异常处理方法 + +## 实战 + +- [SpringBoot 官网](https://spring.io/projects/spring-boot/) + +- [SpringBoot 实战](https://snailclimb.gitee.io/springboot-guide/#/) + +# SpringBoot 面试题 + +- [SpringBoot 自动装配原理](https://www.cnblogs.com/javaguide/p/springboot-auto-config.html) + +- [Spring Boot面试题(2020最新版)](https://thinkwon.blog.csdn.net/article/details/104397299?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-2.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-2.no_search_link) diff --git "a/docs/Zookeeper/1_\346\246\202\350\277\260.md" "b/docs/Zookeeper/1_\346\246\202\350\277\260.md" new file mode 100644 index 00000000..ced63e54 --- /dev/null +++ "b/docs/Zookeeper/1_\346\246\202\350\277\260.md" @@ -0,0 +1,51 @@ +# Zookeeper 概述 + +ZooKeeper 是一个开源的**分布式协调服务**,它的设计目标为分布式软件提供一致性服务。 + +ZooKeeper 提供了一个类似 Linux 文件系统的树形结构,Zookeeper 的每个节点既可以是目录也可以是数据,同时 Zookeeper 提供了对每个节点的监控与通知机制。基于 Zookeeper 的一致性服务,可以方便地实现分布式锁、分布式选举、数据发布订阅、配置中心等功能。 + +## 特点 + +- 原子性 + + 所有事务请求的处理结果在整个集群中所有机器上的应用情况是一致的,也就是说,要么整个集群中所有的机器都成功应用了某一个事务,要么都没有应用。 + +- 顺序一致性 + + 从同一客户端发起的事务请求,最终将会严格地按照顺序被应用到 ZooKeeper 中去。 + +- 可靠性 + + 一旦一次更改请求被应用,更改的结果就会被持久化,直到被下一次更改覆盖。 + +- 单一系统映像 + + 无论客户端连到哪一个 ZooKeeper 服务器,其看到的服务端数据模型都是一致的。 + +## 典型应用场景 + +- 统一命名服务 + + 可以通过 ZooKeeper 的顺序节点生成全局唯一 ID。 + +- 分布式锁 + + 通过创建唯一节点获得分布式锁,当获得锁的一方执行完相关代码或者是挂掉之后就释放锁。 + +- 数据发布 / 订阅 + + 通过 **Watcher 机制**可以很方便地实现数据发布 / 订阅。将数据发布到 ZooKeeper 被监听的节点上,其他机器可通过监听 ZooKeeper 上节点的变化来实现配置的动态更新。 + +## 支持的框架 + +- Kafka + + ZooKeeper 主要为 Kafka 提供 Broker 和 Topic 的注册以及多个 Partition 的负载均衡等功能。 + +- HBase + + ZooKeeper 确保 Hbase 整个集群只有一个 Master 以及保存和提供 regionserver 状态信息(是否在线)等功能。 + +- Hadoop + + ZooKeeper 为 NameNode 提供高可用支持。 \ No newline at end of file diff --git "a/docs/Zookeeper/2_\346\225\260\346\215\256\346\250\241\345\236\213.md" "b/docs/Zookeeper/2_\346\225\260\346\215\256\346\250\241\345\236\213.md" new file mode 100644 index 00000000..045b82f4 --- /dev/null +++ "b/docs/Zookeeper/2_\346\225\260\346\215\256\346\250\241\345\236\213.md" @@ -0,0 +1,80 @@ +# Zookeeper 的数据模型 + +ZooKeeper 使用一个树形结构的命令空间表示其数据结构。ZooKeeper 树中的每个节点称为一个 Znode,Znode是 ZooKeeper 中数据的最小单元并且每个 Znode 都有一个唯一的路径标识。**ZooKeeper 主要是用来协调服务的,而不是用来存储业务数据的,一般不要放比较大的数据在 Znode 上,所以 ZooKeeper 给出的上限是每个结点的数据大小最大是 1M。** + +ZooKeeper 的每个节点不经存储了数据信息,同时提供对节点信息监控 (Watch) 等操作。 + +每个节点还可以拥有 N 个子节点,最上层是根节点以“/”来代表,Zookeeper 的数据模型如下图: + +
+ +## Znode 数据模型 + +Znode 兼具文件和目录两种特点:既可以像文件一样保存和维护数据,又可以像目录一样作为路径标识的一部分。每个 Znode 有 3 部分组成: + +- Stat + + 状态信息,用于存储 Znode 的版本、权限和时间戳等信息。 + +- Data + + Znode 中具体存储的数据。 + +- Children + + Znode 子节点的信息描述。 + +Znode 节点虽然可以存储数据,但并不能存储大量数据,主要是用来存储分布式应用中的配置文件、集群状态等元数据信息。 + +## Znode 节点类型 + +Zookeeper 中节点有 3 种:永久节点、临时节点、有序节点。节点的类型在创建时被确定并且不能改变。 + +- 永久节点 + + 一旦创建就一直存在即使 ZooKeeper 集群宕机,直到将其删除。该类节点一般用于存储一些永久性的配置信息。 + +- 临时节点 + + 如果会话结束或者超时临时节点就会消失。此外,**临时节点只能做叶子节点** ,不能创建子节点。 + +- 有序节点 + + 在节点名的后面加一个数字后缀,并且是有序的,例如生成的有序节点为 /lock/node-0000000000,它的下一个有序节点则为 /lock/node-0000000001,以此类推。显然有序节点包括永久有序结点和临时有序节点。 + +## Znode 访问控制 + +### ACL + +每个 Znode 节点都有一个访问控制列表(Access Control List,ACL),该列表规定了用户对节点的访问权限,应用程序可以根据需求将用户分为只读、只写和读写用户。对于 Znode 操作的权限,ZooKeeper 提供了以下 5 种: + +- READ:获取节点数据和列出其子节点 +- WRITE:设置 / 更新节点数据 +- ADMIN: 设置节点 ACL 权限 +- DELETE:删除子节点 +- CREATE:创建子节点 + +对于身份认证,提供了以下几种方式: + +- world: 默认方式,所有用户都可无条件访问。 +- auth:不使用任何 id,代表任何已认证的用户。 +- digest:用户名+密码认证方式。 +- ip:对指定 IP 进行限制。 + +### 原子操作 + +每个 Znode 节点上的数据都具有原子操作的特性,读操作将获取与节点相关的数据,写操作将替换节点上的数据。 + +## Watcher + +Watcher 即事件监听器,是 ZooKeeper 中的一个很重要的特性。 + +ZooKeeper 允许用户在指定节点上注册一些 Watcher,并且在一些特定事件触发的时候,ZooKeeper 服务端会将事件通知到感兴趣的客户端上去,该机制是 ZooKeeper 实现分布式协调服务的重要特性。 + +## Session + +Session 即会话,可以看作是 ZooKeeper 服务器与客户端的之间的一个 TCP 长连接,通过这个连接,客户端能够通过心跳检测与服务器是否保持有效的会话,也能够向 ZooKeeper 服务器发送请求并接受响应,同时还能够通过该连接接收来自服务器的 Watcher 事件通知。 + +Session 有一个属性叫做:sessionTimeout ,即会话的超时时间。当由于服务器压力太大、网络故障或是客户端主动断开连接等各种原因导致客户端连接断开时,只要在 sessionTimeout 规定的时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话仍然有效。 + +另外,在为客户端创建会话之前,服务端首先会为每个客户端都分配一个 sessionID。由于 sessionID是 ZooKeeper 会话的一个重要标识,许多与会话相关的运行机制都是基于这个 sessionID 的,因此无论是哪台服务器为客户端分配的 sessionID,都必须是全局唯一的。 \ No newline at end of file diff --git "a/docs/Zookeeper/3_\345\216\237\347\220\206.md" "b/docs/Zookeeper/3_\345\216\237\347\220\206.md" new file mode 100644 index 00000000..1c7db949 --- /dev/null +++ "b/docs/Zookeeper/3_\345\216\237\347\220\206.md" @@ -0,0 +1,152 @@ +# Zookeeper 原理 + +## ZooKeeper 集群的角色 + +Zookeeper 是一个基于主从复制的高可用集群。通常 3 台服务器就可构成一个 ZooKeeper 集群。ZooKeeper 官方提供的架构图就是一个 ZooKeeper 集群整体对外提供服务。 + +
+ +每一个 Server 代表一个安装 ZooKeeper 服务的服务器,组成 ZooKeeper 服务的服务器都会在内存中维护当前的服务器状态,并且每台服务器之间都互相保持着通信。 + +Zookeeper 的角色包括 Leader、Follower 和 Observer。 + +### Leader + +一个运行中的 Zookeeper 集群只有一个 Leader 服务,Leader 服务主要有 2 个职责: + +- 负责集群数据的写操作 +- 发起并维护各个 Follower 及 Observer 之间的心跳以监听集群的运行状态。 + +Zookeeper 集群中所有写操作都必须经过 Leader,只有 Leader 写操作完成后,才将写操作广播到其他 Follower。只有超过半数节点(不包括 Observer 节点)写入成功时,该写请求才算写成功。 + +### Follower + +一个运行中的 Zookeeper 集群只有一个 Follower,Follower 通过心跳和 Leader 保持连接。Follower 服务主要有 2 个职责: + +- 负责集群数据的读操作 +- 参与集群的 Leader 选举 + +Follower 在接收到一个客户端请求后会先判断该请求是读请求还是写请求,如果是读请求,那么 Follower 从本地节点上读取数据并返回给客户端;如果是写请求,那么 Follower 会将写请求转发给 Leader 来处理;此外,如果 Leader 失效, Follower 需要在集群选举时进行投票。 + +### Observer + +一个运行中的 Zookeeper 集群只有一个 Observer,Observer 的主要职责是负责集群数据的读操作。Observer 与 Follower 的功能类似,但是 Observer 无投票权。 + +Zookeeper 集群在运行过程中需要支持更多的客户端并发操作,就需要更多的服务实例,而更多的服务实例会使集群投票阶段变得复杂,集群选主时间过程,不利于集群故障快速恢复。因此,Zookeeper 中引入 Observer,Observer 不参与投票,但是接收来自客户端的连接并响应客户端的读请求,将写请求转发给 Leader。加入更多的 Observer节点,不仅提高了 Zookeeper 集群的吞吐量,也保障了系统的稳定性。 + +
+ +## ZAB 协议 + +ZAB(ZooKeeper Atomic Broadcast) 协议即 Zookeeper 原子广播协议。 + +在 ZooKeeper 中,主要依赖 ZAB 协议来实现分布式数据一致性,基于该协议,ZooKeeper 实现了一种主备模式的系统架构来保持集群中各个副本之间的数据一致性。 + +### Epoch & Zxid + +Epoch 即**当前集群的周期号(年代号)**,集群的每次 Leader 变更都会产生一个新的周期号,周期号的产生规则是在上一周期号的基础上加 1,当之前的 Leader 崩溃恢复后会发现自己的周期号比当前的周期号小,说明此时集群已经产生了新的 Leader,旧的 Leader 会再次以 Follower 的角色进入集群。 + +Zxid 即 ZAB 协议的事务编号,是一个 64 bit 的数据,其中低 32 bit 是一个单调递增的计数器,对于客户端的每一个事务请求,计数器都加 1,高 32 bit 存储 Epoch。每次选举产生一个新的 Leader 时,该 Leader 都会从当前服务器日志中取出最大的 Zxid,获取其中搞 32 bit 的 Epoch 值并加 1,作为新的 Epoch,并将低 32 bit 从 0 开始计数。Zxid 用于标识一次提议的 id,为了保证顺序性,Zxid 必须单调递增。 + +### ZAB 协议的 2 种模式 + +ZAB 协议有 2 种模式:恢复模式和广播模式。 + +- 恢复模式(集群选主) + + 在集群启动、集群重启或者 Leader 奔溃后,集群将开始选主,该过程即为恢复模式。 + +- 广播模式(数据同步) + + 当 Leader 被选举出来后,Leader 将最新的集群状态广播给其他 Follower,该过程即为广播模式。在半数以上是我 Follower 完成与 Leader 状态同步后,广播模式结束。这里的**状态同步是指数据同步**,用来保证集群中存在过半的机器能够和 Leader 的数据状态保持一致。 + +### ZAB 协议的 4 个阶段 + +#### 1. 选举阶段 (Leader Election) + +在集群选举开始时,所有节点都处于选举阶段。当某一个节点的票数查过半数节点后,该节点将被当选为**准 Leader** 。 + +只有到达广播阶段后,准 Leader 才会成为真正的 Leader。 + +选举阶段的目的是产生一个准 Leader。 + +#### 2. 发现阶段 (Discovery) + +在发现阶段,Followers 开始和准 Leader 进行通信,同步 Followers 最近接收的事务提议。此时,准 Leader 会产生一个新的 Epoch,并尝试让 Followers 接收该 Epoch 后再更新到本地。 + +发现阶段的一个 Follower 只会连接一个 Leader,如果节点 1 认为节点 2 是 Leader,那么节点 1 就是就会尝试连接节点 2,若连接被拒绝,则集群会重新进入选举阶段。 + +发现阶段的主要目的是发现大多数节点接收的最新提议。 + +#### 3. 同步阶段 (Synchronization) + +同步阶段主要是利用 Leader 前一阶段获得的最新提议信息同步到集群中所有副本,只有当半数以上的节点都同步完成后,准 Leader 才会成为真正的 Leader。 + +同步阶段完成后集群选主操作才完成,新的 Leader 将产生。 + +#### 4. 广播阶段 (Broadcast) + +到了广播阶段,ZooKeeper 集群才能正式对外提供事务服务,并且 Leader 进行消息广播,将其上的状态通知到其他 Follower。如果有新的节点加入,那么 Leader 需要对新节点进行状态同步。 + +ZAB 协议的 Java + +## Zookeeper 选举机制和流程 + +### Zookeeper 选举机制 + +每个 Server 都可提议自己是 Leader,并为自己投票,然后将投票结果与其他 Server 的选票进行对比,权重大的胜出。具体选举过程如下: + +- 每个 Server 启动后都询问其他 Server 给谁投票,其他 Server 根据自己的状态回复自己推荐的 Leader 返回对应的 Leader id 和 Zxid。在集群初次启动时,每个 Server 都会推荐自己为 Leader。 +- 当 Server 收到所有其他 Server 回复后,计算出 Zxid 最大的 Server,并将该 Server 设置成下一次要投票的 Server。 +- 计算过程中票数最多的 Server 将成为获胜者,如果获胜者的票数超过集群个数的一半,则该 Server 就被推选为 Leader。否则继续投票,直到 Leader 被选举出来。 +- Leader 等待其他 Server 连接。 +- Follower 连接 Leader,将最大的 Zxid 发送给 Leader。 +- Leader 根据 Follower 的 Zxid 确定同步点,至此,选举阶段完成。 + +在选举阶段完成后,Leader 通知其他 Follower,集群已成为 Uptodate 状态,Follower 收到 Uptodate 消息后,接收客户端请求开始对外提供服务。 + +### ZAB 协议的 Java 实现 + +ZAB 协议的 Java 实现与其定义略有不同,在实际实现时,选举阶段采用 Fast Leader Election 模式。在该模式下,节点首先向所有 Server 提议自己要成为 Leader,当其它 Server 收到提议后,判断 Epoch 信息并接受对方的提议,然后向对方发送接收提议完成的消息;同时,在 Java 的实现过程中将发现阶段和同步阶段合并为恢复阶段(Recovery)。因此,ZAB 协议的 Java 实现只有 3 个阶段:Fast Leader Election、Recovery 和 Broadcast。 + +### 具体选举过程 + +以 5 台 Server 选主为例: + +- S1 启动 + + S1 提议自己为 Leader 并为自己投票,然后将投票结果发送给其他 Server,此时其他 Server 还未启动,因此收不到任何反馈信息。S1 就处于 Looking 状态。 + +- S2 启动 + + S2 提议自己为 Leader 并为自己投票,然后与 S1 交换投票结果,由于 S2 的 Zxid 大于 S1 的 Zxid,S2 胜出,但是投票未过半,S1 和 S2 均处于 Looking 状态。 + +- S3 启动 + + S3 提议自己为 Leader 并为自己投票,然后与 S1、S2 交换投票结果,由于 S3 的 Zxid 值最大,所以 S3 胜出。此时 S3 的票数过半,S3 就是 Leader,S1 和 S2 成为 Follower。 + +- S4 启动 + + S4 提议自己为 Leader 并为自己投票,然后与 S1、S2、S3 交换投票结果,发现 S3 已经成为 Leader,S4 就成为 Follower。 + +- S5 启动 + + S5 提议自己为 Leader 并为自己投票,然后与 S1、S2、S3和 S4 交换投票结果,发现 S3 已经成为 Leader,S5 就成为 Follower。 + +
+ +## 关于 ZooKeeper 集群的几个问题 + +- ZooKeeper 集群中一般部署奇数台 Server,why ? + + 这是因为 ZooKeeper 集群在宕掉几个 Server 后,如果剩下的 Server 数大于宕掉的 Server 数的话整个 ZooKeeper 才依然可用。假如我们的集群中有 n 台 Server ,那么也就是剩下的服务数必须大于 n/2。 + + 假设有 3 台服务器,那么最大允许宕掉 1 台 ZooKeeper 服务器,如果我们有 4 台的的时候也同样只允许宕掉 1 台;假如有 5 台服务器,那么最大允许宕掉 2 台 ZooKeeper 服务器,如果我们有 6 台的的时候也同样只允许宕掉 2 台。可以看出,2n 和 2n-1 的容错度是一样的,所以采用奇数台 Server 就行了。 + +- ZooKeeper 为何采用选举过半机制? + + 采用过半机制是如何防止集群脑裂。 + + 集群脑裂指的是对于一个集群,如果发生网络故障,Server 之间不能正常通信,集群被割裂成几个小集群,此时子集群各自进行选主导致“脑裂”的情况。 + + 假设一个由 6 台服务器所组成的集群,部署在了 2 个机房,每个机房 3 台。正常情况下只有 1 个 Leader,但是当两个机房中间网络断开的时候,每个机房的 3 台服务器都会认为另一个机房的 3 台服务器下线,而选出自己的 Leader 并对外提供服务。若没有过半机制,当网络恢复的时候会发现有 2 个 Leader。这好比原来的 1 个大脑(Leader)分散成了 2 个大脑,这就发生了脑裂现象。脑裂期间 2 个 “大脑” 都可能对外提供了服务,会带来数据一致性等问题。而 ZooKeeper 的过半机制导致不可能产生 2 个 Leader,因为少于等于一半是不可能产生 Leader 的,这就使得不论机房的机器如何分配都不可能发生脑裂。 diff --git "a/docs/Zookeeper/4_\345\272\224\347\224\250.md" "b/docs/Zookeeper/4_\345\272\224\347\224\250.md" new file mode 100644 index 00000000..4a1826b1 --- /dev/null +++ "b/docs/Zookeeper/4_\345\272\224\347\224\250.md" @@ -0,0 +1,33 @@ +# Zookeeper 的应用 + +## Zookeeper 应用场景 + +### 统一命名服务 + +在分布式环境中,应用程序经常需要对服务进行统一命名,以便识别不同的服务和快速获取服务列表,应用程序可以将服务名称和服务地址信息维护在 Zookeeper 上,客户端通过 Zookeeper 获取可用服务列表。 + +### 配置管理 + +在分布式环境下,应用程序可以将配置文件统一在 Zookeeper 上管理。配置信息可以按照系统配置、告警配置、业务开关配置等分类存储在不同的 Znode 上,各个服务在启动的时候从 Zookeeper 上读取配置,同时监听各个节点的 Znode 数据,一旦 Znode 中的配置被修改,Zookeeper 将通知各个服务然后在线更新配置。 + +使用 Zookeeper 做统一配置管理,不但避免了维护散落在各个服务器上的配置文件的复杂性,同时在配置信息变化时能够及时通知各个服务器在线更新配置,而不用重启服务。 + +### 集群管理 + +在分布式环境下,实时管理每个服务的状态是 Zookeeper 使用最广的场景,常见的 Hbase、Kafka 和 HDFS 等集群都依赖 Zookeeper 做统一的状态管理。 + +### 分布式通知协调 + +基于 Znode 的临时结点和 Watch 特性,应用程序可以很容易地实现一个分布式通知协调系统。比如在集群中为每个服务器都创建一个周期为 30 s 的临时节点作为服务状态监控,要求每个服务每 10 s 定时向 Zookeeper 汇报监控状态。当 Zookeeper 连接 30 s 未收到服务的状态反馈时,则可以认为该服务异常,将其从服务列表中移除,同时将该结果通知其他监控该节点状态的服务。 + +### 分布式锁 + +由于 Zookeeper 是强一致性的,多个客户端同时在 Zookeeper 上创建相同的 Znode 时,只有一个能创建。基于该机制,应用程序可以实现锁的独占性,当多个客户端同时在 Zookeeper 上创建相同的 Znode 时,创建成功的那个客户端将得到锁,其他客户端则等待。 + +将锁节点设置为 EPHEMERAL_SEQUENTIAL,则该 Znode 可掌握全局锁的访问时序。 + +## Zookeeper 实战 + +- [Zookeeper 官网](https://zookeeper.apache.org/) +- [Zookeeper 实战](https://snailclimb.gitee.io/javaguide/#/docs/system-design/distributed-system/zookeeper/zookeeper-in-action) + diff --git a/docs/_coverpage.md b/docs/_coverpage.md index c5455ab9..1a7a8681 100644 --- a/docs/_coverpage.md +++ b/docs/_coverpage.md @@ -1,29 +1,28 @@ -
+
# Java -[![watch](https://badgen.net/github/watchers/DuHouAn/Java?icon=github&color=4ab8a1)](https://github.com/DuHouAn/Java) [![stars](https://badgen.net/github/stars/DuHouAn/Java?icon=github&color=4ab8a1)](https://github.com/DuHouAn/Java) [![forks](https://badgen.net/github/forks/DuHouAn/Java?icon=github&color=4ab8a1)](https://github.com/DuHouAn/Java) - -- 本仓库是对计算机基础、Java 的一些基础知识以及一些数据库知识进行收集、整理(持续更新中)。 - +
+ + + +

+- 本仓库是对 Java 的一些基础知识、数据库知识、以及框架知识进行收集、整理(持续更新中)。

- 计算机基础 Java - 面向对象 + 面向对象 编程题 数据库

-

- 消息中间件 - 系统设计 - 工具 - 进阶指南 - 参考资料 +

+ 系统设计 + 常用框架 + 工具 + 参考资料

- -[开始阅读](./README.md) \ No newline at end of file +[开始阅读](./README.md) diff --git a/docs/_pics/AimToOffer/0038204c-4b8a-42a5-921d-080f6674f989.png b/docs/_pics/AimToOffer/0038204c-4b8a-42a5-921d-080f6674f989.png deleted file mode 100644 index 31a2d8d2..00000000 Binary files a/docs/_pics/AimToOffer/0038204c-4b8a-42a5-921d-080f6674f989.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/047faac4-a368-4565-8331-2b66253080d3.jpg b/docs/_pics/AimToOffer/047faac4-a368-4565-8331-2b66253080d3.jpg deleted file mode 100644 index f6506214..00000000 Binary files a/docs/_pics/AimToOffer/047faac4-a368-4565-8331-2b66253080d3.jpg and /dev/null differ diff --git a/docs/_pics/AimToOffer/05a08f2e-9914-4a77-92ef-aebeaecf4f66.jpg b/docs/_pics/AimToOffer/05a08f2e-9914-4a77-92ef-aebeaecf4f66.jpg deleted file mode 100644 index 0e9bbc86..00000000 Binary files a/docs/_pics/AimToOffer/05a08f2e-9914-4a77-92ef-aebeaecf4f66.jpg and /dev/null differ diff --git a/docs/_pics/AimToOffer/0ad9f7ba-f408-4999-a77a-9b73562c9088.gif b/docs/_pics/AimToOffer/0ad9f7ba-f408-4999-a77a-9b73562c9088.gif deleted file mode 100644 index 81ebc9d2..00000000 Binary files a/docs/_pics/AimToOffer/0ad9f7ba-f408-4999-a77a-9b73562c9088.gif and /dev/null differ diff --git a/docs/_pics/AimToOffer/0c12221f-729e-4c22-b0ba-0dfc909f8adf.jpg b/docs/_pics/AimToOffer/0c12221f-729e-4c22-b0ba-0dfc909f8adf.jpg deleted file mode 100644 index 068f6dee..00000000 Binary files a/docs/_pics/AimToOffer/0c12221f-729e-4c22-b0ba-0dfc909f8adf.jpg and /dev/null differ diff --git a/docs/_pics/AimToOffer/0dae7e93-cfd1-4bd3-97e8-325b032b716f.gif b/docs/_pics/AimToOffer/0dae7e93-cfd1-4bd3-97e8-325b032b716f.gif deleted file mode 100644 index c8507532..00000000 Binary files a/docs/_pics/AimToOffer/0dae7e93-cfd1-4bd3-97e8-325b032b716f.gif and /dev/null differ diff --git a/docs/_pics/AimToOffer/1176f9e1-3442-4808-a47a-76fbaea1b806.png b/docs/_pics/AimToOffer/1176f9e1-3442-4808-a47a-76fbaea1b806.png deleted file mode 100644 index 581673ff..00000000 Binary files a/docs/_pics/AimToOffer/1176f9e1-3442-4808-a47a-76fbaea1b806.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/13454fa1-23a8-4578-9663-2b13a6af564a.jpg b/docs/_pics/AimToOffer/13454fa1-23a8-4578-9663-2b13a6af564a.jpg deleted file mode 100644 index 5284457b..00000000 Binary files a/docs/_pics/AimToOffer/13454fa1-23a8-4578-9663-2b13a6af564a.jpg and /dev/null differ diff --git a/docs/_pics/AimToOffer/17e301df-52e8-4886-b593-841a16d13e44.png b/docs/_pics/AimToOffer/17e301df-52e8-4886-b593-841a16d13e44.png deleted file mode 100644 index b1ae4d1c..00000000 Binary files a/docs/_pics/AimToOffer/17e301df-52e8-4886-b593-841a16d13e44.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/195f8693-5ec4-4987-8560-f25e365879dd.png b/docs/_pics/AimToOffer/195f8693-5ec4-4987-8560-f25e365879dd.png deleted file mode 100644 index b2ea3f87..00000000 Binary files a/docs/_pics/AimToOffer/195f8693-5ec4-4987-8560-f25e365879dd.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/1b80288d-1b35-4cd3-aa17-7e27ab9a2389.png b/docs/_pics/AimToOffer/1b80288d-1b35-4cd3-aa17-7e27ab9a2389.png deleted file mode 100644 index 44b12ccd..00000000 Binary files a/docs/_pics/AimToOffer/1b80288d-1b35-4cd3-aa17-7e27ab9a2389.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/1db1c7ea-0443-478b-8df9-7e33b1336cc4.png b/docs/_pics/AimToOffer/1db1c7ea-0443-478b-8df9-7e33b1336cc4.png deleted file mode 100644 index e3082177..00000000 Binary files a/docs/_pics/AimToOffer/1db1c7ea-0443-478b-8df9-7e33b1336cc4.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/31d9adce-2af8-4754-8386-0aabb4e500b0.png b/docs/_pics/AimToOffer/31d9adce-2af8-4754-8386-0aabb4e500b0.png deleted file mode 100644 index 1248f60f..00000000 Binary files a/docs/_pics/AimToOffer/31d9adce-2af8-4754-8386-0aabb4e500b0.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/3ea280b5-be7d-471b-ac76-ff020384357c.gif b/docs/_pics/AimToOffer/3ea280b5-be7d-471b-ac76-ff020384357c.gif deleted file mode 100644 index 4c8540a9..00000000 Binary files a/docs/_pics/AimToOffer/3ea280b5-be7d-471b-ac76-ff020384357c.gif and /dev/null differ diff --git a/docs/_pics/AimToOffer/4240a69f-4d51-4d16-b797-2dfe110f30bd.png b/docs/_pics/AimToOffer/4240a69f-4d51-4d16-b797-2dfe110f30bd.png deleted file mode 100644 index d889e516..00000000 Binary files a/docs/_pics/AimToOffer/4240a69f-4d51-4d16-b797-2dfe110f30bd.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/424f34ab-a9fd-49a6-9969-d76b42251365.png b/docs/_pics/AimToOffer/424f34ab-a9fd-49a6-9969-d76b42251365.png deleted file mode 100644 index d5c1a58d..00000000 Binary files a/docs/_pics/AimToOffer/424f34ab-a9fd-49a6-9969-d76b42251365.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/42661013-750f-420b-b3c1-437e9a11fb65.png b/docs/_pics/AimToOffer/42661013-750f-420b-b3c1-437e9a11fb65.png deleted file mode 100644 index 964f97f8..00000000 Binary files a/docs/_pics/AimToOffer/42661013-750f-420b-b3c1-437e9a11fb65.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/45be9587-6069-4ab7-b9ac-840db1a53744.jpg b/docs/_pics/AimToOffer/45be9587-6069-4ab7-b9ac-840db1a53744.jpg deleted file mode 100644 index 336ee59b..00000000 Binary files a/docs/_pics/AimToOffer/45be9587-6069-4ab7-b9ac-840db1a53744.jpg and /dev/null differ diff --git a/docs/_pics/AimToOffer/48517227-324c-4664-bd26-a2d2cffe2bfe.png b/docs/_pics/AimToOffer/48517227-324c-4664-bd26-a2d2cffe2bfe.png deleted file mode 100644 index ca11ce32..00000000 Binary files a/docs/_pics/AimToOffer/48517227-324c-4664-bd26-a2d2cffe2bfe.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/48b1d459-8832-4e92-938a-728aae730739.jpg b/docs/_pics/AimToOffer/48b1d459-8832-4e92-938a-728aae730739.jpg deleted file mode 100644 index 8adfea82..00000000 Binary files a/docs/_pics/AimToOffer/48b1d459-8832-4e92-938a-728aae730739.jpg and /dev/null differ diff --git a/docs/_pics/AimToOffer/49d2adc1-b28a-44bf-babb-d44993f4a2e3.gif b/docs/_pics/AimToOffer/49d2adc1-b28a-44bf-babb-d44993f4a2e3.gif deleted file mode 100644 index e2af5f10..00000000 Binary files a/docs/_pics/AimToOffer/49d2adc1-b28a-44bf-babb-d44993f4a2e3.gif and /dev/null differ diff --git a/docs/_pics/AimToOffer/4bf8d0ba-36f0-459e-83a0-f15278a5a157.png b/docs/_pics/AimToOffer/4bf8d0ba-36f0-459e-83a0-f15278a5a157.png deleted file mode 100644 index 4b485ecd..00000000 Binary files a/docs/_pics/AimToOffer/4bf8d0ba-36f0-459e-83a0-f15278a5a157.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/508c6e52-9f93-44ed-b6b9-e69050e14807.jpg b/docs/_pics/AimToOffer/508c6e52-9f93-44ed-b6b9-e69050e14807.jpg deleted file mode 100644 index f7f3ca5b..00000000 Binary files a/docs/_pics/AimToOffer/508c6e52-9f93-44ed-b6b9-e69050e14807.jpg and /dev/null differ diff --git a/docs/_pics/AimToOffer/5f1cb999-cb9a-4f6c-a0af-d90377295ab8.png b/docs/_pics/AimToOffer/5f1cb999-cb9a-4f6c-a0af-d90377295ab8.png deleted file mode 100644 index a3fdc228..00000000 Binary files a/docs/_pics/AimToOffer/5f1cb999-cb9a-4f6c-a0af-d90377295ab8.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/66a01953-5303-43b1-8646-0c77b825e980.png b/docs/_pics/AimToOffer/66a01953-5303-43b1-8646-0c77b825e980.png deleted file mode 100644 index 48a120be..00000000 Binary files a/docs/_pics/AimToOffer/66a01953-5303-43b1-8646-0c77b825e980.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/6980aef0-debe-4b4b-8da5-8b1befbc1408.gif b/docs/_pics/AimToOffer/6980aef0-debe-4b4b-8da5-8b1befbc1408.gif deleted file mode 100644 index 05704a44..00000000 Binary files a/docs/_pics/AimToOffer/6980aef0-debe-4b4b-8da5-8b1befbc1408.gif and /dev/null differ diff --git a/docs/_pics/AimToOffer/6b504f1f-bf76-4aab-a146-a9c7a58c2029.png b/docs/_pics/AimToOffer/6b504f1f-bf76-4aab-a146-a9c7a58c2029.png deleted file mode 100644 index b613aa38..00000000 Binary files a/docs/_pics/AimToOffer/6b504f1f-bf76-4aab-a146-a9c7a58c2029.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/72aac98a-d5df-4bfa-a71a-4bb16a87474c.png b/docs/_pics/AimToOffer/72aac98a-d5df-4bfa-a71a-4bb16a87474c.png deleted file mode 100644 index 125a804c..00000000 Binary files a/docs/_pics/AimToOffer/72aac98a-d5df-4bfa-a71a-4bb16a87474c.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/84a5b15a-86c5-4d8e-9439-d9fd5a4699a1.jpg b/docs/_pics/AimToOffer/84a5b15a-86c5-4d8e-9439-d9fd5a4699a1.jpg deleted file mode 100644 index 22bf89a3..00000000 Binary files a/docs/_pics/AimToOffer/84a5b15a-86c5-4d8e-9439-d9fd5a4699a1.jpg and /dev/null differ diff --git a/docs/_pics/AimToOffer/95080fae-de40-463d-a76e-783a0c677fec.gif b/docs/_pics/AimToOffer/95080fae-de40-463d-a76e-783a0c677fec.gif deleted file mode 100644 index 9de55285..00000000 Binary files a/docs/_pics/AimToOffer/95080fae-de40-463d-a76e-783a0c677fec.gif and /dev/null differ diff --git a/docs/_pics/AimToOffer/9d1deeba-4ae1-41dc-98f4-47d85b9831bc.gif b/docs/_pics/AimToOffer/9d1deeba-4ae1-41dc-98f4-47d85b9831bc.gif deleted file mode 100644 index c34f7419..00000000 Binary files a/docs/_pics/AimToOffer/9d1deeba-4ae1-41dc-98f4-47d85b9831bc.gif and /dev/null differ diff --git a/docs/_pics/AimToOffer/9dae7475-934f-42e5-b3b3-12724337170a.png b/docs/_pics/AimToOffer/9dae7475-934f-42e5-b3b3-12724337170a.png deleted file mode 100644 index 7508fa08..00000000 Binary files a/docs/_pics/AimToOffer/9dae7475-934f-42e5-b3b3-12724337170a.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/af1d1166-63af-47b6-9aa3-2bf2bd37bd03.jpg b/docs/_pics/AimToOffer/af1d1166-63af-47b6-9aa3-2bf2bd37bd03.jpg deleted file mode 100644 index 00ea4f6b..00000000 Binary files a/docs/_pics/AimToOffer/af1d1166-63af-47b6-9aa3-2bf2bd37bd03.jpg and /dev/null differ diff --git a/docs/_pics/AimToOffer/b0611f89-1e5f-4494-a795-3544bf65042a.gif b/docs/_pics/AimToOffer/b0611f89-1e5f-4494-a795-3544bf65042a.gif deleted file mode 100644 index 3e1fdb8b..00000000 Binary files a/docs/_pics/AimToOffer/b0611f89-1e5f-4494-a795-3544bf65042a.gif and /dev/null differ diff --git a/docs/_pics/AimToOffer/b903fda8-07d0-46a7-91a7-e803892895cf.gif b/docs/_pics/AimToOffer/b903fda8-07d0-46a7-91a7-e803892895cf.gif deleted file mode 100644 index 873aac86..00000000 Binary files a/docs/_pics/AimToOffer/b903fda8-07d0-46a7-91a7-e803892895cf.gif and /dev/null differ diff --git a/docs/_pics/AimToOffer/ba355101-4a93-4c71-94fb-1da83639727b.jpg b/docs/_pics/AimToOffer/ba355101-4a93-4c71-94fb-1da83639727b.jpg deleted file mode 100644 index b5625ae0..00000000 Binary files a/docs/_pics/AimToOffer/ba355101-4a93-4c71-94fb-1da83639727b.jpg and /dev/null differ diff --git a/docs/_pics/AimToOffer/bb7fc182-98c2-4860-8ea3-630e27a5f29f.png b/docs/_pics/AimToOffer/bb7fc182-98c2-4860-8ea3-630e27a5f29f.png deleted file mode 100644 index 92dfa7f5..00000000 Binary files a/docs/_pics/AimToOffer/bb7fc182-98c2-4860-8ea3-630e27a5f29f.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/c094d2bc-ec75-444b-af77-d369dfb6b3b4.png b/docs/_pics/AimToOffer/c094d2bc-ec75-444b-af77-d369dfb6b3b4.png deleted file mode 100644 index 9e8e93c9..00000000 Binary files a/docs/_pics/AimToOffer/c094d2bc-ec75-444b-af77-d369dfb6b3b4.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/c13e2a3d-b01c-4a08-a69b-db2c4e821e09.png b/docs/_pics/AimToOffer/c13e2a3d-b01c-4a08-a69b-db2c4e821e09.png deleted file mode 100644 index 69fe1ba2..00000000 Binary files a/docs/_pics/AimToOffer/c13e2a3d-b01c-4a08-a69b-db2c4e821e09.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/c269e362-1128-4212-9cf3-d4c12b363b2f.gif b/docs/_pics/AimToOffer/c269e362-1128-4212-9cf3-d4c12b363b2f.gif deleted file mode 100644 index e372dea1..00000000 Binary files a/docs/_pics/AimToOffer/c269e362-1128-4212-9cf3-d4c12b363b2f.gif and /dev/null differ diff --git a/docs/_pics/AimToOffer/cafbfeb8-7dfe-4c0a-a3c9-750eeb824068.png b/docs/_pics/AimToOffer/cafbfeb8-7dfe-4c0a-a3c9-750eeb824068.png deleted file mode 100644 index 8cd71a87..00000000 Binary files a/docs/_pics/AimToOffer/cafbfeb8-7dfe-4c0a-a3c9-750eeb824068.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/cd411a94-3786-4c94-9e08-f28320e010d5.png b/docs/_pics/AimToOffer/cd411a94-3786-4c94-9e08-f28320e010d5.png deleted file mode 100644 index d941a7cb..00000000 Binary files a/docs/_pics/AimToOffer/cd411a94-3786-4c94-9e08-f28320e010d5.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/d03a2efa-ef19-4c96-97e8-ff61df8061d3.png b/docs/_pics/AimToOffer/d03a2efa-ef19-4c96-97e8-ff61df8061d3.png deleted file mode 100644 index 5a4fdbcf..00000000 Binary files a/docs/_pics/AimToOffer/d03a2efa-ef19-4c96-97e8-ff61df8061d3.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/d27c99f0-7881-4f2d-9675-c75cbdee3acd.jpg b/docs/_pics/AimToOffer/d27c99f0-7881-4f2d-9675-c75cbdee3acd.jpg deleted file mode 100644 index fe765547..00000000 Binary files a/docs/_pics/AimToOffer/d27c99f0-7881-4f2d-9675-c75cbdee3acd.jpg and /dev/null differ diff --git a/docs/_pics/AimToOffer/d5e838cf-d8a2-49af-90df-1b2a714ee676.jpg b/docs/_pics/AimToOffer/d5e838cf-d8a2-49af-90df-1b2a714ee676.jpg deleted file mode 100644 index dca06e46..00000000 Binary files a/docs/_pics/AimToOffer/d5e838cf-d8a2-49af-90df-1b2a714ee676.jpg and /dev/null differ diff --git a/docs/_pics/AimToOffer/dc964b86-7a08-4bde-a3d9-e6ddceb29f98.png b/docs/_pics/AimToOffer/dc964b86-7a08-4bde-a3d9-e6ddceb29f98.png deleted file mode 100644 index a08e6be1..00000000 Binary files a/docs/_pics/AimToOffer/dc964b86-7a08-4bde-a3d9-e6ddceb29f98.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/dfd5d3f8-673c-486b-8ecf-d2082107b67b.png b/docs/_pics/AimToOffer/dfd5d3f8-673c-486b-8ecf-d2082107b67b.png deleted file mode 100644 index 11aaa325..00000000 Binary files a/docs/_pics/AimToOffer/dfd5d3f8-673c-486b-8ecf-d2082107b67b.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/e151b5df-5390-4365-b66e-b130cd253c12.png b/docs/_pics/AimToOffer/e151b5df-5390-4365-b66e-b130cd253c12.png deleted file mode 100644 index d1ea8287..00000000 Binary files a/docs/_pics/AimToOffer/e151b5df-5390-4365-b66e-b130cd253c12.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/eaa506b6-0747-4bee-81f8-3cda795d8154.png b/docs/_pics/AimToOffer/eaa506b6-0747-4bee-81f8-3cda795d8154.png deleted file mode 100644 index 2cf0d857..00000000 Binary files a/docs/_pics/AimToOffer/eaa506b6-0747-4bee-81f8-3cda795d8154.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/ed77b0e6-38d9-4a34-844f-724f3ffa2c12.jpg b/docs/_pics/AimToOffer/ed77b0e6-38d9-4a34-844f-724f3ffa2c12.jpg deleted file mode 100644 index 9aac1677..00000000 Binary files a/docs/_pics/AimToOffer/ed77b0e6-38d9-4a34-844f-724f3ffa2c12.jpg and /dev/null differ diff --git a/docs/_pics/AimToOffer/f5792051-d9b2-4ca4-a234-a4a2de3d5a57.png b/docs/_pics/AimToOffer/f5792051-d9b2-4ca4-a234-a4a2de3d5a57.png deleted file mode 100644 index 95a55e94..00000000 Binary files a/docs/_pics/AimToOffer/f5792051-d9b2-4ca4-a234-a4a2de3d5a57.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/f6e146f1-57ad-411b-beb3-770a142164ef.png b/docs/_pics/AimToOffer/f6e146f1-57ad-411b-beb3-770a142164ef.png deleted file mode 100644 index 8b25cf20..00000000 Binary files a/docs/_pics/AimToOffer/f6e146f1-57ad-411b-beb3-770a142164ef.png and /dev/null differ diff --git a/docs/_pics/AimToOffer/fb3b8f7a-4293-4a38-aae1-62284db979a3.png b/docs/_pics/AimToOffer/fb3b8f7a-4293-4a38-aae1-62284db979a3.png deleted file mode 100644 index 6575dc98..00000000 Binary files a/docs/_pics/AimToOffer/fb3b8f7a-4293-4a38-aae1-62284db979a3.png and /dev/null differ diff --git a/docs/_pics/JVM/005b481b-502b-4e3f-985d-d043c2b330aa.png b/docs/_pics/JVM/005b481b-502b-4e3f-985d-d043c2b330aa.png deleted file mode 100644 index 0f8543c2..00000000 Binary files a/docs/_pics/JVM/005b481b-502b-4e3f-985d-d043c2b330aa.png and /dev/null differ diff --git a/docs/_pics/JVM/08f32fd3-f736-4a67-81ca-295b2a7972f2.jpg b/docs/_pics/JVM/08f32fd3-f736-4a67-81ca-295b2a7972f2.jpg deleted file mode 100644 index c87a6f86..00000000 Binary files a/docs/_pics/JVM/08f32fd3-f736-4a67-81ca-295b2a7972f2.jpg and /dev/null differ diff --git a/docs/_pics/JVM/0dd2d40a-5b2b-4d45-b176-e75a4cd4bdbf.png b/docs/_pics/JVM/0dd2d40a-5b2b-4d45-b176-e75a4cd4bdbf.png deleted file mode 100644 index 1bb834c8..00000000 Binary files a/docs/_pics/JVM/0dd2d40a-5b2b-4d45-b176-e75a4cd4bdbf.png and /dev/null differ diff --git a/docs/_pics/JVM/22fda4ae-4dd5-489d-ab10-9ebfdad22ae0.jpg b/docs/_pics/JVM/22fda4ae-4dd5-489d-ab10-9ebfdad22ae0.jpg deleted file mode 100644 index b45bb546..00000000 Binary files a/docs/_pics/JVM/22fda4ae-4dd5-489d-ab10-9ebfdad22ae0.jpg and /dev/null differ diff --git a/docs/_pics/JVM/278fe431-af88-4a95-a895-9c3b80117de3.jpg b/docs/_pics/JVM/278fe431-af88-4a95-a895-9c3b80117de3.jpg deleted file mode 100644 index c2697572..00000000 Binary files a/docs/_pics/JVM/278fe431-af88-4a95-a895-9c3b80117de3.jpg and /dev/null differ diff --git a/docs/_pics/JVM/335fe19c-4a76-45ab-9320-88c90d6a0d7e.png b/docs/_pics/JVM/335fe19c-4a76-45ab-9320-88c90d6a0d7e.png deleted file mode 100644 index 57fad732..00000000 Binary files a/docs/_pics/JVM/335fe19c-4a76-45ab-9320-88c90d6a0d7e.png and /dev/null differ diff --git a/docs/_pics/JVM/4cf711a8-7ab2-4152-b85c-d5c226733807.png b/docs/_pics/JVM/4cf711a8-7ab2-4152-b85c-d5c226733807.png deleted file mode 100644 index 03d1e447..00000000 Binary files a/docs/_pics/JVM/4cf711a8-7ab2-4152-b85c-d5c226733807.png and /dev/null differ diff --git a/docs/_pics/JVM/5778d113-8e13-4c53-b5bf-801e58080b97.png b/docs/_pics/JVM/5778d113-8e13-4c53-b5bf-801e58080b97.png deleted file mode 100644 index b0f94a99..00000000 Binary files a/docs/_pics/JVM/5778d113-8e13-4c53-b5bf-801e58080b97.png and /dev/null differ diff --git a/docs/_pics/JVM/62e77997-6957-4b68-8d12-bfd609bb2c68.jpg b/docs/_pics/JVM/62e77997-6957-4b68-8d12-bfd609bb2c68.jpg deleted file mode 100644 index 2c6a953e..00000000 Binary files a/docs/_pics/JVM/62e77997-6957-4b68-8d12-bfd609bb2c68.jpg and /dev/null differ diff --git a/docs/_pics/JVM/66a6899d-c6b0-4a47-8569-9d08f0baf86c.png b/docs/_pics/JVM/66a6899d-c6b0-4a47-8569-9d08f0baf86c.png deleted file mode 100644 index a8536c94..00000000 Binary files a/docs/_pics/JVM/66a6899d-c6b0-4a47-8569-9d08f0baf86c.png and /dev/null differ diff --git a/docs/_pics/JVM/81538cd5-1bcf-4e31-86e5-e198df1e013b.jpg b/docs/_pics/JVM/81538cd5-1bcf-4e31-86e5-e198df1e013b.jpg deleted file mode 100644 index ecf6a7d0..00000000 Binary files a/docs/_pics/JVM/81538cd5-1bcf-4e31-86e5-e198df1e013b.jpg and /dev/null differ diff --git a/docs/_pics/JVM/83d909d2-3858-4fe1-8ff4-16471db0b180.png b/docs/_pics/JVM/83d909d2-3858-4fe1-8ff4-16471db0b180.png deleted file mode 100644 index 5904529b..00000000 Binary files a/docs/_pics/JVM/83d909d2-3858-4fe1-8ff4-16471db0b180.png and /dev/null differ diff --git a/docs/_pics/JVM/8442519f-0b4d-48f4-8229-56f984363c69.png b/docs/_pics/JVM/8442519f-0b4d-48f4-8229-56f984363c69.png deleted file mode 100644 index b8290f0b..00000000 Binary files a/docs/_pics/JVM/8442519f-0b4d-48f4-8229-56f984363c69.png and /dev/null differ diff --git a/docs/_pics/JVM/9bbddeeb-e939-41f0-8e8e-2b1a0aa7e0a7.png b/docs/_pics/JVM/9bbddeeb-e939-41f0-8e8e-2b1a0aa7e0a7.png deleted file mode 100644 index 02b1f35a..00000000 Binary files a/docs/_pics/JVM/9bbddeeb-e939-41f0-8e8e-2b1a0aa7e0a7.png and /dev/null differ diff --git a/docs/_pics/JVM/b2b77b9e-958c-4016-8ae5-9c6edd83871e.png b/docs/_pics/JVM/b2b77b9e-958c-4016-8ae5-9c6edd83871e.png deleted file mode 100644 index c376a027..00000000 Binary files a/docs/_pics/JVM/b2b77b9e-958c-4016-8ae5-9c6edd83871e.png and /dev/null differ diff --git a/docs/_pics/JVM/c625baa0-dde6-449e-93df-c3a67f2f430f.jpg b/docs/_pics/JVM/c625baa0-dde6-449e-93df-c3a67f2f430f.jpg deleted file mode 100644 index 4a6d5197..00000000 Binary files a/docs/_pics/JVM/c625baa0-dde6-449e-93df-c3a67f2f430f.jpg and /dev/null differ diff --git a/docs/_pics/JVM/ccd773a5-ad38-4022-895c-7ac318f31437.png b/docs/_pics/JVM/ccd773a5-ad38-4022-895c-7ac318f31437.png deleted file mode 100644 index e4706e59..00000000 Binary files a/docs/_pics/JVM/ccd773a5-ad38-4022-895c-7ac318f31437.png and /dev/null differ diff --git a/docs/_pics/JVM/f99ee771-c56f-47fb-9148-c0036695b5fe.jpg b/docs/_pics/JVM/f99ee771-c56f-47fb-9148-c0036695b5fe.jpg deleted file mode 100644 index 96e22a72..00000000 Binary files a/docs/_pics/JVM/f99ee771-c56f-47fb-9148-c0036695b5fe.jpg and /dev/null differ diff --git a/docs/_pics/JVM/j_1.png b/docs/_pics/JVM/j_1.png deleted file mode 100644 index a0a2f2b1..00000000 Binary files a/docs/_pics/JVM/j_1.png and /dev/null differ diff --git a/docs/_pics/JVM/j_2.png b/docs/_pics/JVM/j_2.png deleted file mode 100644 index a490d7a4..00000000 Binary files a/docs/_pics/JVM/j_2.png and /dev/null differ diff --git a/docs/_pics/JVM/j_3.png b/docs/_pics/JVM/j_3.png deleted file mode 100644 index 7b03cc79..00000000 Binary files a/docs/_pics/JVM/j_3.png and /dev/null differ diff --git a/docs/_pics/JVM/j_4.jpg b/docs/_pics/JVM/j_4.jpg deleted file mode 100644 index f926506d..00000000 Binary files a/docs/_pics/JVM/j_4.jpg and /dev/null differ diff --git a/docs/_pics/JVM/j_5.png b/docs/_pics/JVM/j_5.png deleted file mode 100644 index b08d3f66..00000000 Binary files a/docs/_pics/JVM/j_5.png and /dev/null differ diff --git a/docs/_pics/JVM/j_6.png b/docs/_pics/JVM/j_6.png deleted file mode 100644 index 23d123f0..00000000 Binary files a/docs/_pics/JVM/j_6.png and /dev/null differ diff --git a/docs/_pics/JVM/j_7.png b/docs/_pics/JVM/j_7.png deleted file mode 100644 index 6d883eb0..00000000 Binary files a/docs/_pics/JVM/j_7.png and /dev/null differ diff --git a/docs/_pics/JVM/j_8.jpg b/docs/_pics/JVM/j_8.jpg deleted file mode 100644 index 6320473b..00000000 Binary files a/docs/_pics/JVM/j_8.jpg and /dev/null differ diff --git a/docs/_pics/MessageQueuue/mq_1.png b/docs/_pics/MessageQueuue/mq_1.png deleted file mode 100644 index 069b7e17..00000000 Binary files a/docs/_pics/MessageQueuue/mq_1.png and /dev/null differ diff --git a/docs/_pics/MessageQueuue/mq_2.png b/docs/_pics/MessageQueuue/mq_2.png deleted file mode 100644 index b48fb693..00000000 Binary files a/docs/_pics/MessageQueuue/mq_2.png and /dev/null differ diff --git a/docs/_pics/MessageQueuue/mq_3.png b/docs/_pics/MessageQueuue/mq_3.png deleted file mode 100644 index 656e6501..00000000 Binary files a/docs/_pics/MessageQueuue/mq_3.png and /dev/null differ diff --git a/docs/_pics/MessageQueuue/mq_4.png b/docs/_pics/MessageQueuue/mq_4.png deleted file mode 100644 index fd93e1ab..00000000 Binary files a/docs/_pics/MessageQueuue/mq_4.png and /dev/null differ diff --git a/docs/_pics/MessageQueuue/mq_5.png b/docs/_pics/MessageQueuue/mq_5.png deleted file mode 100644 index 875b8ec3..00000000 Binary files a/docs/_pics/MessageQueuue/mq_5.png and /dev/null differ diff --git a/docs/_pics/MessageQueuue/mq_6.png b/docs/_pics/MessageQueuue/mq_6.png deleted file mode 100644 index 25e1d0ab..00000000 Binary files a/docs/_pics/MessageQueuue/mq_6.png and /dev/null differ diff --git a/docs/_pics/baseball/b_1.jpg b/docs/_pics/baseball/b_1.jpg deleted file mode 100644 index db55d014..00000000 Binary files a/docs/_pics/baseball/b_1.jpg and /dev/null differ diff --git a/docs/_pics/baseball/b_2.jpg b/docs/_pics/baseball/b_2.jpg deleted file mode 100644 index ff90fcf6..00000000 Binary files a/docs/_pics/baseball/b_2.jpg and /dev/null differ diff --git a/docs/_pics/draw.io/JVM.drawio b/docs/_pics/draw.io/JVM.drawio deleted file mode 100644 index a397820e..00000000 --- a/docs/_pics/draw.io/JVM.drawio +++ /dev/null @@ -1 +0,0 @@ -5Z1bd5s4EMc/jR67B3EzPPqW5HQ3J93tbvb0KYcaxaZLUIpxk+ynX2EgNhqlZamRkJyX2OL++0ujGY2EkTN/eL7Mo8fNNY1JimwrfkbOAtk2trDF/pUlL1WJ59UF6zyJ650OBR+Tf0lzZF26S2Kybe1YUJoWyWO7cEWzjKyKVlmU5/Spvds9TdtXfYzWBBR8XEUpLP07iYtNXYpt67DhiiTrTX1p1wqrDQ9Rs3O953YTxfTpqMhZImeeU1pUn+jnL+UD2FYafWYQ9/tVRzagDkc9PM9JykBfVF/Zh/ro1lYr4Z81J1lxfFMXbxwQz26+bW9+/W1q39z+9fX29+2Vc/fOqc7yLUp35Pj2tsVLQyunuywm5Uks5MyeNklBPj5Gq3LrE6sfrGxTPJTPhtnH+yRN5zSlOfue0YztNFvnUZywe2yKke3c7//YtvrqJC/I85uPhY8QXRL6QIr8he3SHODXWrw0YlVfnw7KBnXRpqVpXZ/qurR+PbF9LEFN8X8QdY0jancj6gyG1BMg9dOihEOzosXW/7qjzYZ3273lmbIdcPD4fNjIPq3L/+9vr9k2tPRRMEfBBC1dNJuhqducnd1sdYFqd6AjQ1y0xcoJu2b0eb9Dqe0jTbJij8ObIW/BSqJdQav72h8Qpck6Y59Tcl+eqtQtYYZqWhcXtKwNW1Y5kmz9Z/llwSrYSVR22yL7UORQILI9lMaTNzVmT5/11xgtPTTDiFXYZYgCB02DsiS4QGGwL5mUn5cBmuJy00H46qpnIDwWGEyMZUofDia9j0IHBYt9w16gcL4vYfVh3+anFgrOUnGRQZfa1pvaNYDik7IVB1WrnzZNe1m27oP0HgrDM23sjqW6sWMMtE+Yn78t7qwO5LdFTv8hnDsk8JAa4CuGieQC5A9JHJeXEXpfbf/sFB5V0JbBgyq4UkWw9XdSOaRYYNRETP3BmBoQSgU/7ihETAdz/DEMpqCV0AyqyATLbfyicIpjSrJ4Wg60HCgdMaz2JjEYZPkhkqNH9gSP3JTlJI2K5Fv79CIO9RU+lP3vd0yDF/7itU+ypbt8RerjjsdMuFO9qvD2qYooX5MCnGovzeuj/4Ra/rmp5ZxOLdeSrZYojDVJLffHDaKrWi43zIR96WoFhqv12tf8fNty4LCBbLVE4wRGq9W/bQG1pLetxk4Yq5bHI+7ftjy+C5TetmwYlZulFghtTudl2EOqNbUXHz6tLz5d/TH38M3q+st7+t6ApJ3NyyExxSREqn/WzrVGhrRDmDlypB7f0lUj7RALjhypz+ciVCPtELCNHOlEYbpeiLRDVDVypMHYuqcOoc/IkWJrbP2TKBO1LWhO7rSDy7tTEudAidFCdypJaRQLcnwjJ8t7VcrJQq+qIot1I8s7V8rJQucqieJYN6y8g6UcK3SwdLWyvKOlHC10tCpboB1Z3t9SThb6W0lOil2e6UYWuF2q0XYZFx45UzBOL3HqiZhph9HbkTMFeUXlTG3tmYIMhXKm+g9c+3zSWjlT/UeuJwqn8omZ6j90HY6ujxKNCp5qln81lb+c8x2g6eQsJnbjidcS2AmgwKJ86immFYoF1n+MknfsZM5/FSd69XeWecdOPVP9nWXesVPPVH9nmXfs1DPV31nmHTv1TPV3lnnHTj1T6Cxrl4vgO36ZazTEUGEyQruayvf86qHCVISjG1S+61cPFcZ1rm5Q+b5fPVQYS3m6QeU7f+VQm4sdQfV1g8r3/uqhmj5xHCQm+k8cBwuepE8cdzvEFFqrBVIe/ZfQgEUZQy6hEavVIVrRWi2QTOnftib8eLf8tmX6smqgVv+2BdSS37ZMX1YNkmqsbTFvwcKBgy3P9YOgn3SBYI319847tI6mL7gGOYnTLeGVv3DNM91bBGnX/mqFMhdci9UyfcE1yE2dzreX37aaGzBWLZD1Ot2Ca/lqTUy3hMD/4GeW9HY4fCxXKdP9etBn9VUK9FiylTLdpwc5aj6p1zf+sl3JSpnutYPMd/++ird/8vuqwPS+CuTU+7Yr3gLKbleBDZSaAKlGnuLA1tjyxgEch4UzHIx9V23XmcPD4e8yF8Jc/IIFW3Lxw77aOh/8XadXDYcfzq8wuPaDpXXKaz8cAzPY9vdd2TgY/uZnCc7D+AD8gh9lkYsfev4G1/6+SxWGww/deYNtP5hpo9r2h3C2jMHGB+BXbnxg2GVw7e+7pmc4/F3m9ZuLX7nxgVGvwcYHzNtTbnxg1Gtw7e/79o3h8MOwCy7VMAZ/37WHg+HHTSBynn2vcs8TW2cVePV9Uc2A/GHkZTD/vi+1GZA/DL0Mtj+Av3r70+WH18zlr9r7xNZZBV99XwA1IP+zyjkC/urtT5el2ubyV29/YNbRYPvT92VpA/KH8a/B9gfwV25/BL9QbrD9eXMGsDr+MP49I/4Djv+wrzkt35/4uu2SPdTmmsak3OM/7Vldj6M2FP01ltqHqfgyYx4hYbZ9WGmkWanTfakccAgtYArOJumv7zXY4bMhqy2jqtN5GHyv7Wt8zvG1TZC9yc8fKloePvKYZcgy4jOyt8iyTMM04CE9l9aDsXIkVRqrRp3jJf2T6Z7Ke0xjVg8aCs4zkZZDZ8SLgkVi4KNVxU/DZnueDUctacImjpeIZlPvz2ksDq3XMYzO/yNLk4Ma2dYVOdVtlaM+0Jifei47RPam4ly0Jb77Tb6/ZWR0Bxg27dqeGqfaPuBffzrnT59f/U8vu+fANz8+dKHy84ZlAP5Ta0JBhRzU3o52nW3FCrE0qh7pvtBqMl9odmT92dXiorE+HVLBXkoaSfsEekJ2cBC5BMOEIq3LluF9emYwVLBPs2zDM16Br+AF9AqSisYpvLt2I8veN39Qp16AVYKdR+QuTNfsgfyB8ZyJ6gL9VBRLc64kTpR56vRiusp36Gnl6qRKo8k1tNWnUeH7FVjby1hX/FjEEsWtAdAsIP8vAdpZxJm8KczODMxuJiRgvBADvN0/jlxXPNRNlvOhgemU564SSknzDF3kG4gQFGJEXESwLPhb5BE9ALxvO4bqMaYXkBdDDmtR8d/ZiMUZYmmWJgWYETDFwB9IHlNIiL6qyNM4lsPMimYoq7WEgPGSEPCMEKy1dIBX0wFudOD+r4O/ybx4MSPMJd7VhOCumHdlgiURi6IJh1CzI9jBayI9Sr2mdV/uddZC+nGCdPnOtO+OGLGnjDhvqX2yrvZjzEjszGmfWDvbdd/wfDcn/tkDHlkLa2+CdUFzBp7GMIqjDNFuELtK7w3yoqNbGNNqeNJcclDs6vIdbiXjDIfvy3D2WhzrC/CtBcWK2Jd3XIltRus6jYYEsXMqXnvlXySIP2Blbc8K08a4aKOAt3/tG20vC2u769dYlwEzLJ7cp+/hBabFj1XEljdXQauE3YpH5nleOBxqX8UyKtIvwznMkatGeOZpc8TTYrSHMrLHO2A7TdWrf4ceByLDQJY3CtTiMAnUaO067W+QnzmVH1xLyAaRR3kKhdfxzOY4+oQCTxYCH3kOCh+R5yPizCSYG6dZ6OV7MkLoIe8RBc47yz7mOPuY9+3mq11tzennmmdW1byQc+02GnaC/23Fd9+/8x3ENIa30pkDmfmm11Jz5jNQf8W16xQWNZiwiuWi3qDAaApbpPPPTf4qBndYumsaSGxLmY6aeeAA4a1k7ih4e89tv+EpvjK2FzM0Ci75qoG+tEg+SWP74Kz5IWGUZB1y16a/HmUzn5RuU6bSL0GBifw2IYfIC1HooCBEpMm6niXztuyFG34hwQZNiv7P83tl6gbB5j90TQKz+5rfbsPdTyJ2+Bc= \ No newline at end of file diff --git a/docs/_pics/draw.io/kafka.drawio b/docs/_pics/draw.io/kafka.drawio deleted file mode 100644 index 39ee7e7b..00000000 --- a/docs/_pics/draw.io/kafka.drawio +++ /dev/null @@ -1 +0,0 @@ -7Z1dc9o4FIZ/DZft+Jtw2SRN2pl2NrOZ3XYvXayAdw1ijCmwv35tkImt1y6gWBZIm5tgYQv7fSzpnKMje+DezTaPabiYfqURSQaOFW0G7v3AcWzLtvJ/Rcl2X+L7rGCSxhHb6bXgOf6XlEey0lUckWVtx4zSJIsX9cIxnc/JOKuVhWlK1/XdXmhS/9VFOCFQ8DwOEyz9FkfZdF96Y1mv5Z9IPJmyX3Zv2BezsNyXFSynYUTXlSL348C9SynN9p/oj7+L83esJPyRa7jbb39kqdPrUbPNHUlynR/2m/kHdnTtWyvmLzUl86x6Ug8tB4yektVtSj//+fTO/vr8x2f/8WnxjtXyM0xWTJunlEarMUnZBWbbUrSUruYRKSqzB+7tehpn5HkRjotv1/ltkpdNs1nCvp6kYRTnJ3ZHE5rujndfdn/5dy9xkpTlczrPK7hlZ0HSjGxaL8+uSPVI6Ixk6TbfhR3gMSTbEuZ+c13luy+aVtCWR4Xsjpoc6nWqJJiYZwjrgLBvE7RBtF9pHIXL6aHqDtS1g7q8AcobWA36upYsgV28c8M0i7OYzkHp/LqzupzLLKX/EE7QBo3DJJ7M881xrlPeJtzbQsU470k+sC9mcRQVP9PI75Ww1QMTB5l4DUgcWUS8c255q/NbvgNFHauuqH2qpNI09a9eU88T1FRazxFcvaaB6H0qTdPhcU1zW2lRfHxJyOZDYcTlWpB5xD7ej5NwuYzHdWn3lZAIzLmjSlWU8BuUKMtSkoRZ/LNefZM67BeeaDzPKh0GdMGcwEu6SseEHVU1zriKPOdIRVmYTkgGFe1gHS5bnN/oOL8KK3aXXxMovhcKBEHBEMFXJBlU6UIZQ8rlx1ZRUlCRbFLoW+lN6uBgvZUUVCSb1AnOml6keLdXmBRfkWxS6PVpTkrUoABSPRsU9gneoFakINIhPE71TeoEf0grUnZXvR9UJJvUjWmkurIooCLZpEzzpxx79N7vaKTCqiTTctCqQFyXEUNmJ9ppkLNEqSpu7KCp4Bokf8NUSr/yY4zZM0j+honCfuVH88s3R/7DtjL5MXIdGCS/6jlDB03aoUHyKx960U69MUh+1UOvi8H8kUHyqx56XYzQl9MrJujvqh57XQf1tw3SX/Xg65oWo3W6mvWFiiRHKFx0EgxqKKent8hqKLoHXvkkLeGpXO/mSEWSG4qn+6Q7pNP5o25IQUWySek+lQttSjg3jG9TPU8QerqbCUBKdIIQSPU8QeidkC581aQg8VXUoAuO5QPKJqX7pDtPSrj340n13vudkNp81aTAohh2ZVHwFUkmVeqhLSloU6KkoE31TUp3Kx1IiVoUQKpni8LX3UoHi6IrUn0nh/m6W+lASjRGAaR6jlH4plnpEJgT7v16XsLh6277ASnRaBKQ6jma5OseoQVSI+GES2CFVcmmpXt6LESUROMUEFHqOU5R/pw5pETHKiDV81gV6O5TASnxHhBY9d4DBo5htIT9Kogq9exXBegBazz1Lv60jvYb5G0P60BTQeP06NY5Q1XyD3H813hxANz9qjMUhzioa7w0BuRXnZ87xFEah2l95VednT7EoVfjtRmtnogy+TGirPHajNYkEGXyY5hY47UZrRETZfI3pNxa+urPxxeVLw4YYuxd47UxEN5VbnliQF3j3gfkV977oNur8djbmi6hSv6y9ZlhebbO1yqTH91ejYM+ramSyuRHt1fjoE9rXoky+dHt1Tjo05rSrUx+dHs1Dvq05r8pkx/dXo3nW8Sf5C1LfnR7NXa7Wtc9KpMfvV6dow68/sqjDqOGR0Jg9yPz9QBdvyUEJFbdw4waJtSvW2KInSmX+Ly81cZXL5BNnH0v5H/vs62/Kt/cbxiZ3ca21iMcT0rJT2SX/fGLS1CavMKlnLguB0r40b58RS25KzmYcFvZbVHssDz7fF9voX2FnebFjDBAeKl2WtftfXRhg5ZtnZf719je62AGxVUVf83yXW7emcu3BVuw7br8uMlXJP1FHxiFMRWq73QElU8m7B/qeevpdIYKo5Yl/ghpcCP6zue1rfMW9F/l264CnpjT2RqUhqqkE+vASL50YtBxilqy0HGeaMl2R+u8BV5XSQt6xDL09ub+kK9I+quv/rddWjtNUdsFusy+bZczXxRzlU0Q3AfRJgjuQ+9NEDNIvnz8DYBdhofOTvVND40Ec1K1V17WbCwB1WFQ28G4yKdv+gKAzkt9E0A7wCgA6lvA9bzaqOsobevrSdXBuJ4XHXUNw+UcUXuoHAbmmWg9OkMoQH3fhP6EWQTUD88YAtOaAAYwlRPAsJZZBNS3AfSTLzXZufPUoLaH9SuD0fBCJFNgwFO51MO4nlUYnZurlwcDPekvJIxy4Rzrd5Iskni8NATOYYUAgzNCNofYW09w0Mt+oElC1ybi8ayLw4N+t7l4/P5aT76ZUppVJzbyK5x+pREp9vgP \ No newline at end of file diff --git "a/docs/_pics/draw.io/\345\215\241\345\214\205\351\241\271\347\233\256.drawio" "b/docs/_pics/draw.io/\345\215\241\345\214\205\351\241\271\347\233\256.drawio" deleted file mode 100644 index 056336aa..00000000 --- "a/docs/_pics/draw.io/\345\215\241\345\214\205\351\241\271\347\233\256.drawio" +++ /dev/null @@ -1 +0,0 @@ -7Vxbb6s4EP41PBaBwWAegSbnSGdXqrbSnt19qRxwErYEcoD0sr9+bW4B4yQ0DaRtUlVNsc0Q5vs8Mx4PSJq7evmW4PXy99gnoQQU/0XSbiUAVEVV6AdreS1aICwbFkngl4O2DffBf6Q6s2zdBD5JWwOzOA6zYN1u9OIoIl7WasNJEj+3h83jsH3VNV6QTsO9h8Nu68/Az5ZFK7KUbft3EiyW5ZVNrexY4Wps2ZAusR8/N5q0iaS5SRxnxX/x7F/2/YES4hnVYT6uOLPS018Yr21X+WdBNnfJ5s+faPnj8WYravXikpAqf1oc0n9Kka3e/dLqu01IlB26anWlfqLLm3nC4YY07y7NXitd43RdQDgPXgiV5SyzFdOESv9dkyRYkYwk9DiKI3qGg8NgEdFDj35X1u7UKlbogY/TJROSHwSrHGZnHkdZyTEVbNtvg9WC3k4YzOjfVephQj9d7C3Jwx/ED9KHuyT2N14mp08LelZ5JyTJyAvHkgN6UxtofSMxvaHklZ5XSoGaXsgp5wpAJXeet8zTUdG0bJBOL4fhkuuLWjJo0qHE6Q2YaYcxizdZGETEradfR/dMTwGdUL8xXt/FaZAFMYNtFmdZvGoMsEs8s3gtQrdBBgr0ml1/9bJgVkfGz6kmr17TX+GDP3sIojTDkZfjHYShG4dxkn9ZDUxMzUG0nZ7mB1Rw1VcyaihgVb0NrGp0gVUrQzEKsvphZA/i1gCE1zOy0UTZCe06DqIsvyPo0F9FVnUoAbf+lCAV4uY9CMI9XXlP+dnoqcR1e4rjbRvVOXTUfDDkm4smtS2hPZT+lndDkskTKW5K3WGaWoz1gtSLH1I8J7KH13gWhEH2Km/SfPBQNEQVnSpfDAT2ZUwWwsMsrBRXWfAG6cTkOkjb3L862HtcJPEm8hu0nec/lVuwK2/EiNxxTZXnWGYZi0Rspggw9fxIkQMai8yDyCeJ7NErgqmPM0w/WHtKPx/xioQkjm7mCSE3a/pNbvJvQuWCqcoCn+n9Ml7fqADJ62gxmFFqk0Fkk0Ylg9GDDFkSP9ZxGOOA0P0UwxrAKvkP75j2eZhGoIAE05cEa3lF0pRywF1ieu3wRMwTc3ooCgCzzQFRwDGuXzI7JPhO5x45wASwPzyYh/Gzt8RJJrOZOGPyNOd5GWTkns4+NvKZjuu6MR8S5OsiQiEw0wxjSGRQGxlDF8zOUacnEkxPI8zKudJCx/i1iauOmzSfRdQ+Kqq+ftl20v8W+ecEShaUkCFNDAkhyTErufRrFqLLgTwHqMozDvgWTmVQ1wR118qBn3arwPfDXSypjHVhRUYyz/XxAfMMhsK/uv4ABDAlS5dsdCXA/mjNBDI8MwfUwz66rRyR+nasG0poahetDqhbzu8J5pahCBSrDadZUXpkIPPKWqaSY7MWy5VsV5rokjOVbDUfo0r29DoDRct2oPQMj4ajSY8VE4l8myU+mXJDnKaB10aoOIH4nbzncbpq6AIKVFG1JSTEWfDUvqZIP+UV7tiqeguFwU1YxGk4jTeJR8qTmqlJTo6qHRCU4WRBso6gHK36ro8HEIi86BsB7LG8+bQ4qzpo4WMdiTMw9ssZGmaRoyxMarrG0XvMObXQNrPozHhTsz3JjbcrWUX4pEm2wgw8s/SoYcWLq16mFQdcJG2e24pXc37IMGoobfILU0EKURXFpLXhPb02uzmDjJI3qtg/SyriUzWRbmtH9VX6wMOJf5Tui0l8aw0Ig8Y5RGDJiDkBTYGqCU1UxZPnSuaBbrrgAkBRPjYo2nBeaY8PUkrgFbbQZ6M01mnfsuFXB8Xw1lCLNn03BwfzT1oP/3QJq4x6vff26LMtSOMF7Qg/qUbxa2NYuVG6O1pWxNfZgl9IPGlsq4mqAk5jRepUBWRWAqn77AqLf6HkKHnXrWRP8rMUCbmshZoXG17NSy4FwjZJ9G4WkbqnMe1Lt/rg8oIDxAcHZ44NupmlC8DE+NiY9NgSP1GyiGoxef2LWSKZmovy+G96fKPICqhbbl9KY1UcvTaP7hoVelt7dmQEQKPG3EXuW2KcM1LQ9Q5z4HGxAuxykJN0omCBv5A1Rqxwgl1j1Hdbo5sZK5YZxcJDk5x8q5H+tYbc32hWc5wyPBhlXwwKdsLqwqBRYoNqXjUY8wPPH/GFBW3AbK8J6wR2ExjRFuVwwPTY+73U+qxd8384dnDrvrPXbVXbNtfy/v7l/SLURi3v17sr+Y9sa0fxgPz+hnZ+w9ujVv/4VYD0GfN0GrDaIPHK7xt8dwSZnKCB94n1QVd4nxJbCNFpsO0IGhvb7r7k8Lst3UXQNQfKpMC2VReWAOhjGvXdC+NxtuIsiTo6y5Ym+dq5VfF3wTzp2AxRrrwqLRqHKNbVQ3Sctim3tzT04/1/R9TIXgJ28x2nKvzd4yUgK/9tbsSzLpslxqg9oIOd274ZsjfVKe0m4pgVTEZ7jutnXzzD3XUZJ3+0ghp+G5R7rGgqWUa/+sGeHFA/CQd0rjy0NwcGq2KDogTKUBzQ2QR3po2uKwcA1M7Ogd2VFafnQD790W1uEPLGLws0tMyPBrQopTMU0AZz6zbMu1wJ7V8hXAoHPoDBP8GjPF8usG9n46B25J46L8jgURw6qBel9cZI/bhV0Rxd3sO8y2Uh3nVtT3tqOXufp1UVs8vvwVb3UJQjHG2FNcqGis6VH+qGKVB7nZFrPTOiwKH0Lkq/XbbxhYgzvggcXdKEzmx+B82Zff6HLCHkgiHzWKB5QaBf6dqpgDZ2J8/e62eRhCbbdRLzoUqrHvayfSmfJ9cFr6dRq1k3iic1epQnfclnVhD3RDvk05R9J7PJv29E61uJerLp3ON9k18TQ071xrEPHvEYGkY/z/vWWmKkiq8zaDGxsTtr8v6H6h0tz5rkJcXsEaI97yS6YLvf4ZdgI33UcmGjRxblU7yRSLV2VCI1NSvyqNpwL2TcncV4b/G+KB3NdiT1Rte1eP+dFDIsSzYty0KmaQHLQKgbop1ortLD7YvMC3O/fRu8Nvkf7Vnbdps4FP0aHpOFuNjw6AuJ23EnTpiZjPuSJUAGpTKiQvjSr6/ExTZGdTxdcdO1JnkJbIkjae+jLZ1EM0fLzS2DWfKJRohohh5tNHOsGQbQgS5+SWRbIbYOKiBmOKo77QEff0PNlzVa4AjlrY6cUsJx1gZDmqYo5C0MMkbX7W4LStqjZjBGHcAPIemijzjiSb0KS9/jE4TjpBnZqRuWsOlbA3kCI7o+gExPM0eMUl490eBZzt/QCQwEh2W/6suGJ+RfeWS+nM5RiuMh1Of0n/BqH2q5GSEiyL+pXsVDHbLVejrabrUMpfylUZuROqEhvtvMgu3N9N78NH5++MMv3I/1J/oKkgI1q+sRMcgwz2AqCeLbmvTe10KyMlzQlF/lZUoMRAdgZZt9o3iKy9+epQ1HmutoXk9zTG2ga56tOY42rJCB5trywRXdvGZIMfdq1DpGpdBuAgZHG4knfCmVAHKSnNEvaEQJZQJJaYrkBDEhRxAkOE7FaygIRAIfrhDjWOTToG5Y4iiSwwzXCebIz2Aox1yL3SMwRos0QpJGfTctGQBtjrLxBX3AQVbcIrpEnG3Fd3WUJnnrLdmrX9f7/AaGVWHJQW43mQzrLRXvAhuHWVengzo1NpZ/tUCrz+FDMB0S5+E5uH1UpsaRIG1iVNQdaKWQ5VJUgl6bS0PBpaXgElivQKa3vv3X6/sk+zwATupvsw9/FueQidJoIK1RpimBeY7DU7muGaZe/oiWCOZJqQLYcYqijoH+HKMHlNl6l7EGY4hAjlftMVUs1iPMKE75XjCz19LLPZIhpwULUf3NocUdhQH26TgcshjxTpxS0d2azxI5s4r0ceXOWDT5gHoOnU7pQiFykQtdancLWGNsXOiYdmGxQVAX7eSJOLIy+RhCFv23XSdyZuGEKJSJVRn42FUlVuDYln1Jqzv2OsO9dmQum7oN+nbfAUZnv1qK5HuN3aoUEnSExCnm5SXnzZSLbORE1gvKOUZg9nqXdFbH+F2UC2+zSfLFyv4Gj3cT994bBeZfb3Kf2aWFwPplL1M2Dsay+/vVRtq7ccbdRpEmr3G3UaZJd4O/H8f8x3o57s+dx5Z1Os7rncfxOF99fPKyYnqnz2yq338zZ+dcumCeVfXpAm+kZocKZ4hhMQV0arPt6seW8PIFL8satrSXuoAW5rjDx3gZi0URHMil5SEUB8jNCIYJenpAEc6fZoxGRciv81V8yaLj6Kbc7+5My7mMf/t9b7Z1oEuosZiv+HzytX+qHpU8nuXfjsq/fcQPrLiK9VtbscJELuDLR+oDXSH/hXxZKf8ZvrynCbx8h2qx+GPH/pWlad+6tttXKLt7GAJb4e32hUg3/oeku8b1W9Nudmh/LzLURYZ7XGScZVIXO6OsjnDvZf5ZZf4v01G87v+EX90m9/8HMb3v \ No newline at end of file diff --git "a/docs/_pics/draw.io/\346\266\210\346\201\257\351\230\237\345\210\227.drawio" "b/docs/_pics/draw.io/\346\266\210\346\201\257\351\230\237\345\210\227.drawio" deleted file mode 100644 index e5f1acab..00000000 --- "a/docs/_pics/draw.io/\346\266\210\346\201\257\351\230\237\345\210\227.drawio" +++ /dev/null @@ -1 +0,0 @@ -5VjJbtswEP0aHgNoX46Sl/TQogUcIOipoCRaZkuLLkXHdr6+Q4mKtSVODBsI3JOHjzMj8r2hhjKyJ+v9vcCb1TeeEYYsI9sje4osyzRMA34UcqgR1zBrIBc0005HYEGfSROp0S3NSNlxlJwzSTddMOVFQVLZwbAQfNd1W3LWfeoG52QALFLMhugjzeRK7yIwjvgXQvOVfrJl6Ik1bnw1UK5wxnctyJ4heyI4l7XFk99q/ZbBcAIcVn51ZMPTMWq9nxAGPM/rIRg6ujNr0P5WBSlke1HzVwJ+FVn+8PBzYUy8pf/X9mePkX+nV/OE2Za0l1fKQ0MW7HGjTJxKLpAd71ZUksUGpwrcQYEAtpJrtTsTTJ2QCEn2r67UbO36nvA1keIALvuG7zpCV5ejh7ujVA20aqnUqId1ceQvea02qZqXD3Bkn+aIFFmk6hJGKcNlSdMxUkg2KMuTlLQ27Y5susEEYVjSp276MSb0E35wWsgj436Xcb9HZcm3IiU6pl1ivTSm+XYeiUVO5CBPpcrLns8Xyh0KNXNRMEehiWYhggIJwPBQbKMoqKY8FEyqKQtFMzRzUByj2BvIK/i2yEimxTxxAJaUsQln6qxArJ25JMgcwEsp+B/SmgmsxPa8Cx2ZoMu8NzwzpjVSP861Do13phY+CucomlZagGFeVAtighr+mBah59v4Qlo4zifTwh/RYsh8QzhMgTTRHFkeg5XFiQArV5Z2DsPKcFHkKufQRbFRRU2rQwTDCIX2q30kPTAKCr6jlSS11F+TFwCnf/KqAL5vJWQh4zIvg5Sk6ZjMSeA68Mq8iMzm6TY1KvPV+lRw+33KMXoNJjyvUfWPaD/PlRtVePtKWT2G+1X/XqX6ja2f58pKNReaW5aqf2sLz5TKfzvNtZUafsoofox1ORAM3vmyK5EgJX3GSeWg2sNGrbJatxsjdwoI3kpe1h+yKgAzmhdgM7JUqVQjofB9GWlYctXKSuhstMgf1GB651yo7Xhdms2RvjNWLNa12o45/D66SeKd4LMR7/wfxNvWZyN+/EMT7rxwBdb3X2d4fTZf1BlcrG9Kr34fcN+llv1xtWB4/Kut7iPH/yvt2T8=7ZjbctowEIafRpfp+Ix9aXNIO9NOO0kP6aVsC+NGWFQWBfL0Xdky+JRAoW5mkl6h/SWt5P1WrEbIHC+31xyvFh9YTCgytHiLzAkyDF3TNfiRyq5UbE0vhYSnsRp0EG7TB1LNVOo6jUneGCgYoyJdNcWIZRmJREPDnLNNc9ic0eaqK5yQjnAbYdpVv6WxWJSqZXsH/S1Jk4Va2dDUvpe4GquEfIFjtqlJ5hSZY86YKFss/CH3b2gUhxDDYlw5s4oTvnl35d7Ns693D9uV+PIzpjfzq4Or5XZMKAR/VprQUC4bvU97238tJ5k4tmq10mmu1cf8wnRN6l+Xi10VawjRSjZxJBhHZrBZpILcrnAkxQ3kF2gLsZTB0aGpHBIuyLYF68j29VrQrglbEsF3MG9bMSzdqIy1lLmp4VfSokbeVRpWCZfs/Rp1JipYfxA483jgSBb7MtfBiijO8zTqixSJO6l+XpxqkbB7IlFpnFAs0l/NNfvCo1b4xNJMHDCMmhhGrfjmbM0joubUk7HlRtef9iMwT4jo+ClQ7b/5fHpWl97URu4MeTqaegiyxoWGgwIT+W7R5SB3XHQZyJ+iqYWCAAVOhzln6ywmsSJ85KjMU0rHjMpTBXPN2CZubIGeC87uSa3HNULTcQY8XGbrdBnd06UbPUllDXW87DMBjZA3Q/6kAAQN/a8CIjogGvUB8pyRif8lIF17bkJOD6EujwoDdAEwf4YMh8LOgpBDK5EtNdjzioaNfFsO9mwUaMWsSXHewPSRZz5anKIdTYHrCfUpLBPgfbgXcHSfFGnxcS3AC+mHP3cjEkV98EPXtuDfdTj4+vHa18t+sOI3urz4QTz47g4MrTK+S+ONXZmTbb1zslPWRUUTbqlFXTn2x/NcxXVPdtcP8NTqup/4iJ+Bq6v7ctPD+p8eF6eHd3l6XIT5ufC177zemfhGT7sZmF71ETV8Mj7aMu9QhDIomtw4ydMHHBYD5HFdyV0W+7YDZE9AwWvB8vK9QU7ANE0yaFMyl65kbU0jTH0lCyarew7FPs2Sz9KYXFlDVmKndQ3rKcV9GWQMVYl1/RXTMFs0nOeG0fMoADdXuLbCLVZdYa3uDbi4yxfEOnfjl8+w/Wdmn4TQHAxh92Xg9Z4n3TqNxhkHCszDI2tZmg4v1eb0Nw==7Zlbc6s2EIB/jR7d4SYMj2DstDNtTzp2p6dPHQEypsaIghzb+fVdgQjX2qkTmk7Tp0graYX3W2l3FaQvDueHnGS7H1hIE6Qp4RnpHtI0VVEV+CMkl0qCsRREeRzKSY1gHT/TeqWUHuOQFp2JnLGEx1lXGLA0pQHvyEies1N32pYl3V0zEtGBYB2QZCj9JQ75rpKamtLIv6VxtJM7a4ocOJB6rhQUOxKyU0ukL5G+yBnjVYv5v4vv15SE+GDDcl61sraTdynSy1O2US77n1dL59nbPs5mjarDeUETMP6q6kJDquyMXtf28mtzmvJbu9Y7DVTra3PtfnnYzvZ/5Mn+Jx58Z+ZyifJEkiNt/7qCX2pbg4ky0SQBZznS3dMu5nSdkUAIT+BfINvxgzCOCk2pkOacnnuwbny+2jLaA2UHyvMLrDvXDCs10mMN2T01+GvRrkXekjIiHS560au1mUhj/Q3DqbcNR9PQEb4OvSAhRREHY5ai4cDV77NTyxJ4xBK1LKcJ4fFTd88x88gdHlmc8gbDvIth3rNvwY55QOWatjP21KjqdT2c5BHlAz0lqpfffD89bUhviZG1QraKljYCr7GgYSJXR45VDpnIWpRDGnKWaGkg10WuOWCes2Ma0lASvnFUtnGSLFgiThWs1UNMrdAAecFztqetEUvzddOc8HAZVheHNjxdqjbiVMZUx0u/E9Ac2SvkeCUgaKjvCoiqgGg+Bsg25zr5JwGpykcTMkYIDXnUGGAIgDkrpJkJfJnr59CKREtOtu2ygZGDxWQbI1cpV3nleYOug2z9L4NTcEli4PqK+ORXDvC9/yIgwT4q3eLLkYMWOg5/awU0CMbg+xY24HadDr56O/aNsp8s+OG3Bz+wR375Ch2l7vwqOt/guuud24PeRfbeFDQhSy3jyq3I8FHB1VB6UdG+L7oaxnU9E0dX85PmRlrP7P3z91p8mnVdz8T45p8UXz8nte/EN7+uZmJ61oCeMI9yKAYQIUrxLracFvEz8csJ4rLNxEeWn41dhD2QkCNnRfUcIBaQJI5SaCd0K1SJ0BdDle5IMWci+BYQi+M02oiONzOmDJRmL0saiZRjDqRNFShrj2rTgMSHHIRdUr/ISmN8Dji69m+DM1LDQ6IJWSYknTLjNIYJK8aS2CCT/e8z7F9t+FUE9XcguM5+PGyi3x7PfDNLV7tsUzjp2OuVqEGgarCaigPKRdsSZaGoL6AxH3B6S0243W618bIgNH0TT1kT9tMEc3ieJisJR3G8w5vYvWUBPcf8a60D2q1V0GsWic47lRJVOnDbO2+WHPpHJj2DkgPfl/QYuJezTlZyjNp6/EHPXYj3OrgJHK98V7DEfeC69VPS8PlueGN3j3XKxPNA5w6QovqiDsB3ymeI/lV9iMNQbDN6vzQ30JTvCH3WeHhhWBMF4FFo4498/0O7Cs2eDBp0m398VQez+e+hvvwT \ No newline at end of file diff --git a/docs/_pics/hbase/h_1.png b/docs/_pics/hbase/h_1.png deleted file mode 100644 index a3c980dd..00000000 Binary files a/docs/_pics/hbase/h_1.png and /dev/null differ diff --git a/docs/_pics/hbase/h_10.jpg b/docs/_pics/hbase/h_10.jpg deleted file mode 100644 index 8a8808f4..00000000 Binary files a/docs/_pics/hbase/h_10.jpg and /dev/null differ diff --git a/docs/_pics/hbase/h_11.jpg b/docs/_pics/hbase/h_11.jpg deleted file mode 100644 index 0d661cbe..00000000 Binary files a/docs/_pics/hbase/h_11.jpg and /dev/null differ diff --git a/docs/_pics/hbase/h_12.png b/docs/_pics/hbase/h_12.png deleted file mode 100644 index 106e433d..00000000 Binary files a/docs/_pics/hbase/h_12.png and /dev/null differ diff --git a/docs/_pics/hbase/h_13.png b/docs/_pics/hbase/h_13.png deleted file mode 100644 index 196d2a2a..00000000 Binary files a/docs/_pics/hbase/h_13.png and /dev/null differ diff --git a/docs/_pics/hbase/h_14.png b/docs/_pics/hbase/h_14.png deleted file mode 100644 index 34de5769..00000000 Binary files a/docs/_pics/hbase/h_14.png and /dev/null differ diff --git a/docs/_pics/hbase/h_15.png b/docs/_pics/hbase/h_15.png deleted file mode 100644 index c27ebee1..00000000 Binary files a/docs/_pics/hbase/h_15.png and /dev/null differ diff --git a/docs/_pics/hbase/h_16.png b/docs/_pics/hbase/h_16.png deleted file mode 100644 index 58d687c2..00000000 Binary files a/docs/_pics/hbase/h_16.png and /dev/null differ diff --git a/docs/_pics/hbase/h_17.png b/docs/_pics/hbase/h_17.png deleted file mode 100644 index 7061e461..00000000 Binary files a/docs/_pics/hbase/h_17.png and /dev/null differ diff --git a/docs/_pics/hbase/h_18.png b/docs/_pics/hbase/h_18.png deleted file mode 100644 index 729e2375..00000000 Binary files a/docs/_pics/hbase/h_18.png and /dev/null differ diff --git a/docs/_pics/hbase/h_19.jpg b/docs/_pics/hbase/h_19.jpg deleted file mode 100644 index 81b4ef27..00000000 Binary files a/docs/_pics/hbase/h_19.jpg and /dev/null differ diff --git a/docs/_pics/hbase/h_2.jpg b/docs/_pics/hbase/h_2.jpg deleted file mode 100644 index 0592ad2a..00000000 Binary files a/docs/_pics/hbase/h_2.jpg and /dev/null differ diff --git a/docs/_pics/hbase/h_20.png b/docs/_pics/hbase/h_20.png deleted file mode 100644 index baa82860..00000000 Binary files a/docs/_pics/hbase/h_20.png and /dev/null differ diff --git a/docs/_pics/hbase/h_21.jpg b/docs/_pics/hbase/h_21.jpg deleted file mode 100644 index 8efad160..00000000 Binary files a/docs/_pics/hbase/h_21.jpg and /dev/null differ diff --git a/docs/_pics/hbase/h_22.jpg b/docs/_pics/hbase/h_22.jpg deleted file mode 100644 index 27bbff6b..00000000 Binary files a/docs/_pics/hbase/h_22.jpg and /dev/null differ diff --git a/docs/_pics/hbase/h_23.jpg b/docs/_pics/hbase/h_23.jpg deleted file mode 100644 index 7a191277..00000000 Binary files a/docs/_pics/hbase/h_23.jpg and /dev/null differ diff --git a/docs/_pics/hbase/h_24.jpg b/docs/_pics/hbase/h_24.jpg deleted file mode 100644 index 1067eeac..00000000 Binary files a/docs/_pics/hbase/h_24.jpg and /dev/null differ diff --git a/docs/_pics/hbase/h_25.jpg b/docs/_pics/hbase/h_25.jpg deleted file mode 100644 index 8e0dd086..00000000 Binary files a/docs/_pics/hbase/h_25.jpg and /dev/null differ diff --git a/docs/_pics/hbase/h_26.png b/docs/_pics/hbase/h_26.png deleted file mode 100644 index 9b7fa021..00000000 Binary files a/docs/_pics/hbase/h_26.png and /dev/null differ diff --git a/docs/_pics/hbase/h_3.jpg b/docs/_pics/hbase/h_3.jpg deleted file mode 100644 index 27f0cec0..00000000 Binary files a/docs/_pics/hbase/h_3.jpg and /dev/null differ diff --git a/docs/_pics/hbase/h_4.jpg b/docs/_pics/hbase/h_4.jpg deleted file mode 100644 index a1b7b87a..00000000 Binary files a/docs/_pics/hbase/h_4.jpg and /dev/null differ diff --git a/docs/_pics/hbase/h_5.jpg b/docs/_pics/hbase/h_5.jpg deleted file mode 100644 index e1a1b630..00000000 Binary files a/docs/_pics/hbase/h_5.jpg and /dev/null differ diff --git a/docs/_pics/hbase/h_6.jpg b/docs/_pics/hbase/h_6.jpg deleted file mode 100644 index b73f85d4..00000000 Binary files a/docs/_pics/hbase/h_6.jpg and /dev/null differ diff --git a/docs/_pics/hbase/h_7.jpg b/docs/_pics/hbase/h_7.jpg deleted file mode 100644 index 70d82114..00000000 Binary files a/docs/_pics/hbase/h_7.jpg and /dev/null differ diff --git a/docs/_pics/hbase/h_8.jpg b/docs/_pics/hbase/h_8.jpg deleted file mode 100644 index 60af46e4..00000000 Binary files a/docs/_pics/hbase/h_8.jpg and /dev/null differ diff --git a/docs/_pics/hbase/h_9.jpg b/docs/_pics/hbase/h_9.jpg deleted file mode 100644 index e571c5fd..00000000 Binary files a/docs/_pics/hbase/h_9.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_1.png b/docs/_pics/java-notes/book/book_1.png deleted file mode 100644 index 19d4da8b..00000000 Binary files a/docs/_pics/java-notes/book/book_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_10.jpg b/docs/_pics/java-notes/book/book_10.jpg deleted file mode 100644 index 14bac71c..00000000 Binary files a/docs/_pics/java-notes/book/book_10.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_11.jpg b/docs/_pics/java-notes/book/book_11.jpg deleted file mode 100644 index 0dd78c71..00000000 Binary files a/docs/_pics/java-notes/book/book_11.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_12.jpg b/docs/_pics/java-notes/book/book_12.jpg deleted file mode 100644 index a76d4c70..00000000 Binary files a/docs/_pics/java-notes/book/book_12.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_13.jpg b/docs/_pics/java-notes/book/book_13.jpg deleted file mode 100644 index 2230c4aa..00000000 Binary files a/docs/_pics/java-notes/book/book_13.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_14.jpg b/docs/_pics/java-notes/book/book_14.jpg deleted file mode 100644 index 86686f9a..00000000 Binary files a/docs/_pics/java-notes/book/book_14.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_15.jpg b/docs/_pics/java-notes/book/book_15.jpg deleted file mode 100644 index c01e0ed1..00000000 Binary files a/docs/_pics/java-notes/book/book_15.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_16.jpg b/docs/_pics/java-notes/book/book_16.jpg deleted file mode 100644 index 7454069e..00000000 Binary files a/docs/_pics/java-notes/book/book_16.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_17.jpg b/docs/_pics/java-notes/book/book_17.jpg deleted file mode 100644 index 00769928..00000000 Binary files a/docs/_pics/java-notes/book/book_17.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_18.jpg b/docs/_pics/java-notes/book/book_18.jpg deleted file mode 100644 index 1837ec8c..00000000 Binary files a/docs/_pics/java-notes/book/book_18.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_19.jpg b/docs/_pics/java-notes/book/book_19.jpg deleted file mode 100644 index 8faa080a..00000000 Binary files a/docs/_pics/java-notes/book/book_19.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_2.png b/docs/_pics/java-notes/book/book_2.png deleted file mode 100644 index bf39b5ee..00000000 Binary files a/docs/_pics/java-notes/book/book_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_20.jpg b/docs/_pics/java-notes/book/book_20.jpg deleted file mode 100644 index 3e18c643..00000000 Binary files a/docs/_pics/java-notes/book/book_20.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_21.jpg b/docs/_pics/java-notes/book/book_21.jpg deleted file mode 100644 index b893c5fb..00000000 Binary files a/docs/_pics/java-notes/book/book_21.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_22.jpg b/docs/_pics/java-notes/book/book_22.jpg deleted file mode 100644 index 481b7ffd..00000000 Binary files a/docs/_pics/java-notes/book/book_22.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_23.jpg b/docs/_pics/java-notes/book/book_23.jpg deleted file mode 100644 index 94a4d901..00000000 Binary files a/docs/_pics/java-notes/book/book_23.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_3.png b/docs/_pics/java-notes/book/book_3.png deleted file mode 100644 index b2062a70..00000000 Binary files a/docs/_pics/java-notes/book/book_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_4.jpg b/docs/_pics/java-notes/book/book_4.jpg deleted file mode 100644 index 2a76e311..00000000 Binary files a/docs/_pics/java-notes/book/book_4.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_5.jpg b/docs/_pics/java-notes/book/book_5.jpg deleted file mode 100644 index 9c57f957..00000000 Binary files a/docs/_pics/java-notes/book/book_5.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_6.jpg b/docs/_pics/java-notes/book/book_6.jpg deleted file mode 100644 index 9f380a82..00000000 Binary files a/docs/_pics/java-notes/book/book_6.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_7.jpg b/docs/_pics/java-notes/book/book_7.jpg deleted file mode 100644 index a856d63f..00000000 Binary files a/docs/_pics/java-notes/book/book_7.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_8.jpg b/docs/_pics/java-notes/book/book_8.jpg deleted file mode 100644 index 49d6012a..00000000 Binary files a/docs/_pics/java-notes/book/book_8.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/book/book_9.jpg b/docs/_pics/java-notes/book/book_9.jpg deleted file mode 100644 index 0a27b6b3..00000000 Binary files a/docs/_pics/java-notes/book/book_9.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/1.png b/docs/_pics/java-notes/dataStructure/1.png deleted file mode 100644 index 048a68a4..00000000 Binary files a/docs/_pics/java-notes/dataStructure/1.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/2.png b/docs/_pics/java-notes/dataStructure/2.png deleted file mode 100644 index 022ba597..00000000 Binary files a/docs/_pics/java-notes/dataStructure/2.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/3.png b/docs/_pics/java-notes/dataStructure/3.png deleted file mode 100644 index 81810b7c..00000000 Binary files a/docs/_pics/java-notes/dataStructure/3.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/4.png b/docs/_pics/java-notes/dataStructure/4.png deleted file mode 100644 index 2625c4d8..00000000 Binary files a/docs/_pics/java-notes/dataStructure/4.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/array/array_1.png b/docs/_pics/java-notes/dataStructure/array/array_1.png deleted file mode 100644 index 1fdfd6df..00000000 Binary files a/docs/_pics/java-notes/dataStructure/array/array_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/array/array_2.png b/docs/_pics/java-notes/dataStructure/array/array_2.png deleted file mode 100644 index 5550a76e..00000000 Binary files a/docs/_pics/java-notes/dataStructure/array/array_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/array/array_3.png b/docs/_pics/java-notes/dataStructure/array/array_3.png deleted file mode 100644 index 839c48e4..00000000 Binary files a/docs/_pics/java-notes/dataStructure/array/array_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/array/array_4.png b/docs/_pics/java-notes/dataStructure/array/array_4.png deleted file mode 100644 index 818492ea..00000000 Binary files a/docs/_pics/java-notes/dataStructure/array/array_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/array/array_5.png b/docs/_pics/java-notes/dataStructure/array/array_5.png deleted file mode 100644 index 429ca838..00000000 Binary files a/docs/_pics/java-notes/dataStructure/array/array_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/avl/avl_1.png b/docs/_pics/java-notes/dataStructure/avl/avl_1.png deleted file mode 100644 index cb24300b..00000000 Binary files a/docs/_pics/java-notes/dataStructure/avl/avl_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/avl/avl_10.png b/docs/_pics/java-notes/dataStructure/avl/avl_10.png deleted file mode 100644 index d16d06fd..00000000 Binary files a/docs/_pics/java-notes/dataStructure/avl/avl_10.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/avl/avl_11.png b/docs/_pics/java-notes/dataStructure/avl/avl_11.png deleted file mode 100644 index f075d70b..00000000 Binary files a/docs/_pics/java-notes/dataStructure/avl/avl_11.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/avl/avl_12.png b/docs/_pics/java-notes/dataStructure/avl/avl_12.png deleted file mode 100644 index 3888a84e..00000000 Binary files a/docs/_pics/java-notes/dataStructure/avl/avl_12.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/avl/avl_13.png b/docs/_pics/java-notes/dataStructure/avl/avl_13.png deleted file mode 100644 index 31fe8bce..00000000 Binary files a/docs/_pics/java-notes/dataStructure/avl/avl_13.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/avl/avl_14.png b/docs/_pics/java-notes/dataStructure/avl/avl_14.png deleted file mode 100644 index ea6332f3..00000000 Binary files a/docs/_pics/java-notes/dataStructure/avl/avl_14.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/avl/avl_15.png b/docs/_pics/java-notes/dataStructure/avl/avl_15.png deleted file mode 100644 index 0f02b43b..00000000 Binary files a/docs/_pics/java-notes/dataStructure/avl/avl_15.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/avl/avl_2.png b/docs/_pics/java-notes/dataStructure/avl/avl_2.png deleted file mode 100644 index 22f8286a..00000000 Binary files a/docs/_pics/java-notes/dataStructure/avl/avl_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/avl/avl_3.png b/docs/_pics/java-notes/dataStructure/avl/avl_3.png deleted file mode 100644 index 1d31f6d0..00000000 Binary files a/docs/_pics/java-notes/dataStructure/avl/avl_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/avl/avl_4.png b/docs/_pics/java-notes/dataStructure/avl/avl_4.png deleted file mode 100644 index 7827e574..00000000 Binary files a/docs/_pics/java-notes/dataStructure/avl/avl_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/avl/avl_5.png b/docs/_pics/java-notes/dataStructure/avl/avl_5.png deleted file mode 100644 index f329dae5..00000000 Binary files a/docs/_pics/java-notes/dataStructure/avl/avl_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/avl/avl_6.png b/docs/_pics/java-notes/dataStructure/avl/avl_6.png deleted file mode 100644 index f0d72c7f..00000000 Binary files a/docs/_pics/java-notes/dataStructure/avl/avl_6.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/avl/avl_7.png b/docs/_pics/java-notes/dataStructure/avl/avl_7.png deleted file mode 100644 index e83e07d7..00000000 Binary files a/docs/_pics/java-notes/dataStructure/avl/avl_7.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/avl/avl_8.png b/docs/_pics/java-notes/dataStructure/avl/avl_8.png deleted file mode 100644 index 3101c610..00000000 Binary files a/docs/_pics/java-notes/dataStructure/avl/avl_8.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/avl/avl_9.png b/docs/_pics/java-notes/dataStructure/avl/avl_9.png deleted file mode 100644 index 23ef04c9..00000000 Binary files a/docs/_pics/java-notes/dataStructure/avl/avl_9.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/bst_00.png b/docs/_pics/java-notes/dataStructure/bst/bst_00.png deleted file mode 100644 index babc7b96..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/bst_00.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/bst_01.png b/docs/_pics/java-notes/dataStructure/bst/bst_01.png deleted file mode 100644 index 2764e6a5..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/bst_01.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/bst_02.png b/docs/_pics/java-notes/dataStructure/bst/bst_02.png deleted file mode 100644 index ccd50d82..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/bst_02.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/bst_03.png b/docs/_pics/java-notes/dataStructure/bst/bst_03.png deleted file mode 100644 index 4b1d56a7..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/bst_03.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/bst_04.png b/docs/_pics/java-notes/dataStructure/bst/bst_04.png deleted file mode 100644 index c19043c2..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/bst_04.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/bst_05.png b/docs/_pics/java-notes/dataStructure/bst/bst_05.png deleted file mode 100644 index 05cacf0a..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/bst_05.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/bst_06.png b/docs/_pics/java-notes/dataStructure/bst/bst_06.png deleted file mode 100644 index 25dd1819..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/bst_06.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/bst_07.png b/docs/_pics/java-notes/dataStructure/bst/bst_07.png deleted file mode 100644 index 5d0aadc7..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/bst_07.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/bst_08.png b/docs/_pics/java-notes/dataStructure/bst/bst_08.png deleted file mode 100644 index f0ff9ed6..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/bst_08.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/bst_09.png b/docs/_pics/java-notes/dataStructure/bst/bst_09.png deleted file mode 100644 index 3e3596db..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/bst_09.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/bst_10.png b/docs/_pics/java-notes/dataStructure/bst/bst_10.png deleted file mode 100644 index d93b45d9..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/bst_10.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/bst_11.png b/docs/_pics/java-notes/dataStructure/bst/bst_11.png deleted file mode 100644 index 5739bf60..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/bst_11.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/bst_12.png b/docs/_pics/java-notes/dataStructure/bst/bst_12.png deleted file mode 100644 index 84f35f4a..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/bst_12.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/bst_13.png b/docs/_pics/java-notes/dataStructure/bst/bst_13.png deleted file mode 100644 index 88677f2b..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/bst_13.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/bst_14.png b/docs/_pics/java-notes/dataStructure/bst/bst_14.png deleted file mode 100644 index 22cd89ad..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/bst_14.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/bst_15.png b/docs/_pics/java-notes/dataStructure/bst/bst_15.png deleted file mode 100644 index 8dd45a29..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/bst_15.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/bst_16.png b/docs/_pics/java-notes/dataStructure/bst/bst_16.png deleted file mode 100644 index b6b7e060..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/bst_16.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/bst_17.png b/docs/_pics/java-notes/dataStructure/bst/bst_17.png deleted file mode 100644 index b7a4fc43..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/bst_17.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/bst_18.png b/docs/_pics/java-notes/dataStructure/bst/bst_18.png deleted file mode 100644 index 48b3f41c..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/bst_18.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/bst_19.png b/docs/_pics/java-notes/dataStructure/bst/bst_19.png deleted file mode 100644 index 689a0d74..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/bst_19.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/bst_20.png b/docs/_pics/java-notes/dataStructure/bst/bst_20.png deleted file mode 100644 index e32f8e19..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/bst_20.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/stack_1.png b/docs/_pics/java-notes/dataStructure/bst/stack_1.png deleted file mode 100644 index 46c36a6e..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/stack_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/stack_2.png b/docs/_pics/java-notes/dataStructure/bst/stack_2.png deleted file mode 100644 index 03c91208..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/stack_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/stack_3.png b/docs/_pics/java-notes/dataStructure/bst/stack_3.png deleted file mode 100644 index 8bd22f76..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/stack_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/stack_4.png b/docs/_pics/java-notes/dataStructure/bst/stack_4.png deleted file mode 100644 index 5bf4fdbb..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/stack_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/stack_5.png b/docs/_pics/java-notes/dataStructure/bst/stack_5.png deleted file mode 100644 index 04e4cfcd..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/stack_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/stack_6.png b/docs/_pics/java-notes/dataStructure/bst/stack_6.png deleted file mode 100644 index bf97bff9..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/stack_6.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/bst/stack_7.png b/docs/_pics/java-notes/dataStructure/bst/stack_7.png deleted file mode 100644 index 4bf60208..00000000 Binary files a/docs/_pics/java-notes/dataStructure/bst/stack_7.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_1.png b/docs/_pics/java-notes/dataStructure/ds_1.png deleted file mode 100644 index 609571bf..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_10.png b/docs/_pics/java-notes/dataStructure/ds_10.png deleted file mode 100644 index 9970a6c4..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_10.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_11.png b/docs/_pics/java-notes/dataStructure/ds_11.png deleted file mode 100644 index 2f8b2d9a..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_11.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_12.png b/docs/_pics/java-notes/dataStructure/ds_12.png deleted file mode 100644 index f8037339..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_12.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_13.png b/docs/_pics/java-notes/dataStructure/ds_13.png deleted file mode 100644 index 0635fe9f..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_13.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_14.png b/docs/_pics/java-notes/dataStructure/ds_14.png deleted file mode 100644 index 73c04e73..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_14.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_15.png b/docs/_pics/java-notes/dataStructure/ds_15.png deleted file mode 100644 index 86943cd6..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_15.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_16.png b/docs/_pics/java-notes/dataStructure/ds_16.png deleted file mode 100644 index bfcf76c9..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_16.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_17.png b/docs/_pics/java-notes/dataStructure/ds_17.png deleted file mode 100644 index 82fa2a1e..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_17.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_18.png b/docs/_pics/java-notes/dataStructure/ds_18.png deleted file mode 100644 index 3132f10f..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_18.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_19.png b/docs/_pics/java-notes/dataStructure/ds_19.png deleted file mode 100644 index dbac0f3a..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_19.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_2.png b/docs/_pics/java-notes/dataStructure/ds_2.png deleted file mode 100644 index 9c954ec2..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_20.png b/docs/_pics/java-notes/dataStructure/ds_20.png deleted file mode 100644 index 273ae055..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_20.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_21.png b/docs/_pics/java-notes/dataStructure/ds_21.png deleted file mode 100644 index 03935e3f..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_21.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_22.png b/docs/_pics/java-notes/dataStructure/ds_22.png deleted file mode 100644 index 4d25b1d3..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_22.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_23.png b/docs/_pics/java-notes/dataStructure/ds_23.png deleted file mode 100644 index 810d88a4..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_23.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_3.png b/docs/_pics/java-notes/dataStructure/ds_3.png deleted file mode 100644 index 9c901543..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_4.png b/docs/_pics/java-notes/dataStructure/ds_4.png deleted file mode 100644 index f96603ad..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_5.png b/docs/_pics/java-notes/dataStructure/ds_5.png deleted file mode 100644 index e86adf53..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_6.png b/docs/_pics/java-notes/dataStructure/ds_6.png deleted file mode 100644 index 76feac80..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_6.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_7.png b/docs/_pics/java-notes/dataStructure/ds_7.png deleted file mode 100644 index f8b7a7aa..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_7.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_8.png b/docs/_pics/java-notes/dataStructure/ds_8.png deleted file mode 100644 index d926fa86..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_8.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/ds_9.png b/docs/_pics/java-notes/dataStructure/ds_9.png deleted file mode 100644 index 42aa0197..00000000 Binary files a/docs/_pics/java-notes/dataStructure/ds_9.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/graph/14_1.png b/docs/_pics/java-notes/dataStructure/graph/14_1.png deleted file mode 100644 index e4e8daac..00000000 Binary files a/docs/_pics/java-notes/dataStructure/graph/14_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/graph/14_2.png b/docs/_pics/java-notes/dataStructure/graph/14_2.png deleted file mode 100644 index 9515ba5b..00000000 Binary files a/docs/_pics/java-notes/dataStructure/graph/14_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/graph/14_3.png b/docs/_pics/java-notes/dataStructure/graph/14_3.png deleted file mode 100644 index e3856961..00000000 Binary files a/docs/_pics/java-notes/dataStructure/graph/14_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/graph/14_4.png b/docs/_pics/java-notes/dataStructure/graph/14_4.png deleted file mode 100644 index 5e8c304d..00000000 Binary files a/docs/_pics/java-notes/dataStructure/graph/14_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/hash/13_1.png b/docs/_pics/java-notes/dataStructure/hash/13_1.png deleted file mode 100644 index 5be4bb04..00000000 Binary files a/docs/_pics/java-notes/dataStructure/hash/13_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/hash/13_2.png b/docs/_pics/java-notes/dataStructure/hash/13_2.png deleted file mode 100644 index d096905f..00000000 Binary files a/docs/_pics/java-notes/dataStructure/hash/13_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/hash/13_3.png b/docs/_pics/java-notes/dataStructure/hash/13_3.png deleted file mode 100644 index f41c13dd..00000000 Binary files a/docs/_pics/java-notes/dataStructure/hash/13_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/hash/13_4.png b/docs/_pics/java-notes/dataStructure/hash/13_4.png deleted file mode 100644 index b9a9e8a2..00000000 Binary files a/docs/_pics/java-notes/dataStructure/hash/13_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/hash/13_5.png b/docs/_pics/java-notes/dataStructure/hash/13_5.png deleted file mode 100644 index d24ffc10..00000000 Binary files a/docs/_pics/java-notes/dataStructure/hash/13_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/hash/13_6.png b/docs/_pics/java-notes/dataStructure/hash/13_6.png deleted file mode 100644 index 6b422f33..00000000 Binary files a/docs/_pics/java-notes/dataStructure/hash/13_6.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/hash/13_7.png b/docs/_pics/java-notes/dataStructure/hash/13_7.png deleted file mode 100644 index fd31f45e..00000000 Binary files a/docs/_pics/java-notes/dataStructure/hash/13_7.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/heap/heap_1.png b/docs/_pics/java-notes/dataStructure/heap/heap_1.png deleted file mode 100644 index 679349fa..00000000 Binary files a/docs/_pics/java-notes/dataStructure/heap/heap_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/heap/heap_2.png b/docs/_pics/java-notes/dataStructure/heap/heap_2.png deleted file mode 100644 index 5a8f6121..00000000 Binary files a/docs/_pics/java-notes/dataStructure/heap/heap_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/heap/heap_3.png b/docs/_pics/java-notes/dataStructure/heap/heap_3.png deleted file mode 100644 index beb6d5f5..00000000 Binary files a/docs/_pics/java-notes/dataStructure/heap/heap_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/heap/heap_4.png b/docs/_pics/java-notes/dataStructure/heap/heap_4.png deleted file mode 100644 index 324a9606..00000000 Binary files a/docs/_pics/java-notes/dataStructure/heap/heap_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/heap/heap_5.png b/docs/_pics/java-notes/dataStructure/heap/heap_5.png deleted file mode 100644 index ca89c4c0..00000000 Binary files a/docs/_pics/java-notes/dataStructure/heap/heap_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_1.png b/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_1.png deleted file mode 100644 index 60319a11..00000000 Binary files a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_10.png b/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_10.png deleted file mode 100644 index 13a24ecb..00000000 Binary files a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_10.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_11.png b/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_11.png deleted file mode 100644 index 9562a271..00000000 Binary files a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_11.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_12.png b/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_12.png deleted file mode 100644 index 9a664338..00000000 Binary files a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_12.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_13.png b/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_13.png deleted file mode 100644 index 29c37b9c..00000000 Binary files a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_13.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_14.png b/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_14.png deleted file mode 100644 index 73c32f05..00000000 Binary files a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_14.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_15.png b/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_15.png deleted file mode 100644 index 4122bbf3..00000000 Binary files a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_15.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_16.png b/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_16.png deleted file mode 100644 index 05afeb8e..00000000 Binary files a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_16.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_17.png b/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_17.png deleted file mode 100644 index 4253aeac..00000000 Binary files a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_17.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_2.png b/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_2.png deleted file mode 100644 index 445fb91a..00000000 Binary files a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_3.png b/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_3.png deleted file mode 100644 index 987245a3..00000000 Binary files a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_4.png b/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_4.png deleted file mode 100644 index 4ad908d4..00000000 Binary files a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_5.png b/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_5.png deleted file mode 100644 index 2f460915..00000000 Binary files a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_6.png b/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_6.png deleted file mode 100644 index 33078fe0..00000000 Binary files a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_6.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_7.png b/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_7.png deleted file mode 100644 index 67f1a681..00000000 Binary files a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_7.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_8.png b/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_8.png deleted file mode 100644 index 3b859ee1..00000000 Binary files a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_8.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_9.png b/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_9.png deleted file mode 100644 index bd5c0895..00000000 Binary files a/docs/_pics/java-notes/dataStructure/linkedlist/linkedlist_9.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/lr/lr_1.png b/docs/_pics/java-notes/dataStructure/lr/lr_1.png deleted file mode 100644 index 37b6112c..00000000 Binary files a/docs/_pics/java-notes/dataStructure/lr/lr_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/lr/lr_2.png b/docs/_pics/java-notes/dataStructure/lr/lr_2.png deleted file mode 100644 index a54e2388..00000000 Binary files a/docs/_pics/java-notes/dataStructure/lr/lr_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/lr/lr_3.png b/docs/_pics/java-notes/dataStructure/lr/lr_3.png deleted file mode 100644 index 3f80501a..00000000 Binary files a/docs/_pics/java-notes/dataStructure/lr/lr_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/lr/lr_4.png b/docs/_pics/java-notes/dataStructure/lr/lr_4.png deleted file mode 100644 index 862d3244..00000000 Binary files a/docs/_pics/java-notes/dataStructure/lr/lr_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/minSpanTree/15_1.png b/docs/_pics/java-notes/dataStructure/minSpanTree/15_1.png deleted file mode 100644 index bef9f484..00000000 Binary files a/docs/_pics/java-notes/dataStructure/minSpanTree/15_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/minSpanTree/15_2.png b/docs/_pics/java-notes/dataStructure/minSpanTree/15_2.png deleted file mode 100644 index 8d54ca66..00000000 Binary files a/docs/_pics/java-notes/dataStructure/minSpanTree/15_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/minSpanTree/15_3.png b/docs/_pics/java-notes/dataStructure/minSpanTree/15_3.png deleted file mode 100644 index dea2df7a..00000000 Binary files a/docs/_pics/java-notes/dataStructure/minSpanTree/15_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/minSpanTree/15_4.png b/docs/_pics/java-notes/dataStructure/minSpanTree/15_4.png deleted file mode 100644 index ca82370d..00000000 Binary files a/docs/_pics/java-notes/dataStructure/minSpanTree/15_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/minSpanTree/15_5.png b/docs/_pics/java-notes/dataStructure/minSpanTree/15_5.png deleted file mode 100644 index 96065623..00000000 Binary files a/docs/_pics/java-notes/dataStructure/minSpanTree/15_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_1.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_1.png deleted file mode 100644 index c9835b8f..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_10.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_10.png deleted file mode 100644 index 3f02d09c..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_10.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_11.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_11.png deleted file mode 100644 index 59f9dc4e..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_11.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_12.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_12.png deleted file mode 100644 index 09adc3c4..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_12.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_13.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_13.png deleted file mode 100644 index 96d236b6..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_13.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_14.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_14.png deleted file mode 100644 index 9817e18b..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_14.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_15.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_15.png deleted file mode 100644 index a1d2707f..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_15.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_16.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_16.png deleted file mode 100644 index 180ab3d9..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_16.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_17.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_17.png deleted file mode 100644 index e77f9196..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_17.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_18.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_18.png deleted file mode 100644 index c525a2b5..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_18.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_19.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_19.png deleted file mode 100644 index 17d2279b..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_19.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_2.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_2.png deleted file mode 100644 index a389e992..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_20.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_20.png deleted file mode 100644 index 6b9b1015..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_20.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_21.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_21.png deleted file mode 100644 index 446d9e24..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_21.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_23.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_23.png deleted file mode 100644 index 9d7ea819..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_23.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_24.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_24.png deleted file mode 100644 index 115e82f2..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_24.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_25.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_25.png deleted file mode 100644 index a3876e3b..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_25.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_3.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_3.png deleted file mode 100644 index 658ad0ed..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_4.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_4.png deleted file mode 100644 index 4858df2a..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_5.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_5.png deleted file mode 100644 index d90946b5..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_6.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_6.png deleted file mode 100644 index df6839a5..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_6.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_8.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_8.png deleted file mode 100644 index f99ca2a7..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_8.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/redBlackTree/12_9.png b/docs/_pics/java-notes/dataStructure/redBlackTree/12_9.png deleted file mode 100644 index 920b8f8b..00000000 Binary files a/docs/_pics/java-notes/dataStructure/redBlackTree/12_9.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/segmentTree/segmentTree.png b/docs/_pics/java-notes/dataStructure/segmentTree/segmentTree.png deleted file mode 100644 index 17a6f4ca..00000000 Binary files a/docs/_pics/java-notes/dataStructure/segmentTree/segmentTree.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/segmentTree/segmentTree00.png b/docs/_pics/java-notes/dataStructure/segmentTree/segmentTree00.png deleted file mode 100644 index 6d70a1a4..00000000 Binary files a/docs/_pics/java-notes/dataStructure/segmentTree/segmentTree00.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/segmentTree/segmentTree01.png b/docs/_pics/java-notes/dataStructure/segmentTree/segmentTree01.png deleted file mode 100644 index c3b86b11..00000000 Binary files a/docs/_pics/java-notes/dataStructure/segmentTree/segmentTree01.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/segmentTree/segmentTree_2.png b/docs/_pics/java-notes/dataStructure/segmentTree/segmentTree_2.png deleted file mode 100644 index f497d07f..00000000 Binary files a/docs/_pics/java-notes/dataStructure/segmentTree/segmentTree_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/segmentTree/segmentTree_3.png b/docs/_pics/java-notes/dataStructure/segmentTree/segmentTree_3.png deleted file mode 100644 index 9e218f40..00000000 Binary files a/docs/_pics/java-notes/dataStructure/segmentTree/segmentTree_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/segmentTree/segmentTree_4.png b/docs/_pics/java-notes/dataStructure/segmentTree/segmentTree_4.png deleted file mode 100644 index 991dd72b..00000000 Binary files a/docs/_pics/java-notes/dataStructure/segmentTree/segmentTree_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/segmentTree/segmentTree_5.png b/docs/_pics/java-notes/dataStructure/segmentTree/segmentTree_5.png deleted file mode 100644 index 205fec50..00000000 Binary files a/docs/_pics/java-notes/dataStructure/segmentTree/segmentTree_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/setAndMap/map_1.png b/docs/_pics/java-notes/dataStructure/setAndMap/map_1.png deleted file mode 100644 index a906a105..00000000 Binary files a/docs/_pics/java-notes/dataStructure/setAndMap/map_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/setAndMap/map_2.png b/docs/_pics/java-notes/dataStructure/setAndMap/map_2.png deleted file mode 100644 index 58083c2f..00000000 Binary files a/docs/_pics/java-notes/dataStructure/setAndMap/map_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/setAndMap/set_1.png b/docs/_pics/java-notes/dataStructure/setAndMap/set_1.png deleted file mode 100644 index 06b47d84..00000000 Binary files a/docs/_pics/java-notes/dataStructure/setAndMap/set_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/setAndMap/set_2.png b/docs/_pics/java-notes/dataStructure/setAndMap/set_2.png deleted file mode 100644 index 57ca60b6..00000000 Binary files a/docs/_pics/java-notes/dataStructure/setAndMap/set_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/setAndMap/set_3.png b/docs/_pics/java-notes/dataStructure/setAndMap/set_3.png deleted file mode 100644 index 4235a13c..00000000 Binary files a/docs/_pics/java-notes/dataStructure/setAndMap/set_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/setAndMap/set_4.png b/docs/_pics/java-notes/dataStructure/setAndMap/set_4.png deleted file mode 100644 index b3a446e3..00000000 Binary files a/docs/_pics/java-notes/dataStructure/setAndMap/set_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/shortestPath/16_1.png b/docs/_pics/java-notes/dataStructure/shortestPath/16_1.png deleted file mode 100644 index 35a3fb5d..00000000 Binary files a/docs/_pics/java-notes/dataStructure/shortestPath/16_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/shortestPath/16_10.png b/docs/_pics/java-notes/dataStructure/shortestPath/16_10.png deleted file mode 100644 index 216fca58..00000000 Binary files a/docs/_pics/java-notes/dataStructure/shortestPath/16_10.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/shortestPath/16_11.png b/docs/_pics/java-notes/dataStructure/shortestPath/16_11.png deleted file mode 100644 index c4f4ea57..00000000 Binary files a/docs/_pics/java-notes/dataStructure/shortestPath/16_11.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/shortestPath/16_2.png b/docs/_pics/java-notes/dataStructure/shortestPath/16_2.png deleted file mode 100644 index a0ac3c67..00000000 Binary files a/docs/_pics/java-notes/dataStructure/shortestPath/16_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/shortestPath/16_3.png b/docs/_pics/java-notes/dataStructure/shortestPath/16_3.png deleted file mode 100644 index 01447a0a..00000000 Binary files a/docs/_pics/java-notes/dataStructure/shortestPath/16_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/shortestPath/16_4.png b/docs/_pics/java-notes/dataStructure/shortestPath/16_4.png deleted file mode 100644 index 33276f8f..00000000 Binary files a/docs/_pics/java-notes/dataStructure/shortestPath/16_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/shortestPath/16_5.png b/docs/_pics/java-notes/dataStructure/shortestPath/16_5.png deleted file mode 100644 index 19388d75..00000000 Binary files a/docs/_pics/java-notes/dataStructure/shortestPath/16_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/shortestPath/16_6.png b/docs/_pics/java-notes/dataStructure/shortestPath/16_6.png deleted file mode 100644 index a526f1c2..00000000 Binary files a/docs/_pics/java-notes/dataStructure/shortestPath/16_6.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/shortestPath/16_7.png b/docs/_pics/java-notes/dataStructure/shortestPath/16_7.png deleted file mode 100644 index 26a3f101..00000000 Binary files a/docs/_pics/java-notes/dataStructure/shortestPath/16_7.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/shortestPath/16_8.png b/docs/_pics/java-notes/dataStructure/shortestPath/16_8.png deleted file mode 100644 index 9997aabb..00000000 Binary files a/docs/_pics/java-notes/dataStructure/shortestPath/16_8.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/shortestPath/16_9.png b/docs/_pics/java-notes/dataStructure/shortestPath/16_9.png deleted file mode 100644 index d6a11f7f..00000000 Binary files a/docs/_pics/java-notes/dataStructure/shortestPath/16_9.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/stack/queue_1.png b/docs/_pics/java-notes/dataStructure/stack/queue_1.png deleted file mode 100644 index b1b89409..00000000 Binary files a/docs/_pics/java-notes/dataStructure/stack/queue_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/stack/queue_2.png b/docs/_pics/java-notes/dataStructure/stack/queue_2.png deleted file mode 100644 index eb14afd9..00000000 Binary files a/docs/_pics/java-notes/dataStructure/stack/queue_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/stack/queue_3.png b/docs/_pics/java-notes/dataStructure/stack/queue_3.png deleted file mode 100644 index 728e840f..00000000 Binary files a/docs/_pics/java-notes/dataStructure/stack/queue_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/stack/queue_4.png b/docs/_pics/java-notes/dataStructure/stack/queue_4.png deleted file mode 100644 index 02bd21b3..00000000 Binary files a/docs/_pics/java-notes/dataStructure/stack/queue_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/stack/queue_5.png b/docs/_pics/java-notes/dataStructure/stack/queue_5.png deleted file mode 100644 index c3b1eaa2..00000000 Binary files a/docs/_pics/java-notes/dataStructure/stack/queue_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/stack/queue_6.png b/docs/_pics/java-notes/dataStructure/stack/queue_6.png deleted file mode 100644 index 94458b16..00000000 Binary files a/docs/_pics/java-notes/dataStructure/stack/queue_6.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/stack/queue_7.png b/docs/_pics/java-notes/dataStructure/stack/queue_7.png deleted file mode 100644 index aae4455a..00000000 Binary files a/docs/_pics/java-notes/dataStructure/stack/queue_7.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/stack/stack_1.png b/docs/_pics/java-notes/dataStructure/stack/stack_1.png deleted file mode 100644 index 8c88f27b..00000000 Binary files a/docs/_pics/java-notes/dataStructure/stack/stack_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/stack/stack_2.png b/docs/_pics/java-notes/dataStructure/stack/stack_2.png deleted file mode 100644 index 658939ab..00000000 Binary files a/docs/_pics/java-notes/dataStructure/stack/stack_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/sz_1.png b/docs/_pics/java-notes/dataStructure/sz_1.png deleted file mode 100644 index 7763fe92..00000000 Binary files a/docs/_pics/java-notes/dataStructure/sz_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/sz_2.png b/docs/_pics/java-notes/dataStructure/sz_2.png deleted file mode 100644 index 62e90ee3..00000000 Binary files a/docs/_pics/java-notes/dataStructure/sz_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/sz_3.png b/docs/_pics/java-notes/dataStructure/sz_3.png deleted file mode 100644 index d4642907..00000000 Binary files a/docs/_pics/java-notes/dataStructure/sz_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/sz_4.png b/docs/_pics/java-notes/dataStructure/sz_4.png deleted file mode 100644 index ccf10672..00000000 Binary files a/docs/_pics/java-notes/dataStructure/sz_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/sz_5.png b/docs/_pics/java-notes/dataStructure/sz_5.png deleted file mode 100644 index bc14b56b..00000000 Binary files a/docs/_pics/java-notes/dataStructure/sz_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/sz_6.png b/docs/_pics/java-notes/dataStructure/sz_6.png deleted file mode 100644 index f8eb1844..00000000 Binary files a/docs/_pics/java-notes/dataStructure/sz_6.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/trie/trie_1.png b/docs/_pics/java-notes/dataStructure/trie/trie_1.png deleted file mode 100644 index 40a7e2db..00000000 Binary files a/docs/_pics/java-notes/dataStructure/trie/trie_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/trie/trie_2.png b/docs/_pics/java-notes/dataStructure/trie/trie_2.png deleted file mode 100644 index a80cdb79..00000000 Binary files a/docs/_pics/java-notes/dataStructure/trie/trie_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/unionFind/uf_1.png b/docs/_pics/java-notes/dataStructure/unionFind/uf_1.png deleted file mode 100644 index f09e91e1..00000000 Binary files a/docs/_pics/java-notes/dataStructure/unionFind/uf_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/unionFind/uf_10.png b/docs/_pics/java-notes/dataStructure/unionFind/uf_10.png deleted file mode 100644 index f63f7b1a..00000000 Binary files a/docs/_pics/java-notes/dataStructure/unionFind/uf_10.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/unionFind/uf_2.png b/docs/_pics/java-notes/dataStructure/unionFind/uf_2.png deleted file mode 100644 index e74ae1b4..00000000 Binary files a/docs/_pics/java-notes/dataStructure/unionFind/uf_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/unionFind/uf_3.png b/docs/_pics/java-notes/dataStructure/unionFind/uf_3.png deleted file mode 100644 index ed737b84..00000000 Binary files a/docs/_pics/java-notes/dataStructure/unionFind/uf_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/unionFind/uf_4.png b/docs/_pics/java-notes/dataStructure/unionFind/uf_4.png deleted file mode 100644 index a91b8381..00000000 Binary files a/docs/_pics/java-notes/dataStructure/unionFind/uf_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/unionFind/uf_5.png b/docs/_pics/java-notes/dataStructure/unionFind/uf_5.png deleted file mode 100644 index e72818a6..00000000 Binary files a/docs/_pics/java-notes/dataStructure/unionFind/uf_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/unionFind/uf_6.png b/docs/_pics/java-notes/dataStructure/unionFind/uf_6.png deleted file mode 100644 index 0fd49f44..00000000 Binary files a/docs/_pics/java-notes/dataStructure/unionFind/uf_6.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/unionFind/uf_7.png b/docs/_pics/java-notes/dataStructure/unionFind/uf_7.png deleted file mode 100644 index 61ea9717..00000000 Binary files a/docs/_pics/java-notes/dataStructure/unionFind/uf_7.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/unionFind/uf_8.png b/docs/_pics/java-notes/dataStructure/unionFind/uf_8.png deleted file mode 100644 index a0e0f003..00000000 Binary files a/docs/_pics/java-notes/dataStructure/unionFind/uf_8.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/unionFind/uf_9.png b/docs/_pics/java-notes/dataStructure/unionFind/uf_9.png deleted file mode 100644 index f75a0943..00000000 Binary files a/docs/_pics/java-notes/dataStructure/unionFind/uf_9.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/unionFind/unionFind_1.png b/docs/_pics/java-notes/dataStructure/unionFind/unionFind_1.png deleted file mode 100644 index a693fe3d..00000000 Binary files a/docs/_pics/java-notes/dataStructure/unionFind/unionFind_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/dataStructure/unionFind/unionFind_2.png b/docs/_pics/java-notes/dataStructure/unionFind/unionFind_2.png deleted file mode 100644 index afd1b566..00000000 Binary files a/docs/_pics/java-notes/dataStructure/unionFind/unionFind_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/061c88c1-572f-424f-b580-9cbce903a3fe.png b/docs/_pics/java-notes/database/061c88c1-572f-424f-b580-9cbce903a3fe.png deleted file mode 100644 index eb25a980..00000000 Binary files a/docs/_pics/java-notes/database/061c88c1-572f-424f-b580-9cbce903a3fe.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/0ea37ee2-c224-4c79-b895-e131c6805c40.png b/docs/_pics/java-notes/database/0ea37ee2-c224-4c79-b895-e131c6805c40.png deleted file mode 100644 index 79ec18df..00000000 Binary files a/docs/_pics/java-notes/database/0ea37ee2-c224-4c79-b895-e131c6805c40.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/1202b2d6-9469-4251-bd47-ca6034fb6116.png b/docs/_pics/java-notes/database/1202b2d6-9469-4251-bd47-ca6034fb6116.png deleted file mode 100644 index b44fa996..00000000 Binary files a/docs/_pics/java-notes/database/1202b2d6-9469-4251-bd47-ca6034fb6116.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/185b9c49-4c13-4241-a848-fbff85c03a64.png b/docs/_pics/java-notes/database/185b9c49-4c13-4241-a848-fbff85c03a64.png deleted file mode 100644 index 6f56bc5e..00000000 Binary files a/docs/_pics/java-notes/database/185b9c49-4c13-4241-a848-fbff85c03a64.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/1a851e90-0d5c-4d4f-ac54-34c20ecfb903.jpg b/docs/_pics/java-notes/database/1a851e90-0d5c-4d4f-ac54-34c20ecfb903.jpg deleted file mode 100644 index 4809984f..00000000 Binary files a/docs/_pics/java-notes/database/1a851e90-0d5c-4d4f-ac54-34c20ecfb903.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/database/292b4a35-4507-4256-84ff-c218f108ee31.jpg b/docs/_pics/java-notes/database/292b4a35-4507-4256-84ff-c218f108ee31.jpg deleted file mode 100644 index 38e0905b..00000000 Binary files a/docs/_pics/java-notes/database/292b4a35-4507-4256-84ff-c218f108ee31.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/database/395a9e83-b1a1-4a1d-b170-d081e7bb5bab.png b/docs/_pics/java-notes/database/395a9e83-b1a1-4a1d-b170-d081e7bb5bab.png deleted file mode 100644 index b486ec02..00000000 Binary files a/docs/_pics/java-notes/database/395a9e83-b1a1-4a1d-b170-d081e7bb5bab.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/423f2a40-bee1-488e-b460-8e76c48ee560.png b/docs/_pics/java-notes/database/423f2a40-bee1-488e-b460-8e76c48ee560.png deleted file mode 100644 index 7b8c8c08..00000000 Binary files a/docs/_pics/java-notes/database/423f2a40-bee1-488e-b460-8e76c48ee560.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/485fdf34-ccf8-4185-97c6-17374ee719a0.png b/docs/_pics/java-notes/database/485fdf34-ccf8-4185-97c6-17374ee719a0.png deleted file mode 100644 index f9f73faf..00000000 Binary files a/docs/_pics/java-notes/database/485fdf34-ccf8-4185-97c6-17374ee719a0.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/6019b2db-bc3e-4408-b6d8-96025f4481d6.png b/docs/_pics/java-notes/database/6019b2db-bc3e-4408-b6d8-96025f4481d6.png deleted file mode 100644 index 900ee963..00000000 Binary files a/docs/_pics/java-notes/database/6019b2db-bc3e-4408-b6d8-96025f4481d6.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/63c2909f-0c5f-496f-9fe5-ee9176b31aba.jpg b/docs/_pics/java-notes/database/63c2909f-0c5f-496f-9fe5-ee9176b31aba.jpg deleted file mode 100644 index d6471621..00000000 Binary files a/docs/_pics/java-notes/database/63c2909f-0c5f-496f-9fe5-ee9176b31aba.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/database/72fe492e-f1cb-4cfc-92f8-412fb3ae6fec.png b/docs/_pics/java-notes/database/72fe492e-f1cb-4cfc-92f8-412fb3ae6fec.png deleted file mode 100644 index 748980c4..00000000 Binary files a/docs/_pics/java-notes/database/72fe492e-f1cb-4cfc-92f8-412fb3ae6fec.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/7ab8ca28-2a41-4adf-9502-cc0a21e63b51.jpg b/docs/_pics/java-notes/database/7ab8ca28-2a41-4adf-9502-cc0a21e63b51.jpg deleted file mode 100644 index 31c70314..00000000 Binary files a/docs/_pics/java-notes/database/7ab8ca28-2a41-4adf-9502-cc0a21e63b51.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/database/7bd202a7-93d4-4f3a-a878-af68ae25539a.png b/docs/_pics/java-notes/database/7bd202a7-93d4-4f3a-a878-af68ae25539a.png deleted file mode 100644 index 711fb45b..00000000 Binary files a/docs/_pics/java-notes/database/7bd202a7-93d4-4f3a-a878-af68ae25539a.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/7c54de21-e2ff-402e-bc42-4037de1c1592.png b/docs/_pics/java-notes/database/7c54de21-e2ff-402e-bc42-4037de1c1592.png deleted file mode 100644 index 8b5ce204..00000000 Binary files a/docs/_pics/java-notes/database/7c54de21-e2ff-402e-bc42-4037de1c1592.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/7ec9d619-fa60-4a2b-95aa-bf1a62aad408.jpg b/docs/_pics/java-notes/database/7ec9d619-fa60-4a2b-95aa-bf1a62aad408.jpg deleted file mode 100644 index 5753388e..00000000 Binary files a/docs/_pics/java-notes/database/7ec9d619-fa60-4a2b-95aa-bf1a62aad408.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/database/88ff46b3-028a-4dbb-a572-1f062b8b96d3.png b/docs/_pics/java-notes/database/88ff46b3-028a-4dbb-a572-1f062b8b96d3.png deleted file mode 100644 index c6344ad2..00000000 Binary files a/docs/_pics/java-notes/database/88ff46b3-028a-4dbb-a572-1f062b8b96d3.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/8b798007-e0fb-420c-b981-ead215692417.jpg b/docs/_pics/java-notes/database/8b798007-e0fb-420c-b981-ead215692417.jpg deleted file mode 100644 index dd218133..00000000 Binary files a/docs/_pics/java-notes/database/8b798007-e0fb-420c-b981-ead215692417.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/database/9ea86eb5-000a-4281-b948-7b567bd6f1d8.png b/docs/_pics/java-notes/database/9ea86eb5-000a-4281-b948-7b567bd6f1d8.png deleted file mode 100644 index ba045e20..00000000 Binary files a/docs/_pics/java-notes/database/9ea86eb5-000a-4281-b948-7b567bd6f1d8.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/B_plus_tree.png b/docs/_pics/java-notes/database/B_plus_tree.png deleted file mode 100644 index c5e8961d..00000000 Binary files a/docs/_pics/java-notes/database/B_plus_tree.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/MySQL_1.png b/docs/_pics/java-notes/database/MySQL_1.png deleted file mode 100644 index 74d42d53..00000000 Binary files a/docs/_pics/java-notes/database/MySQL_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/MySQL_2.png b/docs/_pics/java-notes/database/MySQL_2.png deleted file mode 100644 index 8764a2f9..00000000 Binary files a/docs/_pics/java-notes/database/MySQL_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/MySQL_3.png b/docs/_pics/java-notes/database/MySQL_3.png deleted file mode 100644 index 503ef3b2..00000000 Binary files a/docs/_pics/java-notes/database/MySQL_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/MySQL_4.png b/docs/_pics/java-notes/database/MySQL_4.png deleted file mode 100644 index 7d57aae5..00000000 Binary files a/docs/_pics/java-notes/database/MySQL_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/MySQL_6.gif b/docs/_pics/java-notes/database/MySQL_6.gif deleted file mode 100644 index cac12c03..00000000 Binary files a/docs/_pics/java-notes/database/MySQL_6.gif and /dev/null differ diff --git a/docs/_pics/java-notes/database/MySQL_7.png b/docs/_pics/java-notes/database/MySQL_7.png deleted file mode 100644 index 47a0dc18..00000000 Binary files a/docs/_pics/java-notes/database/MySQL_7.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/MySQL_8.png b/docs/_pics/java-notes/database/MySQL_8.png deleted file mode 100644 index 7fef7ba5..00000000 Binary files a/docs/_pics/java-notes/database/MySQL_8.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/a58e294a-615d-4ea0-9fbf-064a6daec4b2.png b/docs/_pics/java-notes/database/a58e294a-615d-4ea0-9fbf-064a6daec4b2.png deleted file mode 100644 index fdefb823..00000000 Binary files a/docs/_pics/java-notes/database/a58e294a-615d-4ea0-9fbf-064a6daec4b2.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/beba612e-dc5b-4fc2-869d-0b23408ac90a.png b/docs/_pics/java-notes/database/beba612e-dc5b-4fc2-869d-0b23408ac90a.png deleted file mode 100644 index b24241d1..00000000 Binary files a/docs/_pics/java-notes/database/beba612e-dc5b-4fc2-869d-0b23408ac90a.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/c0a9fa91-da2e-4892-8c9f-80206a6f7047.png b/docs/_pics/java-notes/database/c0a9fa91-da2e-4892-8c9f-80206a6f7047.png deleted file mode 100644 index add3c5d4..00000000 Binary files a/docs/_pics/java-notes/database/c0a9fa91-da2e-4892-8c9f-80206a6f7047.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/c28c6fbc-2bc1-47d9-9b2e-cf3d4034f877.jpg b/docs/_pics/java-notes/database/c28c6fbc-2bc1-47d9-9b2e-cf3d4034f877.jpg deleted file mode 100644 index 593513fe..00000000 Binary files a/docs/_pics/java-notes/database/c28c6fbc-2bc1-47d9-9b2e-cf3d4034f877.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/database/c2d343f7-604c-4856-9a3c-c71d6f67fecc.png b/docs/_pics/java-notes/database/c2d343f7-604c-4856-9a3c-c71d6f67fecc.png deleted file mode 100644 index 84b2898b..00000000 Binary files a/docs/_pics/java-notes/database/c2d343f7-604c-4856-9a3c-c71d6f67fecc.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/c8d18ca9-0b09-441a-9a0c-fb063630d708.png b/docs/_pics/java-notes/database/c8d18ca9-0b09-441a-9a0c-fb063630d708.png deleted file mode 100644 index 514ab054..00000000 Binary files a/docs/_pics/java-notes/database/c8d18ca9-0b09-441a-9a0c-fb063630d708.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/cd5fbcff-3f35-43a6-8ffa-082a93ce0f0e.png b/docs/_pics/java-notes/database/cd5fbcff-3f35-43a6-8ffa-082a93ce0f0e.png deleted file mode 100644 index f8550a11..00000000 Binary files a/docs/_pics/java-notes/database/cd5fbcff-3f35-43a6-8ffa-082a93ce0f0e.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/db.png b/docs/_pics/java-notes/database/db.png deleted file mode 100644 index a41b0eb8..00000000 Binary files a/docs/_pics/java-notes/database/db.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/db_1.png b/docs/_pics/java-notes/database/db_1.png deleted file mode 100644 index f097290d..00000000 Binary files a/docs/_pics/java-notes/database/db_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/db_10.png b/docs/_pics/java-notes/database/db_10.png deleted file mode 100644 index 5d20fba8..00000000 Binary files a/docs/_pics/java-notes/database/db_10.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/db_11.png b/docs/_pics/java-notes/database/db_11.png deleted file mode 100644 index 7c8b3df9..00000000 Binary files a/docs/_pics/java-notes/database/db_11.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/db_12.png b/docs/_pics/java-notes/database/db_12.png deleted file mode 100644 index 67b11d24..00000000 Binary files a/docs/_pics/java-notes/database/db_12.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/db_13.png b/docs/_pics/java-notes/database/db_13.png deleted file mode 100644 index 3916e93b..00000000 Binary files a/docs/_pics/java-notes/database/db_13.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/db_14.png b/docs/_pics/java-notes/database/db_14.png deleted file mode 100644 index 968f5b58..00000000 Binary files a/docs/_pics/java-notes/database/db_14.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/db_15.png b/docs/_pics/java-notes/database/db_15.png deleted file mode 100644 index dd2b185a..00000000 Binary files a/docs/_pics/java-notes/database/db_15.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/db_16.png b/docs/_pics/java-notes/database/db_16.png deleted file mode 100644 index a9d55fd4..00000000 Binary files a/docs/_pics/java-notes/database/db_16.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/db_17.png b/docs/_pics/java-notes/database/db_17.png deleted file mode 100644 index 8f1f54f6..00000000 Binary files a/docs/_pics/java-notes/database/db_17.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/db_18.png b/docs/_pics/java-notes/database/db_18.png deleted file mode 100644 index e7399df8..00000000 Binary files a/docs/_pics/java-notes/database/db_18.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/db_19.png b/docs/_pics/java-notes/database/db_19.png deleted file mode 100644 index f5ee41cc..00000000 Binary files a/docs/_pics/java-notes/database/db_19.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/db_2.png b/docs/_pics/java-notes/database/db_2.png deleted file mode 100644 index bf345f40..00000000 Binary files a/docs/_pics/java-notes/database/db_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/db_3.png b/docs/_pics/java-notes/database/db_3.png deleted file mode 100644 index 5c50f36b..00000000 Binary files a/docs/_pics/java-notes/database/db_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/db_4.png b/docs/_pics/java-notes/database/db_4.png deleted file mode 100644 index 8c44e8c0..00000000 Binary files a/docs/_pics/java-notes/database/db_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/db_5.png b/docs/_pics/java-notes/database/db_5.png deleted file mode 100644 index 81c9f2c8..00000000 Binary files a/docs/_pics/java-notes/database/db_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/db_6.png b/docs/_pics/java-notes/database/db_6.png deleted file mode 100644 index 22d5b499..00000000 Binary files a/docs/_pics/java-notes/database/db_6.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/db_7.png b/docs/_pics/java-notes/database/db_7.png deleted file mode 100644 index ec99338f..00000000 Binary files a/docs/_pics/java-notes/database/db_7.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/db_8.png b/docs/_pics/java-notes/database/db_8.png deleted file mode 100644 index c3b8b28b..00000000 Binary files a/docs/_pics/java-notes/database/db_8.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/db_9.png b/docs/_pics/java-notes/database/db_9.png deleted file mode 100644 index 6e35eb8a..00000000 Binary files a/docs/_pics/java-notes/database/db_9.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/dd782132-d830-4c55-9884-cfac0a541b8e.png b/docs/_pics/java-notes/database/dd782132-d830-4c55-9884-cfac0a541b8e.png deleted file mode 100644 index e338c1bb..00000000 Binary files a/docs/_pics/java-notes/database/dd782132-d830-4c55-9884-cfac0a541b8e.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/de9b9ea0-1327-4865-93e5-6f805c48bc9e.png b/docs/_pics/java-notes/database/de9b9ea0-1327-4865-93e5-6f805c48bc9e.png deleted file mode 100644 index cfabd4d7..00000000 Binary files a/docs/_pics/java-notes/database/de9b9ea0-1327-4865-93e5-6f805c48bc9e.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/e130e5b8-b19a-4f1e-b860-223040525cf6.jpg b/docs/_pics/java-notes/database/e130e5b8-b19a-4f1e-b860-223040525cf6.jpg deleted file mode 100644 index 0968e1bc..00000000 Binary files a/docs/_pics/java-notes/database/e130e5b8-b19a-4f1e-b860-223040525cf6.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/database/e41405a8-7c05-4f70-8092-e961e28d3112.jpg b/docs/_pics/java-notes/database/e41405a8-7c05-4f70-8092-e961e28d3112.jpg deleted file mode 100644 index 7fef7ba5..00000000 Binary files a/docs/_pics/java-notes/database/e41405a8-7c05-4f70-8092-e961e28d3112.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/database/f7d170a3-e446-4a64-ac2d-cb95028f81a8.png b/docs/_pics/java-notes/database/f7d170a3-e446-4a64-ac2d-cb95028f81a8.png deleted file mode 100644 index 26799762..00000000 Binary files a/docs/_pics/java-notes/database/f7d170a3-e446-4a64-ac2d-cb95028f81a8.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/fb327611-7e2b-4f2f-9f5b-38592d408f07.png b/docs/_pics/java-notes/database/fb327611-7e2b-4f2f-9f5b-38592d408f07.png deleted file mode 100644 index 774ecf10..00000000 Binary files a/docs/_pics/java-notes/database/fb327611-7e2b-4f2f-9f5b-38592d408f07.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/leetcode_1.png b/docs/_pics/java-notes/database/leetcode_1.png deleted file mode 100644 index 88583f00..00000000 Binary files a/docs/_pics/java-notes/database/leetcode_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/leetcode_2.png b/docs/_pics/java-notes/database/leetcode_2.png deleted file mode 100644 index 1fe2db8d..00000000 Binary files a/docs/_pics/java-notes/database/leetcode_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/leetcode_3.png b/docs/_pics/java-notes/database/leetcode_3.png deleted file mode 100644 index 21cd8fe0..00000000 Binary files a/docs/_pics/java-notes/database/leetcode_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/master-slave-proxy.png b/docs/_pics/java-notes/database/master-slave-proxy.png deleted file mode 100644 index 66be0d61..00000000 Binary files a/docs/_pics/java-notes/database/master-slave-proxy.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/master-slave.png b/docs/_pics/java-notes/database/master-slave.png deleted file mode 100644 index 594a183a..00000000 Binary files a/docs/_pics/java-notes/database/master-slave.png and /dev/null differ diff --git a/docs/_pics/java-notes/database/r_1 b/docs/_pics/java-notes/database/r_1 deleted file mode 100644 index 9f0d22dc..00000000 Binary files a/docs/_pics/java-notes/database/r_1 and /dev/null differ diff --git a/docs/_pics/java-notes/database/r_2 b/docs/_pics/java-notes/database/r_2 deleted file mode 100644 index 69aac318..00000000 Binary files a/docs/_pics/java-notes/database/r_2 and /dev/null differ diff --git a/docs/_pics/java-notes/distribution/cap.png b/docs/_pics/java-notes/distribution/cap.png deleted file mode 100644 index 5000a877..00000000 Binary files a/docs/_pics/java-notes/distribution/cap.png and /dev/null differ diff --git a/docs/_pics/java-notes/distribution/cap_2.png b/docs/_pics/java-notes/distribution/cap_2.png deleted file mode 100644 index 4a99129a..00000000 Binary files a/docs/_pics/java-notes/distribution/cap_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/java/00_1.png b/docs/_pics/java-notes/java/00_1.png deleted file mode 100644 index db310c1f..00000000 Binary files a/docs/_pics/java-notes/java/00_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/java/01_1.png b/docs/_pics/java-notes/java/01_1.png deleted file mode 100644 index d5884935..00000000 Binary files a/docs/_pics/java-notes/java/01_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/java/01_2.png b/docs/_pics/java-notes/java/01_2.png deleted file mode 100644 index b8624ac1..00000000 Binary files a/docs/_pics/java-notes/java/01_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/java/02_1.png b/docs/_pics/java-notes/java/02_1.png deleted file mode 100644 index 897a510b..00000000 Binary files a/docs/_pics/java-notes/java/02_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/java/02_2.png b/docs/_pics/java-notes/java/02_2.png deleted file mode 100644 index 7a3d9fc1..00000000 Binary files a/docs/_pics/java-notes/java/02_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/java/04_01.png b/docs/_pics/java-notes/java/04_01.png deleted file mode 100644 index cf9e87d9..00000000 Binary files a/docs/_pics/java-notes/java/04_01.png and /dev/null differ diff --git a/docs/_pics/java-notes/java/06_00.png b/docs/_pics/java-notes/java/06_00.png deleted file mode 100644 index 994c15c4..00000000 Binary files a/docs/_pics/java-notes/java/06_00.png and /dev/null differ diff --git a/docs/_pics/java-notes/java/16_00.jpg b/docs/_pics/java-notes/java/16_00.jpg deleted file mode 100644 index 7701e8b1..00000000 Binary files a/docs/_pics/java-notes/java/16_00.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/java/16_03.jpg b/docs/_pics/java-notes/java/16_03.jpg deleted file mode 100644 index 43e9fed1..00000000 Binary files a/docs/_pics/java-notes/java/16_03.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/java/1bea398f-17a7-4f67-a90b-9e2d243eaa9a.png b/docs/_pics/java-notes/java/1bea398f-17a7-4f67-a90b-9e2d243eaa9a.png deleted file mode 100644 index e4286077..00000000 Binary files a/docs/_pics/java-notes/java/1bea398f-17a7-4f67-a90b-9e2d243eaa9a.png and /dev/null differ diff --git a/docs/_pics/java-notes/java/3fdfc89d-719e-4d93-b518-29fa612b3b18.png b/docs/_pics/java-notes/java/3fdfc89d-719e-4d93-b518-29fa612b3b18.png deleted file mode 100644 index 781dca88..00000000 Binary files a/docs/_pics/java-notes/java/3fdfc89d-719e-4d93-b518-29fa612b3b18.png and /dev/null differ diff --git a/docs/_pics/java-notes/java/49495c95-52e5-4c9a-b27b-92cf235ff5ec.png b/docs/_pics/java-notes/java/49495c95-52e5-4c9a-b27b-92cf235ff5ec.png deleted file mode 100644 index 662d99c3..00000000 Binary files a/docs/_pics/java-notes/java/49495c95-52e5-4c9a-b27b-92cf235ff5ec.png and /dev/null differ diff --git a/docs/_pics/java-notes/java/49d6de7b-0d0d-425c-9e49-a1559dc23b10.png b/docs/_pics/java-notes/java/49d6de7b-0d0d-425c-9e49-a1559dc23b10.png deleted file mode 100644 index eea3c6b4..00000000 Binary files a/docs/_pics/java-notes/java/49d6de7b-0d0d-425c-9e49-a1559dc23b10.png and /dev/null differ diff --git a/docs/_pics/java-notes/java/4d930e22-f493-49ae-8dff-ea21cd6895dc.png b/docs/_pics/java-notes/java/4d930e22-f493-49ae-8dff-ea21cd6895dc.png deleted file mode 100644 index 7ee2ffb4..00000000 Binary files a/docs/_pics/java-notes/java/4d930e22-f493-49ae-8dff-ea21cd6895dc.png and /dev/null differ diff --git a/docs/_pics/java-notes/java/67bf5487-c45d-49b6-b9c0-a058d8c68902.png b/docs/_pics/java-notes/java/67bf5487-c45d-49b6-b9c0-a058d8c68902.png deleted file mode 100644 index b63baeb2..00000000 Binary files a/docs/_pics/java-notes/java/67bf5487-c45d-49b6-b9c0-a058d8c68902.png and /dev/null differ diff --git a/docs/_pics/java-notes/java/80804f52-8815-4096-b506-48eef3eed5c6.png b/docs/_pics/java-notes/java/80804f52-8815-4096-b506-48eef3eed5c6.png deleted file mode 100644 index 06c8760b..00000000 Binary files a/docs/_pics/java-notes/java/80804f52-8815-4096-b506-48eef3eed5c6.png and /dev/null differ diff --git a/docs/_pics/java-notes/java/8fe838e3-ef77-4f63-bf45-417b6bc5c6bb.png b/docs/_pics/java-notes/java/8fe838e3-ef77-4f63-bf45-417b6bc5c6bb.png deleted file mode 100644 index 2e53fbe3..00000000 Binary files a/docs/_pics/java-notes/java/8fe838e3-ef77-4f63-bf45-417b6bc5c6bb.png and /dev/null differ diff --git a/docs/_pics/java-notes/java/952e06bd-5a65-4cab-82e4-dd1536462f38.png b/docs/_pics/java-notes/java/952e06bd-5a65-4cab-82e4-dd1536462f38.png deleted file mode 100644 index 4983142a..00000000 Binary files a/docs/_pics/java-notes/java/952e06bd-5a65-4cab-82e4-dd1536462f38.png and /dev/null differ diff --git a/docs/_pics/java-notes/java/ClienteServidorSockets1521731145260.jpg b/docs/_pics/java-notes/java/ClienteServidorSockets1521731145260.jpg deleted file mode 100644 index 18976fb3..00000000 Binary files a/docs/_pics/java-notes/java/ClienteServidorSockets1521731145260.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/java/PPjwP.png b/docs/_pics/java-notes/java/PPjwP.png deleted file mode 100644 index 80631505..00000000 Binary files a/docs/_pics/java-notes/java/PPjwP.png and /dev/null differ diff --git a/docs/_pics/java-notes/java/SoWkIImgAStDuUBAp2j9BKfBJ4vLy0G.png b/docs/_pics/java-notes/java/SoWkIImgAStDuUBAp2j9BKfBJ4vLy0G.png deleted file mode 100644 index 124ec977..00000000 Binary files a/docs/_pics/java-notes/java/SoWkIImgAStDuUBAp2j9BKfBJ4vLy0G.png and /dev/null differ diff --git a/docs/_pics/java-notes/java/SoWkIImgAStDuUBAp2j9BKfBJ4vLy4q.png b/docs/_pics/java-notes/java/SoWkIImgAStDuUBAp2j9BKfBJ4vLy4q.png deleted file mode 100644 index 7a202d35..00000000 Binary files a/docs/_pics/java-notes/java/SoWkIImgAStDuUBAp2j9BKfBJ4vLy4q.png and /dev/null differ diff --git a/docs/_pics/java-notes/java/VP6n3i8W48Ptde8NQ9_0eSR5eOD6uqx.png b/docs/_pics/java-notes/java/VP6n3i8W48Ptde8NQ9_0eSR5eOD6uqx.png deleted file mode 100644 index d2559cf3..00000000 Binary files a/docs/_pics/java-notes/java/VP6n3i8W48Ptde8NQ9_0eSR5eOD6uqx.png and /dev/null differ diff --git a/docs/_pics/java-notes/java/b5bdcbe2-b958-4aef-9151-6ad963cb28b4.png b/docs/_pics/java-notes/java/b5bdcbe2-b958-4aef-9151-6ad963cb28b4.png deleted file mode 100644 index dd32da70..00000000 Binary files a/docs/_pics/java-notes/java/b5bdcbe2-b958-4aef-9151-6ad963cb28b4.png and /dev/null differ diff --git a/docs/_pics/java-notes/java/f76067a5-7d5f-4135-9549-8199c77d8f1c.jpg b/docs/_pics/java-notes/java/f76067a5-7d5f-4135-9549-8199c77d8f1c.jpg deleted file mode 100644 index d710e659..00000000 Binary files a/docs/_pics/java-notes/java/f76067a5-7d5f-4135-9549-8199c77d8f1c.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/java/threadPool.png b/docs/_pics/java-notes/java/threadPool.png deleted file mode 100644 index 7efa6898..00000000 Binary files a/docs/_pics/java-notes/java/threadPool.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/array/array_01.png b/docs/_pics/java-notes/leetcode/array/array_01.png deleted file mode 100644 index c7cf3d0d..00000000 Binary files a/docs/_pics/java-notes/leetcode/array/array_01.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/array/array_02.png b/docs/_pics/java-notes/leetcode/array/array_02.png deleted file mode 100644 index d4aaffa5..00000000 Binary files a/docs/_pics/java-notes/leetcode/array/array_02.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/array/array_03.png b/docs/_pics/java-notes/leetcode/array/array_03.png deleted file mode 100644 index 1450b10d..00000000 Binary files a/docs/_pics/java-notes/leetcode/array/array_03.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/array/array_04.png b/docs/_pics/java-notes/leetcode/array/array_04.png deleted file mode 100644 index e7663a64..00000000 Binary files a/docs/_pics/java-notes/leetcode/array/array_04.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/array/array_05.png b/docs/_pics/java-notes/leetcode/array/array_05.png deleted file mode 100644 index d68a40da..00000000 Binary files a/docs/_pics/java-notes/leetcode/array/array_05.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_1.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_1.png deleted file mode 100644 index 6e1f1b5e..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_10.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_10.png deleted file mode 100644 index 44aebf9e..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_10.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_11.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_11.png deleted file mode 100644 index 8ed6eecf..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_11.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_12.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_12.png deleted file mode 100644 index ba3163fe..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_12.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_13.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_13.png deleted file mode 100644 index e2f6d4d3..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_13.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_14.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_14.png deleted file mode 100644 index b6331d27..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_14.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_15.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_15.png deleted file mode 100644 index 909aeeb1..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_15.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_16.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_16.png deleted file mode 100644 index 0ea9cad5..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_16.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_17.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_17.png deleted file mode 100644 index 66736fef..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_17.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_18.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_18.png deleted file mode 100644 index 920e918b..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_18.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_19.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_19.png deleted file mode 100644 index 3763557e..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_19.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_2.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_2.png deleted file mode 100644 index f59c0c48..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_20.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_20.png deleted file mode 100644 index 58f6da7c..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_20.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_21.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_21.png deleted file mode 100644 index 73142c37..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_21.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_22.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_22.png deleted file mode 100644 index 3ed5fb51..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_22.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_23.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_23.png deleted file mode 100644 index 42b7c305..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_23.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_24.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_24.png deleted file mode 100644 index 1ea5720a..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_24.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_25.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_25.png deleted file mode 100644 index 888cb6c4..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_25.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_26.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_26.png deleted file mode 100644 index 8c7b8942..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_26.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_27.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_27.png deleted file mode 100644 index e6b99579..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_27.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_28.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_28.png deleted file mode 100644 index 3969ba13..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_28.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_3.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_3.png deleted file mode 100644 index 0036f91e..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_4.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_4.png deleted file mode 100644 index 958005ec..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_5.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_5.png deleted file mode 100644 index 443e3a88..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_6.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_6.png deleted file mode 100644 index cc09d8e7..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_6.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_7.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_7.png deleted file mode 100644 index 29cd4865..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_7.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_8.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_8.png deleted file mode 100644 index c5476080..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_8.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/backtrack/backtrack_9.png b/docs/_pics/java-notes/leetcode/backtrack/backtrack_9.png deleted file mode 100644 index 0a054ea6..00000000 Binary files a/docs/_pics/java-notes/leetcode/backtrack/backtrack_9.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_1.png b/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_1.png deleted file mode 100644 index 81d04271..00000000 Binary files a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_10.png b/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_10.png deleted file mode 100644 index 8fcb0561..00000000 Binary files a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_10.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_11.png b/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_11.png deleted file mode 100644 index 6ed45552..00000000 Binary files a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_11.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_13.png b/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_13.png deleted file mode 100644 index 317dddf4..00000000 Binary files a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_13.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_14.png b/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_14.png deleted file mode 100644 index c6d75749..00000000 Binary files a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_14.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_15.png b/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_15.png deleted file mode 100644 index fc77e5de..00000000 Binary files a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_15.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_2.png b/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_2.png deleted file mode 100644 index 73dbce00..00000000 Binary files a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_3.png b/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_3.png deleted file mode 100644 index bda5a311..00000000 Binary files a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_4.png b/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_4.png deleted file mode 100644 index 432c2b9b..00000000 Binary files a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_5.png b/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_5.png deleted file mode 100644 index 79892231..00000000 Binary files a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_6.png b/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_6.png deleted file mode 100644 index d3a8c107..00000000 Binary files a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_6.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_7.png b/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_7.png deleted file mode 100644 index 43bf7d20..00000000 Binary files a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_7.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_8.png b/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_8.png deleted file mode 100644 index ea13e9ac..00000000 Binary files a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_8.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_9.png b/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_9.png deleted file mode 100644 index 017952d8..00000000 Binary files a/docs/_pics/java-notes/leetcode/binaryTree/binaryTree_9.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_1.png b/docs/_pics/java-notes/leetcode/dp/dp_1.png deleted file mode 100644 index 33b55e33..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_10.png b/docs/_pics/java-notes/leetcode/dp/dp_10.png deleted file mode 100644 index 1ff25c9a..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_10.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_11.png b/docs/_pics/java-notes/leetcode/dp/dp_11.png deleted file mode 100644 index a5f200e7..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_11.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_12.png b/docs/_pics/java-notes/leetcode/dp/dp_12.png deleted file mode 100644 index d3b94fba..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_12.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_13.png b/docs/_pics/java-notes/leetcode/dp/dp_13.png deleted file mode 100644 index 16ddd43f..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_13.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_14.png b/docs/_pics/java-notes/leetcode/dp/dp_14.png deleted file mode 100644 index 1ea45c5c..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_14.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_15.png b/docs/_pics/java-notes/leetcode/dp/dp_15.png deleted file mode 100644 index f4abec3e..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_15.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_16.png b/docs/_pics/java-notes/leetcode/dp/dp_16.png deleted file mode 100644 index 2756995c..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_16.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_17.png b/docs/_pics/java-notes/leetcode/dp/dp_17.png deleted file mode 100644 index 6dff07c8..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_17.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_18.png b/docs/_pics/java-notes/leetcode/dp/dp_18.png deleted file mode 100644 index ffcd62f6..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_18.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_19.png b/docs/_pics/java-notes/leetcode/dp/dp_19.png deleted file mode 100644 index 3f3c9aca..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_19.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_2.png b/docs/_pics/java-notes/leetcode/dp/dp_2.png deleted file mode 100644 index 3d9fd50d..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_20.png b/docs/_pics/java-notes/leetcode/dp/dp_20.png deleted file mode 100644 index 9f49363c..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_20.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_21.png b/docs/_pics/java-notes/leetcode/dp/dp_21.png deleted file mode 100644 index e4f48597..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_21.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_22.png b/docs/_pics/java-notes/leetcode/dp/dp_22.png deleted file mode 100644 index 2e4f6b4e..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_22.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_23.png b/docs/_pics/java-notes/leetcode/dp/dp_23.png deleted file mode 100644 index bd3815ea..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_23.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_24.png b/docs/_pics/java-notes/leetcode/dp/dp_24.png deleted file mode 100644 index 4a8e68e8..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_24.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_25.gif b/docs/_pics/java-notes/leetcode/dp/dp_25.gif deleted file mode 100644 index c0522889..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_25.gif and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_3.png b/docs/_pics/java-notes/leetcode/dp/dp_3.png deleted file mode 100644 index eb37b4c9..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_4.png b/docs/_pics/java-notes/leetcode/dp/dp_4.png deleted file mode 100644 index 951b4776..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_5.png b/docs/_pics/java-notes/leetcode/dp/dp_5.png deleted file mode 100644 index 57e1b081..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_6.png b/docs/_pics/java-notes/leetcode/dp/dp_6.png deleted file mode 100644 index b17fce41..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_6.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_7.png b/docs/_pics/java-notes/leetcode/dp/dp_7.png deleted file mode 100644 index ea11b879..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_7.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_8.png b/docs/_pics/java-notes/leetcode/dp/dp_8.png deleted file mode 100644 index 402929d1..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_8.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/dp/dp_9.png b/docs/_pics/java-notes/leetcode/dp/dp_9.png deleted file mode 100644 index d1fa7e64..00000000 Binary files a/docs/_pics/java-notes/leetcode/dp/dp_9.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/find/find_1.png b/docs/_pics/java-notes/leetcode/find/find_1.png deleted file mode 100644 index 83246508..00000000 Binary files a/docs/_pics/java-notes/leetcode/find/find_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/greedyAlgorithms/ga_1.png b/docs/_pics/java-notes/leetcode/greedyAlgorithms/ga_1.png deleted file mode 100644 index 1ff25c9a..00000000 Binary files a/docs/_pics/java-notes/leetcode/greedyAlgorithms/ga_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/greedyAlgorithms/ga_2.png b/docs/_pics/java-notes/leetcode/greedyAlgorithms/ga_2.png deleted file mode 100644 index 266a4687..00000000 Binary files a/docs/_pics/java-notes/leetcode/greedyAlgorithms/ga_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_1.png b/docs/_pics/java-notes/leetcode/list/list_1.png deleted file mode 100644 index 585d1d56..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_10.png b/docs/_pics/java-notes/leetcode/list/list_10.png deleted file mode 100644 index e382e8a0..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_10.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_11.png b/docs/_pics/java-notes/leetcode/list/list_11.png deleted file mode 100644 index f77a2d89..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_11.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_12.png b/docs/_pics/java-notes/leetcode/list/list_12.png deleted file mode 100644 index 4a4c57d7..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_12.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_13.png b/docs/_pics/java-notes/leetcode/list/list_13.png deleted file mode 100644 index 61020ab2..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_13.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_14.png b/docs/_pics/java-notes/leetcode/list/list_14.png deleted file mode 100644 index a5aa664d..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_14.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_15.png b/docs/_pics/java-notes/leetcode/list/list_15.png deleted file mode 100644 index 54fefaf9..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_15.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_16.png b/docs/_pics/java-notes/leetcode/list/list_16.png deleted file mode 100644 index 4146c34a..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_16.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_17.png b/docs/_pics/java-notes/leetcode/list/list_17.png deleted file mode 100644 index c502f049..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_17.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_18.png b/docs/_pics/java-notes/leetcode/list/list_18.png deleted file mode 100644 index d55ea3f9..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_18.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_19.png b/docs/_pics/java-notes/leetcode/list/list_19.png deleted file mode 100644 index f353582b..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_19.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_2.png b/docs/_pics/java-notes/leetcode/list/list_2.png deleted file mode 100644 index 2a0833b3..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_20.png b/docs/_pics/java-notes/leetcode/list/list_20.png deleted file mode 100644 index e0eef68f..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_20.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_21.png b/docs/_pics/java-notes/leetcode/list/list_21.png deleted file mode 100644 index 09b342d9..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_21.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_22.png b/docs/_pics/java-notes/leetcode/list/list_22.png deleted file mode 100644 index 2afc066d..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_22.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_23.png b/docs/_pics/java-notes/leetcode/list/list_23.png deleted file mode 100644 index f627b2a7..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_23.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_24.png b/docs/_pics/java-notes/leetcode/list/list_24.png deleted file mode 100644 index edfa8365..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_24.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_3.png b/docs/_pics/java-notes/leetcode/list/list_3.png deleted file mode 100644 index eaa78050..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_4.png b/docs/_pics/java-notes/leetcode/list/list_4.png deleted file mode 100644 index 2e98e4f7..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_5.png b/docs/_pics/java-notes/leetcode/list/list_5.png deleted file mode 100644 index 34a23477..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_6.png b/docs/_pics/java-notes/leetcode/list/list_6.png deleted file mode 100644 index 24581d88..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_6.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_7.png b/docs/_pics/java-notes/leetcode/list/list_7.png deleted file mode 100644 index 16cc8c39..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_7.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_8.png b/docs/_pics/java-notes/leetcode/list/list_8.png deleted file mode 100644 index f44ecf7d..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_8.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/list/list_9.png b/docs/_pics/java-notes/leetcode/list/list_9.png deleted file mode 100644 index 205a3bfb..00000000 Binary files a/docs/_pics/java-notes/leetcode/list/list_9.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/math/math_1.png b/docs/_pics/java-notes/leetcode/math/math_1.png deleted file mode 100644 index 930382c5..00000000 Binary files a/docs/_pics/java-notes/leetcode/math/math_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/stackQueue/stack_1.png b/docs/_pics/java-notes/leetcode/stackQueue/stack_1.png deleted file mode 100644 index b37208eb..00000000 Binary files a/docs/_pics/java-notes/leetcode/stackQueue/stack_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/stackQueue/stack_10.png b/docs/_pics/java-notes/leetcode/stackQueue/stack_10.png deleted file mode 100644 index 0b52e1be..00000000 Binary files a/docs/_pics/java-notes/leetcode/stackQueue/stack_10.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/stackQueue/stack_11.png b/docs/_pics/java-notes/leetcode/stackQueue/stack_11.png deleted file mode 100644 index 09931d48..00000000 Binary files a/docs/_pics/java-notes/leetcode/stackQueue/stack_11.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/stackQueue/stack_12.png b/docs/_pics/java-notes/leetcode/stackQueue/stack_12.png deleted file mode 100644 index 74ef244f..00000000 Binary files a/docs/_pics/java-notes/leetcode/stackQueue/stack_12.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/stackQueue/stack_2.png b/docs/_pics/java-notes/leetcode/stackQueue/stack_2.png deleted file mode 100644 index 493ff443..00000000 Binary files a/docs/_pics/java-notes/leetcode/stackQueue/stack_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/stackQueue/stack_3.png b/docs/_pics/java-notes/leetcode/stackQueue/stack_3.png deleted file mode 100644 index 8d683d6d..00000000 Binary files a/docs/_pics/java-notes/leetcode/stackQueue/stack_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/stackQueue/stack_4.png b/docs/_pics/java-notes/leetcode/stackQueue/stack_4.png deleted file mode 100644 index e79cb1b2..00000000 Binary files a/docs/_pics/java-notes/leetcode/stackQueue/stack_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/stackQueue/stack_5.png b/docs/_pics/java-notes/leetcode/stackQueue/stack_5.png deleted file mode 100644 index 28326468..00000000 Binary files a/docs/_pics/java-notes/leetcode/stackQueue/stack_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/stackQueue/stack_6.png b/docs/_pics/java-notes/leetcode/stackQueue/stack_6.png deleted file mode 100644 index e78336b4..00000000 Binary files a/docs/_pics/java-notes/leetcode/stackQueue/stack_6.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/stackQueue/stack_7.png b/docs/_pics/java-notes/leetcode/stackQueue/stack_7.png deleted file mode 100644 index 7b6ac17e..00000000 Binary files a/docs/_pics/java-notes/leetcode/stackQueue/stack_7.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/stackQueue/stack_8.png b/docs/_pics/java-notes/leetcode/stackQueue/stack_8.png deleted file mode 100644 index 5fbd2d38..00000000 Binary files a/docs/_pics/java-notes/leetcode/stackQueue/stack_8.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/stackQueue/stack_9.png b/docs/_pics/java-notes/leetcode/stackQueue/stack_9.png deleted file mode 100644 index 2fac440e..00000000 Binary files a/docs/_pics/java-notes/leetcode/stackQueue/stack_9.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/string/str_1.png b/docs/_pics/java-notes/leetcode/string/str_1.png deleted file mode 100644 index 07acaafe..00000000 Binary files a/docs/_pics/java-notes/leetcode/string/str_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/timeComplexity/tc.png b/docs/_pics/java-notes/leetcode/timeComplexity/tc.png deleted file mode 100644 index dc99e67c..00000000 Binary files a/docs/_pics/java-notes/leetcode/timeComplexity/tc.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/timeComplexity/tc_2.png b/docs/_pics/java-notes/leetcode/timeComplexity/tc_2.png deleted file mode 100644 index 6c374d39..00000000 Binary files a/docs/_pics/java-notes/leetcode/timeComplexity/tc_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/timeComplexity/tc_3.png b/docs/_pics/java-notes/leetcode/timeComplexity/tc_3.png deleted file mode 100644 index e1570b18..00000000 Binary files a/docs/_pics/java-notes/leetcode/timeComplexity/tc_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/timeComplexity/tc_4.png b/docs/_pics/java-notes/leetcode/timeComplexity/tc_4.png deleted file mode 100644 index 2755f443..00000000 Binary files a/docs/_pics/java-notes/leetcode/timeComplexity/tc_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/timeComplexity/tc_5.png b/docs/_pics/java-notes/leetcode/timeComplexity/tc_5.png deleted file mode 100644 index 61641485..00000000 Binary files a/docs/_pics/java-notes/leetcode/timeComplexity/tc_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/timeComplexity/tc_6.png b/docs/_pics/java-notes/leetcode/timeComplexity/tc_6.png deleted file mode 100644 index 34c5de21..00000000 Binary files a/docs/_pics/java-notes/leetcode/timeComplexity/tc_6.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/timeComplexity/tc_7.png b/docs/_pics/java-notes/leetcode/timeComplexity/tc_7.png deleted file mode 100644 index aff5087a..00000000 Binary files a/docs/_pics/java-notes/leetcode/timeComplexity/tc_7.png and /dev/null differ diff --git a/docs/_pics/java-notes/leetcode/timeComplexity/tc_8.png b/docs/_pics/java-notes/leetcode/timeComplexity/tc_8.png deleted file mode 100644 index 6286cbed..00000000 Binary files a/docs/_pics/java-notes/leetcode/timeComplexity/tc_8.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/03f47940-3843-4b51-9e42-5dcaff44858b.jpg b/docs/_pics/java-notes/network/03f47940-3843-4b51-9e42-5dcaff44858b.jpg deleted file mode 100644 index 9a6f75c6..00000000 Binary files a/docs/_pics/java-notes/network/03f47940-3843-4b51-9e42-5dcaff44858b.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/0a9f4125-b6ab-4e94-a807-fd7070ae726a.png b/docs/_pics/java-notes/network/0a9f4125-b6ab-4e94-a807-fd7070ae726a.png deleted file mode 100644 index 395d9201..00000000 Binary files a/docs/_pics/java-notes/network/0a9f4125-b6ab-4e94-a807-fd7070ae726a.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/1492928105791_3.png b/docs/_pics/java-notes/network/1492928105791_3.png deleted file mode 100644 index d18fc1ca..00000000 Binary files a/docs/_pics/java-notes/network/1492928105791_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/1492928416812_4.png b/docs/_pics/java-notes/network/1492928416812_4.png deleted file mode 100644 index a43a731b..00000000 Binary files a/docs/_pics/java-notes/network/1492928416812_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/1492929000361_5.png b/docs/_pics/java-notes/network/1492929000361_5.png deleted file mode 100644 index 919d1222..00000000 Binary files a/docs/_pics/java-notes/network/1492929000361_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/1492929444818_6.png b/docs/_pics/java-notes/network/1492929444818_6.png deleted file mode 100644 index 0aea3f9a..00000000 Binary files a/docs/_pics/java-notes/network/1492929444818_6.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/1492929553651_7.png b/docs/_pics/java-notes/network/1492929553651_7.png deleted file mode 100644 index 13cf0b4e..00000000 Binary files a/docs/_pics/java-notes/network/1492929553651_7.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/1492930243286_8.png b/docs/_pics/java-notes/network/1492930243286_8.png deleted file mode 100644 index 6ee721ff..00000000 Binary files a/docs/_pics/java-notes/network/1492930243286_8.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/1556770b-8c01-4681-af10-46f1df69202c.jpg b/docs/_pics/java-notes/network/1556770b-8c01-4681-af10-46f1df69202c.jpg deleted file mode 100644 index 94d1adc2..00000000 Binary files a/docs/_pics/java-notes/network/1556770b-8c01-4681-af10-46f1df69202c.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/168e893c-e4a0-4ba4-b81f-9d993483abd0.jpg b/docs/_pics/java-notes/network/168e893c-e4a0-4ba4-b81f-9d993483abd0.jpg deleted file mode 100644 index cbb68f98..00000000 Binary files a/docs/_pics/java-notes/network/168e893c-e4a0-4ba4-b81f-9d993483abd0.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/1ab49e39-012b-4383-8284-26570987e3c4.jpg b/docs/_pics/java-notes/network/1ab49e39-012b-4383-8284-26570987e3c4.jpg deleted file mode 100644 index 48a91211..00000000 Binary files a/docs/_pics/java-notes/network/1ab49e39-012b-4383-8284-26570987e3c4.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/2017-06-11-ca.png b/docs/_pics/java-notes/network/2017-06-11-ca.png deleted file mode 100644 index 550292c1..00000000 Binary files a/docs/_pics/java-notes/network/2017-06-11-ca.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/21041ec2-babb-483f-bf47-8b8148eec162.png b/docs/_pics/java-notes/network/21041ec2-babb-483f-bf47-8b8148eec162.png deleted file mode 100644 index 7de9b57a..00000000 Binary files a/docs/_pics/java-notes/network/21041ec2-babb-483f-bf47-8b8148eec162.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/23ba890e-e11c-45e2-a20c-64d217f83430.png b/docs/_pics/java-notes/network/23ba890e-e11c-45e2-a20c-64d217f83430.png deleted file mode 100644 index 5fccbd1c..00000000 Binary files a/docs/_pics/java-notes/network/23ba890e-e11c-45e2-a20c-64d217f83430.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/2719067e-b299-4639-9065-bed6729dbf0b.png b/docs/_pics/java-notes/network/2719067e-b299-4639-9065-bed6729dbf0b.png deleted file mode 100644 index 95057e05..00000000 Binary files a/docs/_pics/java-notes/network/2719067e-b299-4639-9065-bed6729dbf0b.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/2ad244f5-939c-49fa-9385-69bc688677ab.jpg b/docs/_pics/java-notes/network/2ad244f5-939c-49fa-9385-69bc688677ab.jpg deleted file mode 100644 index 5c1e0af9..00000000 Binary files a/docs/_pics/java-notes/network/2ad244f5-939c-49fa-9385-69bc688677ab.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/2d09a847-b854-439c-9198-b29c65810944.png b/docs/_pics/java-notes/network/2d09a847-b854-439c-9198-b29c65810944.png deleted file mode 100644 index 384f7ef0..00000000 Binary files a/docs/_pics/java-notes/network/2d09a847-b854-439c-9198-b29c65810944.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/3939369b-3a4a-48a0-b9eb-3efae26dd400.png b/docs/_pics/java-notes/network/3939369b-3a4a-48a0-b9eb-3efae26dd400.png deleted file mode 100644 index c2cf9d1b..00000000 Binary files a/docs/_pics/java-notes/network/3939369b-3a4a-48a0-b9eb-3efae26dd400.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/39ccb299-ee99-4dd1-b8b4-2f9ec9495cb4.png b/docs/_pics/java-notes/network/39ccb299-ee99-4dd1-b8b4-2f9ec9495cb4.png deleted file mode 100644 index 8e363e47..00000000 Binary files a/docs/_pics/java-notes/network/39ccb299-ee99-4dd1-b8b4-2f9ec9495cb4.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/426df589-6f97-4622-b74d-4a81fcb1da8e.png b/docs/_pics/java-notes/network/426df589-6f97-4622-b74d-4a81fcb1da8e.png deleted file mode 100644 index 98327bba..00000000 Binary files a/docs/_pics/java-notes/network/426df589-6f97-4622-b74d-4a81fcb1da8e.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/45e0e0bf-386d-4280-a341-a0b9496c7674.png b/docs/_pics/java-notes/network/45e0e0bf-386d-4280-a341-a0b9496c7674.png deleted file mode 100644 index 32fb94aa..00000000 Binary files a/docs/_pics/java-notes/network/45e0e0bf-386d-4280-a341-a0b9496c7674.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/46cec213-3048-4a80-aded-fdd577542801.jpg b/docs/_pics/java-notes/network/46cec213-3048-4a80-aded-fdd577542801.jpg deleted file mode 100644 index 32e1f3d5..00000000 Binary files a/docs/_pics/java-notes/network/46cec213-3048-4a80-aded-fdd577542801.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/50d38e84-238f-4081-8876-14ef6d7938b5.jpg b/docs/_pics/java-notes/network/50d38e84-238f-4081-8876-14ef6d7938b5.jpg deleted file mode 100644 index 6546cefa..00000000 Binary files a/docs/_pics/java-notes/network/50d38e84-238f-4081-8876-14ef6d7938b5.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/51e2ed95-65b8-4ae9-8af3-65602d452a25.jpg b/docs/_pics/java-notes/network/51e2ed95-65b8-4ae9-8af3-65602d452a25.jpg deleted file mode 100644 index 595cdc58..00000000 Binary files a/docs/_pics/java-notes/network/51e2ed95-65b8-4ae9-8af3-65602d452a25.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/55dc4e84-573d-4c13-a765-52ed1dd251f9.png b/docs/_pics/java-notes/network/55dc4e84-573d-4c13-a765-52ed1dd251f9.png deleted file mode 100644 index 525b323a..00000000 Binary files a/docs/_pics/java-notes/network/55dc4e84-573d-4c13-a765-52ed1dd251f9.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/5999e5de-7c16-4b52-b3aa-6dc7b58c7894.png b/docs/_pics/java-notes/network/5999e5de-7c16-4b52-b3aa-6dc7b58c7894.png deleted file mode 100644 index a61a7dc4..00000000 Binary files a/docs/_pics/java-notes/network/5999e5de-7c16-4b52-b3aa-6dc7b58c7894.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/5aa82b89-f266-44da-887d-18f31f01d8ef.png b/docs/_pics/java-notes/network/5aa82b89-f266-44da-887d-18f31f01d8ef.png deleted file mode 100644 index 68310502..00000000 Binary files a/docs/_pics/java-notes/network/5aa82b89-f266-44da-887d-18f31f01d8ef.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/66192382-558b-4b05-a35d-ac4a2b1a9811.jpg b/docs/_pics/java-notes/network/66192382-558b-4b05-a35d-ac4a2b1a9811.jpg deleted file mode 100644 index eec226c5..00000000 Binary files a/docs/_pics/java-notes/network/66192382-558b-4b05-a35d-ac4a2b1a9811.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/69f16984-a66f-4288-82e4-79b4aa43e835.jpg b/docs/_pics/java-notes/network/69f16984-a66f-4288-82e4-79b4aa43e835.jpg deleted file mode 100644 index 03b7f3da..00000000 Binary files a/docs/_pics/java-notes/network/69f16984-a66f-4288-82e4-79b4aa43e835.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/7b038838-c75b-4538-ae84-6299386704e5.jpg b/docs/_pics/java-notes/network/7b038838-c75b-4538-ae84-6299386704e5.jpg deleted file mode 100644 index 919a0e58..00000000 Binary files a/docs/_pics/java-notes/network/7b038838-c75b-4538-ae84-6299386704e5.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/7b3efa99-d306-4982-8cfb-e7153c33aab4.png b/docs/_pics/java-notes/network/7b3efa99-d306-4982-8cfb-e7153c33aab4.png deleted file mode 100644 index 21aafd0a..00000000 Binary files a/docs/_pics/java-notes/network/7b3efa99-d306-4982-8cfb-e7153c33aab4.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/7fffa4b8-b36d-471f-ad0c-a88ee763bb76.png b/docs/_pics/java-notes/network/7fffa4b8-b36d-471f-ad0c-a88ee763bb76.png deleted file mode 100644 index b636edf5..00000000 Binary files a/docs/_pics/java-notes/network/7fffa4b8-b36d-471f-ad0c-a88ee763bb76.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/8006a450-6c2f-498c-a928-c927f758b1d0.png b/docs/_pics/java-notes/network/8006a450-6c2f-498c-a928-c927f758b1d0.png deleted file mode 100644 index ac453eeb..00000000 Binary files a/docs/_pics/java-notes/network/8006a450-6c2f-498c-a928-c927f758b1d0.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/85c05fb1-5546-4c50-9221-21f231cdc8c5.jpg b/docs/_pics/java-notes/network/85c05fb1-5546-4c50-9221-21f231cdc8c5.jpg deleted file mode 100644 index 4b911193..00000000 Binary files a/docs/_pics/java-notes/network/85c05fb1-5546-4c50-9221-21f231cdc8c5.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/86e6a91d-a285-447a-9345-c5484b8d0c47.png b/docs/_pics/java-notes/network/86e6a91d-a285-447a-9345-c5484b8d0c47.png deleted file mode 100644 index 56f83eaf..00000000 Binary files a/docs/_pics/java-notes/network/86e6a91d-a285-447a-9345-c5484b8d0c47.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/910f613f-514f-4534-87dd-9b4699d59d31.png b/docs/_pics/java-notes/network/910f613f-514f-4534-87dd-9b4699d59d31.png deleted file mode 100644 index 28362d47..00000000 Binary files a/docs/_pics/java-notes/network/910f613f-514f-4534-87dd-9b4699d59d31.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/92ad9bae-7d02-43ba-8115-a9d6f530ca28.png b/docs/_pics/java-notes/network/92ad9bae-7d02-43ba-8115-a9d6f530ca28.png deleted file mode 100644 index 7b85c49a..00000000 Binary files a/docs/_pics/java-notes/network/92ad9bae-7d02-43ba-8115-a9d6f530ca28.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/9cd0ae20-4fb5-4017-a000-f7d3a0eb3529.png b/docs/_pics/java-notes/network/9cd0ae20-4fb5-4017-a000-f7d3a0eb3529.png deleted file mode 100644 index 49da824e..00000000 Binary files a/docs/_pics/java-notes/network/9cd0ae20-4fb5-4017-a000-f7d3a0eb3529.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/HTTP1_x_Connections.png b/docs/_pics/java-notes/network/HTTP1_x_Connections.png deleted file mode 100644 index d8c18a3c..00000000 Binary files a/docs/_pics/java-notes/network/HTTP1_x_Connections.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/HTTP_RequestMessageExample.png b/docs/_pics/java-notes/network/HTTP_RequestMessageExample.png deleted file mode 100644 index 8fd213cb..00000000 Binary files a/docs/_pics/java-notes/network/HTTP_RequestMessageExample.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/HTTP_ResponseMessageExample.png b/docs/_pics/java-notes/network/HTTP_ResponseMessageExample.png deleted file mode 100644 index 1adf26c5..00000000 Binary files a/docs/_pics/java-notes/network/HTTP_ResponseMessageExample.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/How-HTTPS-Works.png b/docs/_pics/java-notes/network/How-HTTPS-Works.png deleted file mode 100644 index c10605f7..00000000 Binary files a/docs/_pics/java-notes/network/How-HTTPS-Works.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/_u4E0B_u8F7D.png b/docs/_pics/java-notes/network/_u4E0B_u8F7D.png deleted file mode 100644 index 9da97330..00000000 Binary files a/docs/_pics/java-notes/network/_u4E0B_u8F7D.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/a314bb79-5b18-4e63-a976-3448bffa6f1b.png b/docs/_pics/java-notes/network/a314bb79-5b18-4e63-a976-3448bffa6f1b.png deleted file mode 100644 index 1a5a6474..00000000 Binary files a/docs/_pics/java-notes/network/a314bb79-5b18-4e63-a976-3448bffa6f1b.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/a3253deb-8d21-40a1-aae4-7d178e4aa319.jpg b/docs/_pics/java-notes/network/a3253deb-8d21-40a1-aae4-7d178e4aa319.jpg deleted file mode 100644 index 23258afa..00000000 Binary files a/docs/_pics/java-notes/network/a3253deb-8d21-40a1-aae4-7d178e4aa319.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/a6026bb4-3daf-439f-b1ec-a5a24e19d2fb.jpg b/docs/_pics/java-notes/network/a6026bb4-3daf-439f-b1ec-a5a24e19d2fb.jpg deleted file mode 100644 index 4ff577e2..00000000 Binary files a/docs/_pics/java-notes/network/a6026bb4-3daf-439f-b1ec-a5a24e19d2fb.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/a74b70ac-323a-4b31-b4d5-90569b8a944b.png b/docs/_pics/java-notes/network/a74b70ac-323a-4b31-b4d5-90569b8a944b.png deleted file mode 100644 index 3d686121..00000000 Binary files a/docs/_pics/java-notes/network/a74b70ac-323a-4b31-b4d5-90569b8a944b.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/aa29cc88-7256-4399-8c7f-3cf4a6489559.png b/docs/_pics/java-notes/network/aa29cc88-7256-4399-8c7f-3cf4a6489559.png deleted file mode 100644 index 9b93237e..00000000 Binary files a/docs/_pics/java-notes/network/aa29cc88-7256-4399-8c7f-3cf4a6489559.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/ac106e7e-489a-4082-abd9-dabebe48394c.jpg b/docs/_pics/java-notes/network/ac106e7e-489a-4082-abd9-dabebe48394c.jpg deleted file mode 100644 index 0d323411..00000000 Binary files a/docs/_pics/java-notes/network/ac106e7e-489a-4082-abd9-dabebe48394c.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/af198da1-2480-4043-b07f-a3b91a88b815.png b/docs/_pics/java-notes/network/af198da1-2480-4043-b07f-a3b91a88b815.png deleted file mode 100644 index 34d7a286..00000000 Binary files a/docs/_pics/java-notes/network/af198da1-2480-4043-b07f-a3b91a88b815.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/b54eeb16-0b0e-484c-be62-306f57c40d77.jpg b/docs/_pics/java-notes/network/b54eeb16-0b0e-484c-be62-306f57c40d77.jpg deleted file mode 100644 index 692a0357..00000000 Binary files a/docs/_pics/java-notes/network/b54eeb16-0b0e-484c-be62-306f57c40d77.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/b9d79a5a-e7af-499b-b989-f10483e71b8b.jpg b/docs/_pics/java-notes/network/b9d79a5a-e7af-499b-b989-f10483e71b8b.jpg deleted file mode 100644 index b7502831..00000000 Binary files a/docs/_pics/java-notes/network/b9d79a5a-e7af-499b-b989-f10483e71b8b.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/be5c2c61-86d2-4dba-a289-b48ea23219de.jpg b/docs/_pics/java-notes/network/be5c2c61-86d2-4dba-a289-b48ea23219de.jpg deleted file mode 100644 index a2965a7d..00000000 Binary files a/docs/_pics/java-notes/network/be5c2c61-86d2-4dba-a289-b48ea23219de.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/bf16c541-0717-473b-b75d-4115864f4fbf.jpg b/docs/_pics/java-notes/network/bf16c541-0717-473b-b75d-4115864f4fbf.jpg deleted file mode 100644 index a83ba275..00000000 Binary files a/docs/_pics/java-notes/network/bf16c541-0717-473b-b75d-4115864f4fbf.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/c3369072-c740-43b0-b276-202bd1d3960d.jpg b/docs/_pics/java-notes/network/c3369072-c740-43b0-b276-202bd1d3960d.jpg deleted file mode 100644 index 17a2e9bf..00000000 Binary files a/docs/_pics/java-notes/network/c3369072-c740-43b0-b276-202bd1d3960d.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/c4c14368-519c-4a0e-8331-0a553715e3e7.jpg b/docs/_pics/java-notes/network/c4c14368-519c-4a0e-8331-0a553715e3e7.jpg deleted file mode 100644 index 0cb4f0a0..00000000 Binary files a/docs/_pics/java-notes/network/c4c14368-519c-4a0e-8331-0a553715e3e7.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/c5022dd3-be22-4250-b9f6-38ae984a04d7.jpg b/docs/_pics/java-notes/network/c5022dd3-be22-4250-b9f6-38ae984a04d7.jpg deleted file mode 100644 index 3eb79406..00000000 Binary files a/docs/_pics/java-notes/network/c5022dd3-be22-4250-b9f6-38ae984a04d7.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/c9cfcd20-c901-435f-9a07-3e46830c359f.jpg b/docs/_pics/java-notes/network/c9cfcd20-c901-435f-9a07-3e46830c359f.jpg deleted file mode 100644 index 813b5142..00000000 Binary files a/docs/_pics/java-notes/network/c9cfcd20-c901-435f-9a07-3e46830c359f.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/cbf50eb8-22b4-4528-a2e7-d187143d57f7.png b/docs/_pics/java-notes/network/cbf50eb8-22b4-4528-a2e7-d187143d57f7.png deleted file mode 100644 index 27f2b742..00000000 Binary files a/docs/_pics/java-notes/network/cbf50eb8-22b4-4528-a2e7-d187143d57f7.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/d4c3a4a1-0846-46ec-9cc3-eaddfca71254.jpg b/docs/_pics/java-notes/network/d4c3a4a1-0846-46ec-9cc3-eaddfca71254.jpg deleted file mode 100644 index bbc7f102..00000000 Binary files a/docs/_pics/java-notes/network/d4c3a4a1-0846-46ec-9cc3-eaddfca71254.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/d4eef1e2-5703-4ca4-82ab-8dda93d6b81f.png b/docs/_pics/java-notes/network/d4eef1e2-5703-4ca4-82ab-8dda93d6b81f.png deleted file mode 100644 index 4f62e6f5..00000000 Binary files a/docs/_pics/java-notes/network/d4eef1e2-5703-4ca4-82ab-8dda93d6b81f.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/dc00f70e-c5c8-4d20-baf1-2d70014a97e3.jpg b/docs/_pics/java-notes/network/dc00f70e-c5c8-4d20-baf1-2d70014a97e3.jpg deleted file mode 100644 index 8090706b..00000000 Binary files a/docs/_pics/java-notes/network/dc00f70e-c5c8-4d20-baf1-2d70014a97e3.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/ddcf2327-8d84-425d-8535-121a94bcb88d.jpg b/docs/_pics/java-notes/network/ddcf2327-8d84-425d-8535-121a94bcb88d.jpg deleted file mode 100644 index 2a95d92d..00000000 Binary files a/docs/_pics/java-notes/network/ddcf2327-8d84-425d-8535-121a94bcb88d.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/e3124763-f75e-46c3-ba82-341e6c98d862.jpg b/docs/_pics/java-notes/network/e3124763-f75e-46c3-ba82-341e6c98d862.jpg deleted file mode 100644 index 80643657..00000000 Binary files a/docs/_pics/java-notes/network/e3124763-f75e-46c3-ba82-341e6c98d862.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/e3f1657c-80fc-4dfa-9643-bf51abd201c6.png b/docs/_pics/java-notes/network/e3f1657c-80fc-4dfa-9643-bf51abd201c6.png deleted file mode 100644 index 105916c6..00000000 Binary files a/docs/_pics/java-notes/network/e3f1657c-80fc-4dfa-9643-bf51abd201c6.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/e92d0ebc-7d46-413b-aec1-34a39602f787.png b/docs/_pics/java-notes/network/e92d0ebc-7d46-413b-aec1-34a39602f787.png deleted file mode 100644 index 1090a779..00000000 Binary files a/docs/_pics/java-notes/network/e92d0ebc-7d46-413b-aec1-34a39602f787.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/ea5f3efe-d5e6-499b-b278-9e898af61257.jpg b/docs/_pics/java-notes/network/ea5f3efe-d5e6-499b-b278-9e898af61257.jpg deleted file mode 100644 index a07e736d..00000000 Binary files a/docs/_pics/java-notes/network/ea5f3efe-d5e6-499b-b278-9e898af61257.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/ed5522bb-3a60-481c-8654-43e7195a48fe.png b/docs/_pics/java-notes/network/ed5522bb-3a60-481c-8654-43e7195a48fe.png deleted file mode 100644 index 1c153a8a..00000000 Binary files a/docs/_pics/java-notes/network/ed5522bb-3a60-481c-8654-43e7195a48fe.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/f0a31c04-6e26-408c-8395-88f4e2ae928b.jpg b/docs/_pics/java-notes/network/f0a31c04-6e26-408c-8395-88f4e2ae928b.jpg deleted file mode 100644 index 99eb3168..00000000 Binary files a/docs/_pics/java-notes/network/f0a31c04-6e26-408c-8395-88f4e2ae928b.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/f61b5419-c94a-4df1-8d4d-aed9ae8cc6d5.png b/docs/_pics/java-notes/network/f61b5419-c94a-4df1-8d4d-aed9ae8cc6d5.png deleted file mode 100644 index dc0d4e34..00000000 Binary files a/docs/_pics/java-notes/network/f61b5419-c94a-4df1-8d4d-aed9ae8cc6d5.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/f87afe72-c2df-4c12-ac03-9b8d581a8af8.jpg b/docs/_pics/java-notes/network/f87afe72-c2df-4c12-ac03-9b8d581a8af8.jpg deleted file mode 100644 index 6a090993..00000000 Binary files a/docs/_pics/java-notes/network/f87afe72-c2df-4c12-ac03-9b8d581a8af8.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/fa2273c3-1b5f-48ce-8e8b-441a4116c1c4.jpg b/docs/_pics/java-notes/network/fa2273c3-1b5f-48ce-8e8b-441a4116c1c4.jpg deleted file mode 100644 index 42d3c743..00000000 Binary files a/docs/_pics/java-notes/network/fa2273c3-1b5f-48ce-8e8b-441a4116c1c4.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/net_1.png b/docs/_pics/java-notes/network/net_1.png deleted file mode 100644 index 819f735a..00000000 Binary files a/docs/_pics/java-notes/network/net_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/net_2.png b/docs/_pics/java-notes/network/net_2.png deleted file mode 100644 index e5939399..00000000 Binary files a/docs/_pics/java-notes/network/net_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/network-of-networks.gif b/docs/_pics/java-notes/network/network-of-networks.gif deleted file mode 100644 index 7473f913..00000000 Binary files a/docs/_pics/java-notes/network/network-of-networks.gif and /dev/null differ diff --git a/docs/_pics/java-notes/network/socket_tcp.png b/docs/_pics/java-notes/network/socket_tcp.png deleted file mode 100644 index 912c7e9b..00000000 Binary files a/docs/_pics/java-notes/network/socket_tcp.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/socket_tcp_struct.png b/docs/_pics/java-notes/network/socket_tcp_struct.png deleted file mode 100644 index 8faa8b2a..00000000 Binary files a/docs/_pics/java-notes/network/socket_tcp_struct.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/socket_udp.png b/docs/_pics/java-notes/network/socket_udp.png deleted file mode 100644 index 8d240886..00000000 Binary files a/docs/_pics/java-notes/network/socket_udp.png and /dev/null differ diff --git a/docs/_pics/java-notes/network/ssl-offloading.jpg b/docs/_pics/java-notes/network/ssl-offloading.jpg deleted file mode 100644 index 8f01a418..00000000 Binary files a/docs/_pics/java-notes/network/ssl-offloading.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/network/urlnuri.jpg b/docs/_pics/java-notes/network/urlnuri.jpg deleted file mode 100644 index be05f018..00000000 Binary files a/docs/_pics/java-notes/network/urlnuri.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/oo/01_1.png b/docs/_pics/java-notes/oo/01_1.png deleted file mode 100644 index 19408656..00000000 Binary files a/docs/_pics/java-notes/oo/01_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/02_01.png b/docs/_pics/java-notes/oo/02_01.png deleted file mode 100644 index 74ef6cf0..00000000 Binary files a/docs/_pics/java-notes/oo/02_01.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/02_2.png b/docs/_pics/java-notes/oo/02_2.png deleted file mode 100644 index 3a21ee3f..00000000 Binary files a/docs/_pics/java-notes/oo/02_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/04_1.png b/docs/_pics/java-notes/oo/04_1.png deleted file mode 100644 index f8f072a8..00000000 Binary files a/docs/_pics/java-notes/oo/04_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/04_2.png b/docs/_pics/java-notes/oo/04_2.png deleted file mode 100644 index 6b24cfe7..00000000 Binary files a/docs/_pics/java-notes/oo/04_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/04_3.png b/docs/_pics/java-notes/oo/04_3.png deleted file mode 100644 index c3dba285..00000000 Binary files a/docs/_pics/java-notes/oo/04_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/0889c0b4-07b4-45fc-873c-e0e16b97f67d.png b/docs/_pics/java-notes/oo/0889c0b4-07b4-45fc-873c-e0e16b97f67d.png deleted file mode 100644 index 4d9e3e21..00000000 Binary files a/docs/_pics/java-notes/oo/0889c0b4-07b4-45fc-873c-e0e16b97f67d.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/0df5d84c-e7ca-4e3a-a688-bb8e68894467.png b/docs/_pics/java-notes/oo/0df5d84c-e7ca-4e3a-a688-bb8e68894467.png deleted file mode 100644 index 78a69978..00000000 Binary files a/docs/_pics/java-notes/oo/0df5d84c-e7ca-4e3a-a688-bb8e68894467.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/11236498-1417-46ce-a1b0-e10054256955.png b/docs/_pics/java-notes/oo/11236498-1417-46ce-a1b0-e10054256955.png deleted file mode 100644 index 19cd1543..00000000 Binary files a/docs/_pics/java-notes/oo/11236498-1417-46ce-a1b0-e10054256955.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/137c593d-0a9e-47b8-a9e6-b71f540b82dd.png b/docs/_pics/java-notes/oo/137c593d-0a9e-47b8-a9e6-b71f540b82dd.png deleted file mode 100644 index a5ac33ca..00000000 Binary files a/docs/_pics/java-notes/oo/137c593d-0a9e-47b8-a9e6-b71f540b82dd.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/13b0940e-d1d7-4b17-af4f-b70cb0a75e08.png b/docs/_pics/java-notes/oo/13b0940e-d1d7-4b17-af4f-b70cb0a75e08.png deleted file mode 100644 index 27f1c9f1..00000000 Binary files a/docs/_pics/java-notes/oo/13b0940e-d1d7-4b17-af4f-b70cb0a75e08.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/1818e141-8700-4026-99f7-900a545875f5.png b/docs/_pics/java-notes/oo/1818e141-8700-4026-99f7-900a545875f5.png deleted file mode 100644 index 1523ad79..00000000 Binary files a/docs/_pics/java-notes/oo/1818e141-8700-4026-99f7-900a545875f5.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/1fc969e4-0e7c-441b-b53c-01950d2f2be5.png b/docs/_pics/java-notes/oo/1fc969e4-0e7c-441b-b53c-01950d2f2be5.png deleted file mode 100644 index 0f7d6660..00000000 Binary files a/docs/_pics/java-notes/oo/1fc969e4-0e7c-441b-b53c-01950d2f2be5.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/396be981-3f2c-4fd9-8101-dbf9c841504b.jpg b/docs/_pics/java-notes/oo/396be981-3f2c-4fd9-8101-dbf9c841504b.jpg deleted file mode 100644 index 60c19f88..00000000 Binary files a/docs/_pics/java-notes/oo/396be981-3f2c-4fd9-8101-dbf9c841504b.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/oo/3d5b828e-5c4d-48d8-a440-281e4a8e1c92.png b/docs/_pics/java-notes/oo/3d5b828e-5c4d-48d8-a440-281e4a8e1c92.png deleted file mode 100644 index 2f39530c..00000000 Binary files a/docs/_pics/java-notes/oo/3d5b828e-5c4d-48d8-a440-281e4a8e1c92.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/5359cbf5-5a79-4874-9b17-f23c53c2cb80.jpg b/docs/_pics/java-notes/oo/5359cbf5-5a79-4874-9b17-f23c53c2cb80.jpg deleted file mode 100644 index a449f38c..00000000 Binary files a/docs/_pics/java-notes/oo/5359cbf5-5a79-4874-9b17-f23c53c2cb80.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/oo/562f2844-d77c-40e0-887a-28a7128abd42.png b/docs/_pics/java-notes/oo/562f2844-d77c-40e0-887a-28a7128abd42.png deleted file mode 100644 index b74f69b7..00000000 Binary files a/docs/_pics/java-notes/oo/562f2844-d77c-40e0-887a-28a7128abd42.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/691f11eb-31a7-46be-9de1-61f433c4b3c7.png b/docs/_pics/java-notes/oo/691f11eb-31a7-46be-9de1-61f433c4b3c7.png deleted file mode 100644 index b61e2174..00000000 Binary files a/docs/_pics/java-notes/oo/691f11eb-31a7-46be-9de1-61f433c4b3c7.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/77931a4b-72ba-4016-827d-84b9a6845a51.png b/docs/_pics/java-notes/oo/77931a4b-72ba-4016-827d-84b9a6845a51.png deleted file mode 100644 index 6f8a3c52..00000000 Binary files a/docs/_pics/java-notes/oo/77931a4b-72ba-4016-827d-84b9a6845a51.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/794239e3-4baf-4aad-92df-f02f59b2a6fe.png b/docs/_pics/java-notes/oo/794239e3-4baf-4aad-92df-f02f59b2a6fe.png deleted file mode 100644 index 7adddd91..00000000 Binary files a/docs/_pics/java-notes/oo/794239e3-4baf-4aad-92df-f02f59b2a6fe.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/7a3c6a30-c735-4edb-8115-337288a4f0f2.jpg b/docs/_pics/java-notes/oo/7a3c6a30-c735-4edb-8115-337288a4f0f2.jpg deleted file mode 100644 index 8d40765e..00000000 Binary files a/docs/_pics/java-notes/oo/7a3c6a30-c735-4edb-8115-337288a4f0f2.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/oo/82cfda3b-b53b-4c89-9fdb-26dd2db0cd02.jpg b/docs/_pics/java-notes/oo/82cfda3b-b53b-4c89-9fdb-26dd2db0cd02.jpg deleted file mode 100644 index 2b510c43..00000000 Binary files a/docs/_pics/java-notes/oo/82cfda3b-b53b-4c89-9fdb-26dd2db0cd02.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/oo/8668a3e1-c9c7-4fcb-98b2-a96a5d841579.png b/docs/_pics/java-notes/oo/8668a3e1-c9c7-4fcb-98b2-a96a5d841579.png deleted file mode 100644 index 9eda1deb..00000000 Binary files a/docs/_pics/java-notes/oo/8668a3e1-c9c7-4fcb-98b2-a96a5d841579.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/867e93eb-3161-4f39-b2d2-c0cd3788e194.png b/docs/_pics/java-notes/oo/867e93eb-3161-4f39-b2d2-c0cd3788e194.png deleted file mode 100644 index 26cb0153..00000000 Binary files a/docs/_pics/java-notes/oo/867e93eb-3161-4f39-b2d2-c0cd3788e194.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/LOun2W9134NxVugmbJPp15d4LalxC4O.png b/docs/_pics/java-notes/oo/LOun2W9134NxVugmbJPp15d4LalxC4O.png deleted file mode 100644 index 188460a5..00000000 Binary files a/docs/_pics/java-notes/oo/LOun2W9134NxVugmbJPp15d4LalxC4O.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/SoWkIImgAStDuU8goIp9ILK8IatCoQn.png b/docs/_pics/java-notes/oo/SoWkIImgAStDuU8goIp9ILK8IatCoQn.png deleted file mode 100644 index da66da14..00000000 Binary files a/docs/_pics/java-notes/oo/SoWkIImgAStDuU8goIp9ILK8IatCoQn.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/SoWkIImgAStDuU8goIp9ILLmB2xEJyv.png b/docs/_pics/java-notes/oo/SoWkIImgAStDuU8goIp9ILLmB2xEJyv.png deleted file mode 100644 index e5ae5234..00000000 Binary files a/docs/_pics/java-notes/oo/SoWkIImgAStDuU8goIp9ILLmB2xEJyv.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/SoWkIImgAStDuU8goIp9ILLmJ4ylIar.png b/docs/_pics/java-notes/oo/SoWkIImgAStDuU8goIp9ILLmJ4ylIar.png deleted file mode 100644 index e31398b6..00000000 Binary files a/docs/_pics/java-notes/oo/SoWkIImgAStDuU8goIp9ILLmJ4ylIar.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/SoWkIImgAStDuU8goIp9ILLmJyrBBKh.png b/docs/_pics/java-notes/oo/SoWkIImgAStDuU8goIp9ILLmJyrBBKh.png deleted file mode 100644 index 055d995e..00000000 Binary files a/docs/_pics/java-notes/oo/SoWkIImgAStDuU8goIp9ILLmJyrBBKh.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/SoWkIImgAStDuU8goIp9ILLmpiyjo2_.png b/docs/_pics/java-notes/oo/SoWkIImgAStDuU8goIp9ILLmpiyjo2_.png deleted file mode 100644 index 37f32c4c..00000000 Binary files a/docs/_pics/java-notes/oo/SoWkIImgAStDuU8goIp9ILLmpiyjo2_.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/a40661e4-1a71-46d2-a158-ff36f7fc3331.png b/docs/_pics/java-notes/oo/a40661e4-1a71-46d2-a158-ff36f7fc3331.png deleted file mode 100644 index e980a2e3..00000000 Binary files a/docs/_pics/java-notes/oo/a40661e4-1a71-46d2-a158-ff36f7fc3331.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/a6c20f60-5eba-427d-9413-352ada4b40fe.png b/docs/_pics/java-notes/oo/a6c20f60-5eba-427d-9413-352ada4b40fe.png deleted file mode 100644 index 11d36653..00000000 Binary files a/docs/_pics/java-notes/oo/a6c20f60-5eba-427d-9413-352ada4b40fe.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/ae1b27b8-bc13-42e7-ac12-a2242e125499.png b/docs/_pics/java-notes/oo/ae1b27b8-bc13-42e7-ac12-a2242e125499.png deleted file mode 100644 index 27977740..00000000 Binary files a/docs/_pics/java-notes/oo/ae1b27b8-bc13-42e7-ac12-a2242e125499.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/b0f61ac2-a4b6-4042-9cf0-ccf4238c1ff7.png b/docs/_pics/java-notes/oo/b0f61ac2-a4b6-4042-9cf0-ccf4238c1ff7.png deleted file mode 100644 index 73dad483..00000000 Binary files a/docs/_pics/java-notes/oo/b0f61ac2-a4b6-4042-9cf0-ccf4238c1ff7.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/b1df9732-86ce-4d69-9f06-fba1db7b3b5a.jpg b/docs/_pics/java-notes/oo/b1df9732-86ce-4d69-9f06-fba1db7b3b5a.jpg deleted file mode 100644 index a71d167e..00000000 Binary files a/docs/_pics/java-notes/oo/b1df9732-86ce-4d69-9f06-fba1db7b3b5a.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/oo/c2cbf5d2-82af-4c78-bd43-495da5adf55f.png b/docs/_pics/java-notes/oo/c2cbf5d2-82af-4c78-bd43-495da5adf55f.png deleted file mode 100644 index 70ecc3ec..00000000 Binary files a/docs/_pics/java-notes/oo/c2cbf5d2-82af-4c78-bd43-495da5adf55f.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/c3c1c0e8-3a78-4426-961f-b46dd0879dd8.png b/docs/_pics/java-notes/oo/c3c1c0e8-3a78-4426-961f-b46dd0879dd8.png deleted file mode 100644 index 48e980fc..00000000 Binary files a/docs/_pics/java-notes/oo/c3c1c0e8-3a78-4426-961f-b46dd0879dd8.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/c5085437-54df-4304-b62d-44b961711ba7.png b/docs/_pics/java-notes/oo/c5085437-54df-4304-b62d-44b961711ba7.png deleted file mode 100644 index 36c948aa..00000000 Binary files a/docs/_pics/java-notes/oo/c5085437-54df-4304-b62d-44b961711ba7.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/c79da808-0f28-4a36-bc04-33ccc5b83c13.png b/docs/_pics/java-notes/oo/c79da808-0f28-4a36-bc04-33ccc5b83c13.png deleted file mode 100644 index 91e2c144..00000000 Binary files a/docs/_pics/java-notes/oo/c79da808-0f28-4a36-bc04-33ccc5b83c13.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/c9cfd600-bc91-4f3a-9f99-b42f88a5bb24.jpg b/docs/_pics/java-notes/oo/c9cfd600-bc91-4f3a-9f99-b42f88a5bb24.jpg deleted file mode 100644 index fa7ce3ae..00000000 Binary files a/docs/_pics/java-notes/oo/c9cfd600-bc91-4f3a-9f99-b42f88a5bb24.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/oo/d0afdd23-c9a5-4d1c-9b3d-404bff3bd0d1.png b/docs/_pics/java-notes/oo/d0afdd23-c9a5-4d1c-9b3d-404bff3bd0d1.png deleted file mode 100644 index 58ff84fe..00000000 Binary files a/docs/_pics/java-notes/oo/d0afdd23-c9a5-4d1c-9b3d-404bff3bd0d1.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/d52270b4-9097-4667-9f18-f405fc661c99.png b/docs/_pics/java-notes/oo/d52270b4-9097-4667-9f18-f405fc661c99.png deleted file mode 100644 index 347fe316..00000000 Binary files a/docs/_pics/java-notes/oo/d52270b4-9097-4667-9f18-f405fc661c99.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/dd3b289c-d90e-44a6-a44c-4880517eb1de.png b/docs/_pics/java-notes/oo/dd3b289c-d90e-44a6-a44c-4880517eb1de.png deleted file mode 100644 index 51055a2b..00000000 Binary files a/docs/_pics/java-notes/oo/dd3b289c-d90e-44a6-a44c-4880517eb1de.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/e6bded8e-41a0-489a-88a6-638e88ab7666.jpg b/docs/_pics/java-notes/oo/e6bded8e-41a0-489a-88a6-638e88ab7666.jpg deleted file mode 100644 index f4aa12a0..00000000 Binary files a/docs/_pics/java-notes/oo/e6bded8e-41a0-489a-88a6-638e88ab7666.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/oo/ec923dc7-864c-47b0-a411-1f2c48d084de.png b/docs/_pics/java-notes/oo/ec923dc7-864c-47b0-a411-1f2c48d084de.png deleted file mode 100644 index 007e9646..00000000 Binary files a/docs/_pics/java-notes/oo/ec923dc7-864c-47b0-a411-1f2c48d084de.png and /dev/null differ diff --git a/docs/_pics/java-notes/oo/f9978fa6-9f49-4a0f-8540-02d269ac448f.png b/docs/_pics/java-notes/oo/f9978fa6-9f49-4a0f-8540-02d269ac448f.png deleted file mode 100644 index 7da6d4b2..00000000 Binary files a/docs/_pics/java-notes/oo/f9978fa6-9f49-4a0f-8540-02d269ac448f.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/014fbc4d-d873-4a12-b160-867ddaed9807.jpg b/docs/_pics/java-notes/os/014fbc4d-d873-4a12-b160-867ddaed9807.jpg deleted file mode 100644 index 39c003ce..00000000 Binary files a/docs/_pics/java-notes/os/014fbc4d-d873-4a12-b160-867ddaed9807.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/os/042cf928-3c8e-4815-ae9c-f2780202c68f.png b/docs/_pics/java-notes/os/042cf928-3c8e-4815-ae9c-f2780202c68f.png deleted file mode 100644 index 57d8c810..00000000 Binary files a/docs/_pics/java-notes/os/042cf928-3c8e-4815-ae9c-f2780202c68f.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/075e1977-7846-4928-96c8-bb5b0268693c.jpg b/docs/_pics/java-notes/os/075e1977-7846-4928-96c8-bb5b0268693c.jpg deleted file mode 100644 index b8b100a0..00000000 Binary files a/docs/_pics/java-notes/os/075e1977-7846-4928-96c8-bb5b0268693c.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/os/22de0538-7c6e-4365-bd3b-8ce3c5900216.png b/docs/_pics/java-notes/os/22de0538-7c6e-4365-bd3b-8ce3c5900216.png deleted file mode 100644 index 8d7dc098..00000000 Binary files a/docs/_pics/java-notes/os/22de0538-7c6e-4365-bd3b-8ce3c5900216.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/271ce08f-c124-475f-b490-be44fedc6d2e.png b/docs/_pics/java-notes/os/271ce08f-c124-475f-b490-be44fedc6d2e.png deleted file mode 100644 index 8de6367b..00000000 Binary files a/docs/_pics/java-notes/os/271ce08f-c124-475f-b490-be44fedc6d2e.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/2_14_microkernelArchitecture.jpg b/docs/_pics/java-notes/os/2_14_microkernelArchitecture.jpg deleted file mode 100644 index 21c2a58f..00000000 Binary files a/docs/_pics/java-notes/os/2_14_microkernelArchitecture.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/os/2ac50b81-d92a-4401-b9ec-f2113ecc3076.png b/docs/_pics/java-notes/os/2ac50b81-d92a-4401-b9ec-f2113ecc3076.png deleted file mode 100644 index 173ce970..00000000 Binary files a/docs/_pics/java-notes/os/2ac50b81-d92a-4401-b9ec-f2113ecc3076.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/3cd630ea-017c-488d-ad1d-732b4efeddf5.png b/docs/_pics/java-notes/os/3cd630ea-017c-488d-ad1d-732b4efeddf5.png deleted file mode 100644 index 9dc77733..00000000 Binary files a/docs/_pics/java-notes/os/3cd630ea-017c-488d-ad1d-732b4efeddf5.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/47d98583-8bb0-45cc-812d-47eefa0a4a40.jpg b/docs/_pics/java-notes/os/47d98583-8bb0-45cc-812d-47eefa0a4a40.jpg deleted file mode 100644 index f6ddd2c2..00000000 Binary files a/docs/_pics/java-notes/os/47d98583-8bb0-45cc-812d-47eefa0a4a40.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/os/4e2485e4-34bd-4967-9f02-0c093b797aaa.png b/docs/_pics/java-notes/os/4e2485e4-34bd-4967-9f02-0c093b797aaa.png deleted file mode 100644 index a5643118..00000000 Binary files a/docs/_pics/java-notes/os/4e2485e4-34bd-4967-9f02-0c093b797aaa.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/50831a6f-2777-46ea-a571-29f23c85cc21.jpg b/docs/_pics/java-notes/os/50831a6f-2777-46ea-a571-29f23c85cc21.jpg deleted file mode 100644 index 4a3f798b..00000000 Binary files a/docs/_pics/java-notes/os/50831a6f-2777-46ea-a571-29f23c85cc21.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/os/53cd9ade-b0a6-4399-b4de-7f1fbd06cdfb.png b/docs/_pics/java-notes/os/53cd9ade-b0a6-4399-b4de-7f1fbd06cdfb.png deleted file mode 100644 index 2666f9c2..00000000 Binary files a/docs/_pics/java-notes/os/53cd9ade-b0a6-4399-b4de-7f1fbd06cdfb.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/5942debd-fc00-477a-b390-7c5692cc8070.jpg b/docs/_pics/java-notes/os/5942debd-fc00-477a-b390-7c5692cc8070.jpg deleted file mode 100644 index 62b39e4b..00000000 Binary files a/docs/_pics/java-notes/os/5942debd-fc00-477a-b390-7c5692cc8070.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/os/5f5ef0b6-98ea-497c-a007-f6c55288eab1.png b/docs/_pics/java-notes/os/5f5ef0b6-98ea-497c-a007-f6c55288eab1.png deleted file mode 100644 index a3ea0a24..00000000 Binary files a/docs/_pics/java-notes/os/5f5ef0b6-98ea-497c-a007-f6c55288eab1.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/62e0dd4f-44c3-43ee-bb6e-fedb9e068519.png b/docs/_pics/java-notes/os/62e0dd4f-44c3-43ee-bb6e-fedb9e068519.png deleted file mode 100644 index 3a41cdce..00000000 Binary files a/docs/_pics/java-notes/os/62e0dd4f-44c3-43ee-bb6e-fedb9e068519.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/658fc5e7-79c0-4247-9445-d69bf194c539.png b/docs/_pics/java-notes/os/658fc5e7-79c0-4247-9445-d69bf194c539.png deleted file mode 100644 index f488859e..00000000 Binary files a/docs/_pics/java-notes/os/658fc5e7-79c0-4247-9445-d69bf194c539.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/6729baa0-57d7-4817-b3aa-518cbccf824c.jpg b/docs/_pics/java-notes/os/6729baa0-57d7-4817-b3aa-518cbccf824c.jpg deleted file mode 100644 index 7035f001..00000000 Binary files a/docs/_pics/java-notes/os/6729baa0-57d7-4817-b3aa-518cbccf824c.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/os/76a49594323247f21c9b3a69945445ee.png b/docs/_pics/java-notes/os/76a49594323247f21c9b3a69945445ee.png deleted file mode 100644 index 788ba0b1..00000000 Binary files a/docs/_pics/java-notes/os/76a49594323247f21c9b3a69945445ee.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/76dc7769-1aac-4888-9bea-064f1caa8e77.jpg b/docs/_pics/java-notes/os/76dc7769-1aac-4888-9bea-064f1caa8e77.jpg deleted file mode 100644 index 642aba6b..00000000 Binary files a/docs/_pics/java-notes/os/76dc7769-1aac-4888-9bea-064f1caa8e77.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/os/7b281b1e-0595-402b-ae35-8c91084c33c1.png b/docs/_pics/java-notes/os/7b281b1e-0595-402b-ae35-8c91084c33c1.png deleted file mode 100644 index 9308ecb6..00000000 Binary files a/docs/_pics/java-notes/os/7b281b1e-0595-402b-ae35-8c91084c33c1.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/83185315-793a-453a-a927-5e8d92b5c0ef.jpg b/docs/_pics/java-notes/os/83185315-793a-453a-a927-5e8d92b5c0ef.jpg deleted file mode 100644 index cb83edea..00000000 Binary files a/docs/_pics/java-notes/os/83185315-793a-453a-a927-5e8d92b5c0ef.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/os/8c662999-c16c-481c-9f40-1fdba5bc9167.png b/docs/_pics/java-notes/os/8c662999-c16c-481c-9f40-1fdba5bc9167.png deleted file mode 100644 index ff810e91..00000000 Binary files a/docs/_pics/java-notes/os/8c662999-c16c-481c-9f40-1fdba5bc9167.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/924914c0-660c-4e4a-bbc0-1df1146e7516.jpg b/docs/_pics/java-notes/os/924914c0-660c-4e4a-bbc0-1df1146e7516.jpg deleted file mode 100644 index 5bb29598..00000000 Binary files a/docs/_pics/java-notes/os/924914c0-660c-4e4a-bbc0-1df1146e7516.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/os/BSD_disk.png b/docs/_pics/java-notes/os/BSD_disk.png deleted file mode 100644 index 48b0e0e8..00000000 Binary files a/docs/_pics/java-notes/os/BSD_disk.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/ProcessState.png b/docs/_pics/java-notes/os/ProcessState.png deleted file mode 100644 index 39269579..00000000 Binary files a/docs/_pics/java-notes/os/ProcessState.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/a6ac2b08-3861-4e85-baa8-382287bfee9f.png b/docs/_pics/java-notes/os/a6ac2b08-3861-4e85-baa8-382287bfee9f.png deleted file mode 100644 index 26b0bd03..00000000 Binary files a/docs/_pics/java-notes/os/a6ac2b08-3861-4e85-baa8-382287bfee9f.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/a9077f06-7584-4f2b-8c20-3a8e46928820.jpg b/docs/_pics/java-notes/os/a9077f06-7584-4f2b-8c20-3a8e46928820.jpg deleted file mode 100644 index 67b2264d..00000000 Binary files a/docs/_pics/java-notes/os/a9077f06-7584-4f2b-8c20-3a8e46928820.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/os/b1fa0453-a4b0-4eae-a352-48acca8fff74.png b/docs/_pics/java-notes/os/b1fa0453-a4b0-4eae-a352-48acca8fff74.png deleted file mode 100644 index 842b2f6c..00000000 Binary files a/docs/_pics/java-notes/os/b1fa0453-a4b0-4eae-a352-48acca8fff74.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/b396d726-b75f-4a32-89a2-03a7b6e19f6f.jpg b/docs/_pics/java-notes/os/b396d726-b75f-4a32-89a2-03a7b6e19f6f.jpg deleted file mode 100644 index a3e1a656..00000000 Binary files a/docs/_pics/java-notes/os/b396d726-b75f-4a32-89a2-03a7b6e19f6f.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/os/b8081c84-62c4-4019-b3ee-4bd0e443d647.jpg b/docs/_pics/java-notes/os/b8081c84-62c4-4019-b3ee-4bd0e443d647.jpg deleted file mode 100644 index 58fb0a05..00000000 Binary files a/docs/_pics/java-notes/os/b8081c84-62c4-4019-b3ee-4bd0e443d647.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/os/c037c901-7eae-4e31-a1e4-9d41329e5c3e.png b/docs/_pics/java-notes/os/c037c901-7eae-4e31-a1e4-9d41329e5c3e.png deleted file mode 100644 index 9e2feb27..00000000 Binary files a/docs/_pics/java-notes/os/c037c901-7eae-4e31-a1e4-9d41329e5c3e.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/cf4386a1-58c9-4eca-a17f-e12b1e9770eb.png b/docs/_pics/java-notes/os/cf4386a1-58c9-4eca-a17f-e12b1e9770eb.png deleted file mode 100644 index 9b9f3838..00000000 Binary files a/docs/_pics/java-notes/os/cf4386a1-58c9-4eca-a17f-e12b1e9770eb.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/d160ec2e-cfe2-4640-bda7-62f53e58b8c0.png b/docs/_pics/java-notes/os/d160ec2e-cfe2-4640-bda7-62f53e58b8c0.png deleted file mode 100644 index cc971351..00000000 Binary files a/docs/_pics/java-notes/os/d160ec2e-cfe2-4640-bda7-62f53e58b8c0.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/e0900bb2-220a-43b7-9aa9-1d5cd55ff56e.png b/docs/_pics/java-notes/os/e0900bb2-220a-43b7-9aa9-1d5cd55ff56e.png deleted file mode 100644 index b4c565fb..00000000 Binary files a/docs/_pics/java-notes/os/e0900bb2-220a-43b7-9aa9-1d5cd55ff56e.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/e1eda3d5-5ec8-4708-8e25-1a04c5e11f48.png b/docs/_pics/java-notes/os/e1eda3d5-5ec8-4708-8e25-1a04c5e11f48.png deleted file mode 100644 index bffe7520..00000000 Binary files a/docs/_pics/java-notes/os/e1eda3d5-5ec8-4708-8e25-1a04c5e11f48.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/eb859228-c0f2-4bce-910d-d9f76929352b.png b/docs/_pics/java-notes/os/eb859228-c0f2-4bce-910d-d9f76929352b.png deleted file mode 100644 index 7104f020..00000000 Binary files a/docs/_pics/java-notes/os/eb859228-c0f2-4bce-910d-d9f76929352b.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/ecf8ad5d-5403-48b9-b6e7-f2e20ffe8fca.png b/docs/_pics/java-notes/os/ecf8ad5d-5403-48b9-b6e7-f2e20ffe8fca.png deleted file mode 100644 index 25ed7497..00000000 Binary files a/docs/_pics/java-notes/os/ecf8ad5d-5403-48b9-b6e7-f2e20ffe8fca.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/ed523051-608f-4c3f-b343-383e2d194470.png b/docs/_pics/java-notes/os/ed523051-608f-4c3f-b343-383e2d194470.png deleted file mode 100644 index 1f703e2f..00000000 Binary files a/docs/_pics/java-notes/os/ed523051-608f-4c3f-b343-383e2d194470.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/f0574025-c514-49f5-a591-6d6a71f271f7.jpg b/docs/_pics/java-notes/os/f0574025-c514-49f5-a591-6d6a71f271f7.jpg deleted file mode 100644 index 66a2ecbe..00000000 Binary files a/docs/_pics/java-notes/os/f0574025-c514-49f5-a591-6d6a71f271f7.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/os/f900f266-a323-42b2-bc43-218fdb8811a8.jpg b/docs/_pics/java-notes/os/f900f266-a323-42b2-bc43-218fdb8811a8.jpg deleted file mode 100644 index 95b92d60..00000000 Binary files a/docs/_pics/java-notes/os/f900f266-a323-42b2-bc43-218fdb8811a8.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/os/f9f2a16b-4843-44d1-9759-c745772e9bcf.jpg b/docs/_pics/java-notes/os/f9f2a16b-4843-44d1-9759-c745772e9bcf.jpg deleted file mode 100644 index b6a0ba79..00000000 Binary files a/docs/_pics/java-notes/os/f9f2a16b-4843-44d1-9759-c745772e9bcf.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/os/flow.png b/docs/_pics/java-notes/os/flow.png deleted file mode 100644 index aa0492a4..00000000 Binary files a/docs/_pics/java-notes/os/flow.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/inode_with_signatures.jpg b/docs/_pics/java-notes/os/inode_with_signatures.jpg deleted file mode 100644 index 518ba5ac..00000000 Binary files a/docs/_pics/java-notes/os/inode_with_signatures.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/os/linux-filesystem.png b/docs/_pics/java-notes/os/linux-filesystem.png deleted file mode 100644 index ae965295..00000000 Binary files a/docs/_pics/java-notes/os/linux-filesystem.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/linux_1.png b/docs/_pics/java-notes/os/linux_1.png deleted file mode 100644 index 592d2095..00000000 Binary files a/docs/_pics/java-notes/os/linux_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/os/tGPV0.png b/docs/_pics/java-notes/os/tGPV0.png deleted file mode 100644 index 89fb7bfe..00000000 Binary files a/docs/_pics/java-notes/os/tGPV0.png and /dev/null differ diff --git a/docs/_pics/java-notes/sfatety/safety_1.png b/docs/_pics/java-notes/sfatety/safety_1.png deleted file mode 100644 index c5a218cc..00000000 Binary files a/docs/_pics/java-notes/sfatety/safety_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/sfatety/safety_2.png b/docs/_pics/java-notes/sfatety/safety_2.png deleted file mode 100644 index a00616a9..00000000 Binary files a/docs/_pics/java-notes/sfatety/safety_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/sfatety/safety_3.png b/docs/_pics/java-notes/sfatety/safety_3.png deleted file mode 100644 index feadf5df..00000000 Binary files a/docs/_pics/java-notes/sfatety/safety_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/sfatety/safety_4.png b/docs/_pics/java-notes/sfatety/safety_4.png deleted file mode 100644 index 0af5922c..00000000 Binary files a/docs/_pics/java-notes/sfatety/safety_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/sort/01_1.png b/docs/_pics/java-notes/sort/01_1.png deleted file mode 100644 index 3b05b25b..00000000 Binary files a/docs/_pics/java-notes/sort/01_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/sort/01_2.png b/docs/_pics/java-notes/sort/01_2.png deleted file mode 100644 index c4592305..00000000 Binary files a/docs/_pics/java-notes/sort/01_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/sort/01_3.png b/docs/_pics/java-notes/sort/01_3.png deleted file mode 100644 index fc0999f9..00000000 Binary files a/docs/_pics/java-notes/sort/01_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/sort/02_1.png b/docs/_pics/java-notes/sort/02_1.png deleted file mode 100644 index 79105257..00000000 Binary files a/docs/_pics/java-notes/sort/02_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/sort/02_2.png b/docs/_pics/java-notes/sort/02_2.png deleted file mode 100644 index e8a69bff..00000000 Binary files a/docs/_pics/java-notes/sort/02_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/sort/02_3.png b/docs/_pics/java-notes/sort/02_3.png deleted file mode 100644 index 86e2294f..00000000 Binary files a/docs/_pics/java-notes/sort/02_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/sort/03_1.png b/docs/_pics/java-notes/sort/03_1.png deleted file mode 100644 index 77868686..00000000 Binary files a/docs/_pics/java-notes/sort/03_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/sort/03_2.png b/docs/_pics/java-notes/sort/03_2.png deleted file mode 100644 index 6e8383f3..00000000 Binary files a/docs/_pics/java-notes/sort/03_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/sort/03_3.png b/docs/_pics/java-notes/sort/03_3.png deleted file mode 100644 index 5299728f..00000000 Binary files a/docs/_pics/java-notes/sort/03_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/sort/03_4.png b/docs/_pics/java-notes/sort/03_4.png deleted file mode 100644 index dc44da3d..00000000 Binary files a/docs/_pics/java-notes/sort/03_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/sort/03_5.png b/docs/_pics/java-notes/sort/03_5.png deleted file mode 100644 index d49b1727..00000000 Binary files a/docs/_pics/java-notes/sort/03_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/sort/03_6.png b/docs/_pics/java-notes/sort/03_6.png deleted file mode 100644 index 3d5adabd..00000000 Binary files a/docs/_pics/java-notes/sort/03_6.png and /dev/null differ diff --git a/docs/_pics/java-notes/sort/04_1.gif b/docs/_pics/java-notes/sort/04_1.gif deleted file mode 100644 index 2a556955..00000000 Binary files a/docs/_pics/java-notes/sort/04_1.gif and /dev/null differ diff --git a/docs/_pics/java-notes/sort/05_1.png b/docs/_pics/java-notes/sort/05_1.png deleted file mode 100644 index 9d720f4f..00000000 Binary files a/docs/_pics/java-notes/sort/05_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/sort/06_1.png b/docs/_pics/java-notes/sort/06_1.png deleted file mode 100644 index 325d983e..00000000 Binary files a/docs/_pics/java-notes/sort/06_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/01_1.png b/docs/_pics/java-notes/spring/01_1.png deleted file mode 100644 index 1bcb8af0..00000000 Binary files a/docs/_pics/java-notes/spring/01_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/01_2.png b/docs/_pics/java-notes/spring/01_2.png deleted file mode 100644 index 58360fb4..00000000 Binary files a/docs/_pics/java-notes/spring/01_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/02_1.png b/docs/_pics/java-notes/spring/02_1.png deleted file mode 100644 index c4748cda..00000000 Binary files a/docs/_pics/java-notes/spring/02_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/03_1.png b/docs/_pics/java-notes/spring/03_1.png deleted file mode 100644 index 75e9c190..00000000 Binary files a/docs/_pics/java-notes/spring/03_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/03_2.png b/docs/_pics/java-notes/spring/03_2.png deleted file mode 100644 index 21fb3e30..00000000 Binary files a/docs/_pics/java-notes/spring/03_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/03_3.png b/docs/_pics/java-notes/spring/03_3.png deleted file mode 100644 index 81b2ba88..00000000 Binary files a/docs/_pics/java-notes/spring/03_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/03_4.png b/docs/_pics/java-notes/spring/03_4.png deleted file mode 100644 index e91de3b9..00000000 Binary files a/docs/_pics/java-notes/spring/03_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/03_5.png b/docs/_pics/java-notes/spring/03_5.png deleted file mode 100644 index 59f9aa87..00000000 Binary files a/docs/_pics/java-notes/spring/03_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/05_1.jpg b/docs/_pics/java-notes/spring/05_1.jpg deleted file mode 100644 index 16f76ddb..00000000 Binary files a/docs/_pics/java-notes/spring/05_1.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/spring/06_1.jpg b/docs/_pics/java-notes/spring/06_1.jpg deleted file mode 100644 index 19019d55..00000000 Binary files a/docs/_pics/java-notes/spring/06_1.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/spring/06_2.jpg b/docs/_pics/java-notes/spring/06_2.jpg deleted file mode 100644 index 202d1bf2..00000000 Binary files a/docs/_pics/java-notes/spring/06_2.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/spring/SpringMCVInterview_00.jpg b/docs/_pics/java-notes/spring/SpringMCVInterview_00.jpg deleted file mode 100644 index 3f21b38b..00000000 Binary files a/docs/_pics/java-notes/spring/SpringMCVInterview_00.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/spring/ioc_1.png b/docs/_pics/java-notes/spring/ioc_1.png deleted file mode 100644 index 2c3f84df..00000000 Binary files a/docs/_pics/java-notes/spring/ioc_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/ioc_11.png b/docs/_pics/java-notes/spring/ioc_11.png deleted file mode 100644 index 7fb5fa81..00000000 Binary files a/docs/_pics/java-notes/spring/ioc_11.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/ioc_12.png b/docs/_pics/java-notes/spring/ioc_12.png deleted file mode 100644 index 3ada545f..00000000 Binary files a/docs/_pics/java-notes/spring/ioc_12.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/ioc_13.png b/docs/_pics/java-notes/spring/ioc_13.png deleted file mode 100644 index 69570fa5..00000000 Binary files a/docs/_pics/java-notes/spring/ioc_13.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/ioc_14.png b/docs/_pics/java-notes/spring/ioc_14.png deleted file mode 100644 index 048cf914..00000000 Binary files a/docs/_pics/java-notes/spring/ioc_14.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/ioc_15.png b/docs/_pics/java-notes/spring/ioc_15.png deleted file mode 100644 index 3f3b6b1e..00000000 Binary files a/docs/_pics/java-notes/spring/ioc_15.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/ioc_16.png b/docs/_pics/java-notes/spring/ioc_16.png deleted file mode 100644 index d6b807fb..00000000 Binary files a/docs/_pics/java-notes/spring/ioc_16.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/ioc_17.png b/docs/_pics/java-notes/spring/ioc_17.png deleted file mode 100644 index 29fb311d..00000000 Binary files a/docs/_pics/java-notes/spring/ioc_17.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/ioc_2.png b/docs/_pics/java-notes/spring/ioc_2.png deleted file mode 100644 index d9073ab8..00000000 Binary files a/docs/_pics/java-notes/spring/ioc_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/ioc_3.png b/docs/_pics/java-notes/spring/ioc_3.png deleted file mode 100644 index 8dadc8eb..00000000 Binary files a/docs/_pics/java-notes/spring/ioc_3.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/ioc_4.png b/docs/_pics/java-notes/spring/ioc_4.png deleted file mode 100644 index ce1f0a84..00000000 Binary files a/docs/_pics/java-notes/spring/ioc_4.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/ioc_5.png b/docs/_pics/java-notes/spring/ioc_5.png deleted file mode 100644 index 29a7f660..00000000 Binary files a/docs/_pics/java-notes/spring/ioc_5.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/ioc_6.png b/docs/_pics/java-notes/spring/ioc_6.png deleted file mode 100644 index 5a4c4332..00000000 Binary files a/docs/_pics/java-notes/spring/ioc_6.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/ioc_7.png b/docs/_pics/java-notes/spring/ioc_7.png deleted file mode 100644 index 84cbfe90..00000000 Binary files a/docs/_pics/java-notes/spring/ioc_7.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/ioc_8.png b/docs/_pics/java-notes/spring/ioc_8.png deleted file mode 100644 index aa0aaba8..00000000 Binary files a/docs/_pics/java-notes/spring/ioc_8.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/ioc_9.png b/docs/_pics/java-notes/spring/ioc_9.png deleted file mode 100644 index 3032221e..00000000 Binary files a/docs/_pics/java-notes/spring/ioc_9.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/spring_1.gif b/docs/_pics/java-notes/spring/spring_1.gif deleted file mode 100644 index 2671173e..00000000 Binary files a/docs/_pics/java-notes/spring/spring_1.gif and /dev/null differ diff --git a/docs/_pics/java-notes/spring/spring_1.png b/docs/_pics/java-notes/spring/spring_1.png deleted file mode 100644 index 03d3d768..00000000 Binary files a/docs/_pics/java-notes/spring/spring_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/spring_2.png b/docs/_pics/java-notes/spring/spring_2.png deleted file mode 100644 index 08ef4ee4..00000000 Binary files a/docs/_pics/java-notes/spring/spring_2.png and /dev/null differ diff --git a/docs/_pics/java-notes/spring/springmvc_1.png b/docs/_pics/java-notes/spring/springmvc_1.png deleted file mode 100644 index 8891be88..00000000 Binary files a/docs/_pics/java-notes/spring/springmvc_1.png and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/04f41228-375d-4b7d-bfef-738c5a7c8f07.jpg b/docs/_pics/java-notes/systemDesign/04f41228-375d-4b7d-bfef-738c5a7c8f07.jpg deleted file mode 100644 index 17eb93b6..00000000 Binary files a/docs/_pics/java-notes/systemDesign/04f41228-375d-4b7d-bfef-738c5a7c8f07.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/0b587744-c0a8-46f2-8d72-e8f070d67b4b.jpg b/docs/_pics/java-notes/systemDesign/0b587744-c0a8-46f2-8d72-e8f070d67b4b.jpg deleted file mode 100644 index 7b88e7a0..00000000 Binary files a/docs/_pics/java-notes/systemDesign/0b587744-c0a8-46f2-8d72-e8f070d67b4b.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/0ee0f61b-c782-441e-bf34-665650198ae0.jpg b/docs/_pics/java-notes/systemDesign/0ee0f61b-c782-441e-bf34-665650198ae0.jpg deleted file mode 100644 index f3e7163b..00000000 Binary files a/docs/_pics/java-notes/systemDesign/0ee0f61b-c782-441e-bf34-665650198ae0.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/10.gif b/docs/_pics/java-notes/systemDesign/10.gif deleted file mode 100644 index d52a911e..00000000 Binary files a/docs/_pics/java-notes/systemDesign/10.gif and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/11.gif b/docs/_pics/java-notes/systemDesign/11.gif deleted file mode 100644 index 5d9c6f00..00000000 Binary files a/docs/_pics/java-notes/systemDesign/11.gif and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/111521118015898.gif b/docs/_pics/java-notes/systemDesign/111521118015898.gif deleted file mode 100644 index 5c31da1d..00000000 Binary files a/docs/_pics/java-notes/systemDesign/111521118015898.gif and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/111521118445538.gif b/docs/_pics/java-notes/systemDesign/111521118445538.gif deleted file mode 100644 index 323d129c..00000000 Binary files a/docs/_pics/java-notes/systemDesign/111521118445538.gif and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/111521118483039.gif b/docs/_pics/java-notes/systemDesign/111521118483039.gif deleted file mode 100644 index a81124dd..00000000 Binary files a/docs/_pics/java-notes/systemDesign/111521118483039.gif and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/111521118640738.gif b/docs/_pics/java-notes/systemDesign/111521118640738.gif deleted file mode 100644 index 7a7b05a9..00000000 Binary files a/docs/_pics/java-notes/systemDesign/111521118640738.gif and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/111521119203347.gif b/docs/_pics/java-notes/systemDesign/111521119203347.gif deleted file mode 100644 index 37cdb5a5..00000000 Binary files a/docs/_pics/java-notes/systemDesign/111521119203347.gif and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/111521119368714.gif b/docs/_pics/java-notes/systemDesign/111521119368714.gif deleted file mode 100644 index 216c3033..00000000 Binary files a/docs/_pics/java-notes/systemDesign/111521119368714.gif and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/15313ed8-a520-4799-a300-2b6b36be314f.jpg b/docs/_pics/java-notes/systemDesign/15313ed8-a520-4799-a300-2b6b36be314f.jpg deleted file mode 100644 index cbba7f36..00000000 Binary files a/docs/_pics/java-notes/systemDesign/15313ed8-a520-4799-a300-2b6b36be314f.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/1a9977e4-2f5c-49a6-aec9-f3027c9f46a7.png b/docs/_pics/java-notes/systemDesign/1a9977e4-2f5c-49a6-aec9-f3027c9f46a7.png deleted file mode 100644 index 590a4299..00000000 Binary files a/docs/_pics/java-notes/systemDesign/1a9977e4-2f5c-49a6-aec9-f3027c9f46a7.png and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/1f4a7f10-52b2-4bd7-a67d-a9581d66dc62.jpg b/docs/_pics/java-notes/systemDesign/1f4a7f10-52b2-4bd7-a67d-a9581d66dc62.jpg deleted file mode 100644 index e4f0f3ac..00000000 Binary files a/docs/_pics/java-notes/systemDesign/1f4a7f10-52b2-4bd7-a67d-a9581d66dc62.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/2018040302.jpg b/docs/_pics/java-notes/systemDesign/2018040302.jpg deleted file mode 100644 index 27daefae..00000000 Binary files a/docs/_pics/java-notes/systemDesign/2018040302.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/211c60d4-75ca-4acd-8a4f-171458ed58b4.jpg b/docs/_pics/java-notes/systemDesign/211c60d4-75ca-4acd-8a4f-171458ed58b4.jpg deleted file mode 100644 index efb47ffc..00000000 Binary files a/docs/_pics/java-notes/systemDesign/211c60d4-75ca-4acd-8a4f-171458ed58b4.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/2766d04f-7dad-42e4-99d1-60682c9d5c61.jpg b/docs/_pics/java-notes/systemDesign/2766d04f-7dad-42e4-99d1-60682c9d5c61.jpg deleted file mode 100644 index f9a9489b..00000000 Binary files a/docs/_pics/java-notes/systemDesign/2766d04f-7dad-42e4-99d1-60682c9d5c61.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/2991c772-fb1c-4051-a9c7-932b68e76bd7.jpg b/docs/_pics/java-notes/systemDesign/2991c772-fb1c-4051-a9c7-932b68e76bd7.jpg deleted file mode 100644 index d99eb3a7..00000000 Binary files a/docs/_pics/java-notes/systemDesign/2991c772-fb1c-4051-a9c7-932b68e76bd7.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/2bcc58ad-bf7f-485c-89b5-e7cafc211ce2.jpg b/docs/_pics/java-notes/systemDesign/2bcc58ad-bf7f-485c-89b5-e7cafc211ce2.jpg deleted file mode 100644 index 983ddd70..00000000 Binary files a/docs/_pics/java-notes/systemDesign/2bcc58ad-bf7f-485c-89b5-e7cafc211ce2.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/31d99967-1171-448e-8531-bccf5c14cffe.jpg b/docs/_pics/java-notes/systemDesign/31d99967-1171-448e-8531-bccf5c14cffe.jpg deleted file mode 100644 index 61e00649..00000000 Binary files a/docs/_pics/java-notes/systemDesign/31d99967-1171-448e-8531-bccf5c14cffe.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/3b0d1aa8-d0e0-46c2-8fd1-736bf08a11aa.jpg b/docs/_pics/java-notes/systemDesign/3b0d1aa8-d0e0-46c2-8fd1-736bf08a11aa.jpg deleted file mode 100644 index f7e9f145..00000000 Binary files a/docs/_pics/java-notes/systemDesign/3b0d1aa8-d0e0-46c2-8fd1-736bf08a11aa.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/44edefb7-4b58-4519-b8ee-4aca01697b78.jpg b/docs/_pics/java-notes/systemDesign/44edefb7-4b58-4519-b8ee-4aca01697b78.jpg deleted file mode 100644 index 32d0f3d2..00000000 Binary files a/docs/_pics/java-notes/systemDesign/44edefb7-4b58-4519-b8ee-4aca01697b78.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/66402828-fb2b-418f-83f6-82153491bcfe.jpg b/docs/_pics/java-notes/systemDesign/66402828-fb2b-418f-83f6-82153491bcfe.jpg deleted file mode 100644 index fc86a236..00000000 Binary files a/docs/_pics/java-notes/systemDesign/66402828-fb2b-418f-83f6-82153491bcfe.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/685a692f-8f76-4cac-baac-b68e2df9a30f.jpg b/docs/_pics/java-notes/systemDesign/685a692f-8f76-4cac-baac-b68e2df9a30f.jpg deleted file mode 100644 index a1d12d17..00000000 Binary files a/docs/_pics/java-notes/systemDesign/685a692f-8f76-4cac-baac-b68e2df9a30f.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/68b110b9-76c6-4ee2-b541-4145e65adb3e.jpg b/docs/_pics/java-notes/systemDesign/68b110b9-76c6-4ee2-b541-4145e65adb3e.jpg deleted file mode 100644 index d82f46eb..00000000 Binary files a/docs/_pics/java-notes/systemDesign/68b110b9-76c6-4ee2-b541-4145e65adb3e.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/7.gif b/docs/_pics/java-notes/systemDesign/7.gif deleted file mode 100644 index fad88513..00000000 Binary files a/docs/_pics/java-notes/systemDesign/7.gif and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/76a25fc8-a579-4d7c-974b-7640b57fbf39.jpg b/docs/_pics/java-notes/systemDesign/76a25fc8-a579-4d7c-974b-7640b57fbf39.jpg deleted file mode 100644 index fd13a137..00000000 Binary files a/docs/_pics/java-notes/systemDesign/76a25fc8-a579-4d7c-974b-7640b57fbf39.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/9.gif b/docs/_pics/java-notes/systemDesign/9.gif deleted file mode 100644 index f81aa264..00000000 Binary files a/docs/_pics/java-notes/systemDesign/9.gif and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/9b838aee-0996-44a5-9b0f-3d1e3e2f5100.png b/docs/_pics/java-notes/systemDesign/9b838aee-0996-44a5-9b0f-3d1e3e2f5100.png deleted file mode 100644 index c7f87138..00000000 Binary files a/docs/_pics/java-notes/systemDesign/9b838aee-0996-44a5-9b0f-3d1e3e2f5100.png and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/MultiNode-SessionReplication.jpg b/docs/_pics/java-notes/systemDesign/MultiNode-SessionReplication.jpg deleted file mode 100644 index 0223bd80..00000000 Binary files a/docs/_pics/java-notes/systemDesign/MultiNode-SessionReplication.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/MultiNode-SpringSession.jpg b/docs/_pics/java-notes/systemDesign/MultiNode-SpringSession.jpg deleted file mode 100644 index 38d56e2c..00000000 Binary files a/docs/_pics/java-notes/systemDesign/MultiNode-SpringSession.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/MultiNode-StickySessions.jpg b/docs/_pics/java-notes/systemDesign/MultiNode-StickySessions.jpg deleted file mode 100644 index a7e1c6aa..00000000 Binary files a/docs/_pics/java-notes/systemDesign/MultiNode-StickySessions.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/b988877c-0f0a-4593-916d-de2081320628.jpg b/docs/_pics/java-notes/systemDesign/b988877c-0f0a-4593-916d-de2081320628.jpg deleted file mode 100644 index 67339134..00000000 Binary files a/docs/_pics/java-notes/systemDesign/b988877c-0f0a-4593-916d-de2081320628.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/bc603930-d74d-4499-a3e7-2d740fc07f33.png b/docs/_pics/java-notes/systemDesign/bc603930-d74d-4499-a3e7-2d740fc07f33.png deleted file mode 100644 index 6c9a572e..00000000 Binary files a/docs/_pics/java-notes/systemDesign/bc603930-d74d-4499-a3e7-2d740fc07f33.png and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/bee1ff1d-c80f-4b3c-b58c-7073a8896ab2.jpg b/docs/_pics/java-notes/systemDesign/bee1ff1d-c80f-4b3c-b58c-7073a8896ab2.jpg deleted file mode 100644 index e4becc19..00000000 Binary files a/docs/_pics/java-notes/systemDesign/bee1ff1d-c80f-4b3c-b58c-7073a8896ab2.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/bf667594-bb4b-4634-bf9b-0596a45415ba.jpg b/docs/_pics/java-notes/systemDesign/bf667594-bb4b-4634-bf9b-0596a45415ba.jpg deleted file mode 100644 index 30956ccf..00000000 Binary files a/docs/_pics/java-notes/systemDesign/bf667594-bb4b-4634-bf9b-0596a45415ba.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/c5f611f0-fd5c-4158-9003-278141136e6e.jpg b/docs/_pics/java-notes/systemDesign/c5f611f0-fd5c-4158-9003-278141136e6e.jpg deleted file mode 100644 index 473091be..00000000 Binary files a/docs/_pics/java-notes/systemDesign/c5f611f0-fd5c-4158-9003-278141136e6e.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/ddb5ff4c-4ada-46aa-9bf1-140bdb5e4676.jpg b/docs/_pics/java-notes/systemDesign/ddb5ff4c-4ada-46aa-9bf1-140bdb5e4676.jpg deleted file mode 100644 index 73b3d739..00000000 Binary files a/docs/_pics/java-notes/systemDesign/ddb5ff4c-4ada-46aa-9bf1-140bdb5e4676.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/e3bf5de4-ab1e-4a9b-896d-4b0ad7e9220a.jpg b/docs/_pics/java-notes/systemDesign/e3bf5de4-ab1e-4a9b-896d-4b0ad7e9220a.jpg deleted file mode 100644 index a3ead324..00000000 Binary files a/docs/_pics/java-notes/systemDesign/e3bf5de4-ab1e-4a9b-896d-4b0ad7e9220a.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/f1109d04-3c67-48a3-9963-2c475f94e175.jpg b/docs/_pics/java-notes/systemDesign/f1109d04-3c67-48a3-9963-2c475f94e175.jpg deleted file mode 100644 index cdd1b55e..00000000 Binary files a/docs/_pics/java-notes/systemDesign/f1109d04-3c67-48a3-9963-2c475f94e175.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/f7ecbb8d-bb8b-4d45-a3b7-f49425d6d83d.jpg b/docs/_pics/java-notes/systemDesign/f7ecbb8d-bb8b-4d45-a3b7-f49425d6d83d.jpg deleted file mode 100644 index ab51d486..00000000 Binary files a/docs/_pics/java-notes/systemDesign/f7ecbb8d-bb8b-4d45-a3b7-f49425d6d83d.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/fb44307f-8e98-4ff7-a918-31dacfa564b4.jpg b/docs/_pics/java-notes/systemDesign/fb44307f-8e98-4ff7-a918-31dacfa564b4.jpg deleted file mode 100644 index 36c1d9b7..00000000 Binary files a/docs/_pics/java-notes/systemDesign/fb44307f-8e98-4ff7-a918-31dacfa564b4.jpg and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/kafka_00.png b/docs/_pics/java-notes/systemDesign/kafka_00.png deleted file mode 100644 index 298481f2..00000000 Binary files a/docs/_pics/java-notes/systemDesign/kafka_00.png and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/kafka_01.png b/docs/_pics/java-notes/systemDesign/kafka_01.png deleted file mode 100644 index 42a54e20..00000000 Binary files a/docs/_pics/java-notes/systemDesign/kafka_01.png and /dev/null differ diff --git a/docs/_pics/java-notes/systemDesign/kafka_02.png b/docs/_pics/java-notes/systemDesign/kafka_02.png deleted file mode 100644 index 30d18e3a..00000000 Binary files a/docs/_pics/java-notes/systemDesign/kafka_02.png and /dev/null differ diff --git a/docs/_pics/kafka/k_1.png b/docs/_pics/kafka/k_1.png deleted file mode 100644 index 42a54e20..00000000 Binary files a/docs/_pics/kafka/k_1.png and /dev/null differ diff --git a/docs/_pics/kafka/k_10.png b/docs/_pics/kafka/k_10.png deleted file mode 100644 index 9ce858ea..00000000 Binary files a/docs/_pics/kafka/k_10.png and /dev/null differ diff --git a/docs/_pics/kafka/k_2.png b/docs/_pics/kafka/k_2.png deleted file mode 100644 index 1e3fb16b..00000000 Binary files a/docs/_pics/kafka/k_2.png and /dev/null differ diff --git a/docs/_pics/kafka/k_3.png b/docs/_pics/kafka/k_3.png deleted file mode 100644 index 22c3fe1e..00000000 Binary files a/docs/_pics/kafka/k_3.png and /dev/null differ diff --git a/docs/_pics/kafka/k_4.png b/docs/_pics/kafka/k_4.png deleted file mode 100644 index a6494999..00000000 Binary files a/docs/_pics/kafka/k_4.png and /dev/null differ diff --git a/docs/_pics/kafka/k_5.png b/docs/_pics/kafka/k_5.png deleted file mode 100644 index 16fe2936..00000000 Binary files a/docs/_pics/kafka/k_5.png and /dev/null differ diff --git a/docs/_pics/kafka/k_6.png b/docs/_pics/kafka/k_6.png deleted file mode 100644 index a5a888ed..00000000 Binary files a/docs/_pics/kafka/k_6.png and /dev/null differ diff --git a/docs/_pics/kafka/k_7.png b/docs/_pics/kafka/k_7.png deleted file mode 100644 index fcde5b03..00000000 Binary files a/docs/_pics/kafka/k_7.png and /dev/null differ diff --git a/docs/_pics/kafka/k_8.png b/docs/_pics/kafka/k_8.png deleted file mode 100644 index 44d8fe9d..00000000 Binary files a/docs/_pics/kafka/k_8.png and /dev/null differ diff --git a/docs/_pics/kafka/k_9.png b/docs/_pics/kafka/k_9.png deleted file mode 100644 index dd8eb14a..00000000 Binary files a/docs/_pics/kafka/k_9.png and /dev/null differ diff --git a/docs/_pics/leetcode/127.png b/docs/_pics/leetcode/127.png deleted file mode 100644 index 529a8276..00000000 Binary files a/docs/_pics/leetcode/127.png and /dev/null differ diff --git a/docs/_pics/leetcode/17.png b/docs/_pics/leetcode/17.png deleted file mode 100644 index 2b5fcfad..00000000 Binary files a/docs/_pics/leetcode/17.png and /dev/null differ diff --git a/docs/_pics/leetcode/243.png b/docs/_pics/leetcode/243.png deleted file mode 100644 index cc2f7dc4..00000000 Binary files a/docs/_pics/leetcode/243.png and /dev/null differ diff --git a/docs/_pics/leetcode/257_1.png b/docs/_pics/leetcode/257_1.png deleted file mode 100644 index 384ede5c..00000000 Binary files a/docs/_pics/leetcode/257_1.png and /dev/null differ diff --git a/docs/_pics/leetcode/257_2.png b/docs/_pics/leetcode/257_2.png deleted file mode 100644 index 79892231..00000000 Binary files a/docs/_pics/leetcode/257_2.png and /dev/null differ diff --git a/docs/_pics/leetcode/257_3.png b/docs/_pics/leetcode/257_3.png deleted file mode 100644 index d3a8c107..00000000 Binary files a/docs/_pics/leetcode/257_3.png and /dev/null differ diff --git a/docs/_pics/leetcode/279_1.png b/docs/_pics/leetcode/279_1.png deleted file mode 100644 index cb498a9e..00000000 Binary files a/docs/_pics/leetcode/279_1.png and /dev/null differ diff --git a/docs/_pics/leetcode/279_2.png b/docs/_pics/leetcode/279_2.png deleted file mode 100644 index 373f05b0..00000000 Binary files a/docs/_pics/leetcode/279_2.png and /dev/null differ diff --git a/docs/_pics/leetcode/279_3.png b/docs/_pics/leetcode/279_3.png deleted file mode 100644 index 34095a9e..00000000 Binary files a/docs/_pics/leetcode/279_3.png and /dev/null differ diff --git a/docs/_pics/leetcode/46_1.png b/docs/_pics/leetcode/46_1.png deleted file mode 100644 index f0862e06..00000000 Binary files a/docs/_pics/leetcode/46_1.png and /dev/null differ diff --git a/docs/_pics/leetcode/51_1.png b/docs/_pics/leetcode/51_1.png deleted file mode 100644 index 0f6f4d33..00000000 Binary files a/docs/_pics/leetcode/51_1.png and /dev/null differ diff --git a/docs/_pics/leetcode/51_2.png b/docs/_pics/leetcode/51_2.png deleted file mode 100644 index edea2826..00000000 Binary files a/docs/_pics/leetcode/51_2.png and /dev/null differ diff --git a/docs/_pics/leetcode/l_1.png b/docs/_pics/leetcode/l_1.png deleted file mode 100644 index 3faec6ce..00000000 Binary files a/docs/_pics/leetcode/l_1.png and /dev/null differ diff --git a/docs/_pics/leetcode/l_2.png b/docs/_pics/leetcode/l_2.png deleted file mode 100644 index d5a2019b..00000000 Binary files a/docs/_pics/leetcode/l_2.png and /dev/null differ diff --git a/docs/_pics/logo_1.png b/docs/_pics/logo_1.png deleted file mode 100644 index f909159b..00000000 Binary files a/docs/_pics/logo_1.png and /dev/null differ diff --git a/docs/_pics/mcard/m_1.png b/docs/_pics/mcard/m_1.png deleted file mode 100644 index 4b5c7244..00000000 Binary files a/docs/_pics/mcard/m_1.png and /dev/null differ diff --git a/docs/_pics/mcard/m_10.png b/docs/_pics/mcard/m_10.png deleted file mode 100644 index 86488d22..00000000 Binary files a/docs/_pics/mcard/m_10.png and /dev/null differ diff --git a/docs/_pics/mcard/m_11.png b/docs/_pics/mcard/m_11.png deleted file mode 100644 index b59715ef..00000000 Binary files a/docs/_pics/mcard/m_11.png and /dev/null differ diff --git a/docs/_pics/mcard/m_12.png b/docs/_pics/mcard/m_12.png deleted file mode 100644 index 3ca85215..00000000 Binary files a/docs/_pics/mcard/m_12.png and /dev/null differ diff --git a/docs/_pics/mcard/m_13.png b/docs/_pics/mcard/m_13.png deleted file mode 100644 index 1df2ec64..00000000 Binary files a/docs/_pics/mcard/m_13.png and /dev/null differ diff --git a/docs/_pics/mcard/m_2.png b/docs/_pics/mcard/m_2.png deleted file mode 100644 index 5810e47f..00000000 Binary files a/docs/_pics/mcard/m_2.png and /dev/null differ diff --git a/docs/_pics/mcard/m_3.png b/docs/_pics/mcard/m_3.png deleted file mode 100644 index da1f3b47..00000000 Binary files a/docs/_pics/mcard/m_3.png and /dev/null differ diff --git a/docs/_pics/mcard/m_4.png b/docs/_pics/mcard/m_4.png deleted file mode 100644 index d3844f93..00000000 Binary files a/docs/_pics/mcard/m_4.png and /dev/null differ diff --git a/docs/_pics/mcard/m_5.png b/docs/_pics/mcard/m_5.png deleted file mode 100644 index f4f4bc2e..00000000 Binary files a/docs/_pics/mcard/m_5.png and /dev/null differ diff --git a/docs/_pics/mcard/m_6.png b/docs/_pics/mcard/m_6.png deleted file mode 100644 index 5470e346..00000000 Binary files a/docs/_pics/mcard/m_6.png and /dev/null differ diff --git a/docs/_pics/mcard/m_7.png b/docs/_pics/mcard/m_7.png deleted file mode 100644 index 7dc446ec..00000000 Binary files a/docs/_pics/mcard/m_7.png and /dev/null differ diff --git a/docs/_pics/mcard/m_8.png b/docs/_pics/mcard/m_8.png deleted file mode 100644 index 7eb26665..00000000 Binary files a/docs/_pics/mcard/m_8.png and /dev/null differ diff --git a/docs/_pics/mcard/m_9.png b/docs/_pics/mcard/m_9.png deleted file mode 100644 index 6577b921..00000000 Binary files a/docs/_pics/mcard/m_9.png and /dev/null differ diff --git a/docs/_pics/others/class_loader.png b/docs/_pics/others/class_loader.png deleted file mode 100644 index e6dedf9f..00000000 Binary files a/docs/_pics/others/class_loader.png and /dev/null differ diff --git a/docs/_pics/pics/blog/aqs_1.png b/docs/_pics/pics/blog/aqs_1.png deleted file mode 100644 index 3e42b225..00000000 Binary files a/docs/_pics/pics/blog/aqs_1.png and /dev/null differ diff --git a/docs/_pics/pics/blog/aqs_10.png b/docs/_pics/pics/blog/aqs_10.png deleted file mode 100644 index 15320c5e..00000000 Binary files a/docs/_pics/pics/blog/aqs_10.png and /dev/null differ diff --git a/docs/_pics/pics/blog/aqs_11.png b/docs/_pics/pics/blog/aqs_11.png deleted file mode 100644 index 5bdb9cd5..00000000 Binary files a/docs/_pics/pics/blog/aqs_11.png and /dev/null differ diff --git a/docs/_pics/pics/blog/aqs_12.png b/docs/_pics/pics/blog/aqs_12.png deleted file mode 100644 index 3f7073ec..00000000 Binary files a/docs/_pics/pics/blog/aqs_12.png and /dev/null differ diff --git a/docs/_pics/pics/blog/aqs_2.png b/docs/_pics/pics/blog/aqs_2.png deleted file mode 100644 index 12b7530b..00000000 Binary files a/docs/_pics/pics/blog/aqs_2.png and /dev/null differ diff --git a/docs/_pics/pics/blog/aqs_3.png b/docs/_pics/pics/blog/aqs_3.png deleted file mode 100644 index 7b41eae2..00000000 Binary files a/docs/_pics/pics/blog/aqs_3.png and /dev/null differ diff --git a/docs/_pics/pics/blog/aqs_4.png b/docs/_pics/pics/blog/aqs_4.png deleted file mode 100644 index 8d85dd73..00000000 Binary files a/docs/_pics/pics/blog/aqs_4.png and /dev/null differ diff --git a/docs/_pics/pics/blog/aqs_5.png b/docs/_pics/pics/blog/aqs_5.png deleted file mode 100644 index 57ba383d..00000000 Binary files a/docs/_pics/pics/blog/aqs_5.png and /dev/null differ diff --git a/docs/_pics/pics/blog/aqs_6.png b/docs/_pics/pics/blog/aqs_6.png deleted file mode 100644 index 374011f0..00000000 Binary files a/docs/_pics/pics/blog/aqs_6.png and /dev/null differ diff --git a/docs/_pics/pics/blog/aqs_7.png b/docs/_pics/pics/blog/aqs_7.png deleted file mode 100644 index 57b1e46e..00000000 Binary files a/docs/_pics/pics/blog/aqs_7.png and /dev/null differ diff --git a/docs/_pics/pics/blog/aqs_8.png b/docs/_pics/pics/blog/aqs_8.png deleted file mode 100644 index a55c11df..00000000 Binary files a/docs/_pics/pics/blog/aqs_8.png and /dev/null differ diff --git a/docs/_pics/pics/blog/aqs_9.png b/docs/_pics/pics/blog/aqs_9.png deleted file mode 100644 index 7490cab2..00000000 Binary files a/docs/_pics/pics/blog/aqs_9.png and /dev/null differ diff --git a/docs/_pics/pics/blog/b_1.png b/docs/_pics/pics/blog/b_1.png deleted file mode 100644 index 4023ab87..00000000 Binary files a/docs/_pics/pics/blog/b_1.png and /dev/null differ diff --git a/docs/_pics/pics/blog/h_1.png b/docs/_pics/pics/blog/h_1.png deleted file mode 100644 index 2e53fbe3..00000000 Binary files a/docs/_pics/pics/blog/h_1.png and /dev/null differ diff --git a/docs/_pics/pics/blog/h_2.png b/docs/_pics/pics/blog/h_2.png deleted file mode 100644 index eea3c6b4..00000000 Binary files a/docs/_pics/pics/blog/h_2.png and /dev/null differ diff --git a/docs/_pics/pics/blog/k_1.png b/docs/_pics/pics/blog/k_1.png deleted file mode 100644 index 68631bd0..00000000 Binary files a/docs/_pics/pics/blog/k_1.png and /dev/null differ diff --git a/docs/_pics/pics/blog/k_2.png b/docs/_pics/pics/blog/k_2.png deleted file mode 100644 index 1ee37ac4..00000000 Binary files a/docs/_pics/pics/blog/k_2.png and /dev/null differ diff --git a/docs/_pics/pics/blog/ka_1.png b/docs/_pics/pics/blog/ka_1.png deleted file mode 100644 index 771cdb8d..00000000 Binary files a/docs/_pics/pics/blog/ka_1.png and /dev/null differ diff --git a/docs/_pics/pics/blog/ka_2.png b/docs/_pics/pics/blog/ka_2.png deleted file mode 100644 index d50b6047..00000000 Binary files a/docs/_pics/pics/blog/ka_2.png and /dev/null differ diff --git a/docs/_pics/pics/blog/ka_3.png b/docs/_pics/pics/blog/ka_3.png deleted file mode 100644 index 1bdee84d..00000000 Binary files a/docs/_pics/pics/blog/ka_3.png and /dev/null differ diff --git a/docs/_pics/pics/blog/ka_4.png b/docs/_pics/pics/blog/ka_4.png deleted file mode 100644 index 6df6b9e6..00000000 Binary files a/docs/_pics/pics/blog/ka_4.png and /dev/null differ diff --git a/docs/_pics/pics/blog/key_1.png b/docs/_pics/pics/blog/key_1.png deleted file mode 100644 index e3a9bf9b..00000000 Binary files a/docs/_pics/pics/blog/key_1.png and /dev/null differ diff --git a/docs/_pics/pics/blog/key_2.png b/docs/_pics/pics/blog/key_2.png deleted file mode 100644 index 950eca00..00000000 Binary files a/docs/_pics/pics/blog/key_2.png and /dev/null differ diff --git a/docs/_pics/pics/blog/key_3.png b/docs/_pics/pics/blog/key_3.png deleted file mode 100644 index 4031c097..00000000 Binary files a/docs/_pics/pics/blog/key_3.png and /dev/null differ diff --git a/docs/_pics/pics/blog/key_4.png b/docs/_pics/pics/blog/key_4.png deleted file mode 100644 index 7427cc43..00000000 Binary files a/docs/_pics/pics/blog/key_4.png and /dev/null differ diff --git a/docs/_pics/pics/blog/key_5.png b/docs/_pics/pics/blog/key_5.png deleted file mode 100644 index 0280a2af..00000000 Binary files a/docs/_pics/pics/blog/key_5.png and /dev/null differ diff --git a/docs/_pics/pics/blog/key_6.png b/docs/_pics/pics/blog/key_6.png deleted file mode 100644 index dfeeb327..00000000 Binary files a/docs/_pics/pics/blog/key_6.png and /dev/null differ diff --git a/docs/_pics/pics/blog/lp_1.png b/docs/_pics/pics/blog/lp_1.png deleted file mode 100644 index c7e7974f..00000000 Binary files a/docs/_pics/pics/blog/lp_1.png and /dev/null differ diff --git a/docs/_pics/pics/blog/m_1.jpg b/docs/_pics/pics/blog/m_1.jpg deleted file mode 100644 index 3692acbd..00000000 Binary files a/docs/_pics/pics/blog/m_1.jpg and /dev/null differ diff --git a/docs/_pics/pics/blog/m_1.png b/docs/_pics/pics/blog/m_1.png deleted file mode 100644 index 9392717b..00000000 Binary files a/docs/_pics/pics/blog/m_1.png and /dev/null differ diff --git a/docs/_pics/pics/blog/m_2.jpg b/docs/_pics/pics/blog/m_2.jpg deleted file mode 100644 index 5c68f854..00000000 Binary files a/docs/_pics/pics/blog/m_2.jpg and /dev/null differ diff --git a/docs/_pics/pics/blog/m_2.png b/docs/_pics/pics/blog/m_2.png deleted file mode 100644 index 7896512a..00000000 Binary files a/docs/_pics/pics/blog/m_2.png and /dev/null differ diff --git a/docs/_pics/pics/blog/m_3.jpg b/docs/_pics/pics/blog/m_3.jpg deleted file mode 100644 index 84a35441..00000000 Binary files a/docs/_pics/pics/blog/m_3.jpg and /dev/null differ diff --git a/docs/_pics/pics/blog/m_4.jpg b/docs/_pics/pics/blog/m_4.jpg deleted file mode 100644 index ed052b83..00000000 Binary files a/docs/_pics/pics/blog/m_4.jpg and /dev/null differ diff --git a/docs/_pics/pics/blog/m_5.png b/docs/_pics/pics/blog/m_5.png deleted file mode 100644 index 7775f0b6..00000000 Binary files a/docs/_pics/pics/blog/m_5.png and /dev/null differ diff --git a/docs/_pics/pics/blog/ms_1.png b/docs/_pics/pics/blog/ms_1.png deleted file mode 100644 index 21240d2d..00000000 Binary files a/docs/_pics/pics/blog/ms_1.png and /dev/null differ diff --git a/docs/_pics/pics/blog/net_1.jpg b/docs/_pics/pics/blog/net_1.jpg deleted file mode 100644 index 13b609a4..00000000 Binary files a/docs/_pics/pics/blog/net_1.jpg and /dev/null differ diff --git a/docs/_pics/pics/blog/s_1.png b/docs/_pics/pics/blog/s_1.png deleted file mode 100644 index c0d478af..00000000 Binary files a/docs/_pics/pics/blog/s_1.png and /dev/null differ diff --git a/docs/_pics/pics/blog/s_2.png b/docs/_pics/pics/blog/s_2.png deleted file mode 100644 index 07a127ac..00000000 Binary files a/docs/_pics/pics/blog/s_2.png and /dev/null differ diff --git a/docs/_pics/pics/blog/s_3.png b/docs/_pics/pics/blog/s_3.png deleted file mode 100644 index 6a99dd76..00000000 Binary files a/docs/_pics/pics/blog/s_3.png and /dev/null differ diff --git a/docs/_pics/pics/blog/s_4.png b/docs/_pics/pics/blog/s_4.png deleted file mode 100644 index 33189c0b..00000000 Binary files a/docs/_pics/pics/blog/s_4.png and /dev/null differ diff --git a/docs/_pics/pics/blog/s_5.png b/docs/_pics/pics/blog/s_5.png deleted file mode 100644 index d841a621..00000000 Binary files a/docs/_pics/pics/blog/s_5.png and /dev/null differ diff --git a/docs/_pics/pics/blog/s_6.png b/docs/_pics/pics/blog/s_6.png deleted file mode 100644 index 42895a69..00000000 Binary files a/docs/_pics/pics/blog/s_6.png and /dev/null differ diff --git a/docs/_pics/pics/blog/s_7.png b/docs/_pics/pics/blog/s_7.png deleted file mode 100644 index d92ba074..00000000 Binary files a/docs/_pics/pics/blog/s_7.png and /dev/null differ diff --git a/docs/_pics/pics/blog/s_8.png b/docs/_pics/pics/blog/s_8.png deleted file mode 100644 index 6153c579..00000000 Binary files a/docs/_pics/pics/blog/s_8.png and /dev/null differ diff --git a/docs/_pics/pics/blog/s_9.png b/docs/_pics/pics/blog/s_9.png deleted file mode 100644 index d32a9e84..00000000 Binary files a/docs/_pics/pics/blog/s_9.png and /dev/null differ diff --git a/docs/_pics/pics/blog/trie_1.png b/docs/_pics/pics/blog/trie_1.png deleted file mode 100644 index 40a7e2db..00000000 Binary files a/docs/_pics/pics/blog/trie_1.png and /dev/null differ diff --git a/docs/_pics/pics/blog/trie_2.png b/docs/_pics/pics/blog/trie_2.png deleted file mode 100644 index a80cdb79..00000000 Binary files a/docs/_pics/pics/blog/trie_2.png and /dev/null differ diff --git a/docs/_pics/pics/blog/zky_1.jpg b/docs/_pics/pics/blog/zky_1.jpg deleted file mode 100644 index c51cb947..00000000 Binary files a/docs/_pics/pics/blog/zky_1.jpg and /dev/null differ diff --git a/docs/_pics/pics/concurrent/051e436c-0e46-4c59-8f67-52d89d656182.png b/docs/_pics/pics/concurrent/051e436c-0e46-4c59-8f67-52d89d656182.png deleted file mode 100644 index e3054539..00000000 Binary files a/docs/_pics/pics/concurrent/051e436c-0e46-4c59-8f67-52d89d656182.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/066f9c11-0154-42c3-8685-301a70e9bd39.jpg b/docs/_pics/pics/concurrent/066f9c11-0154-42c3-8685-301a70e9bd39.jpg deleted file mode 100644 index 84d371b8..00000000 Binary files a/docs/_pics/pics/concurrent/066f9c11-0154-42c3-8685-301a70e9bd39.jpg and /dev/null differ diff --git a/docs/_pics/pics/concurrent/3144015c-dcfb-47ac-94a5-bab3b78b0f14.jpg b/docs/_pics/pics/concurrent/3144015c-dcfb-47ac-94a5-bab3b78b0f14.jpg deleted file mode 100644 index a805e7f9..00000000 Binary files a/docs/_pics/pics/concurrent/3144015c-dcfb-47ac-94a5-bab3b78b0f14.jpg and /dev/null differ diff --git a/docs/_pics/pics/concurrent/390c913b-5f31-444f-bbdb-2b88b688e7ce.jpg b/docs/_pics/pics/concurrent/390c913b-5f31-444f-bbdb-2b88b688e7ce.jpg deleted file mode 100644 index 78eb732b..00000000 Binary files a/docs/_pics/pics/concurrent/390c913b-5f31-444f-bbdb-2b88b688e7ce.jpg and /dev/null differ diff --git a/docs/_pics/pics/concurrent/54e6d499-80df-488e-aa7e-081766c41538.jpg b/docs/_pics/pics/concurrent/54e6d499-80df-488e-aa7e-081766c41538.jpg deleted file mode 100644 index 0ffa2a5b..00000000 Binary files a/docs/_pics/pics/concurrent/54e6d499-80df-488e-aa7e-081766c41538.jpg and /dev/null differ diff --git a/docs/_pics/pics/concurrent/5e6e05d6-1028-4f5c-b9bd-1a40b90d6070.jpg b/docs/_pics/pics/concurrent/5e6e05d6-1028-4f5c-b9bd-1a40b90d6070.jpg deleted file mode 100644 index f2a6c03a..00000000 Binary files a/docs/_pics/pics/concurrent/5e6e05d6-1028-4f5c-b9bd-1a40b90d6070.jpg and /dev/null differ diff --git a/docs/_pics/pics/concurrent/8162aebb-8fd2-4620-b771-e65751ba7e41.png b/docs/_pics/pics/concurrent/8162aebb-8fd2-4620-b771-e65751ba7e41.png deleted file mode 100644 index 2fa69582..00000000 Binary files a/docs/_pics/pics/concurrent/8162aebb-8fd2-4620-b771-e65751ba7e41.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/847b9ba1-b3cd-4e52-aa72-dee0222ae09f.jpg b/docs/_pics/pics/concurrent/847b9ba1-b3cd-4e52-aa72-dee0222ae09f.jpg deleted file mode 100644 index c68fd3ef..00000000 Binary files a/docs/_pics/pics/concurrent/847b9ba1-b3cd-4e52-aa72-dee0222ae09f.jpg and /dev/null differ diff --git a/docs/_pics/pics/concurrent/912a7886-fb1d-4a05-902d-ab17ea17007f.jpg b/docs/_pics/pics/concurrent/912a7886-fb1d-4a05-902d-ab17ea17007f.jpg deleted file mode 100644 index e30c6908..00000000 Binary files a/docs/_pics/pics/concurrent/912a7886-fb1d-4a05-902d-ab17ea17007f.jpg and /dev/null differ diff --git a/docs/_pics/pics/concurrent/94414cd3-5db9-4aca-a2af-539140955c62.jpg b/docs/_pics/pics/concurrent/94414cd3-5db9-4aca-a2af-539140955c62.jpg deleted file mode 100644 index 5c53d38b..00000000 Binary files a/docs/_pics/pics/concurrent/94414cd3-5db9-4aca-a2af-539140955c62.jpg and /dev/null differ diff --git a/docs/_pics/pics/concurrent/96706658-b3f8-4f32-8eb3-dcb7fc8d5381.jpg b/docs/_pics/pics/concurrent/96706658-b3f8-4f32-8eb3-dcb7fc8d5381.jpg deleted file mode 100644 index c4e77ba1..00000000 Binary files a/docs/_pics/pics/concurrent/96706658-b3f8-4f32-8eb3-dcb7fc8d5381.jpg and /dev/null differ diff --git a/docs/_pics/pics/concurrent/b6a7e8af-91bf-44b2-8874-ccc6d9d52afc.jpg b/docs/_pics/pics/concurrent/b6a7e8af-91bf-44b2-8874-ccc6d9d52afc.jpg deleted file mode 100644 index 96b80deb..00000000 Binary files a/docs/_pics/pics/concurrent/b6a7e8af-91bf-44b2-8874-ccc6d9d52afc.jpg and /dev/null differ diff --git a/docs/_pics/pics/concurrent/baaa681f-7c52-4198-a5ae-303b9386cf47.png b/docs/_pics/pics/concurrent/baaa681f-7c52-4198-a5ae-303b9386cf47.png deleted file mode 100644 index 6664d398..00000000 Binary files a/docs/_pics/pics/concurrent/baaa681f-7c52-4198-a5ae-303b9386cf47.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/bb6a49be-00f2-4f27-a0ce-4ed764bc605c.png b/docs/_pics/pics/concurrent/bb6a49be-00f2-4f27-a0ce-4ed764bc605c.png deleted file mode 100644 index 07d8692d..00000000 Binary files a/docs/_pics/pics/concurrent/bb6a49be-00f2-4f27-a0ce-4ed764bc605c.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/bc5826f5-014d-47b4-9a76-d86b80968643.jpg b/docs/_pics/pics/concurrent/bc5826f5-014d-47b4-9a76-d86b80968643.jpg deleted file mode 100644 index b27cd02f..00000000 Binary files a/docs/_pics/pics/concurrent/bc5826f5-014d-47b4-9a76-d86b80968643.jpg and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_1.png b/docs/_pics/pics/concurrent/c_1.png deleted file mode 100644 index 30910c3a..00000000 Binary files a/docs/_pics/pics/concurrent/c_1.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_10.png b/docs/_pics/pics/concurrent/c_10.png deleted file mode 100644 index 7356cf7c..00000000 Binary files a/docs/_pics/pics/concurrent/c_10.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_11.png b/docs/_pics/pics/concurrent/c_11.png deleted file mode 100644 index d8b6778c..00000000 Binary files a/docs/_pics/pics/concurrent/c_11.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_12.png b/docs/_pics/pics/concurrent/c_12.png deleted file mode 100644 index ba59e5f7..00000000 Binary files a/docs/_pics/pics/concurrent/c_12.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_13.png b/docs/_pics/pics/concurrent/c_13.png deleted file mode 100644 index f825ab24..00000000 Binary files a/docs/_pics/pics/concurrent/c_13.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_14.png b/docs/_pics/pics/concurrent/c_14.png deleted file mode 100644 index eded5fe7..00000000 Binary files a/docs/_pics/pics/concurrent/c_14.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_15.png b/docs/_pics/pics/concurrent/c_15.png deleted file mode 100644 index 7a704f2d..00000000 Binary files a/docs/_pics/pics/concurrent/c_15.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_16.png b/docs/_pics/pics/concurrent/c_16.png deleted file mode 100644 index 7417cbad..00000000 Binary files a/docs/_pics/pics/concurrent/c_16.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_17.png b/docs/_pics/pics/concurrent/c_17.png deleted file mode 100644 index 469916fb..00000000 Binary files a/docs/_pics/pics/concurrent/c_17.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_18.png b/docs/_pics/pics/concurrent/c_18.png deleted file mode 100644 index e90c55bc..00000000 Binary files a/docs/_pics/pics/concurrent/c_18.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_19.png b/docs/_pics/pics/concurrent/c_19.png deleted file mode 100644 index fffc0dc3..00000000 Binary files a/docs/_pics/pics/concurrent/c_19.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_2.png b/docs/_pics/pics/concurrent/c_2.png deleted file mode 100644 index d5f7564c..00000000 Binary files a/docs/_pics/pics/concurrent/c_2.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_20.png b/docs/_pics/pics/concurrent/c_20.png deleted file mode 100644 index e23da54e..00000000 Binary files a/docs/_pics/pics/concurrent/c_20.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_21.png b/docs/_pics/pics/concurrent/c_21.png deleted file mode 100644 index 71556948..00000000 Binary files a/docs/_pics/pics/concurrent/c_21.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_22.png b/docs/_pics/pics/concurrent/c_22.png deleted file mode 100644 index 032269ec..00000000 Binary files a/docs/_pics/pics/concurrent/c_22.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_23.png b/docs/_pics/pics/concurrent/c_23.png deleted file mode 100644 index 90065cc5..00000000 Binary files a/docs/_pics/pics/concurrent/c_23.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_24.png b/docs/_pics/pics/concurrent/c_24.png deleted file mode 100644 index f9685021..00000000 Binary files a/docs/_pics/pics/concurrent/c_24.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_3.png b/docs/_pics/pics/concurrent/c_3.png deleted file mode 100644 index 079f2c70..00000000 Binary files a/docs/_pics/pics/concurrent/c_3.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_4.png b/docs/_pics/pics/concurrent/c_4.png deleted file mode 100644 index 267d5b5d..00000000 Binary files a/docs/_pics/pics/concurrent/c_4.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_5.png b/docs/_pics/pics/concurrent/c_5.png deleted file mode 100644 index 7b46cdd4..00000000 Binary files a/docs/_pics/pics/concurrent/c_5.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_6.png b/docs/_pics/pics/concurrent/c_6.png deleted file mode 100644 index 0be9ad77..00000000 Binary files a/docs/_pics/pics/concurrent/c_6.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_7.png b/docs/_pics/pics/concurrent/c_7.png deleted file mode 100644 index c81810a8..00000000 Binary files a/docs/_pics/pics/concurrent/c_7.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_8.png b/docs/_pics/pics/concurrent/c_8.png deleted file mode 100644 index 53a32162..00000000 Binary files a/docs/_pics/pics/concurrent/c_8.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/c_9.png b/docs/_pics/pics/concurrent/c_9.png deleted file mode 100644 index 8d798968..00000000 Binary files a/docs/_pics/pics/concurrent/c_9.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/d2a12961-2b36-4463-b017-ca46a3308b8e.png b/docs/_pics/pics/concurrent/d2a12961-2b36-4463-b017-ca46a3308b8e.png deleted file mode 100644 index ce6663bc..00000000 Binary files a/docs/_pics/pics/concurrent/d2a12961-2b36-4463-b017-ca46a3308b8e.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/de9d8133-4c98-4e07-b39c-302e162784ea.jpg b/docs/_pics/pics/concurrent/de9d8133-4c98-4e07-b39c-302e162784ea.jpg deleted file mode 100644 index 4a923dd8..00000000 Binary files a/docs/_pics/pics/concurrent/de9d8133-4c98-4e07-b39c-302e162784ea.jpg and /dev/null differ diff --git a/docs/_pics/pics/concurrent/e19452dd-220a-4a6b-bcb0-91ad5e5c4706.png b/docs/_pics/pics/concurrent/e19452dd-220a-4a6b-bcb0-91ad5e5c4706.png deleted file mode 100644 index c21e9af2..00000000 Binary files a/docs/_pics/pics/concurrent/e19452dd-220a-4a6b-bcb0-91ad5e5c4706.png and /dev/null differ diff --git a/docs/_pics/pics/concurrent/f944fac3-482b-4ca3-9447-17aec4a3cca0.png b/docs/_pics/pics/concurrent/f944fac3-482b-4ca3-9447-17aec4a3cca0.png deleted file mode 100644 index 56f05611..00000000 Binary files a/docs/_pics/pics/concurrent/f944fac3-482b-4ca3-9447-17aec4a3cca0.png and /dev/null differ diff --git a/docs/_pics/redis/0ea37ee2-c224-4c79-b895-e131c6805c40.png b/docs/_pics/redis/0ea37ee2-c224-4c79-b895-e131c6805c40.png deleted file mode 100644 index 837088e8..00000000 Binary files a/docs/_pics/redis/0ea37ee2-c224-4c79-b895-e131c6805c40.png and /dev/null differ diff --git a/docs/_pics/redis/1202b2d6-9469-4251-bd47-ca6034fb6116.png b/docs/_pics/redis/1202b2d6-9469-4251-bd47-ca6034fb6116.png deleted file mode 100644 index 0db77ec4..00000000 Binary files a/docs/_pics/redis/1202b2d6-9469-4251-bd47-ca6034fb6116.png and /dev/null differ diff --git a/docs/_pics/redis/395a9e83-b1a1-4a1d-b170-d081e7bb5bab.png b/docs/_pics/redis/395a9e83-b1a1-4a1d-b170-d081e7bb5bab.png deleted file mode 100644 index 4f3d0ccd..00000000 Binary files a/docs/_pics/redis/395a9e83-b1a1-4a1d-b170-d081e7bb5bab.png and /dev/null differ diff --git a/docs/_pics/redis/485fdf34-ccf8-4185-97c6-17374ee719a0.png b/docs/_pics/redis/485fdf34-ccf8-4185-97c6-17374ee719a0.png deleted file mode 100644 index 7f842458..00000000 Binary files a/docs/_pics/redis/485fdf34-ccf8-4185-97c6-17374ee719a0.png and /dev/null differ diff --git a/docs/_pics/redis/6019b2db-bc3e-4408-b6d8-96025f4481d6.png b/docs/_pics/redis/6019b2db-bc3e-4408-b6d8-96025f4481d6.png deleted file mode 100644 index 79388c09..00000000 Binary files a/docs/_pics/redis/6019b2db-bc3e-4408-b6d8-96025f4481d6.png and /dev/null differ diff --git a/docs/_pics/redis/7bd202a7-93d4-4f3a-a878-af68ae25539a.png b/docs/_pics/redis/7bd202a7-93d4-4f3a-a878-af68ae25539a.png deleted file mode 100644 index d325c907..00000000 Binary files a/docs/_pics/redis/7bd202a7-93d4-4f3a-a878-af68ae25539a.png and /dev/null differ diff --git a/docs/_pics/redis/7c54de21-e2ff-402e-bc42-4037de1c1592.png b/docs/_pics/redis/7c54de21-e2ff-402e-bc42-4037de1c1592.png deleted file mode 100644 index 5f493376..00000000 Binary files a/docs/_pics/redis/7c54de21-e2ff-402e-bc42-4037de1c1592.png and /dev/null differ diff --git a/docs/_pics/redis/9ea86eb5-000a-4281-b948-7b567bd6f1d8.png b/docs/_pics/redis/9ea86eb5-000a-4281-b948-7b567bd6f1d8.png deleted file mode 100644 index 61717a26..00000000 Binary files a/docs/_pics/redis/9ea86eb5-000a-4281-b948-7b567bd6f1d8.png and /dev/null differ diff --git a/docs/_pics/redis/beba612e-dc5b-4fc2-869d-0b23408ac90a.png b/docs/_pics/redis/beba612e-dc5b-4fc2-869d-0b23408ac90a.png deleted file mode 100644 index 158015bc..00000000 Binary files a/docs/_pics/redis/beba612e-dc5b-4fc2-869d-0b23408ac90a.png and /dev/null differ diff --git a/docs/_pics/redis/c0a9fa91-da2e-4892-8c9f-80206a6f7047.png b/docs/_pics/redis/c0a9fa91-da2e-4892-8c9f-80206a6f7047.png deleted file mode 100644 index 14c225b3..00000000 Binary files a/docs/_pics/redis/c0a9fa91-da2e-4892-8c9f-80206a6f7047.png and /dev/null differ diff --git a/docs/_pics/redis/cd5fbcff-3f35-43a6-8ffa-082a93ce0f0e.png b/docs/_pics/redis/cd5fbcff-3f35-43a6-8ffa-082a93ce0f0e.png deleted file mode 100644 index b721cc0a..00000000 Binary files a/docs/_pics/redis/cd5fbcff-3f35-43a6-8ffa-082a93ce0f0e.png and /dev/null differ diff --git a/docs/_pics/redis/f7d170a3-e446-4a64-ac2d-cb95028f81a8.png b/docs/_pics/redis/f7d170a3-e446-4a64-ac2d-cb95028f81a8.png deleted file mode 100644 index ccc91793..00000000 Binary files a/docs/_pics/redis/f7d170a3-e446-4a64-ac2d-cb95028f81a8.png and /dev/null differ diff --git a/docs/_pics/redis/fb327611-7e2b-4f2f-9f5b-38592d408f07.png b/docs/_pics/redis/fb327611-7e2b-4f2f-9f5b-38592d408f07.png deleted file mode 100644 index 774ecf10..00000000 Binary files a/docs/_pics/redis/fb327611-7e2b-4f2f-9f5b-38592d408f07.png and /dev/null differ diff --git a/docs/_pics/redis/r_1.png b/docs/_pics/redis/r_1.png deleted file mode 100644 index 8b7b822a..00000000 Binary files a/docs/_pics/redis/r_1.png and /dev/null differ diff --git a/docs/_pics/redis/r_10.png b/docs/_pics/redis/r_10.png deleted file mode 100644 index a5ce5187..00000000 Binary files a/docs/_pics/redis/r_10.png and /dev/null differ diff --git a/docs/_pics/redis/r_11.jpg b/docs/_pics/redis/r_11.jpg deleted file mode 100644 index 3b506337..00000000 Binary files a/docs/_pics/redis/r_11.jpg and /dev/null differ diff --git a/docs/_pics/redis/r_12.jpg b/docs/_pics/redis/r_12.jpg deleted file mode 100644 index e6d88249..00000000 Binary files a/docs/_pics/redis/r_12.jpg and /dev/null differ diff --git a/docs/_pics/redis/r_13.jpg b/docs/_pics/redis/r_13.jpg deleted file mode 100644 index dcbd3ef0..00000000 Binary files a/docs/_pics/redis/r_13.jpg and /dev/null differ diff --git a/docs/_pics/redis/r_14.jpg b/docs/_pics/redis/r_14.jpg deleted file mode 100644 index 6d2fd9fd..00000000 Binary files a/docs/_pics/redis/r_14.jpg and /dev/null differ diff --git a/docs/_pics/redis/r_15.jpg b/docs/_pics/redis/r_15.jpg deleted file mode 100644 index 68fc7d4e..00000000 Binary files a/docs/_pics/redis/r_15.jpg and /dev/null differ diff --git a/docs/_pics/redis/r_16.jpg b/docs/_pics/redis/r_16.jpg deleted file mode 100644 index 9d839eea..00000000 Binary files a/docs/_pics/redis/r_16.jpg and /dev/null differ diff --git a/docs/_pics/redis/r_17.jpg b/docs/_pics/redis/r_17.jpg deleted file mode 100644 index c7a91ab4..00000000 Binary files a/docs/_pics/redis/r_17.jpg and /dev/null differ diff --git a/docs/_pics/redis/r_18.jpg b/docs/_pics/redis/r_18.jpg deleted file mode 100644 index e0dd6416..00000000 Binary files a/docs/_pics/redis/r_18.jpg and /dev/null differ diff --git a/docs/_pics/redis/r_19.png b/docs/_pics/redis/r_19.png deleted file mode 100644 index 898361b9..00000000 Binary files a/docs/_pics/redis/r_19.png and /dev/null differ diff --git a/docs/_pics/redis/r_2.png b/docs/_pics/redis/r_2.png deleted file mode 100644 index 24d19da1..00000000 Binary files a/docs/_pics/redis/r_2.png and /dev/null differ diff --git a/docs/_pics/redis/r_3.png b/docs/_pics/redis/r_3.png deleted file mode 100644 index a61f2337..00000000 Binary files a/docs/_pics/redis/r_3.png and /dev/null differ diff --git a/docs/_pics/redis/r_4.png b/docs/_pics/redis/r_4.png deleted file mode 100644 index 7c915784..00000000 Binary files a/docs/_pics/redis/r_4.png and /dev/null differ diff --git a/docs/_pics/redis/r_5.png b/docs/_pics/redis/r_5.png deleted file mode 100644 index bc3a2997..00000000 Binary files a/docs/_pics/redis/r_5.png and /dev/null differ diff --git a/docs/_pics/redis/r_6.png b/docs/_pics/redis/r_6.png deleted file mode 100644 index 6144c24b..00000000 Binary files a/docs/_pics/redis/r_6.png and /dev/null differ diff --git a/docs/_pics/redis/r_7.png b/docs/_pics/redis/r_7.png deleted file mode 100644 index ad4eb80b..00000000 Binary files a/docs/_pics/redis/r_7.png and /dev/null differ diff --git a/docs/_pics/redis/r_8.png b/docs/_pics/redis/r_8.png deleted file mode 100644 index 055bf629..00000000 Binary files a/docs/_pics/redis/r_8.png and /dev/null differ diff --git a/docs/_pics/redis/r_9.png b/docs/_pics/redis/r_9.png deleted file mode 100644 index b7e58eb3..00000000 Binary files a/docs/_pics/redis/r_9.png and /dev/null differ diff --git a/docs/_pics/redis/redis_1.png b/docs/_pics/redis/redis_1.png deleted file mode 100644 index 1fc9381d..00000000 Binary files a/docs/_pics/redis/redis_1.png and /dev/null differ diff --git a/docs/_pics/redis/redis_2.png b/docs/_pics/redis/redis_2.png deleted file mode 100644 index 4499813b..00000000 Binary files a/docs/_pics/redis/redis_2.png and /dev/null differ diff --git a/docs/_pics/redis/redis_3.png b/docs/_pics/redis/redis_3.png deleted file mode 100644 index 140d3628..00000000 Binary files a/docs/_pics/redis/redis_3.png and /dev/null differ diff --git a/docs/_pics/redis/redis_4.png b/docs/_pics/redis/redis_4.png deleted file mode 100644 index 66d618b0..00000000 Binary files a/docs/_pics/redis/redis_4.png and /dev/null differ diff --git a/docs/_pics/redis/redis_5.png b/docs/_pics/redis/redis_5.png deleted file mode 100644 index 7e4c94fc..00000000 Binary files a/docs/_pics/redis/redis_5.png and /dev/null differ diff --git a/docs/_pics/redis/redis_6.png b/docs/_pics/redis/redis_6.png deleted file mode 100644 index 0acc4624..00000000 Binary files a/docs/_pics/redis/redis_6.png and /dev/null differ diff --git a/docs/_pics/redis/redis_7.png b/docs/_pics/redis/redis_7.png deleted file mode 100644 index 95eb0858..00000000 Binary files a/docs/_pics/redis/redis_7.png and /dev/null differ diff --git a/docs/_pics/redis/redis_8.png b/docs/_pics/redis/redis_8.png deleted file mode 100644 index 17674dac..00000000 Binary files a/docs/_pics/redis/redis_8.png and /dev/null differ diff --git a/docs/_pics/redis/redis_9.png b/docs/_pics/redis/redis_9.png deleted file mode 100644 index 72d349ae..00000000 Binary files a/docs/_pics/redis/redis_9.png and /dev/null differ diff --git a/docs/_pics/xyjy.png b/docs/_pics/xyjy.png deleted file mode 100644 index 015e315b..00000000 Binary files a/docs/_pics/xyjy.png and /dev/null differ diff --git a/docs/_pics/zookeeper/z_2.png b/docs/_pics/zookeeper/z_2.png deleted file mode 100644 index cf8a5923..00000000 Binary files a/docs/_pics/zookeeper/z_2.png and /dev/null differ diff --git a/docs/_pics/zookeeper/z_3.png b/docs/_pics/zookeeper/z_3.png deleted file mode 100644 index 65956b97..00000000 Binary files a/docs/_pics/zookeeper/z_3.png and /dev/null differ diff --git a/docs/_pics/zookeeper/z_4.png b/docs/_pics/zookeeper/z_4.png deleted file mode 100644 index 716297ef..00000000 Binary files a/docs/_pics/zookeeper/z_4.png and /dev/null differ diff --git a/docs/_pics/zookeeper/z_5.jpg b/docs/_pics/zookeeper/z_5.jpg deleted file mode 100644 index aba9c7e5..00000000 Binary files a/docs/_pics/zookeeper/z_5.jpg and /dev/null differ diff --git a/docs/_pics/zookeeper/z_6.jpg b/docs/_pics/zookeeper/z_6.jpg deleted file mode 100644 index 24b8a8f1..00000000 Binary files a/docs/_pics/zookeeper/z_6.jpg and /dev/null differ diff --git "a/docs/cache/1_\347\274\223\345\255\230\351\234\200\350\246\201\350\200\203\350\231\221\347\232\204\351\227\256\351\242\230.md" "b/docs/cache/1_\347\274\223\345\255\230\351\234\200\350\246\201\350\200\203\350\231\221\347\232\204\351\227\256\351\242\230.md" new file mode 100644 index 00000000..cb9b1194 --- /dev/null +++ "b/docs/cache/1_\347\274\223\345\255\230\351\234\200\350\246\201\350\200\203\350\231\221\347\232\204\351\227\256\351\242\230.md" @@ -0,0 +1,54 @@ +# 缓存需要考虑的问题 + +## 缓存命中率 + +当某个请求能够通过访问缓存而得到响应时,称为缓存命中。缓存命中率越高,缓存的利用率也就越高。 + +## 最大空间 + +缓存通常位于**内存**中,内存的空间通常比磁盘空间小的多,因此缓存的最大空间不可能非常大。当缓存存放的数据量超过最大空间时,就需要**淘汰部分数据**来存放新到达的数据。常见的数据淘汰策略: + +- FIFO(First In First Out) + + FIFO 即先进先出策略,在实时性的场景下,需要经常访问最新的数据,那么就可以使用 FIFO,使得最先进入的数据(最晚的数据)被淘汰。 + +- LRU(Least Recently Used) + + LRU 即最近最久未使用策略,优先淘汰最久未使用的数据,也就是上次被访问时间距离现在最久的数据。该策略可以保证内存中的数据都是热点数据,也就是经常被访问的数据,从而保证缓存命中率。 + +- LFU(Least Frequently Used) + + LFU 即最不经常使用策略,优先淘汰一段时间内使用次数最少的数据。 + +## 缓存位置 + +缓存位置一般有: + +- 浏览器 + + 当 HTTP 响应允许进行缓存时,浏览器会将 HTML、CSS、JavaScript、图片等静态资源进行缓存。 + +- 反向代理 + + 反向代理**位于服务器之前**,请求与响应都需要经过反向代理。通过将数据缓存在反向代理,在用户请求反向代理时就可以直接使用缓存进行响应。 + +- 本地缓存 + + 使用 Guava Cache 将数据缓存在服务器本地内存中,服务器代码可以直接读取本地内存中的缓存,速度非常快。 + +- 分布式缓存 + + 使用 **Redis、Memcache** 等分布式缓存将数据缓存在分布式缓存系统中。相对于本地缓存来说,分布式缓存单独部署,可以根据需求分配硬件资源。不仅如此,服务器集群都可以访问分布式缓存,而本地缓存需要在服务器集群之间进行同步,实现难度和性能开销上都非常大。 + +- 数据库缓存 + + MySQL 等数据库管理系统具有自己的查询缓存机制来提高查询效率。 + +- CPU 多级缓存 + + CPU 为了解决运算速度与主存 IO 速度不匹配的问题,引入了多级缓存结构,同时使用 MESI 等缓存一致性协议来解决多核 CPU 缓存数据一致性的问题。 + +- Java 内部的缓存 + + Java 为了优化空间,提高字符串、基本数据类型包装类的创建效率,设计了字符串常量池及 Byte、Short、Character、Integer、Long、Boolean 这六种包装类缓冲池。 + diff --git "a/docs/cache/2_\347\274\223\345\255\230\345\270\270\350\247\201\351\227\256\351\242\230.md" "b/docs/cache/2_\347\274\223\345\255\230\345\270\270\350\247\201\351\227\256\351\242\230.md" new file mode 100644 index 00000000..2702d9ef --- /dev/null +++ "b/docs/cache/2_\347\274\223\345\255\230\345\270\270\350\247\201\351\227\256\351\242\230.md" @@ -0,0 +1,45 @@ +# 缓存常见问题 + +## 缓存穿透 + +指的是对某个一定不存在的数据进行请求,该请求将会穿透缓存到达数据库。 + +解决方案: + +- 对这些不存在的数据缓存一个空数据; +- 对这类请求进行过滤。 + +## 缓存雪崩 + +指的是由于数据没有被加载到缓存中,或者缓存数据在同一时间大面积失效(过期),又或者缓存服务器宕机,导致大量的请求都到达数据库。 + +在有缓存的系统中,系统非常依赖于缓存,缓存分担了很大一部分的数据请求。当发生缓存雪崩时,数据库无法处理这么大的请求,导致数据库崩溃。 + +解决方案: + +- 为了防止缓存在同一时间大面积过期导致的缓存雪崩,可以通过观察用户行为,合理设置缓存过期时间来实现; +- 为了防止缓存服务器宕机出现的缓存雪崩,可以使用分布式缓存,分布式缓存中每一个节点只缓存部分的数据,当某个节点宕机时可以保证其它节点的缓存仍然可用。 +- 也可以进行缓存预热,避免在系统刚启动不久由于还未将大量数据进行缓存而导致缓存雪崩。 + +## 缓存一致性 + +缓存一致性要求数据更新的同时缓存数据也能够实时更新。 + +解决方案: + +- 在数据更新的同时立即去更新缓存; +- 在读缓存之前先判断缓存是否是最新的,如果不是最新的先进行更新。 + +要保证缓存一致性需要付出很大的代价,缓存数据最好是那些对一致性要求不高的数据,允许缓存数据存在一些脏数据。 + +## 缓存 “无底洞” 现象 + +指的是为了满足业务要求添加了大量缓存节点,但是**性能不但没有好转反而下降了的现象**。 + +产生原因:缓存系统通常采用 hash 函数将 key 映射到对应的缓存节点,随着缓存节点数目的增加,键值分布到更多的节点上,导致客户端一次批量操作会涉及多次网络操作,这意味着批量操作的耗时会随着节点数目的增加而不断增大。此外,网络连接数变多,对节点的性能也有一定影响。 + +解决方案: + +- 优化批量数据操作命令; +- 减少网络通信次数; +- 降低接入成本,使用长连接 / 连接池,NIO 等。 \ No newline at end of file diff --git "a/docs/cache/3_\346\225\260\346\215\256\345\210\206\345\270\203.md" "b/docs/cache/3_\346\225\260\346\215\256\345\210\206\345\270\203.md" new file mode 100644 index 00000000..70ec2ffb --- /dev/null +++ "b/docs/cache/3_\346\225\260\346\215\256\345\210\206\345\270\203.md" @@ -0,0 +1,16 @@ +# 数据分布 + +## 哈希分布 + +哈希分布就是将数据计算哈希值之后,按照哈希值分配到不同的节点上。例如有 N 个节点,数据的主键为 key,则将该数据分配的节点序号为:hash(key)%N。 + +传统的哈希分布算法存在一个问题:当节点数量变化时,也就是 N 值变化,那么几乎所有的数据都需要重新分布,将导致大量的数据迁移。 + +## 顺序分布 + +将数据划分为多个连续的部分,按数据的 ID 或者时间分布到不同节点上。例如 User 表的 ID 范围为 1 ~ 7000,使用顺序分布可以将其划分成多个子表,对应的主键范围为 1 ~ 1000,1001 ~ 2000,...,6001 ~ 7000。 + +顺序分布相比于哈希分布的主要优点如下: + +- 能保持数据原有的顺序; +- 并且能够准确控制每台服务器存储的数据量,从而使得存储空间的利用率最大。 \ No newline at end of file diff --git "a/docs/cluster/1_\350\264\237\350\275\275\345\235\207\350\241\241.md" "b/docs/cluster/1_\350\264\237\350\275\275\345\235\207\350\241\241.md" new file mode 100644 index 00000000..e759cf6a --- /dev/null +++ "b/docs/cluster/1_\350\264\237\350\275\275\345\235\207\350\241\241.md" @@ -0,0 +1,3 @@ +# 负载均衡 + +待补充。 \ No newline at end of file diff --git "a/docs/cluster/2_\351\233\206\347\276\244\344\270\213\347\232\204Session\347\256\241\347\220\206.md" "b/docs/cluster/2_\351\233\206\347\276\244\344\270\213\347\232\204Session\347\256\241\347\220\206.md" new file mode 100644 index 00000000..2950e6c7 --- /dev/null +++ "b/docs/cluster/2_\351\233\206\347\276\244\344\270\213\347\232\204Session\347\256\241\347\220\206.md" @@ -0,0 +1,3 @@ +# 集群下的 Session 管理 + +待补充。 diff --git "a/docs/distribution/1_\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\350\256\276\350\256\241\347\220\206\345\277\265.md" "b/docs/distribution/1_\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\350\256\276\350\256\241\347\220\206\345\277\265.md" index 9b53c4bc..066c0fbe 100644 --- "a/docs/distribution/1_\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\350\256\276\350\256\241\347\220\206\345\277\265.md" +++ "b/docs/distribution/1_\345\210\206\345\270\203\345\274\217\347\263\273\347\273\237\350\256\276\350\256\241\347\220\206\345\277\265.md" @@ -2,6 +2,12 @@ ## 目标 +分布式架构的应用十分广泛: + +- 分布式文件系统:比如 Hadoop 的HDFS ,Google 的 GFS,淘宝的 TFS 等。 +- 分布式缓存系统:Memcache,Hbase 等。 +- 分布式数据库:MySQL ,Mariadb,PostgreSQL 等。 + 分布式系统的目标是提升系统的**整体性能**和**吞吐量**另外还要尽量保证分布式系统的**容错性**。 ## 设计思路 @@ -11,7 +17,7 @@ - 中心化 - 去中心化 -### 1. 中心化 +### 中心化 - **2 种角色** @@ -25,7 +31,7 @@ 最大问题是“领导”的安危问题,如果“领导”出了问题,则群龙无首。 -### 2. 去中心化 +### 去中心化 - **地位平等** @@ -39,17 +45,80 @@ 一般的设计思路是,当集群判断发生了脑裂问题时,规模较小的集群就“自杀”或者拒绝服务。 -## 分布式与集群 +## 基本概念 + +### 分布式与集群 + +**分布式指的是一个业务拆分为多个子业务,部署在不同服务器上**。比如一个电商系统,用户模块部署在 server1, 订单模块部署在 server2,促销模块部署在 server3,商品模块部署在 server4,他们之间通过远程 RPC 实现服务调用,这就叫分布式。强调的是不同功能模块,单独部署在不同的 server上,所有 server 加起来是一个完整的系统。 + +**集群指的是同一业务,部署在多个服务器上,更多强调的是灾备**。比如一个电商系统,完整的部署在 server1 上一个,同样完整的部署在 server2 上,当 server1宕机,server2 仍然可以正常提供请求服务,这叫集群。同样对于某一功能模块,比如用户模块部署在 server1 上,同样部署在 server2 上,也叫做集群。分布式系统的每个功能模块节点,都可以用多机做成集群。 + +拿做菜示例,假如一个厨师做菜要经历切菜,炒菜两个功能,饭店为了提高速度招了两个厨师,每个厨师的工作一样,都是切菜,炒菜,这是集群。还有另一种方法提高效率,饭店招了一个切菜师傅,配合厨师,厨师不管切菜,只管炒菜了,和切菜师傅共同配合把菜做好,这叫分布式。 + +### Nginx + +Nginx 作用是反向代理和负载均衡。 + +反向代理是指请求真实是到 server1 中,但是系统中为了统一或者做比如单点登录,会在 server2 服务器上安装一个 nginx,里面配置到 server1 的反向代理,那么之后请求 url 就可以写 server2 的地址,发出后到 server2, server2 会转发到 server1 上,类似一种代理的模式。 + +负载均衡是指如果一个系统的请求很多,我们可以把请求转发到不同的服务器上,用来分流。就类似于接了一个水管放水,水流量很大时候,水压大很可能会让一个水管爆炸,这时候接三个水管,就没问题了(这三个水管就是一个集群)。类似的在 nginx 服务器中配了 3 个 Tomcat 服务器,每个 Tomcat 服务器上都部署了整个系统,那么当请求数大的时候,可以分发到不同的 Tomcat。(其实这里每个 Tomcat 上部署同一个功能模块也叫集群)。 + +### RPC + +RPC (Remote Procedure Call) 即远程过程调用,对于分布式系统来讲,Tomcat1 上部署了用户模块,Tomcat2 上部署了订单模块,当用户下单时,请求到 Tomcat2,这时候可能要判断这个用户是否是 vip,或者是否有优惠券,这些方法是在 Tomcat1 用户模块上的,那么 Tomcat2 调用 Tomcat1 的服务获取这些信息,就叫 RPC 调用。 + +RPC 调用底层涉及到对象的序列化,反序列化,HTTP/TCP 传输,网络异步传输 Netty。 + +### 分布式 Java 应用 + +网络通信: + +- 协议 + + TCP/IP + + UDP/IP + +- IO + + BIO + + NIO-Reactor + + AIO-Proactor + +基于消息方式的系统间通信: + +- socket 方式 + + 基于 Java 包:TCP/IP、UDP/IP + + 基于框架:Netty、Dubbo + +- 消息方式 + + 基于 Java 包:JMS + + 基于框架:RabbitMQ、RocketMQ + +基于远程调用方式的系统间通信: + +- 基于 Java 包 -- 分布式 + WebService - 一个业务拆分为多个子业务,部署在不同服务器上 +- 基于框架 -- 集群 + Spring RMI、CXF - 同一业务,部署在多个服务器上 +### 消息中间件(MQ) +MQ 消息中间件在分布式系统中的作用有很多,但是经常用到的还是**异步解耦**。 +比如天猫下单流程,当用户支付后,后台接口执行的操作可能包括:1 验签,2 支付密码校验,3 扣库存,4 用户积分增加等等操作,其实我们希望的是2操作执行成功后立即给用户结果提示,而不是等到后续各个操作完成后才去提示,因为后续的操作往往大部分是rpc调用,方法执行时间相对较长。另外对于下单支付这个操作,3和4是后续业务的需要,在设计上不能和下单支付本身出现强耦合度。所以这里我们可以引入 MQ 解决,也就是说1和2执行完成后,生产者只需要通知下3和4,把后续的操作扔给消息队列,立即返回。这里的 MQ 起到的作用一个是异步调用,一个是解耦。 +### NoSQL(非关系型数据库) +NoSQL 是所有非关系型数据库的统称,在分布式系统中用到很多,主要用来提高 QPS(query per second)。 +Redis 常用作缓存,或者内存数据库,小巧强大,什么数据适合放在 Redis 也就是缓存中,一个是经常查询的,需要频繁磁盘 io 的,例如有个快件系统,有个需求是当快件状态为异常时候,需要发送邮件提醒给系统管理员。接口入参是快件id,通常做法我们需要拿到id,去数据库查状态,然后发送,但是快件基数很大时候每天的问题件也可能会很多,接口调用频繁时候就需要改进做法,这时我们可以把快件状态信息放在redis里面,key是快件id, value是快进状态,每次进入接口时候直接redis里面取status就可以,速度很快。另一个是查询数据缓慢的,可以放在缓存中。 diff --git "a/docs/distribution/2_CAP\347\220\206\350\256\272.md" "b/docs/distribution/2_CAP\347\220\206\350\256\272.md" index 8d10ab4a..714a0a7d 100644 --- "a/docs/distribution/2_CAP\347\220\206\350\256\272.md" +++ "b/docs/distribution/2_CAP\347\220\206\350\256\272.md" @@ -1,8 +1,8 @@ -# CAP 理论 +# 一、CAP 理论 分布式系统不可能同时满足一致性(C:Consistency)、可用性(A:Availability)和分区容忍性(P:Partition Tolerance),最多只能同时满足其中两项。 -
+
## 一致性(Consistency) @@ -22,11 +22,39 @@ 在分区容忍性条件下,分布式系统在遇到任何网络分区故障的时候,仍然需要能对外提供一致性和可用性的服务,除非是整个网络环境都发生了故障。 -> **权衡** +## 总结 在分布式系统中,分区容忍性必不可少,因为需要总是假设网络是不可靠的。因此,CAP 理论实际上是要在**可用性(P)和一致性(C)之间做权衡**。 可用性和一致性往往是冲突的,很难使它们同时满足。在多个节点之间进行数据同步时, - 为了保证一致性(CP),不能访问未同步完成的节点,也就失去了部分可用性,zookeeper其实就是追求的强一致; -- 为了保证可用性(AP),放弃一致性(这里说的一致性是强一致性),追求分区容错性和可用性,这是很多分布式系统设计时的选择,后面的BASE也是根据AP来扩展。 \ No newline at end of file +- 为了保证可用性(AP),放弃一致性(这里说的一致性是强一致性),追求分区容错性和可用性,这是很多分布式系统设计时的选择,后面的BASE也是根据AP来扩展。 + +注意:如果系统没有发生 ”分区“ 的话,节点间的网络连接通信正常的话,也就不存在 P 了。此时需要考虑保证 CA 了。 + +# 二、BASE 理论 + +在分布式系统中,我们往往追求的是**可用性**,它的重要程序比一致性要高,那么如何实现高可用性呢? 前人已经给我们提出来了另外一个理论,就是 BASE 理论,它是用来对 CAP 定理进行进一步扩充的,更具体地说,**BASE 是 CAP 理论中 AP 方案的延伸。** + +BASE 是基本可用(Basically Available)、软状态(Soft State)和最终一致性(Eventually Consistent)三个短语的缩写。 + +BASE 理论是对 CAP 中一致性和可用性权衡的结果,它的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。 + +## 基本可用 (Basically Available) + +指分布式系统在出现故障的时候,保证核心可用,允许损失部分可用性。 + +例如,电商在做促销时,为了保证购物系统的稳定性,部分消费者可能会被引导到一个降级的页面。 + +## 软状态 (Soft state) + +指允许系统中的数据存在中间状态,并认为该中间状态不会影响系统整体可用性,即允许系统不同节点的数据副本之间进行同步的过程存在时延。 + +## 最终一致性 (Eventually Consistent) + +最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能达到一致的状态。 + +**ACID 要求强一致性**,通常运用在传统的数据库系统上。而 BASE 要求最终一致性,通过**牺牲强一致性来达到可用性**,通常运用在大型分布式系统中。 + +在实际的分布式场景中,不同业务单元和组件对一致性的要求是不同的,因此 ACID 和 BASE 往往会结合在一起使用。 \ No newline at end of file diff --git "a/docs/distribution/3_BASE\347\220\206\350\256\272.md" "b/docs/distribution/3_BASE\347\220\206\350\256\272.md" deleted file mode 100644 index 800e499b..00000000 --- "a/docs/distribution/3_BASE\347\220\206\350\256\272.md" +++ /dev/null @@ -1,25 +0,0 @@ -# BASE 理论 - -在分布式系统中,我们往往追求的是**可用性**,它的重要程序比一致性要高,那么如何实现高可用性呢? 前人已经给我们提出来了另外一个理论,就是BASE理论,它是用来对CAP定理进行进一步扩充的。 - -BASE 是基本可用(Basically Available)、软状态(Soft State)和最终一致性(Eventually Consistent)三个短语的缩写。 - -BASE 理论是对 CAP 中一致性和可用性权衡的结果,它的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。 - -## 基本可用 - -指分布式系统在出现故障的时候,保证核心可用,允许损失部分可用性。 - -例如,电商在做促销时,为了保证购物系统的稳定性,部分消费者可能会被引导到一个降级的页面。 - -## 软状态 - -指允许系统中的数据存在中间状态,并认为该中间状态不会影响系统整体可用性,即允许系统不同节点的数据副本之间进行同步的过程存在时延。 - -## 最终一致性 - -最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能达到一致的状态。 - -ACID 要求强一致性,通常运用在传统的数据库系统上。而 BASE 要求最终一致性,通过牺牲强一致性来达到可用性,通常运用在大型分布式系统中。 - -在实际的分布式场景中,不同业务单元和组件对一致性的要求是不同的,因此 ACID 和 BASE 往往会结合在一起使用。 \ No newline at end of file diff --git "a/docs/distribution/4_\345\210\206\345\270\203\345\274\217\351\224\201.md" "b/docs/distribution/3_\345\210\206\345\270\203\345\274\217\351\224\201.md" similarity index 94% rename from "docs/distribution/4_\345\210\206\345\270\203\345\274\217\351\224\201.md" rename to "docs/distribution/3_\345\210\206\345\270\203\345\274\217\351\224\201.md" index b3f96690..12cdc4b0 100644 --- "a/docs/distribution/4_\345\210\206\345\270\203\345\274\217\351\224\201.md" +++ "b/docs/distribution/3_\345\210\206\345\270\203\345\274\217\351\224\201.md" @@ -45,7 +45,7 @@ EXPIRE 指令可以为一个键值对设置一个过期时间,从而避免了 -
+
@@ -57,7 +57,7 @@ EXPIRE 指令可以为一个键值对设置一个过期时间,从而避免了 Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点的父节点为 /app1。 -

+

### 2. 节点类型 diff --git "a/docs/distribution/4_\345\210\206\345\270\203\345\274\217id.md" "b/docs/distribution/4_\345\210\206\345\270\203\345\274\217id.md" new file mode 100644 index 00000000..96fae9b3 --- /dev/null +++ "b/docs/distribution/4_\345\210\206\345\270\203\345\274\217id.md" @@ -0,0 +1,46 @@ +# 分布式 ID + +## 分布式 ID + +我们知道 ID 是数据的唯一标识,比如比如身份证 ID 对应且仅对应一个人,商品 ID 对应且仅对应一件商品。分布式 ID 是分布式系统下的 ID,分布式 ID 不存在与现实生活中,属于计算机系统中的一个概念。一个最基本的分布式 ID 需要满足以下要求: + +- 全局唯一 + +- 高性能 + + 生成速度快,对本地资源消耗小 + +- 高可用 + + 生成分布式 ID 的服务要保证可用性无限接近于 100% + +- 拿来即可用 + +除此之外,设计一个比较好的分布式 ID 还应保证: + +- 安全性 + + ID 不应该包含敏感信息 + +- 有序递增 + + 如果要把 ID 存放在数据库的话,ID 的有序性可以提升数据库写入速度。并且,很多时候 ,我们还很有可能会直接通过 ID 来进行排序。 + +- 可独立部署 + + 独立部署就是分布式系统单独有一个**发号器服务**,专门用来生成分布式 ID。这样就生成 ID 的服务可以和业务相关的服务解耦。不过,这样同样带来了网络调用消耗增加的问题。总的来说,如果需要用到分布式 ID 的场景比较多的话,独立部署的发号器服务还是很有必要的。 + +- 包含具体业务含义 + + + +## 分布式 ID 生成方案 + +### 数据库 + +### 算法 + +### 开源框架 + + + diff --git "a/docs/distribution/5_\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" "b/docs/distribution/4_\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" similarity index 93% rename from "docs/distribution/5_\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" rename to "docs/distribution/4_\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" index 928c4125..86081ddd 100644 --- "a/docs/distribution/5_\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" +++ "b/docs/distribution/4_\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md" @@ -25,7 +25,7 @@ -

+

**阶段2:提交阶段(commit phase)** @@ -33,7 +33,7 @@ 需要注意的是,在准备阶段,参与者执行了事务,但是还未提交。只有在提交阶段接收到协调者发来的通知后,才进行提交或者回滚。 -

+

### 存在的问题 @@ -89,7 +89,7 @@ TCC 即补偿事务,其实就是采用的**补偿机制**,其核心思想是 3. 在分布式事务操作的另一方从消息队列中读取一个消息,并执行消息中的操作。 4. 如果是业务上面的失败,可以给生产方发送一个业务补偿消息,通知生产方进行回滚等操作。 -
+
这种方案遵循 BASE 理论,采用的是最终一致性,笔者认为是这几种方案里面比较适合实际业务场景的,即不会出现像 2PC 那样复杂的实现(当调用链很长的时候,2PC的可用性是非常低的),也不会像TCC那样可能出现确认或者回滚不了的情况。 diff --git "a/docs/distribution/5_Paxos\347\256\227\346\263\225.md" "b/docs/distribution/5_Paxos\347\256\227\346\263\225.md" new file mode 100644 index 00000000..8815cf14 --- /dev/null +++ "b/docs/distribution/5_Paxos\347\256\227\346\263\225.md" @@ -0,0 +1,8 @@ +# 一、Paxos 算法 + +- [分布式系列文章——Paxos算法原理与推导](https://www.cnblogs.com/linbingdong/p/6253479.html) + +# 二、Raft 算法 + +- [拜占庭将军问题](https://blog.csdn.net/bjweimengshu/article/details/80222416) +- [Raft 算法动画详解](http://thesecretlivesofdata.com/raft/) \ No newline at end of file diff --git "a/docs/distribution/6_\345\210\206\345\270\203\345\274\217\347\274\223\345\255\230\347\232\204\344\270\200\350\207\264\346\200\247\345\223\210\345\270\214\347\256\227\346\263\225.md" "b/docs/distribution/6_\345\210\206\345\270\203\345\274\217\347\274\223\345\255\230\347\232\204\344\270\200\350\207\264\346\200\247\345\223\210\345\270\214\347\256\227\346\263\225.md" deleted file mode 100644 index b0bebd02..00000000 --- "a/docs/distribution/6_\345\210\206\345\270\203\345\274\217\347\274\223\345\255\230\347\232\204\344\270\200\350\207\264\346\200\247\345\223\210\345\270\214\347\256\227\346\263\225.md" +++ /dev/null @@ -1,80 +0,0 @@ -# 分布式缓存的一致性哈希算法 - -## Hash 算法 - -根据键值,首先计算哈希值,然后对节点数取模,然后映射在不同的 Master 节点上。 - -一旦某一个 Master 节点宕机,当请求过来时,会基于最新的剩余 Master 节点数去取模,尝试去获取数据,导致大部分的请求过来,全部无法拿到有效的缓存,大量的流量涌入数据库。 - -换句话说,**当服务器数量发生改变时,所有缓存在一定时间内是失效的,当应用无法从缓存中获取数据时,则会向后端数据库请求数据**。 - -## 一致性 Hash 算法 - -一致性 Hash 算法将整个哈希值空间组织成一个**虚拟的圆环**,假设某哈希函数 H 的值空间为0~2^32-1(即哈希值是一个32位无符号整形)。 - -整个空间按**顺时针方向**组织,圆环的正上方的点代表0,0 点右侧的第一个点代表1,以此类推,2、3 ... 2^32-1,也就是说 0 点左侧的第一个点代表 2^32-1, 0 和 2^32-1 在零点中方向重合,我们把这个由 2^32 个点组成的圆环称为**哈希环**。 - -
- -将各个服务器进行哈希,具体可以选择服务器的 IP 或主机名作为关键字进行哈希,这样每台机器就能**确定其在哈希环上的位置**。假设将 4 台服务器的 IP 地址哈希后在哈希环的位置如下: - -
- -接下来使用如下算法定位数据访问到相应服务器: - -将数据键使用相同的函数 Hash 计算出哈希值,并确定此数据在环上的位置,从此位置**沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器**。 - -例如有 Object A、Object B、Object C、Object D 四个数据对象,经过哈希计算后,在哈希环上的位置如下: - -
- -根据一致性 Hash 算法: - -Object A 会被定位到 Node A 上; - -Object B 会被定位到 Node B 上; - -Object C 会被定位到 Node C 上; - -Object D 会被定位到 Node D 上。 - -### 容错性和可扩展性 - -假设 Node C 宕机,可以看到此时对象 A、B、D 不会受到影响,只有对象 C 被重定位到 Node D。 - -
- -通常情况下,一致性 Hash 算法中,如果一台服务器不可用,则**受影响的数据**仅仅是此服务器到其环空间中前一台服务器(即沿着**逆时针方向行走遇到的第一台服务器**)之间数据,其它数据不会受到影响。 - -下面考虑另外一种情况:如果在系统中增加一台服务器 Node X。 - -此时对象 A、B、D 不受影响,只有对象 C 需要重定位到新的Node X 。 - -
- -通常情况下,一致性 Hash 算法中,如果增加一台服务器,则受影响的数据仅仅是新服务器到其环空间中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据,其它数据也不会受到影响。 - -综上所述,一致性 Hash 算法对于节点的增减都只需**重定位哈希环中的一小部分数据**,具有**较好的容错性和可扩展性**。 - -### 数据倾斜问题 - -一致性 Hash 算法在服务节点太少时,容易因为节点分部不均匀而造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上)问题。例如系统中只有 2 台服务器,如下所示: - -
- -此时必然造成大量数据集中到 Node A 上,而只有极少量会定位到 Node B 上。 - -为了解决这种数据倾斜问题,一致性 Hash 算法引入了**虚拟节点机制**,即**对每一个服务节点计算多个哈希**,每个计算结果位置都放置一个此服务节点,称为虚拟节点。具体做法可以在服务器 IP 或主机名的后面增加编号来实现。 - -例如针对上面的情况,可以为每台服务器计算 3 个虚拟节点: - -- Node A 的 3 个虚拟节点:"Node A#1"、"Node A#2"、"Node A#3" -- Node B 的 3 个虚拟节点:"Node B#1"、"Node B#2"、"Node B#3" - -进行哈希计算后,六个虚拟节点在哈希环中的位置如下: - -
- -同时**数据定位算法不变**,只是多了一步虚拟节点到实际节点的映射过程,例如"Node A#1"、"Node A#2"、"Node A#3" 这 3 个虚拟节点的数据均定位到 Node A 上,解决了服务节点少时数据倾斜的问题。 - -在实际应用中,通常将虚拟节点数设置为 32 甚至更大,因此即使很少的服务节点也能做到相对均匀的数据分布。 \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index 5d0be969..018c63a2 100644 --- a/docs/index.html +++ b/docs/index.html @@ -6,7 +6,7 @@ - +