diff --git a/README.md b/README.md index e30d3cc9..1e614dda 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,15 @@ -> 关于 JavaGuide 的相关介绍请看:[《从编程小白到做了一个接近 90k 点赞的一个国产 Java 开源项目》](https://www.yuque.com/snailclimb/dr6cvl/mr44yt#vu3ok) +👍《JavaGuide 面试突击版》PDF 版本+3 本 PDF Java 学习手册,在公众号 **[JavaGuide](#公众号)** 后台回复“**面试突击**”即可获取。 + +👍 图解操作系统+HTTP+计算机网络的 PDF 资料[点此链接即可下载](https://cowtransfer.com/s/fbed14f0c22a4d)。 + +> 一些闲话: > -> 准备面试的小伙伴可以考虑面试专版:[《Java 面试进阶指南》](https://xiaozhuanlan.com/javainterview?rel=javaguide) ,欢迎加入[我的星球](https://wx.zsxq.com/dweb2/index/group/48418884588288)获取更多实用干货。 -> -> 阿里云最近在做活动,服务器不到 10 元/月,小伙伴们搭建一个网站提高简历质量。支持国内开源做的比较好的公司![点击此链接直达活动首页。](https://promotion.aliyun.com/ntms/yunparter/invite.html?userCode=hf47liqn) -> -> 项目的发展离不开你的支持,如果 JavaGuide 帮助到了你找到自己满意的 offer,那就[请作者喝杯咖啡吧](https://www.yuque.com/snailclimb/dr6cvl/mr44yt#vu3ok)☕!我会继续将项目完善下去!加油! - -如果 Github 访问速度比较慢或者图片无法刷新出来的话,可以转移到[码云](https://gitee.com/SnailClimb/JavaGuide)查看,或者[在线阅读](https://snailclimb.gitee.io/javaguide)。**如果你要提交 issue 或者 pr 的话请到 [Github](https://github.com/Snailclimb/JavaGuide) 提交。** - -《JavaGuide 面试突击版》PDF 版本+3 本 PDF Java 学习手册,在公众号 **[JavaGuide](#公众号)** 后台回复“**面试突击**”即可获取。 - -如要进群或者请教问题,请[联系我](#联系我) (备注来自 Github。请直入问题,工作时间不回复)。 - -**开始阅读之前必看** :[完结撒花!JavaGuide 面试突击版来啦!](./docs/javaguide面试突击版.md) 。 +> 1. **JavaGuide 介绍**:关于 JavaGuide 的相关介绍请看:[关于 JavaGuide 的一些说明](https://www.yuque.com/snailclimb/dr6cvl/mr44yt#vu3ok) 。PDF 版本请看:[完结撒花!JavaGuide 面试突击版来啦!](./docs/javaguide面试突击版.md) 。 +> 2. **在线阅读** :如果 Github 访问速度比较慢或者图片无法刷新出来的话,可以转移到[码云](https://gitee.com/SnailClimb/JavaGuide)查看或者[在线阅读](https://snailclimb.gitee.io/javaguide)。如果你要提交 issue 或者 pr 的话建议到 [Github](https://github.com/Snailclimb/JavaGuide) 提交。 +> 3. **面试专版** :准备面试的小伙伴可以考虑面试专版:[《Java 面试进阶指南》](https://xiaozhuanlan.com/javainterview?rel=javaguide) ,欢迎加入[我的星球](https://wx.zsxq.com/dweb2/index/group/48418884588288)获取更多实用干货。 +> 4. **阿里云活动** :阿里云最近在做活动,服务器不到 10 元/月,小伙伴们搭建一个网站提高简历质量。支持国内开源做的比较好的公司![点击此链接直达活动首页。](https://promotion.aliyun.com/ntms/yunparter/invite.html?userCode=hf47liqn) +> 5. **联系我** :如要进群或者请教问题,请[联系我](#联系我) (备注来自 Github。请直入问题,工作时间不回复)。 +> 6. **转载须知** :以下所有文章如非文首说明皆为我(Guide哥)的原创,转载在文首注明出处,如发现恶意抄袭/搬运,会动用法律武器维护自己的权益。让我们一起维护一个良好的技术创作环境!⛽️

@@ -33,36 +30,31 @@ - - - - + +
- - - -
+ -
-## 目录 -- [目录](#目录) + + + + - [Java](#java) - [基础](#基础) - [容器](#容器) - [并发](#并发) - - [JVM](#jvm) - - [其他](#其他) + - [JVM (必看 :+1:)](#jvm-必看-1) + - [新特性](#新特性) - [网络](#网络) - [操作系统](#操作系统) - - [Linux](#linux) - [数据结构与算法](#数据结构与算法) - [数据结构](#数据结构) - [算法](#算法) @@ -70,90 +62,89 @@ - [MySQL](#mysql) - [Redis](#redis) - [系统设计](#系统设计) - - [必知](#必知) + - [编码之道(必看 :+1:)](#编码之道必看-1) - [常用框架](#常用框架) - [Spring/SpringBoot](#springspringboot) - [MyBatis](#mybatis) - - [Netty](#netty) + - [Netty (必看 :+1:)](#netty-必看-1) - [认证授权](#认证授权) - [JWT](#jwt) - [SSO(单点登录)](#sso单点登录) - [分布式](#分布式) - - [分布式搜索引擎](#分布式搜索引擎) + - [搜索引擎](#搜索引擎) - [RPC](#rpc) - - [消息队列](#消息队列) - [API 网关](#api-网关) - - [分布式 id](#分布式id) - - [分布式限流](#分布式限流) - - [分布式接口幂等性](#分布式接口幂等性) + - [分布式 id](#分布式-id) - [ZooKeeper](#zookeeper) - - [其他](#其他-1) - - [数据库扩展](#数据库扩展) - - [大型网站架构](#大型网站架构) - - [性能测试](#性能测试) - - [高并发](#高并发) - - [高可用](#高可用) - [微服务](#微服务) - - [Spring Cloud](#spring-cloud) -- [必会工具](#必会工具) - - [Git](#git) - - [Docker](#docker) - - [其他](#其他-2) + - [高并发](#高并发) + - [消息队列](#消息队列) + - [读写分离](#读写分离) + - [分库分表](#分库分表) + - [负载均衡](#负载均衡) + - [高可用](#高可用) + - [CAP 理论](#cap-理论) + - [BASE 理论](#base-理论) + - [限流](#限流) + - [降级](#降级) + - [熔断](#熔断) + - [排队](#排队) + - [大型网站架构](#大型网站架构) +- [工具](#工具) - [面试指南](#面试指南) -- [Java 学习常见问题汇总](#java学习常见问题汇总) -- [资源](#资源) - - [Java 程序员必备书单](#java程序员必备书单) - - [实战项目推荐](#实战项目推荐) - - [Github](#github) -- [待办](#待办) -- [说明](#说明) +- [Java 学习常见问题汇总](#java-学习常见问题汇总) +- [书单](#书单) +- [其他](#其他) + - [待办](#待办) + - [联系我](#联系我) + - [捐赠支持](#捐赠支持) + - [Contributor](#contributor) + - [公众号](#公众号) + + + ## Java ### 基础 -**基础知识系统总结:** +**知识点/面试题:**(必看:+1: ) -1. **[Java 基础知识](docs/java/Java基础知识.md)** -2. **[Java 基础知识疑难点/易错点](docs/java/Java疑难点.md)** -3. [【选看】J2EE 基础知识](docs/java/J2EE基础知识.md) +1. **[Java 基础知识](docs/java/basis/Java基础知识.md)** +2. **[Java 基础知识疑难点/易错点](docs/java/basis/Java基础知识疑难点.md)** **重要知识点详解:** -1. [枚举](docs/java/basic/用好Java中的枚举真的没有那么简单.md) (很重要的一个数据结构,用好枚举真的没有那么简单!) -2. [Java 常见关键字总结:final、static、this、super!](docs/java/basic/final,static,this,super.md) -3. [什么是反射机制?反射机制的应用场景有哪些?](docs/java/basic/reflection.md) -4. [代理模式详解:静态代理+JDK/CGLIB 动态代理实战(动态代理和静态代理的区别?JDK 动态代理 和 CGLIB 动态代理的区别?)](docs/java/basic/java-proxy.md) - -**其他:** - -1. [JAD 反编译](docs/java/JAD反编译tricks.md) -2. [手把手教你定位常见 Java 性能问题](./docs/java/手把手教你定位常见Java性能问题.md) +1. [枚举](docs/java/basis/用好Java中的枚举真的没有那么简单.md) (很重要的一个数据结构,用好枚举真的没有那么简单!) +2. [Java 常见关键字总结:final、static、this、super!](docs/java/basis/Java常见关键字总结:final,static,this,super.md) +3. [什么是反射机制?反射机制的应用场景有哪些?](docs/java/basis/什么是反射机制?反射机制的应用场景有哪些?.md) +4. [代理模式详解:静态代理+JDK/CGLIB 动态代理实战](docs/java/basis/静态代理+JDK,CGLIB动态代理实战.md) +5. [BIO,NIO,AIO 总结 ](docs/java/basis/BIO,NIO,AIO总结.md) ### 容器 -1. **[Java 容器常见面试题/知识点总结](docs/java/collection/Java集合框架常见面试题.md)** -2. 源码分析:[ArrayList 源码](docs/java/collection/ArrayList.md) 、[LinkedList 源码](docs/java/collection/LinkedList.md) 、[HashMap(JDK1.8)源码](docs/java/collection/HashMap.md) 、[ConcurrentHashMap 源码](docs/java/collection/ConcurrentHashMap.md) +1. **[Java 容器常见面试题/知识点总结](docs/java/collection/Java集合框架常见面试题.md)** (必看 :+1:) +2. **源码分析** :[ArrayList 源码+扩容机制分析](docs/java/collection/ArrayList源码+扩容机制分析.md) 、[LinkedList 源码](docs/java/collection/LinkedList源码分析.md) 、[HashMap(JDK1.8)源码+底层数据结构分析]() 、[ConcurrentHashMap 源码+底层数据结构分析](docs/java/collection/ConcurrentHashMap源码+底层数据结构分析.md) ### 并发 -**[多线程学习指南](./docs/java/Multithread/多线程学习指南.md)** +并发这部分内容非常重要,还是面试中的重点中的重点!但是,学习起来难度较大,因此我写了:**[多线程学习指南](./docs/java/multi-thread/多线程学习指南.md)** 帮助你学习。 -**面试题总结:** +**知识点/面试题:** (必看 :+1:) -1. **[Java 并发基础常见面试题总结](docs/java/Multithread/JavaConcurrencyBasicsCommonInterviewQuestionsSummary.md)** -2. **[Java 并发进阶常见面试题总结](docs/java/Multithread/JavaConcurrencyAdvancedCommonInterviewQuestions.md)** +1. **[Java 并发基础常见面试题总结](docs/java/multi-thread/2020最新Java并发基础常见面试题总结.md)** +2. **[Java 并发进阶常见面试题总结](docs/java/multi-thread/2020最新Java并发进阶常见面试题总结.md)** -**面试常问知识点:** +**重要知识点详解:** -1. [并发容器总结](docs/java/Multithread/并发容器总结.md) -2. **线程池**:[Java 线程池学习总结](./docs/java/Multithread/java线程池学习总结.md)、[拿来即用的线程池最佳实践](./docs/java/Multithread/best-practice-of-threadpool.md) +2. **线程池**:[Java 线程池学习总结](./docs/java/multi-thread/java线程池学习总结.md)、[拿来即用的线程池最佳实践](./docs/java/multi-thread/拿来即用的线程池最佳实践.md) 3. [乐观锁与悲观锁](docs/essential-content-for-interview/面试必备之乐观锁与悲观锁.md) -4. [万字图文深度解析 ThreadLocal](docs/java/Multithread/ThreadLocal.md) -5. [JUC 中的 Atomic 原子类总结](docs/java/Multithread/Atomic.md) -6. [AQS 原理以及 AQS 同步组件总结](docs/java/Multithread/AQS.md) +4. [ ThreadLocal 关键字解析](docs/java/multi-thread/万字详解ThreadLocal关键字.md) +5. [并发容器总结](docs/java/multi-thread/并发容器总结.md) +6. [JUC 中的 Atomic 原子类总结](docs/java/multi-thread/Atomic原子类总结.md) +7. [AQS 原理以及 AQS 同步组件总结](docs/java/multi-thread/AQS原理以及AQS同步组件总结.md) -### JVM +### JVM (必看 :+1:) 1. **[Java 内存区域](docs/java/jvm/Java内存区域.md)** 2. **[JVM 垃圾回收](docs/java/jvm/JVM垃圾回收.md)** @@ -165,48 +156,38 @@ 8. [JVM 配置常用参数和常用 GC 调优策略](docs/java/jvm/GC调优参数.md) 9. **[【加餐】大白话带你认识 JVM](docs/java/jvm/[加餐]大白话带你认识JVM.md)** -### 其他 +### 新特性 -1. **Linux IO** : [Linux IO](docs/java/Linux_IO.md) -2. **I/O** :[BIO,NIO,AIO 总结 ](docs/java/BIO-NIO-AIO.md) -3. **Java 8** :[Java 8 新特性总结](docs/java/What's%20New%20in%20JDK8/Java8Tutorial.md)、[Java 8 学习资源推荐](docs/java/What's%20New%20in%20JDK8/Java8教程推荐.md)、[Java8 forEach 指南](docs/java/What's%20New%20in%20JDK8/Java8foreach指南.md) -4. **Java9~Java14** : [一文带你看遍 JDK9~14 的重要新特性!](./docs/java/jdk-new-features/new-features-from-jdk8-to-jdk14.md) -5. Java 编程规范:**[Java 编程规范以及优雅 Java 代码实践总结](docs/java/Java编程规范.md)** 、[告别编码 5 分钟,命名 2 小时!史上最全的 Java 命名规范参考!](docs/java/java-naming-conventions.md) -6. 设计模式 :[设计模式系列文章](docs/system-design/设计模式.md) +1. **Java 8** :[Java 8 新特性总结](docs/java/new-features/Java8新特性总结.md)、[Java 8 学习资源推荐](docs/java/new-features/Java8教程推荐.md)、[Java8 forEach 指南](docs/java/new-features/Java8foreach指南.md) +2. **Java9~Java14** : [一文带你看遍 JDK9~14 的重要新特性!](./docs/java/new-features/一文带你看遍JDK9到14的重要新特性.md) ## 网络 1. [计算机网络常见面试题](docs/network/计算机网络.md) -2. [计算机网络基础知识总结](docs/network/干货:计算机网络知识总结.md) +2. [计算机网络基础知识总结](docs/network/计算机网络知识总结.md) ## 操作系统 -[最硬核的操作系统常见问题总结!](docs/operating-system/basis.md) - -### Linux - -- [后端程序员必备的 Linux 基础知识](docs/operating-system/linux.md) -- [Shell 编程入门](docs/operating-system/Shell.md) -- [我为什么从 Windows 转到 Linux?](docs/operating-system/完全使用GNU_Linux学习.md) -- [Linux IO 模型](docs/operating-system/Linux_IO.md) -- [Linux 性能分析工具合集](docs/operating-system/Linux性能分析工具合集.md) +1. [操作系统常见问题总结!](docs/operating-system/basis.md) +2. [后端程序员必备的 Linux 基础知识](docs/operating-system/linux.md) +3. [Shell 编程入门](docs/operating-system/Shell.md) ## 数据结构与算法 ### 数据结构 -- [不了解布隆过滤器?一文给你整的明明白白!](docs/dataStructures-algorithms/data-structure/bloom-filter.md) -- [数据结构知识学习与面试](docs/dataStructures-algorithms/数据结构.md) +1. [不了解布隆过滤器?一文给你整的明明白白!](docs/dataStructures-algorithms/data-structure/bloom-filter.md) +2. [数据结构知识学习与面试](docs/dataStructures-algorithms/数据结构.md) ### 算法 -- [硬核的算法学习书籍+资源推荐](docs/dataStructures-algorithms/算法学习资源推荐.md) -- 常见算法问题总结: - - [几道常见的字符串算法题总结 ](docs/dataStructures-algorithms/几道常见的子符串算法题.md) - - [几道常见的链表算法题总结 ](docs/dataStructures-algorithms/几道常见的链表算法题.md) - - [剑指 offer 部分编程题](docs/dataStructures-algorithms/剑指offer部分编程题.md) - - [公司真题](docs/dataStructures-algorithms/公司真题.md) - - [回溯算法经典案例之 N 皇后问题](docs/dataStructures-algorithms/Backtracking-NQueens.md) +算法这部分内容非常重要,如果你不知道如何学习算法的话,可以看下我写的:[《硬核的算法学习书籍+资源推荐》](docs/dataStructures-algorithms/算法学习资源推荐.md) 。 + +**常见算法问题总结:** + +- [几道常见的字符串算法题总结 ](docs/dataStructures-algorithms/几道常见的子符串算法题.md) +- [几道常见的链表算法题总结 ](docs/dataStructures-algorithms/几道常见的链表算法题.md) +- [剑指 offer 部分编程题](docs/dataStructures-algorithms/剑指offer部分编程题.md) ## 数据库 @@ -228,88 +209,70 @@ ### Redis -- [关于缓存的一些重要概念(Redis 前置菜)](docs/database/Redis/some-concepts-of-caching.md) -- [Redis 常见问题总结](docs/database/Redis/redis-all.md) +1. [关于缓存的一些重要概念(Redis 前置菜)](docs/database/Redis/some-concepts-of-caching.md) +2. [Redis 常见问题总结](docs/database/Redis/redis-all.md) ## 系统设计 -### 必知 +### 编码之道(必看 :+1:) -1. **[RestFul API 简明教程](docs/system-design/restful-api.md)** -2. **[因为命名被 diss 无数次。Guide 简单聊聊编程最头疼的事情之一:命名](docs/system-design/naming.md)** +1. [RestFul API 简明教程](docs/system-design/coding-way/RESTfulAPI简明教程.md) +2. [Java 编程规范以及优雅 Java 代码实践总结](docs/java/Java编程规范.md) +3. [Java 命名之道](docs/system-design/naming.md) ### 常用框架 -#### Spring/SpringBoot +如果你没有接触过 Java Web 开发的话,可以先看一下我总结的 [《J2EE 基础知识》](docs/java/J2EE基础知识.md) 。虽然,这篇文章中的很多内容已经淘汰,但是可以让你对 Java 后台技术发展有更深的认识。 + +#### Spring/SpringBoot (必看 :+1:) + +**知识点/面试题:** 1. **[Spring 常见问题总结](docs/system-design/framework/spring/SpringInterviewQuestions.md)** 2. **[SpringBoot 指南/常见面试题总结](https://github.com/Snailclimb/springboot-guide)** -3. **[Spring/Spring 常用注解总结!安排!](./docs/system-design/framework/spring/spring-annotations.md)** -4. **[Spring 事务总结](docs/system-design/framework/spring/spring-transaction.md)** -5. [Spring IoC 和 AOP 详解](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247486938&idx=1&sn=c99ef0233f39a5ffc1b98c81e02dfcd4&chksm=cea24211f9d5cb07fa901183ba4d96187820713a72387788408040822ffb2ed575d28e953ce7&token=1666190828&lang=zh_CN#rd) -6. [Spring 中 Bean 的作用域与生命周期](docs/system-design/framework/spring/SpringBean.md) -7. [SpringMVC 工作原理详解](docs/system-design/framework/spring/SpringMVC-Principle.md) -8. [Spring 中都用到了那些设计模式?](docs/system-design/framework/spring/Spring-Design-Patterns.md) + +**重要知识点详解:** + +1. **[Spring/Spring 常用注解总结!安排!](./docs/system-design/framework/spring/SpringBoot+Spring常用注解总结.md)** +2. **[Spring 事务总结](docs/system-design/framework/spring/spring-transaction.md)** +3. [Spring 中都用到了那些设计模式?](docs/system-design/framework/spring/Spring-Design-Patterns.md) #### MyBatis - [MyBatis 常见面试题总结](docs/system-design/framework/mybatis/mybatis-interview.md) -#### Netty +#### Netty (必看 :+1:) 1. [剖析面试最常见问题之 Netty(上)](https://xiaozhuanlan.com/topic/4028536971) 2. [剖析面试最常见问题之 Netty(下)](https://xiaozhuanlan.com/topic/3985146207) ### 认证授权 -**[认证授权基础:搞清 Authentication,Authorization 以及 Cookie、Session、Token、OAuth 2、SSO](docs/system-design/authority-certification/basis-of-authority-certification.md)** +**[《认证授权基础》](docs/system-design/authority-certification/basis-of-authority-certification.md)** 这篇文章中我会介绍认证授权常见概念: **Authentication**,**Authorization** 以及 **Cookie**、**Session**、Token、**OAuth 2**、**SSO** 。如果你不清楚这些概念的话,建议好好阅读一下这篇文章。 #### JWT -- **[JWT 优缺点分析以及常见问题解决方案](docs/system-design/authority-certification/JWT-advantages-and-disadvantages.md)** -- **[适合初学者入门 Spring Security With JWT 的 Demo](https://github.com/Snailclimb/spring-security-jwt-guide)** +1. [JWT 优缺点分析以及常见问题解决方案](docs/system-design/authority-certification/JWT优缺点分析以及常见问题解决方案.md) +2. [适合初学者入门 Spring Security With JWT 的 Demo](https://github.com/Snailclimb/spring-security-jwt-guide) #### SSO(单点登录) -SSO(Single Sign On)即单点登录说的是用户登陆多个子系统的其中一个就有权访问与其相关的其他系统。举个例子我们在登陆了京东金融之后,我们同时也成功登陆京东的京东超市、京东家电等子系统。相关阅读:**[SSO 单点登录看这篇就够了!](docs/system-design/authority-certification/sso.md)** +**SSO(Single Sign On)** 即单点登录说的是用户登陆多个子系统的其中一个就有权访问与其相关的其他系统。举个例子我们在登陆了京东金融之后,我们同时也成功登陆京东的京东超市、京东家电等子系统。相关阅读:**[SSO 单点登录看这篇就够了!](docs/system-design/authority-certification/SSO单点登录看这一篇就够了.md)** ### 分布式 -[分布式相关概念入门](docs/system-design/website-architecture/分布式.md) +[分布式相关概念入门](docs/system-design/distributed-system/分布式.md) -#### 分布式搜索引擎 +#### 搜索引擎 -提高搜索效率。常见于电商购物网站的商品搜索于分类。 - -比较常用的是 Elasticsearch 和 Solr。 - -代办。 +用于提高搜索效率,功能和浏览器搜索引擎类似。比较常见的搜索引擎是 Elasticsearch(推荐) 和 Solr。 #### RPC -让调用远程服务调用像调用本地方法那样简单。 +RPC 让调用远程服务调用像调用本地方法那样简单。 -- [Dubbo 总结:关于 Dubbo 的重要知识点](docs/system-design/data-communication/dubbo.md) -- [服务之间的调用为啥不直接用 HTTP 而用 RPC?](docs/system-design/data-communication/why-use-rpc.md) - -#### 消息队列 - -消息队列在分布式系统中主要是为了解耦和削峰。相关阅读: **[消息队列总结](docs/system-design/data-communication/message-queue.md)** 。 - -**RabbitMQ:** - -1. [RabbitMQ 入门](docs/system-design/data-communication/rabbitmq.md) - -**RocketMQ:** - -1. [RocketMQ 入门](docs/system-design/data-communication/RocketMQ.md) -2. [RocketMQ 的几个简单问题与答案](docs/system-design/data-communication/RocketMQ-Questions.md) - -**Kafka:** - -1. **[Kafka 入门+SpringBoot 整合 Kafka 系列](https://github.com/Snailclimb/springboot-kafka)** -2. **[Kafka 常见面试题总结](docs/system-design/data-communication/kafka-inverview.md)** -3. [【加餐】Kafka 入门看这一篇就够了](docs/system-design/data-communication/Kafka入门看这一篇就够了.md) +1. [Dubbo 总结:关于 Dubbo 的重要知识点](docs/system-design/distributed-system/rpc/关于Dubbo的重要知识点.md) +2. [服务之间的调用为啥不直接用 HTTP 而用 RPC?](docs/system-design/distributed-system/rpc/服务之间的调用为啥不直接用HTTP而用RPC.md) #### API 网关 @@ -320,69 +283,99 @@ SSO(Single Sign On)即单点登录说的是用户登陆多个子系统的其中 #### 分布式 id -1. [为什么要分布式 id ?分布式 id 生成方案有哪些?](docs/system-design/micro-service/分布式id生成方案总结.md) - -#### 分布式限流 - -1. [限流算法有哪些?](docs/system-design/micro-service/limit-request.md) - -#### 分布式接口幂等性 +在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识。比如数据量太大之后,往往需要对进行对数据进行分库分表,分库分表后需要有一个唯一 ID 来标识一条数据或消息,数据库的自增 ID 显然不能满足需求。相关阅读:[为什么要分布式 id ?分布式 id 生成方案有哪些?](docs/system-design/micro-service/分布式id生成方案总结.md) #### ZooKeeper > 前两篇文章可能有内容重合部分,推荐都看一遍。 -1. [【入门】ZooKeeper 相关概念总结 01](docs/system-design/framework/zookeeper/zookeeper-intro.md) -2. [【进阶】ZooKeeper 相关概念总结 02](docs/system-design/framework/zookeeper/zookeeper-plus.md) +1. [【入门】ZooKeeper 相关概念总结](docs/system-design/framework/zookeeper/zookeeper-intro.md) +2. [【进阶】ZooKeeper 相关概念总结](docs/system-design/framework/zookeeper/zookeeper-plus.md) 3. [【实战】ZooKeeper 实战](docs/system-design/framework/zookeeper/zookeeper-in-action.md) -#### 其他 +### 微服务 -- 接口幂等性(代办):分布式系统必须要考虑接口的幂等性。 +1. [ 大白话入门 Spring Cloud](docs/system-design/micro-service/spring-cloud.md) +2. [微服务/分布式大厂真实面试问题解答](https://xiaozhuanlan.com/topic/2895047136) -#### 数据库扩展 +### 高并发 -读写分离、分库分表。 +#### 消息队列 -代办..... +消息队列在分布式系统中主要是为了解耦和削峰。相关阅读: **[消息队列总结](docs/system-design/data-communication/message-queue.md)** 。 + +1. **RabbitMQ** : [RabbitMQ 入门](docs/system-design/distributed-system/message-queue/RabbitMQ入门看这一篇就够了.md) +2. **RocketMQ** : [RocketMQ 入门](docs/system-design/distributed-system/message-queue/RocketMQ.md)、[RocketMQ 的几个简单问题与答案](docs/system-design/distributed-system/message-queue/RocketMQ-Questions.md) +3. **Kafka** :**[Kafka 常见面试题总结](docs/system-design/distributed-system/message-queue/Kafka常见面试题总结.md)** + +#### 读写分离 + +读写分离主要是为了将数据库的读和写操作分不到不同的数据库节点上。主服务器负责写,从服务器负责读。另外,一主一从或者一主多从都可以。 + +**读写分离可以大幅提高读性能,小幅提高写的性能。因此,读写分离更适合单机并发读请求比较多的场景。** + +#### 分库分表 + +**分库分表是为了解决由于库、表数据量过大,而导致数据库性能持续下降的问题。** 常见的分库分表工具有:`sharding-jdbc`(当当)、`TSharding`(蘑菇街)、`MyCAT`(基于 Cobar)、`Cobar`(阿里巴巴)...。 + +**推荐使用 `sharding-jdbc`** 。 因为,`sharding-jdbc` 是一款轻量级 `Java` 框架,以 `jar` 包形式提供服务,不要我们做额外的运维工作,并且兼容性也很好。 + +#### 负载均衡 + +负载均衡系统通常用于将任务比如用户请求处理分配到多个服务器处理以提高网站、应用或者数据库的性能和可靠性。 + +常见的负载均衡系统包括 3 种: + +1. **DNS 负载均衡** :一般用来实现地理级别的均衡。 +2. **硬件负载均衡** : 通过单独的硬件设备比如 F5 来实现负载均衡功能(硬件的价格一般很贵)。 +3. **软件负载均衡** :通过负载均衡软件比如 Nginx 来实现负载均衡功能。 + +### 高可用 + +高可用描述的是一个系统在大部分时间都是可用的,可以为我们提供服务的。高可用代表系统即使在发生硬件故障或者系统升级的时候,服务仍然是可用的 。 + +相关阅读: **《[如何设计一个高可用系统?要考虑哪些地方?](docs/system-design/high-availability/如何设计一个高可用系统?要考虑哪些地方?.md)》** 。 + +#### CAP 理论 + +CAP 也就是 Consistency(一致性)、Availability(可用性)、Partition Tolerance(分区容错性) 这三个单词首字母组合。 + +关于 CAP 的详细解读请看:[《CAP理论解读》](docs/system-design/high-availability/CAP理论.md)。 + +#### BASE 理论 + +**BASE** 是 **Basically Available(基本可用)** 、**Soft-state(软状态)** 和 **Eventually Consistent(最终一致性)** 三个短语的缩写。BASE 理论是对 CAP 中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结,是基于 CAP 定理逐步演化而来的,它大大降低了我们对系统的要求。 + +关于 CAP 的详细解读请看:[《BASE理论解读》](docs/system-design/high-availability/BASE理论.md)。 + +#### 限流 + +限流为了对服务端的接口接受请求的频率进行限制,防止服务挂掉。比如某一接口的请求限制为 100 个每秒, 对超过限制的请求放弃处理或者放到队列中等待处理。限流可以有效应对突发请求过多。相关阅读:[限流算法有哪些?](docs/system-design/high-availability/limit-request.md) + +#### 降级 + +限流是从用户访问压力的角度来考虑如何应对故障,降级是从系统功能优先级的角度考虑如何应对故障 + +服务降级指的是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。降级往往会指定不同的级别,面临不同的异常等级执行不同的处理。根据服务方式:可以拒接服务,可以延迟服务,也有时候可以随机服务。根据服务范围:可以砍掉某个功能,也可以砍掉某些模块。总之服务降级需要根据不同的业务需求采用不同的降级策略。主要的目的就是服务虽然有损但是总比没有好。 + +#### 熔断 + +熔断和降级是两个比较容易混淆的概念,因为单纯从名字上看好像都有禁止某个功能的意思,但其实内在含义是不同的,原因在于降级的目的是应对系统自身的故障,而熔断的目的是应对依赖的外部系统故障的情况。 + +#### 排队 + +另类的一种限流,类比于现实世界的排队。玩过英雄联盟的小伙伴应该有体会,每次一有活动,就要经历一波排队才能进入游戏。 ### 大型网站架构 - [8 张图读懂大型网站技术架构](docs/system-design/website-architecture/8%20张图读懂大型网站技术架构.md) - [关于大型网站系统架构你不得不懂的 10 个问题](docs/system-design/website-architecture/关于大型网站系统架构你不得不懂的10个问题.md) -#### 性能测试 +## 工具 -- [后端程序员也要懂的性能测试知识](https://articles.zsxq.com/id_lwl39teglv3d.html) (知识星球) - -#### 高并发 - -待办...... - -#### 高可用 - -高可用描述的是一个系统在大部分时间都是可用的,可以为我们提供服务的。高可用代表系统即使在发生硬件故障或者系统升级的时候,服务仍然是可用的 。相关阅读: **《[如何设计一个高可用系统?要考虑哪些地方?](docs/system-design/website-architecture/如何设计一个高可用系统?要考虑哪些地方?.md)》** 。 - -### 微服务 - -#### Spring Cloud - -- [ 大白话入门 Spring Cloud](docs/system-design/micro-service/spring-cloud.md) - -## 必会工具 - -### Git - -- [Git 入门](docs/tools/Git.md) - -### Docker - -1. [Docker 基本概念解读](docs/tools/Docker.md) -2. [一文搞懂 Docker 镜像的常用操作!](docs/tools/Docker-Image.md) - -### 其他 - -- [【原创】如何使用云服务器?希望这篇文章能够对你有帮助!](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485738&idx=1&sn=f97e91a50e444944076c30b0717b303a&chksm=cea246e1f9d5cff73faf6a778b147ea85162d1f3ed55ca90473c6ebae1e2c4d13e89282aeb24&token=406194678&lang=zh_CN#rd) +1. **Java** :[JAD 反编译](docs/java/JAD反编译tricks.md)、[手把手教你定位常见 Java 性能问题](./docs/java/手把手教你定位常见Java性能问题.md) +2. **Git** :[Git 入门](docs/tools/Git.md) +3. **Docker** : [Docker 基本概念解读](docs/tools/Docker.md) 、[一文搞懂 Docker 镜像的常用操作!](docs/tools/Docker-Image.md) ## 面试指南 @@ -403,51 +396,31 @@ SSO(Single Sign On)即单点登录说的是用户登陆多个子系统的其中 3. [新手学习 Java,有哪些 Java 相关的博客,专栏,和技术学习网站推荐?](docs/questions/java-learning-website-blog.md) 4. [Java 还是大数据,你需要了解这些东西!](docs/questions/java-big-data.md) -## 资源 +## 书单 -### Java 程序员必备书单 - -1. [「基础篇」Guide 的 Java 后端书架来啦!都是 Java 程序员必看的书籍?](./docs/books/java基础篇.md) - -### 实战项目推荐 - -- **[Java、SpringBoot 实战项目推荐](https://github.com/Snailclimb/awesome-java#实战项目)** - -### Github - -- [Github 上非常棒的 Java 开源项目集合](https://github.com/Snailclimb/awesome-java) -- [Github 上 Star 数最多的 10 个项目,看完之后很意外!](docs/tools/github/github-star-ranking.md) -- [年末将至,值得你关注的 16 个 Java 开源项目!](docs/github-trending/2019-12.md) -- [Java 项目历史月榜单](docs/github-trending/JavaGithubTrending.md) +1. [「基础篇」Java 书单](./docs/books/java基础篇.md) --- -## 待办 +## 其他 + +### 待办 - [x] Netty 总结 -- [ ] 数据结构总结重构(---正在进行中---) +- [ ] 数据结构总结重构 +- [ ] 将 JavaGuide 的基础知识部分抽出来单独弄一个 CS-Guide -## 说明 - -开源项目在于大家的参与,这才使得它的价值得到提升。感谢 🙏 有你! - -项目的 Markdown 格式参考:[Github Markdown 格式](https://guides.github.com/features/mastering-markdown/),表情素材来自:[EMOJI CHEAT SHEET](https://www.webpagefx.com/tools/emoji-cheat-sheet/)。 - -利用 docsify 生成文档部署在 Github pages: [docsify 官网介绍](https://docsify.js.org/#/) ,另见[《Guide 哥手把手教你搭建一个文档类型的网站!免费且高速!》](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247486555&idx=2&sn=8486026ee9f9ba645ff0363df6036184&chksm=cea24390f9d5ca86ff4177c0aca5e719de17dc89e918212513ee661dd56f17ca8269f4a6e303&token=298703358&lang=zh_CN#rd) 。 - -Logo 下的小图标是使用[Shields.IO](https://shields.io/) 生成的。 - -## 联系我 +### 联系我 ![个人微信](https://cdn.jsdelivr.net/gh/javaguide-tech/blog-images/2020-08/wechat3.jpeg) -## 捐赠支持 +### 捐赠支持 项目的发展离不开你的支持,如果 JavaGuide 帮助到了你找到自己满意的 offer,请作者喝杯咖啡吧 ☕ 后续会继续完善更新!加油! [点击捐赠支持作者](https://www.yuque.com/snailclimb/dr6cvl/mr44yt#vu3ok) -## Contributor +### Contributor 下面是笔主收集的一些对本仓库提过有价值的 pr 或者 issue 的朋友,人数较多,如果你也对本仓库提过不错的 pr 或者 issue 的话,你可以加我的微信与我联系。下面的排名不分先后! @@ -502,7 +475,7 @@ Logo 下的小图标是使用[Shields.IO](https://shields.io/) 生成的。
-## 公众号 +### 公众号 如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。 diff --git a/docs/dataStructures-algorithms/Backtracking-NQueens.md b/docs/dataStructures-algorithms/Backtracking-NQueens.md deleted file mode 100644 index 1e1367d3..00000000 --- a/docs/dataStructures-algorithms/Backtracking-NQueens.md +++ /dev/null @@ -1,145 +0,0 @@ -# N皇后 -[51. N皇后](https://leetcode-cn.com/problems/n-queens/) -### 题目描述 -> n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。 -> -![ANUzjA.png](https://s2.ax1x.com/2019/03/26/ANUzjA.png) -> -上图为 8 皇后问题的一种解法。 -> -给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。 -> -每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。 - -示例: - -``` -输入: 4 -输出: [ - [".Q..", // 解法 1 - "...Q", - "Q...", - "..Q."], - - ["..Q.", // 解法 2 - "Q...", - "...Q", - ".Q.."] -] -解释: 4 皇后问题存在两个不同的解法。 -``` - -### 问题分析 -约束条件为每个棋子所在的行、列、对角线都不能有另一个棋子。 - -使用一维数组表示一种解法,下标(index)表示行,值(value)表示该行的Q(皇后)在哪一列。 -每行只存储一个元素,然后递归到下一行,这样就不用判断行了,只需要判断列和对角线。 -### Solution1 -当result[row] = column时,即row行的棋子在column列。 - -对于[0, row-1]的任意一行(i 行),若 row 行的棋子和 i 行的棋子在同一列,则有result[i] == column; -若 row 行的棋子和 i 行的棋子在同一对角线,等腰直角三角形两直角边相等,即 row - i == Math.abs(result[i] - column) - -布尔类型变量 isValid 的作用是剪枝,减少不必要的递归。 -```java -public List> solveNQueens(int n) { - // 下标代表行,值代表列。如result[0] = 3 表示第1行的Q在第3列 - int[] result = new int[n]; - List> resultList = new LinkedList<>(); - dfs(resultList, result, 0, n); - return resultList; -} - -void dfs(List> resultList, int[] result, int row, int n) { - // 递归终止条件 - if (row == n) { - List list = new LinkedList<>(); - for (int x = 0; x < n; ++x) { - StringBuilder sb = new StringBuilder(); - for (int y = 0; y < n; ++y) - sb.append(result[x] == y ? "Q" : "."); - list.add(sb.toString()); - } - resultList.add(list); - return; - } - for (int column = 0; column < n; ++column) { - boolean isValid = true; - result[row] = column; - /* - * 逐行往下考察每一行。同列,result[i] == column - * 同对角线,row - i == Math.abs(result[i] - column) - */ - for (int i = row - 1; i >= 0; --i) { - if (result[i] == column || row - i == Math.abs(result[i] - column)) { - isValid = false; - break; - } - } - if (isValid) dfs(resultList, result, row + 1, n); - } -} -``` -### Solution2 -使用LinkedList表示一种解法,下标(index)表示行,值(value)表示该行的Q(皇后)在哪一列。 - -解法二和解法一的不同在于,相同列以及相同对角线的校验。 -将对角线抽象成【一次函数】这个简单的数学模型,根据一次函数的截距是常量这一特性进行校验。 - -这里,我将右上-左下对角线,简称为“\”对角线;左上-右下对角线简称为“/”对角线。 - -“/”对角线斜率为1,对应方程为y = x + b,其中b为截距。 -对于线上任意一点,均有y - x = b,即row - i = b; -定义一个布尔类型数组anti_diag,将b作为下标,当anti_diag[b] = true时,表示相应对角线上已经放置棋子。 -但row - i有可能为负数,负数不能作为数组下标,row - i 的最小值为-n(当row = 0,i = n时),可以加上n作为数组下标,即将row -i + n 作为数组下标。 -row - i + n 的最大值为 2n(当row = n,i = 0时),故anti_diag的容量设置为 2n 即可。 - -![ANXG79.png](https://s2.ax1x.com/2019/03/26/ANXG79.png) - -“\”对角线斜率为-1,对应方程为y = -x + b,其中b为截距。 -对于线上任意一点,均有y + x = b,即row + i = b; -同理,定义数组main_diag,将b作为下标,当main_diag[row + i] = true时,表示相应对角线上已经放置棋子。 - -有了两个校验对角线的数组,再来定义一个用于校验列的数组cols,这个太简单啦,不解释。 - -**解法二时间复杂度为O(n!),在校验相同列和相同对角线时,引入三个布尔类型数组进行判断。相比解法一,少了一层循环,用空间换时间。** - -```java -List> resultList = new LinkedList<>(); - -public List> solveNQueens(int n) { - boolean[] cols = new boolean[n]; - boolean[] main_diag = new boolean[2 * n]; - boolean[] anti_diag = new boolean[2 * n]; - LinkedList result = new LinkedList<>(); - dfs(result, 0, cols, main_diag, anti_diag, n); - return resultList; -} - -void dfs(LinkedList result, int row, boolean[] cols, boolean[] main_diag, boolean[] anti_diag, int n) { - if (row == n) { - List list = new LinkedList<>(); - for (int x = 0; x < n; ++x) { - StringBuilder sb = new StringBuilder(); - for (int y = 0; y < n; ++y) - sb.append(result.get(x) == y ? "Q" : "."); - list.add(sb.toString()); - } - resultList.add(list); - return; - } - for (int i = 0; i < n; ++i) { - if (cols[i] || main_diag[row + i] || anti_diag[row - i + n]) - continue; - result.add(i); - cols[i] = true; - main_diag[row + i] = true; - anti_diag[row - i + n] = true; - dfs(result, row + 1, cols, main_diag, anti_diag, n); - result.removeLast(); - cols[i] = false; - main_diag[row + i] = false; - anti_diag[row - i + n] = false; - } -} -``` \ No newline at end of file diff --git a/docs/dataStructures-algorithms/images/我的第一本算法书.jpeg b/docs/dataStructures-algorithms/images/我的第一本算法书.jpeg new file mode 100644 index 00000000..221f53bc Binary files /dev/null and b/docs/dataStructures-algorithms/images/我的第一本算法书.jpeg differ diff --git a/docs/dataStructures-algorithms/images/我的第一本算法书.png b/docs/dataStructures-algorithms/images/我的第一本算法书.png deleted file mode 100644 index 17c0418e..00000000 Binary files a/docs/dataStructures-algorithms/images/我的第一本算法书.png and /dev/null differ diff --git a/docs/dataStructures-algorithms/images/程序员代码面试指南.jpeg b/docs/dataStructures-algorithms/images/程序员代码面试指南.jpeg new file mode 100644 index 00000000..1f3e8eb8 Binary files /dev/null and b/docs/dataStructures-algorithms/images/程序员代码面试指南.jpeg differ diff --git a/docs/dataStructures-algorithms/images/程序员代码面试指南.png b/docs/dataStructures-algorithms/images/程序员代码面试指南.png deleted file mode 100644 index fa50afec..00000000 Binary files a/docs/dataStructures-algorithms/images/程序员代码面试指南.png and /dev/null differ diff --git a/docs/dataStructures-algorithms/images/算法-4.jpeg b/docs/dataStructures-algorithms/images/算法-4.jpeg new file mode 100644 index 00000000..57af669b Binary files /dev/null and b/docs/dataStructures-algorithms/images/算法-4.jpeg differ diff --git a/docs/dataStructures-algorithms/images/算法-4.png b/docs/dataStructures-algorithms/images/算法-4.png deleted file mode 100644 index bbfd3b5c..00000000 Binary files a/docs/dataStructures-algorithms/images/算法-4.png and /dev/null differ diff --git a/docs/dataStructures-algorithms/images/算法图解.jpeg b/docs/dataStructures-algorithms/images/算法图解.jpeg new file mode 100644 index 00000000..f59c5ccf Binary files /dev/null and b/docs/dataStructures-algorithms/images/算法图解.jpeg differ diff --git a/docs/dataStructures-algorithms/images/算法图解.png b/docs/dataStructures-algorithms/images/算法图解.png deleted file mode 100644 index ce1edb0a..00000000 Binary files a/docs/dataStructures-algorithms/images/算法图解.png and /dev/null differ diff --git a/docs/dataStructures-algorithms/images/算法导论.jpeg b/docs/dataStructures-algorithms/images/算法导论.jpeg new file mode 100644 index 00000000..f7b282be Binary files /dev/null and b/docs/dataStructures-algorithms/images/算法导论.jpeg differ diff --git a/docs/dataStructures-algorithms/images/算法导论.png b/docs/dataStructures-algorithms/images/算法导论.png deleted file mode 100644 index fc1f52b5..00000000 Binary files a/docs/dataStructures-algorithms/images/算法导论.png and /dev/null differ diff --git a/docs/dataStructures-algorithms/images/算法设计手册.png b/docs/dataStructures-algorithms/images/算法设计手册.png index 1fed6659..ecf28334 100644 Binary files a/docs/dataStructures-algorithms/images/算法设计手册.png and b/docs/dataStructures-algorithms/images/算法设计手册.png differ diff --git a/docs/dataStructures-algorithms/images/编程之美.jpeg b/docs/dataStructures-algorithms/images/编程之美.jpeg new file mode 100644 index 00000000..fad8cc7c Binary files /dev/null and b/docs/dataStructures-algorithms/images/编程之美.jpeg differ diff --git a/docs/dataStructures-algorithms/images/编程之美.png b/docs/dataStructures-algorithms/images/编程之美.png deleted file mode 100644 index d137b618..00000000 Binary files a/docs/dataStructures-algorithms/images/编程之美.png and /dev/null differ diff --git a/docs/dataStructures-algorithms/images/编程珠玑.jpeg b/docs/dataStructures-algorithms/images/编程珠玑.jpeg new file mode 100644 index 00000000..79dddcc9 Binary files /dev/null and b/docs/dataStructures-algorithms/images/编程珠玑.jpeg differ diff --git a/docs/dataStructures-algorithms/images/编程珠玑.png b/docs/dataStructures-algorithms/images/编程珠玑.png deleted file mode 100644 index 7695d88e..00000000 Binary files a/docs/dataStructures-algorithms/images/编程珠玑.png and /dev/null differ diff --git a/docs/dataStructures-algorithms/公司真题.md b/docs/dataStructures-algorithms/公司真题.md deleted file mode 100644 index c78ed8f3..00000000 --- a/docs/dataStructures-algorithms/公司真题.md +++ /dev/null @@ -1,254 +0,0 @@ -# 网易 2018 - -下面三道编程题来自网易2018校招编程题,这三道应该来说是非常简单的编程题了,这些题目大家稍微有点编程和数学基础的话应该没什么问题。看答案之前一定要自己先想一下如果是自己做的话会怎么去做,然后再对照这我的答案看看,和你自己想的有什么区别?那一种方法更好? - -## 问题 - -### 一 获得特定数量硬币问题 - -小易准备去魔法王国采购魔法神器,购买魔法神器需要使用魔法币,但是小易现在一枚魔法币都没有,但是小易有两台魔法机器可以通过投入x(x可以为0)个魔法币产生更多的魔法币。 - -魔法机器1:如果投入x个魔法币,魔法机器会将其变为2x+1个魔法币 - -魔法机器2:如果投入x个魔法币,魔法机器会将其变为2x+2个魔法币 - -小易采购魔法神器总共需要n个魔法币,所以小易只能通过两台魔法机器产生恰好n个魔法币,小易需要你帮他设计一个投入方案使他最后恰好拥有n个魔法币。 - -**输入描述:** 输入包括一行,包括一个正整数n(1 ≤ n ≤ 10^9),表示小易需要的魔法币数量。 - -**输出描述:** 输出一个字符串,每个字符表示该次小易选取投入的魔法机器。其中只包含字符'1'和'2'。 - -**输入例子1:** 10 - -**输出例子1:** 122 - -### 二 求“相反数”问题 - -为了得到一个数的"相反数",我们将这个数的数字顺序颠倒,然后再加上原先的数得到"相反数"。例如,为了得到1325的"相反数",首先我们将该数的数字顺序颠倒,我们得到5231,之后再加上原先的数,我们得到5231+1325=6556.如果颠倒之后的数字有前缀零,前缀零将会被忽略。例如n = 100, 颠倒之后是1. - -**输入描述:** 输入包括一个整数n,(1 ≤ n ≤ 10^5) - -**输出描述:** 输出一个整数,表示n的相反数 - -**输入例子1:** 1325 - -**输出例子1:** 6556 - -### 三 字符串碎片的平均长度 - -一个由小写字母组成的字符串可以看成一些同一字母的最大碎片组成的。例如,"aaabbaaac"是由下面碎片组成的:'aaa','bb','c'。牛牛现在给定一个字符串,请你帮助计算这个字符串的所有碎片的平均长度是多少。 - -**输入描述:** 输入包括一个字符串s,字符串s的长度length(1 ≤ length ≤ 50),s只含小写字母('a'-'z') - -**输出描述:** 输出一个整数,表示所有碎片的平均长度,四舍五入保留两位小数。 - -**如样例所示:** s = "aaabbaaac" -所有碎片的平均长度 = (3 + 2 + 3 + 1) / 4 = 2.25 - -**输入例子1:** aaabbaaac - -**输出例子1:** 2.25 - -## 答案 - -### 一 获得特定数量硬币问题 - -#### 分析: - -作为该试卷的第一题,这道题应该只要思路正确就很简单了。 - -解题关键:明确魔法机器1只能产生奇数,魔法机器2只能产生偶数即可。我们从后往前一步一步推回去即可。 - -#### 示例代码 - -注意:由于用户的输入不确定性,一般是为了程序高可用性使需要将捕获用户输入异常然后友好提示用户输入类型错误并重新输入的。所以下面我给了两个版本,这两个版本都是正确的。这里只是给大家演示如何捕获输入类型异常,后面的题目中我给的代码没有异常处理的部分,参照下面两个示例代码,应该很容易添加。(PS:企业面试中没有明确就不用添加异常处理,当然你有的话也更好) - -**不带输入异常处理判断的版本:** - -```java -import java.util.Scanner; - -public class Main2 { - // 解题关键:明确魔法机器1只能产生奇数,魔法机器2只能产生偶数即可。我们从后往前一步一步推回去即可。 - - public static void main(String[] args) { - System.out.println("请输入要获得的硬币数量:"); - Scanner scanner = new Scanner(System.in); - int coincount = scanner.nextInt(); - StringBuilder sb = new StringBuilder(); - while (coincount >= 1) { - // 偶数的情况 - if (coincount % 2 == 0) { - coincount = (coincount - 2) / 2; - sb.append("2"); - // 奇数的情况 - } else { - coincount = (coincount - 1) / 2; - sb.append("1"); - } - } - // 输出反转后的字符串 - System.out.println(sb.reverse()); - - } -} -``` - -**带输入异常处理判断的版本(当输入的不是整数的时候会提示重新输入):** - -```java -import java.util.InputMismatchException; -import java.util.Scanner; - - -public class Main { - // 解题关键:明确魔法机器1只能产生奇数,魔法机器2只能产生偶数即可。我们从后往前一步一步推回去即可。 - - public static void main(String[] args) { - System.out.println("请输入要获得的硬币数量:"); - Scanner scanner = new Scanner(System.in); - boolean flag = true; - while (flag) { - try { - int coincount = scanner.nextInt(); - StringBuilder sb = new StringBuilder(); - while (coincount >= 1) { - // 偶数的情况 - if (coincount % 2 == 0) { - coincount = (coincount - 2) / 2; - sb.append("2"); - // 奇数的情况 - } else { - coincount = (coincount - 1) / 2; - sb.append("1"); - } - } - // 输出反转后的字符串 - System.out.println(sb.reverse()); - flag=false;//程序结束 - } catch (InputMismatchException e) { - System.out.println("输入数据类型不匹配,请您重新输入:"); - scanner.nextLine(); - continue; - } - } - - } -} - -``` - -### 二 求“相反数”问题 - -#### 分析: - -解决本道题有几种不同的方法,但是最快速的方法就是利用reverse()方法反转字符串然后再将字符串转换成int类型的整数,这个方法是快速解决本题关键。我们先来回顾一下下面两个知识点: - -**1)String转int;** - -在 Java 中要将 String 类型转化为 int 类型时,需要使用 Integer 类中的 parseInt() 方法或者 valueOf() 方法进行转换. - -```java - String str = "123"; - int a = Integer.parseInt(str); -``` - - 或 - -```java - String str = "123"; - int a = Integer.valueOf(str).intValue(); -``` - -**2)next()和nextLine()的区别** - -在Java中输入字符串有两种方法,就是next()和nextLine().两者的区别就是:nextLine()的输入是碰到回车就终止输入,而next()方法是碰到空格,回车,Tab键都会被视为终止符。所以next()不会得到带空格的字符串,而nextLine()可以得到带空格的字符串。 - -#### 示例代码: - -```java -import java.util.Scanner; - -/** - * 本题关键:①String转int;②next()和nextLine()的区别 - */ -public class Main { - - public static void main(String[] args) { - - System.out.println("请输入一个整数:"); - Scanner scanner = new Scanner(System.in); - String s=scanner.next(); - //将字符串转换成数字 - int number1=Integer.parseInt(s); - //将字符串倒序后转换成数字 - //因为Integer.parseInt()的参数类型必须是字符串所以必须加上toString() - int number2=Integer.parseInt(new StringBuilder(s).reverse().toString()); - System.out.println(number1+number2); - - } -} -``` - -### 三 字符串碎片的平均长度 - -#### 分析: - -这道题的意思也就是要求:(字符串的总长度)/(相同字母团构成的字符串的个数)。 - -这样就很简单了,就变成了字符串的字符之间的比较。如果需要比较字符串的字符的话,我们可以利用charAt(i)方法:取出特定位置的字符与后一个字符比较,或者利用toCharArray()方法将字符串转换成字符数组采用同样的方法做比较。 - -#### 示例代码 - -**利用charAt(i)方法:** - -```java -import java.util.Scanner; - -public class Main { - - public static void main(String[] args) { - - Scanner sc = new Scanner(System.in); - while (sc.hasNext()) { - String s = sc.next(); - //个数至少为一个 - float count = 1; - for (int i = 0; i < s.length() - 1; i++) { - if (s.charAt(i) != s.charAt(i + 1)) { - count++; - } - } - System.out.println(s.length() / count); - } - } - -} -``` - -**利用toCharArray()方法:** - -```java -import java.util.Scanner; - -public class Main2 { - - public static void main(String[] args) { - - Scanner sc = new Scanner(System.in); - while (sc.hasNext()) { - String s = sc.next(); - //个数至少为一个 - float count = 1; - char [] stringArr = s.toCharArray(); - for (int i = 0; i < stringArr.length - 1; i++) { - if (stringArr[i] != stringArr[i + 1]) { - count++; - } - } - System.out.println(s.length() / count); - } - } - -} -``` \ No newline at end of file diff --git a/docs/dataStructures-algorithms/数据结构.md b/docs/dataStructures-algorithms/数据结构.md index 3a117dd3..a4a1873f 100644 --- a/docs/dataStructures-algorithms/数据结构.md +++ b/docs/dataStructures-algorithms/数据结构.md @@ -129,7 +129,7 @@ Set 继承于 Collection 接口,是一个不允许出现重复元素,并且 ### 4 二叉查找树(BST) -[浅谈算法和数据结构: 七 二叉查找树](http://www.cnblogs.com/yangecnu/p/Introduce-Binary-Search-Tree.html) +[浅谈算法和数据结构: 七 二叉查找树](https://www.yycoding.xyz/post/2014/3/24/introduce-binary-search-tree) 二叉查找树的特点: diff --git a/docs/dataStructures-algorithms/算法学习资源推荐.md b/docs/dataStructures-algorithms/算法学习资源推荐.md index 5af08c9d..699ddf51 100644 --- a/docs/dataStructures-algorithms/算法学习资源推荐.md +++ b/docs/dataStructures-algorithms/算法学习资源推荐.md @@ -12,13 +12,13 @@ ### 入门 - +![](images/我的第一本算法书.jpeg) **[我的第一本算法书](https://book.douban.com/subject/30357170/) (豆瓣评分 7.1,0.2K+人评价)** 一本不那么“专业”的算法书籍。和下面两本推荐的算法书籍都是比较通俗易懂,“不那么深入”的算法书籍。我个人非常推荐,配图和讲解都非常不错! -img +![](images/算法图解.jpeg) **[《算法图解》](https://book.douban.com/subject/26979890/)(豆瓣评分 8.4,1.5K+人评价)** @@ -32,7 +32,7 @@ ### 经典 - +![](images/算法-4.jpeg) **[《算法 第四版》](https://book.douban.com/subject/10432347/)(豆瓣评分 9.3,0.4K+人评价)** @@ -42,7 +42,7 @@ > **下面这些书籍都是经典中的经典,但是阅读起来难度也比较大,不做太多阐述,神书就完事了!推荐先看 《算法》,然后再选下面的书籍进行进一步阅读。不需要都看,找一本好好看或者找某本书的某一个章节知识点好好看。** - +![](images/编程珠玑.jpeg) **[编程珠玑](https://book.douban.com/subject/3227098/)(豆瓣评分 9.1,2K+人评价)** @@ -50,15 +50,13 @@ 很多人都说这本书不是教你具体的算法,而是教你一种编程的思考方式。这种思考方式不仅仅在编程领域适用,在其他同样适用。 - - - +![](images/算法设计手册.png) **[《算法设计手册》](https://book.douban.com/subject/4048566/)(豆瓣评分9.1 , 45人评价)** 被 [Teach Yourself Computer Science](https://teachyourselfcs.com/) 强烈推荐的一本算法书籍。 - +![](images/算法导论.jpeg) **[《算法导论》](https://book.douban.com/subject/20432061/) (豆瓣评分 9.2,0.4K+人评价)** @@ -80,15 +78,13 @@ - +![](images/程序员代码面试指南.jpeg) **[程序员代码面试指南:IT名企算法与数据结构题目最优解(第2版)](https://book.douban.com/subject/30422021/) (豆瓣评分 8.7,0.2K+人评价)** 题目相比于《剑指 offer》 来说要难很多,题目涵盖面相比于《剑指 offer》也更加全面。全书一共有将近300道真实出现过的经典代码面试题。 - - - +![](images/编程之美.jpeg) diff --git a/docs/essential-content-for-interview/面试必备之乐观锁与悲观锁.md b/docs/essential-content-for-interview/面试必备之乐观锁与悲观锁.md index 2b7ef230..0ed56e12 100644 --- a/docs/essential-content-for-interview/面试必备之乐观锁与悲观锁.md +++ b/docs/essential-content-for-interview/面试必备之乐观锁与悲观锁.md @@ -83,14 +83,14 @@ JDK 1.5 以后的 `AtomicStampedReference 类`就提供了此种能力,其中 CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时 CAS 无效。但是从 JDK 1.5开始,提供了`AtomicReference类`来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行 CAS 操作.所以我们可以使用锁或者利用`AtomicReference类`把多个共享变量合并成一个共享变量来操作。 -### CAS与synchronized的使用情景 +### CAS与`synchronized`的使用情景 > **简单的来说CAS适用于写比较少的情况下(多读场景,冲突一般较少),synchronized适用于写比较多的情况下(多写场景,冲突一般较多)** -1. 对于资源竞争较少(线程冲突较轻)的情况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源;而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能。 +1. 对于资源竞争较少(线程冲突较轻)的情况,使用`synchronized`同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源;而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能。 2. 对于资源竞争严重(线程冲突严重)的情况,CAS自旋的概率会比较大,从而浪费更多的CPU资源,效率低于synchronized。 -补充: Java并发编程这个领域中synchronized关键字一直都是元老级的角色,很久之前很多人都会称它为 **“重量级锁”** 。但是,在JavaSE 1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的 **偏向锁** 和 **轻量级锁** 以及其它**各种优化**之后变得在某些情况下并不是那么重了。synchronized的底层实现主要依靠 **Lock-Free** 的队列,基本思路是 **自旋后阻塞**,**竞争切换后继续竞争锁**,**稍微牺牲了公平性,但获得了高吞吐量**。在线程冲突较少的情况下,可以获得和CAS类似的性能;而线程冲突严重的情况下,性能远高于CAS。 +补充: Java并发编程这个领域中`synchronized`关键字一直都是元老级的角色,很久之前很多人都会称它为 **“重量级锁”** 。但是,在JavaSE 1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的 **偏向锁** 和 **轻量级锁** 以及其它**各种优化**之后变得在某些情况下并不是那么重了。`synchronized`的底层实现主要依靠 **Lock-Free** 的队列,基本思路是 **自旋后阻塞**,**竞争切换后继续竞争锁**,**稍微牺牲了公平性,但获得了高吞吐量**。在线程冲突较少的情况下,可以获得和CAS类似的性能;而线程冲突严重的情况下,性能远高于CAS。 ## 公众号 diff --git a/docs/github-trending/2018-12.md b/docs/github-trending/2018-12.md deleted file mode 100644 index 3637e93e..00000000 --- a/docs/github-trending/2018-12.md +++ /dev/null @@ -1,78 +0,0 @@ -本文数据统计于 1.1 号凌晨,由 SnailClimb 整理。 - -### 1. JavaGuide - -- **Github地址**: [https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide) -- **star**: 18.2k -- **介绍**: 【Java学习+面试指南】 一份涵盖大部分Java程序员所需要掌握的核心知识。 - -### 2. mall - -- **Github地址**: [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall) -- **star**: 3.3k -- **介绍**: mall项目是一套电商系统,包括前台商城系统及后台管理系统,基于SpringBoot+MyBatis实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。 - -### 3. advanced-java - -- **Github地址**:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java) -- **star**: 3.3k -- **介绍**: 互联网 Java 工程师进阶知识完全扫盲 - -### 4. matrix - -- **Github地址**:[https://github.com/Tencent/matrix](https://github.com/Tencent/matrix) -- **star**: 2.5k -- **介绍**: Matrix 是一款微信研发并日常使用的 APM(Application Performance Manage),当前主要运行在 Android 平台上。 Matrix 的目标是建立统一的应用性能接入框架,通过各种性能监控方案,对性能监控项的异常数据进行采集和分析,输出相应的问题分析、定位与优化建议,从而帮助开发者开发出更高质量的应用。 - -### 5. miaosha - -- **Github地址**:[https://github.com/qiurunze123/miaosha](https://github.com/qiurunze123/miaosha) -- **star**: 2.4k -- **介绍**: 高并发大流量如何进行秒杀架构,我对这部分知识做了一个系统的整理,写了一套系统。 - -### 6. arthas - -- **Github地址**:[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas) -- **star**: 8.2k -- **介绍**: Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱。 - -### 7 spring-boot - -- **Github地址**: [https://github.com/spring-projects/spring-boot](https://github.com/spring-projects/spring-boot) -- **star:** 32.6k -- **介绍**: 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的(需要大量XML配置),不过Spring Boot 让这一切成为了过去。 另外Spring Cloud也是基于Spring Boot构建的,我个人非常有必要学习一下。 - - **关于Spring Boot官方的介绍:** - - > Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”…Most Spring Boot applications need very little Spring configuration.(Spring Boot可以轻松创建独立的生产级基于Spring的应用程序,只要通过 “just run”(可能是run ‘Application’或java -jar 或 tomcat 或 maven插件run 或 shell脚本)便可以运行项目。大部分Spring Boot项目只需要少量的配置即可) - -### 8. tutorials - -- **Github地址**:[https://github.com/eugenp/tutorials](https://github.com/eugenp/tutorials) -- **star**: 10k -- **介绍**: 该项目是一系列小而专注的教程 - 每个教程都涵盖Java生态系统中单一且定义明确的开发领域。 当然,它们的重点是Spring Framework - Spring,Spring Boot和Spring Securiyt。 除了Spring之外,还有以下技术:核心Java,Jackson,HttpClient,Guava。 - -### 9. qmq - -- **Github地址**:[https://github.com/qunarcorp/qmq](https://github.com/qunarcorp/qmq) -- **star**: 1.1k -- **介绍**: QMQ是去哪儿网内部广泛使用的消息中间件,自2012年诞生以来在去哪儿网所有业务场景中广泛的应用,包括跟交易息息相关的订单场景; 也包括报价搜索等高吞吐量场景。 - - -### 10. symphony - -- **Github地址**:[https://github.com/b3log/symphony](https://github.com/b3log/symphony) -- **star**: 9k -- **介绍**: 一款用 Java 实现的现代化社区(论坛/BBS/社交网络/博客)平台。 - -### 11. incubator-dubbo - -- **Github地址**:[https://github.com/apache/incubator-dubbo](https://github.com/apache/incubator-dubbo) -- **star**: 23.6k -- **介绍**: 阿里开源的一个基于Java的高性能开源RPC框架。 - -### 12. apollo - -- **Github地址**:[https://github.com/ctripcorp/apollo](https://github.com/ctripcorp/apollo) -- **star**: 10k -- **介绍**: Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。 diff --git a/docs/github-trending/2019-1.md b/docs/github-trending/2019-1.md deleted file mode 100644 index aa1de92f..00000000 --- a/docs/github-trending/2019-1.md +++ /dev/null @@ -1,76 +0,0 @@ -### 1. JavaGuide - -- **Github地址**: [https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide) -- **star**: 22.8k -- **介绍**: 【Java学习+面试指南】 一份涵盖大部分Java程序员所需要掌握的核心知识。 - -### 2. advanced-java - -- **Github地址**:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java) -- **star**: 7.9k -- **介绍**: 互联网 Java 工程师进阶知识完全扫盲 - -### 3. fescar - -- **Github地址**:[https://github.com/alibaba/fescar](https://github.com/alibaba/fescar) -- **star**: 4.6k -- **介绍**: 具有 **高性能** 和 **易用性** 的 **微服务架构** 的 **分布式事务** 的解决方案。(特点:高性能且易于使用,旨在实现简单并快速的事务提交与回滚。 - -### 4. mall - -- **Github地址**: [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall) -- **star**: 5.6 k -- **介绍**: mall项目是一套电商系统,包括前台商城系统及后台管理系统,基于SpringBoot+MyBatis实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。 - -### 5. miaosha - -- **Github地址**:[https://github.com/qiurunze123/miaosha](https://github.com/qiurunze123/miaosha) -- **star**: 4.4k -- **介绍**: 高并发大流量如何进行秒杀架构,我对这部分知识做了一个系统的整理,写了一套系统。 - -### 6. flink - -- **Github地址**:[https://github.com/apache/flink](https://github.com/apache/flink) -- **star**: 7.1 k -- **介绍**: Apache Flink是一个开源流处理框架,具有强大的流和批处理功能。 - -### 7. cim - -- **Github地址**:[https://github.com/crossoverJie/cim](https://github.com/crossoverJie/cim) -- **star**: 1.8 k -- **介绍**: cim(cross IM) 适用于开发者的即时通讯系统。 - -### 8. symphony - -- **Github地址**:[https://github.com/b3log/symphony](https://github.com/b3log/symphony) -- **star**: 10k -- **介绍**: 一款用 Java 实现的现代化社区(论坛/BBS/社交网络/博客)平台。 - -### 9. spring-boot - -- **Github地址**: [https://github.com/spring-projects/spring-boot](https://github.com/spring-projects/spring-boot) -- **star:** 32.6k -- **介绍**: 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的(需要大量XML配置),不过Spring Boot 让这一切成为了过去。 另外Spring Cloud也是基于Spring Boot构建的,我个人非常有必要学习一下。 - - **关于Spring Boot官方的介绍:** - - > Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”…Most Spring Boot applications need very little Spring configuration.(Spring Boot可以轻松创建独立的生产级基于Spring的应用程序,只要通过 “just run”(可能是run ‘Application’或java -jar 或 tomcat 或 maven插件run 或 shell脚本)便可以运行项目。大部分Spring Boot项目只需要少量的配置即可) - -### 10. arthas - -- **Github地址**:[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas) -- **star**: 9.5k -- **介绍**: Arthas 是Alibaba开源的Java诊断工具。 - -**概览:** - -当你遇到以下类似问题而束手无策时,`Arthas`可以帮助你解决: - -0. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception? -1. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了? -2. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗? -3. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现! -4. 是否有一个全局视角来查看系统的运行状况? -5. 有什么办法可以监控到JVM的实时运行状态? - -`Arthas`支持JDK 6+,支持Linux/Mac/Winodws,采用命令行交互模式,同时提供丰富的 `Tab` 自动补全功能,进一步方便进行问题的定位和诊断。 diff --git a/docs/github-trending/2019-12.md b/docs/github-trending/2019-12.md deleted file mode 100644 index 9a8f79ed..00000000 --- a/docs/github-trending/2019-12.md +++ /dev/null @@ -1,144 +0,0 @@ -# 年末将至,值得你关注的16个Java 开源项目! - -Star 的数量统计于 2019-12-29。 - -### 1.JavaGuide - -Guide 哥大三开始维护的,目前算是纯 Java 类型项目中 Star 数量最多的项目了。但是,本仓库的价值远远(+N次 )比不上像 Spring Boot、Elasticsearch 等等这样非常非常非常优秀的项目。希望以后我也有能力为这些项目贡献一些有价值的代码。 - -- **Github 地址**: -- **Star**: 66.3k -- **介绍**: 【Java 学习+面试指南】 一份涵盖大部分 Java 程序员所需要掌握的核心知识。 - -### 2.java-design-patterns - -感觉还不错。根据官网介绍: - -> 设计模式是程序员在设计应用程序或系统时可以用来解决常见问题的最佳形式化实践。 设计模式可以通过提供经过测试的,经过验证的开发范例来加快开发过程。 重用设计模式有助于防止引起重大问题的细微问题,并且还可以提高熟悉模式的编码人员和架构师的代码可读性。 - -![java-design-patterns-website](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/java-design-patterns-website.jpg) - -- **Github 地址** : [https://github.com/iluwatar/java-design-patterns](https://github.com/iluwatar/java-design-patterns) -- **Star**: 53.8k -- **介绍**: 用 Java 实现的设计模式。[https://java-design-patterns.com](https://java-design-patterns.com/)。 - -### 3.elasticsearch - -搜索引擎界的扛把子,但不仅仅是搜素引擎那么简单。 - -- **Github 地址** : [https://github.com/elastic/elasticsearch](https://github.com/elastic/elasticsearch) -- **Star**: 46.2k -- **介绍**: 开源,分布式,RESTful 搜索引擎。 - -### 4.spring-boot - -必须好好学啊,一定要好好学!现在 Java 后端新项目有不用 Spring Boot 开发的有吗?如果有的话,请把这个人的联系方式告诉我,我有很多话想给他交流交流! - -- **Github地址**: [https://github.com/spring-projects/spring-boot](https://github.com/spring-projects/spring-boot) -- **star:** 34.8k (1,073 stars this month) -- **介绍**: 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的(需要大量XML配置),不过Spring Boot 让这一切成为了过去。 另外Spring Cloud也是基于Spring Boot构建的,我个人非常有必要学习一下。 - -### 5.RxJava - -这个没怎么用过,不做太多评价。 - -- **Github 地址** : [https://github.com/ReactiveX/RxJava](https://github.com/ReactiveX/RxJava) -- **Star**: 41.5k -- **介绍**: `RxJava` 是一个 基于事件流、实现异步操作的库。 - -### 6.advanced-java - -本项目大部分内容来自中华石杉的一个课程,内容涵盖高并发、分布式、高可用、微服务、海量数据处理等领域知识,非常不错了! - -- **Github 地址**:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java) -- **Star**: 36.7k -- **介绍**: 互联网 Java 工程师进阶知识完全扫盲:涵盖高并发、分布式、高可用、微服务等领域知识,后端同学必看,前端同学也可学习。 - -### 7.mall - -很牛逼的实战项目,还附有详细的文档,作为毕设或者练手项目都再好不过了。 - -- **Github地址**: [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall) -- **star**: 27.6k -- **介绍**: mall项目是一套电商系统,包括前台商城系统及后台管理系统,基于SpringBoot+MyBatis实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。 - -### 8.okhttp - -给我感觉是安卓项目中用的居多。当然,Java 后端项目也会经常用,但是一般使用 Spring Boot 进行开发的时候,如果需要远程调用的话建议使用 Spring 封装的 `RestTemplate `。 - -- **Github地址**:[https://github.com/square/okhttp](https://github.com/square/okhttp) -- **star**: 35.4k -- **介绍**: 适用于Android,Kotlin和Java的HTTP客户端。https://square.github.io/okhttp/。 - -### 9.guava - -很厉害很厉害!提供了很多非常实用的工具类、更加实用的集合类、一些常用的数据结构比如布隆过滤器、缓存等等。 - -- **Github地址**:[https://github.com/google/guava](https://github.com/google/guava) -- **star**: 35.3k -- **介绍**: Guava是一组核心库,其中包括新的集合类型(例如 multimap 和 multiset),不可变集合,图形库以及用于并发,I / O,哈希,基元,字符串等的实用程序! - -### 10.Spark - -我木有用过,留下了没有技术的眼泪。 - -- **Github地址**:[https://github.com/apache/spark](https://github.com/apache/spark) -- **star**: 24.7k -- **介绍**: Spark 是一个快速、通用的大规模数据处理引擎,和Hadoop的MapReduce计算框架类似,但是相对于MapReduce,Spark凭借其可伸缩、基于内存计算等特点,以及可以直接读写Hadoop上任何格式数据的优势,进行批处理时更加高效,并有更低的延迟。 - -### 11.arthas - -虽然我自己没有亲身用过,但是身边用过的朋友评价都还挺好的。根据官网介绍,这家伙可以解决下面这些让人脑壳疼的问题: - -1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception? -2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了? -3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗? -4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现! -5. 是否有一个全局视角来查看系统的运行状况? -6. 有什么办法可以监控到JVM的实时运行状态? -7. 怎么快速定位应用的热点,生成火焰图? - -- **Github 地址**:[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas) -- **star**: 18.8 k -- **介绍**: Arthas 是 Alibaba 开源的 Java 诊断工具。 - -### 12.spring-boot-examples - -学习 Spring Boot 必备!配合上我的 **springboot-guide** :[https://github.com/Snailclimb/springboot-guide](https://github.com/Snailclimb/springboot-guide),效果杠杠滴! - -- **Github 地址**:[https://github.com/ityouknow/spring-boot-examples](https://github.com/ityouknow/spring-boot-examples) -- **star**: 20.2 k -- **介绍**: Spring Boot 教程、技术栈示例代码,快速简单上手教程。 - -### 13.lombok - -使用 Lombok 我们可以简化我们的 Java 代码,比如使用它之后我们通过注释就可以实现 getter/setter、equals等方法。 - -- **Github 地址**:[https://github.com/rzwitserloot/lombok](https://github.com/rzwitserloot/lombok) -- **star**: 20.2 k -- **介绍**: 对 Java 编程语言的非常刺激的补充。[https://projectlombok.org/](https://projectlombok.org/) 。 - -### 14.p3c - -与我而言,没有特别惊艳,但是一些提供的一些代码规范确实挺有用的! - -- **Github 地址**:[https://github.com/alibaba/p3c](https://github.com/alibaba/p3c) -- **star**: 19.8 k -- **介绍**: 阿里巴巴Java编码指南pmd实现和IDE插件。 - -### 15.spring-boot-demo - -- **Github 地址**:[https://github.com/xkcoding/spring-boot-demo](https://github.com/xkcoding/spring-boot-demo) -- **Star**: 8.8k -- **介绍**: spring boot demo 是一个用来深度学习并实战 spring boot 的项目。 - -### 16. awesome-java - -Guide 哥半个多月前开始维护的,虽然现在 Star 数量比较少,我相信后面一定会有更多人喜欢上这个项目,我也会继续认真维护下去。 - -- **Github 地址**:[https://github.com/Snailclimb/awesome-java](https://github.com/Snailclimb/awesome-java) -- **Star**: 0.3 k -- **介绍**: Github 上非常棒的 Java 开源项目集合。 - - - diff --git a/docs/github-trending/2019-2.md b/docs/github-trending/2019-2.md deleted file mode 100644 index 51d34b32..00000000 --- a/docs/github-trending/2019-2.md +++ /dev/null @@ -1,64 +0,0 @@ -### 1. JavaGuide - -- **Github地址**: [https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide) -- **Star**: 27.2k (4,437 stars this month) -- **介绍**: 【Java学习+面试指南】 一份涵盖大部分Java程序员所需要掌握的核心知识。 - -### 2.DoraemonKit - -- **Github地址**: -- **Star**: 5.2k (3,786 stars this month) -- **介绍**: 简称 "DoKit" 。一款功能齐全的客户端( iOS 、Android )研发助手,你值得拥有。 - -### 3.advanced-java - -- **Github地址**:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java) -- **Star**:11.2k (3,042 stars this month) -- **介绍**: 互联网 Java 工程师进阶知识完全扫盲。 - -### 4. spring-boot-examples - -- **Github地址**: -- **star**: 9.6 k (1,764 stars this month) -- **介绍**: Spring Boot 教程、技术栈示例代码,快速简单上手教程。 - -### 5. mall - -- **Github地址**: [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall) -- **star**: 7.4 k (1,736 stars this month) -- **介绍**: mall项目是一套电商系统,包括前台商城系统及后台管理系统,基于SpringBoot+MyBatis实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。 - -### 6. fescar - -- **Github地址**:[https://github.com/alibaba/fescar](https://github.com/alibaba/fescar) -- **star**: 6.0 k (1,308 stars this month) -- **介绍**: 具有 **高性能** 和 **易用性** 的 **微服务架构** 的 **分布式事务** 的解决方案。(特点:高性能且易于使用,旨在实现简单并快速的事务提交与回滚。) - -### 7. h4cker - -- **Github地址**: -- **star**: 2.1 k (1,303 stars this month) -- **介绍**: 该仓库主要由Omar Santos维护,包括与道德黑客/渗透测试,数字取证和事件响应(DFIR),漏洞研究,漏洞利用开发,逆向工程等相关的资源。 - -### 8. spring-boot - -- **Github地址**: [https://github.com/spring-projects/spring-boot](https://github.com/spring-projects/spring-boot) -- **star:** 34.8k (1,073 stars this month) -- **介绍**: 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的(需要大量XML配置),不过Spring Boot 让这一切成为了过去。 另外Spring Cloud也是基于Spring Boot构建的,我个人非常有必要学习一下。 - - **关于Spring Boot官方的介绍:** - - > Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”…Most Spring Boot applications need very little Spring configuration.(Spring Boot可以轻松创建独立的生产级基于Spring的应用程序,只要通过 “just run”(可能是run ‘Application’或java -jar 或 tomcat 或 maven插件run 或 shell脚本)便可以运行项目。大部分Spring Boot项目只需要少量的配置即可) - -### 9. arthas - -- **Github地址**:[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas) -- **star**: 10.5 k (970 stars this month) -- **介绍**: Arthas 是Alibaba开源的Java诊断工具。 - -### 10. tutorials - -- **Github地址**:[https://github.com/eugenp/tutorials](https://github.com/eugenp/tutorials) -- **star**: 12.1 k (789 stars this month) -- **介绍**: 该项目是一系列小而专注的教程 - 每个教程都涵盖Java生态系统中单一且定义明确的开发领域。 当然,它们的重点是Spring Framework - Spring,Spring Boot和Spring Securiyt。 除了Spring之外,还有以下技术:核心Java,Jackson,HttpClient,Guava。 - diff --git a/docs/github-trending/2019-3.md b/docs/github-trending/2019-3.md deleted file mode 100644 index eaed4a5d..00000000 --- a/docs/github-trending/2019-3.md +++ /dev/null @@ -1,60 +0,0 @@ -### 1. JavaGuide - -- **Github 地址**: [https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide) -- **Star**: 32.9k (6,196 stars this month) -- **介绍**: 【Java 学习+面试指南】 一份涵盖大部分 Java 程序员所需要掌握的核心知识。 - -### 2.advanced-java - -- **Github 地址**:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java) -- **Star**: 15.1k (4,012 stars this month) -- **介绍**: 互联网 Java 工程师进阶知识完全扫盲。 - -### 3.spring-boot-examples - -- **Github 地址**:[https://github.com/ityouknow/spring-boot-examples](https://github.com/ityouknow/spring-boot-examples) -- **Star**: 12.8k (3,462 stars this month) -- **介绍**: Spring Boot 教程、技术栈示例代码,快速简单上手教程。 - -### 4. mall - -- **Github 地址**: [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall) -- **star**: 9.7 k (2,418 stars this month) -- **介绍**: mall 项目是一套电商系统,包括前台商城系统及后台管理系统,基于 SpringBoot+MyBatis 实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。 - -### 5. seata - -- **Github 地址** : [https://github.com/seata/seata](https://github.com/seata/seata) -- **star**: 7.2 k (1359 stars this month) -- **介绍**: Seata 是一种易于使用,高性能,基于 Java 的开源分布式事务解决方案。 - -### 6. quarkus - -- **Github 地址**:[https://github.com/quarkusio/quarkus](https://github.com/quarkusio/quarkus) -- **star**: 12 k (1,224 stars this month) -- **介绍**: Quarkus 是为 GraalVM 和 HotSpot 量身定制的 Kubernetes Native Java 框架,由最佳的 Java 库和标准精心打造而成。Quarkus 的目标是使 Java 成为 Kubernetes 和无服务器环境中的领先平台,同时为开发人员提供统一的反应式和命令式编程模型,以优化地满足更广泛的分布式应用程序架构。 - -### 7. arthas - -- **Github 地址**:[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas) -- **star**: 11.6 k (1,199 stars this month) -- **介绍**: Arthas 是 Alibaba 开源的 Java 诊断工具。 - -### 8.DoraemonKit - -- **Github 地址**: -- **Star**: 6.2k (1,177 stars this month) -- **介绍**: 简称 "DoKit" 。一款功能齐全的客户端( iOS 、Android )研发助手,你值得拥有。 - -### 9.elasticsearch - -- **Github 地址** [https://github.com/elastic/elasticsearch](https://github.com/elastic/elasticsearch) -- **Star**: 39.7k (1,069 stars this month) -- **介绍**: 开源,分布式,RESTful 搜索引擎。 - -### 10. tutorials - -- **Github 地址**:[https://github.com/eugenp/tutorials](https://github.com/eugenp/tutorials) -- **star**: 13 k (998 stars this month) -- **介绍**: 该项目是一系列小而专注的教程 - 每个教程都涵盖 Java 生态系统中单一且定义明确的开发领域。 当然,它们的重点是 Spring Framework - Spring,Spring Boot 和 Spring Securiyt。 除了 Spring 之外,还有以下技术:核心 Java,Jackson,HttpClient,Guava。 - diff --git a/docs/github-trending/2019-4.md b/docs/github-trending/2019-4.md deleted file mode 100644 index 713a76da..00000000 --- a/docs/github-trending/2019-4.md +++ /dev/null @@ -1,98 +0,0 @@ -以下涉及到的数据统计与 2019 年 5 月 1 日 12 点,数据来源: 。 - -下面的内容从 Java 学习文档到最热门的框架再到热门的工具应有尽有,比如下面推荐到的开源项目 Hutool 就是近期比较热门的项目之一,它是 Java 工具包,能够帮助我们简化代码!我觉得下面这些项目对于学习 Java 的朋友还是很有帮助的! - - -### 1. JavaGuide - -- **Github 地址**: [https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide) -- **Star**: 37.9k (5,660 stars this month) -- **介绍**: 【Java 学习+面试指南】 一份涵盖大部分 Java 程序员所需要掌握的核心知识。 - -### 2. advanced-java - -- **Github 地址**:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java) -- **Star**: 15.1k (4,654 stars this month) -- **介绍**: 互联网 Java 工程师进阶知识完全扫盲。 - -### 3. CS-Notes - -- **Github 地址**: -- **Star**: 59.2k (4,012 stars this month) -- **介绍**: 技术面试必备基础知识。 - -### 4. ghidra - -- **Github 地址**: -- **Star**: 15.0k (2,995 stars this month) -- **介绍**: Ghidra是一个软件逆向工程(SRE)框架。 - -### 5. mall - -- **Github 地址**: [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall) -- **star**: 11.6 k (2,100 stars this month) -- **介绍**: mall 项目是一套电商系统,包括前台商城系统及后台管理系统,基于 SpringBoot+MyBatis 实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。 - -### 6. ZXBlog - -- **Github 地址**: -- **star**: 2.1 k (2,086 stars this month) -- **介绍**: 记录各种学习笔记(算法、Java、数据库、并发......)。 - -### 7.DoraemonKit - -- **Github地址**: -- **Star**: 7.6k (1,541 stars this month) -- **介绍**: 简称 "DoKit" 。一款功能齐全的客户端( iOS 、Android )研发助手,你值得拥有。 - -### 8. spring-boot - -- **Github地址**: [https://github.com/spring-projects/spring-boot](https://github.com/spring-projects/spring-boot) -- **star:** 37.3k (1,489 stars this month) -- **介绍**: 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的(需要大量XML配置),不过Spring Boot 让这一切成为了过去。 另外Spring Cloud也是基于Spring Boot构建的,我个人非常有必要学习一下。 - -**Spring Boot官方的介绍:** - -> Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”…Most Spring Boot applications need very little Spring configuration.(Spring Boot可以轻松创建独立的生产级基于Spring的应用程序,只要通过 “just run”(可能是run ‘Application’或java -jar 或 tomcat 或 maven插件run 或 shell脚本)便可以运行项目。大部分Spring Boot项目只需要少量的配置即可) - -### 9. spring-boot-examples - -- **Github 地址**:[https://github.com/ityouknow/spring-boot-examples](https://github.com/ityouknow/spring-boot-examples) -- **Star**: 12.8k (1,453 stars this month) -- **介绍**: Spring Boot 教程、技术栈示例代码,快速简单上手教程。 - -### 10. seata - -- **Github 地址** : [https://github.com/seata/seata](https://github.com/seata/seata) -- **star**: 8.4 k (1441 stars this month) -- **介绍**: Seata 是一种易于使用,高性能,基于 Java 的开源分布式事务解决方案。 - -### 11. litemall - -- **Github 地址**:[https://github.com/ityouknow/spring-boot-examples](https://github.com/ityouknow/spring-boot-examples) -- **Star**: 6.0k (1,427 stars this month) -- **介绍**: 又一个小商城。litemall = Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端 + Vue用户移动端。 - -### 12. skywalking - -- **Github 地址**: -- **Star**: 8.0k (1,381 stars this month) -- **介绍**: 针对分布式系统的应用性能监控,尤其是针对微服务、云原生和面向容器的分布式系统架构。 - -### 13. elasticsearch - -- **Github 地址** [https://github.com/elastic/elasticsearch](https://github.com/elastic/elasticsearch) -- **Star**: 4.0k (1,068stars this month) -- **介绍**: 开源,分布式,RESTful 搜索引擎。 - -### 14. arthas - -- **Github地址**:[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas) -- **star**: 12.6 k (1,080 stars this month) -- **介绍**: Arthas 是Alibaba开源的Java诊断工具。 - -### 15. hutool - -- **Github地址**: -- **star**: 4.5 k (1,031 stars this month) -- **介绍**: Hutool是一个Java工具包,也只是一个工具包,它帮助我们简化每一行代码,减少每一个方法,让Java语言也可以“甜甜的”。Hutool最初是我项目中“util”包的一个整理,后来慢慢积累并加入更多非业务相关功能,并广泛学习其它开源项目精髓,经过自己整理修改,最终形成丰富的开源工具集。官网: 。 \ No newline at end of file diff --git a/docs/github-trending/2019-5.md b/docs/github-trending/2019-5.md deleted file mode 100644 index df327b79..00000000 --- a/docs/github-trending/2019-5.md +++ /dev/null @@ -1,125 +0,0 @@ -以下涉及到的数据统计与 2019 年 6 月 1 日 18 点,数据来源: 。下面推荐的内容从 Java 学习文档到最热门的框架再到热门的工具应有尽有,建议收藏+在看! - -### 1.LeetCodeAnimation - -- **Github 地址**: -- **Star**: 29.0k (11,492 stars this month) -- **介绍**: Demonstrate all the questions on LeetCode in the form of animation.(用动画的形式呈现解LeetCode题目的思路)。 - -### 2.CS-Notes - -- **Github 地址**: -- **Star**: 64.4k (5513 stars this month) -- **介绍**: 技术面试必备基础知识、Leetcode 题解、后端面试、Java 面试、春招、秋招、操作系统、计算机网络、系统设计。 - -### 3.JavaGuide - -- **Github 地址**: -- **Star**: 42.0k (4,442 stars this month) -- **介绍**: 【Java 学习+面试指南】 一份涵盖大部分 Java 程序员所需要掌握的核心知识。 - -### 4.mall - -- **Github 地址**: [https://github.com/macrozheng/mall](https://github.com/macrozheng/mall) -- **star**: 14.6 k (3,086 stars this month) -- **介绍**: mall 项目是一套电商系统,包括前台商城系统及后台管理系统,基于 SpringBoot+MyBatis 实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。 - -### 5.advanced-java - -- **Github 地址**:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java) -- **Star**: 20.8k (2,394 stars this month) -- **介绍**: 互联网 Java 工程师进阶知识完全扫盲。 - -### 6.spring-boot - -- **Github地址**: [https://github.com/spring-projects/spring-boot](https://github.com/spring-projects/spring-boot) -- **star:** 38.5k (1,339 stars this month) -- **介绍**: 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的(需要大量XML配置),不过Spring Boot 让这一切成为了过去。 另外Spring Cloud也是基于Spring Boot构建的,我个人非常有必要学习一下。 - -**Spring Boot官方的介绍:** - -> Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”…Most Spring Boot applications need very little Spring configuration.(Spring Boot可以轻松创建独立的生产级基于Spring的应用程序,只要通过 “just run”(可能是run ‘Application’或java -jar 或 tomcat 或 maven插件run 或 shell脚本)便可以运行项目。大部分Spring Boot项目只需要少量的配置即可) - -### 7. Java - -- **Github 地址**: -- **Star**:14.3k (1,334 stars this month) -- **介绍**: All Algorithms implemented in Java。 - -### 8.server - -- **Github 地址**: -- **star**: 2.2 k (1,275 stars this month) -- **介绍**: 全开源即时通讯(IM)系统。 - -### 9.litemall - -- **Github 地址**: -- **Star**: 7.1k (1,114 stars this month) -- **介绍**: 又一个小商城。litemall = Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端 + Vue用户移动端。 - -### 10.Linkage-RecyclerView - -- **Github 地址**: -- **Star**: 10.0k (1,093 stars this month) -- **介绍**: 即使不用饿了么订餐,也请务必收藏好该库!🔥 一行代码即可接入,二级联动订餐列表 - Even if you don't order food by PrubHub, be sure to collect this library, please! 🔥 This secondary linkage list widget can be accessed by only one line of code. Supporting by RecyclerView & AndroidX. - -### 11.toBeTopJavaer - -- **Github 地址** : -- **Star**: 3.3k (1,007 stars this month) -- **介绍**: To Be Top Javaer - Java工程师成神之路 - -### 12.elasticsearch - -- **Github 地址** : [https://github.com/elastic/elasticsearch](https://github.com/elastic/elasticsearch) -- **Star**: 48.0k (968 stars this month) -- **介绍**: 开源,分布式,RESTful 搜索引擎。 - -### 13.java-design-patterns - -- **Github 地址** : -- **Star**: 41.5k (955 stars this month) -- **介绍**: Design patterns implemented in Java。 - -### 14.apollo - -- **Github 地址** : -- **Star**: 14.5k (927 stars this month) -- **介绍**: Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。 - -### 15.arthas - -- **Github地址**:[https://github.com/alibaba/arthas](https://github.com/alibaba/arthas) -- **star**: 13.5 k (933 stars this month) -- **介绍**: Arthas 是Alibaba开源的Java诊断工具。 - -### 16.dubbo - -- **Github地址**: -- **star**: 26.9 k (769 stars this month) -- **介绍**: Apache Dubbo是一个基于Java的高性能开源RPC框架。 - -### 17.DoraemonKit - -- **Github地址**: -- **Star**: 8.5k (909 stars this month) -- **介绍**: 简称 "DoKit" 。一款功能齐全的客户端( iOS 、Android )研发助手,你值得拥有。 - -### 18.halo - -- **Github地址**: -- **Star**: 4.1k (829 stars this month) -- **介绍**: Halo 可能是最好的 Java 博客系统。 - -### 19.seata - -- **Github 地址** : [https://github.com/seata/seata](https://github.com/seata/seata) -- **star**: 9.2 k (776 stars this month) -- **介绍**: Seata 是一种易于使用,高性能,基于 Java 的开源分布式事务解决方案。 - -### 20.hutool - -- **Github地址**: -- **star**: 5,3 k (812 stars this month) -- **介绍**: Hutool是一个Java工具包,也只是一个工具包,它帮助我们简化每一行代码,减少每一个方法,让Java语言也可以“甜甜的”。Hutool最初是我项目中“util”包的一个整理,后来慢慢积累并加入更多非业务相关功能,并广泛学习其它开源项目精髓,经过自己整理修改,最终形成丰富的开源工具集。官网: 。 \ No newline at end of file diff --git a/docs/github-trending/2019-6.md b/docs/github-trending/2019-6.md deleted file mode 100644 index 2a395e16..00000000 --- a/docs/github-trending/2019-6.md +++ /dev/null @@ -1,119 +0,0 @@ -### 1.CS-Notes - -- **Github 地址**:https://github.com/CyC2018/CS-Notes -- **Star**: 69.8k -- **介绍**: 技术面试必备基础知识、Leetcode 题解、后端面试、Java 面试、春招、秋招、操作系统、计算机网络、系统设计。 - -### 2.toBeTopJavaer - -- **Github 地址:**[https://github.com/hollischuang/toBeTopJavaer](https://github.com/hollischuang/toBeTopJavaer) -- **Star**: 4.7k -- **介绍**: To Be Top Javaer - Java工程师成神之路。 - -### 3.p3c - -- **Github 地址:** [https://github.com/alibaba/p3c](https://github.com/alibaba/p3c) -- **Star**: 16.6k -- **介绍**: Alibaba Java Coding Guidelines pmd implements and IDE plugin。Eclipse 和 IDEA 上都有该插件,推荐使用! - -### 4.SpringCloudLearning - -- **Github 地址:** [https://github.com/forezp/SpringCloudLearning](https://github.com/forezp/SpringCloudLearning) -- **Star**: 8.7k -- **介绍**: 史上最简单的Spring Cloud教程源码。 - -### 5.dubbo - -- **Github地址**: -- **star**: 27.6 k -- **介绍**: Apache Dubbo是一个基于Java的高性能开源RPC框架。 - -### 6.jeecg-boot - -- **Github地址**: [https://github.com/zhangdaiscott/jeecg-boot](https://github.com/zhangdaiscott/jeecg-boot) -- **star**: 3.3 k -- **介绍**: 一款基于代码生成器的JAVA快速开发平台!全新架构前后端分离:SpringBoot 2.x,Ant Design&Vue,Mybatis,Shiro,JWT。强大的代码生成器让前后端代码一键生成,无需写任何代码,绝对是全栈开发福音!! JeecgBoot的宗旨是提高UI能力的同时,降低前后分离的开发成本,JeecgBoot还独创在线开发模式,No代码概念,一系列在线智能开发:在线配置表单、在线配置报表、在线设计流程等等。 - -### 7.advanced-java - -- **Github 地址**:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java) -- **Star**: 24.2k -- **介绍**: 互联网 Java 工程师进阶知识完全扫盲:涵盖高并发、分布式、高可用、微服务等领域知识,后端同学必看,前端同学也可学习。 - -### 8.FEBS-Shiro - -- **Github 地址**:[https://github.com/wuyouzhuguli/FEBS-Shiro](https://github.com/wuyouzhuguli/FEBS-Shiro) -- **Star**: 2.6k -- **介绍**: Spring Boot 2.1.3,Shiro1.4.0 & Layui 2.5.4 权限管理系统。预览地址:http://49.234.20.223:8080/login。 - -### 9.SpringAll - -- **Github 地址**: [https://github.com/wuyouzhuguli/SpringAll](https://github.com/wuyouzhuguli/SpringAll) -- **Star**: 5.4k -- **介绍**: 循序渐进,学习Spring Boot、Spring Boot & Shiro、Spring Cloud、Spring Security & Spring Security OAuth2,博客Spring系列源码。 - -### 10.JavaGuide - -- **Github 地址**: -- **Star**: 47.2k -- **介绍**: 【Java 学习+面试指南】 一份涵盖大部分 Java 程序员所需要掌握的核心知识。 - -### 11.vhr - -- **Github 地址**:[https://github.com/lenve/vhr](https://github.com/lenve/vhr) -- **Star**: 4.9k -- **介绍**: 微人事是一个前后端分离的人力资源管理系统,项目采用SpringBoot+Vue开发。 - -### 12. tutorials - -- **Github 地址**:[https://github.com/eugenp/tutorials](https://github.com/eugenp/tutorials) -- **star**: 15.4 k -- **介绍**: 该项目是一系列小而专注的教程 - 每个教程都涵盖 Java 生态系统中单一且定义明确的开发领域。 当然,它们的重点是 Spring Framework - Spring,Spring Boot 和 Spring Securiyt。 除了 Spring 之外,还有以下技术:核心 Java,Jackson,HttpClient,Guava。 - -### 13.EasyScheduler - -- **Github 地址**:[https://github.com/analysys/EasyScheduler](https://github.com/analysys/EasyScheduler) -- **star**: 1.1 k -- **介绍**: Easy Scheduler是一个分布式工作流任务调度系统,主要解决“复杂任务依赖但无法直接监控任务健康状态”的问题。Easy Scheduler以DAG方式组装任务,可以实时监控任务的运行状态。同时,它支持重试,重新运行等操作... 。https://analysys.github.io/easyscheduler_docs_cn/ - -### 14.thingsboard - -- **Github 地址**:[https://github.com/thingsboard/thingsboard](https://github.com/thingsboard/thingsboard) -- **star**: 3.7 k -- **介绍**: 开源物联网平台 - 设备管理,数据收集,处理和可视化。 [https://thingsboard.io](https://thingsboard.io/) - -### 15.mall-learning - -- **Github 地址**: [https://github.com/macrozheng/mall-learning](https://github.com/macrozheng/mall-learning) -- **star**: 0.6 k -- **介绍**: mall学习教程,架构、业务、技术要点全方位解析。mall项目(16k+star)是一套电商系统,使用现阶段主流技术实现。 涵盖了SpringBoot2.1.3、MyBatis3.4.6、Elasticsearch6.2.2、RabbitMQ3.7.15、Redis3.2、Mongodb3.2、Mysql5.7等技术,采用Docker容器化部署。 https://github.com/macrozheng/mall - -### 16. flink - -- **Github地址**:[https://github.com/apache/flink](https://github.com/apache/flink) -- **star**: 9.3 k -- **介绍**: Apache Flink是一个开源流处理框架,具有强大的流和批处理功能。 - -### 17.spring-cloud-kubernetes - -- **Github地址**:[https://github.com/spring-cloud/spring-cloud-kubernetes](https://github.com/spring-cloud/spring-cloud-kubernetes) -- **star**: 1.4 k -- **介绍**: Kubernetes 集成 Spring Cloud Discovery Client, Configuration, etc... - -### 18.springboot-learning-example - -- **Github地址**:[https://github.com/JeffLi1993/springboot-learning-example](https://github.com/JeffLi1993/springboot-learning-example) -- **star**: 10.0 k -- **介绍**: spring boot 实践学习案例,是 spring boot 初学者及核心技术巩固的最佳实践。 - -### 19.canal - -- **Github地址**:[https://github.com/alibaba/canal](https://github.com/alibaba/canal) -- **star**: 9.3 k -- **介绍**: 阿里巴巴 MySQL binlog 增量订阅&消费组件。 - -### 20.react-native-device-info - -- **Github地址**:[https://github.com/react-native-community/react-native-device-info](https://github.com/react-native-community/react-native-device-info) -- **star**: 4.0 k -- **介绍**: React Native iOS和Android的设备信息。 \ No newline at end of file diff --git a/docs/github-trending/JavaGithubTrending.md b/docs/github-trending/JavaGithubTrending.md deleted file mode 100644 index 91d544ed..00000000 --- a/docs/github-trending/JavaGithubTrending.md +++ /dev/null @@ -1,8 +0,0 @@ -- [2018 年 12 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2018-12.md) -- [2019 年 1 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-1.md) -- [2019 年 2 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-2.md) -- [2019 年 3 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-3.md) -- [2019 年 4 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-4.md) -- [2019 年 5 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-5.md) -- [2019 年 6 月](https://github.com/Snailclimb/JavaGuide/blob/master/docs/github-trending/2019-6.md) - diff --git a/docs/java/Java IO与NIO.md b/docs/java/Java IO与NIO.md deleted file mode 100644 index 74bd850e..00000000 --- a/docs/java/Java IO与NIO.md +++ /dev/null @@ -1,200 +0,0 @@ - - -- [IO流学习总结](#io流学习总结) - - [一 Java IO,硬骨头也能变软](#一-java-io,硬骨头也能变软) - - [二 java IO体系的学习总结](#二-java-io体系的学习总结) - - [三 Java IO面试题](#三-java-io面试题) -- [NIO与AIO学习总结](#nio与aio学习总结) - - [一 Java NIO 概览](#一-java-nio-概览) - - [二 Java NIO 之 Buffer\(缓冲区\)](#二-java-nio-之-buffer缓冲区) - - [三 Java NIO 之 Channel(通道)](#三-java-nio-之-channel(通道)) - - [四 Java NIO之Selector(选择器)](#四-java-nio之selector(选择器)) - - [五 Java NIO之拥抱Path和Files](#五-java-nio之拥抱path和files) - - [六 NIO学习总结以及NIO新特性介绍](#六-nio学习总结以及nio新特性介绍) - - [七 Java NIO AsynchronousFileChannel异步文件通](#七-java-nio-asynchronousfilechannel异步文件通) - - [八 高并发Java(8):NIO和AIO](#八-高并发java(8):nio和aio) -- [推荐阅读](#推荐阅读) - - [在 Java 7 中体会 NIO.2 异步执行的快乐](#在-java-7-中体会-nio2-异步执行的快乐) - - [Java AIO总结与示例](#java-aio总结与示例) - - - - - -## IO流学习总结 - -### [一 Java IO,硬骨头也能变软](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247483981&idx=1&sn=6e5c682d76972c8d2cf271a85dcf09e2&chksm=fd98542ccaefdd3a70428e9549bc33e8165836855edaa748928d16c1ebde9648579d3acaac10#rd) - -**(1) 按操作方式分类结构图:** - -![IO-操作方式分类](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/IO-操作方式分类.png) - - -**(2)按操作对象分类结构图** - -![IO-操作对象分类](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/IO-操作对象分类.png) - -### [二 java IO体系的学习总结](https://blog.csdn.net/nightcurtis/article/details/51324105) -1. **IO流的分类:** - - 按照流的流向分,可以分为输入流和输出流; - - 按照操作单元划分,可以划分为字节流和字符流; - - 按照流的角色划分为节点流和处理流。 -2. **流的原理浅析:** - - java Io流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java Io流的40多个类都是从如下4个抽象类基类中派生出来的。 - - - **InputStream/Reader**: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。 - - **OutputStream/Writer**: 所有输出流的基类,前者是字节输出流,后者是字符输出流。 -3. **常用的io流的用法** - -### [三 Java IO面试题](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247483985&idx=1&sn=38531c2cee7b87f125df7aef41637014&chksm=fd985430caefdd26b0506aa84fc26251877eccba24fac73169a4d6bd1eb5e3fbdf3c3b940261#rd) - -## NIO与AIO学习总结 - - -### [一 Java NIO 概览](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247483956&idx=1&sn=57692bc5b7c2c6dfb812489baadc29c9&chksm=fd985455caefdd4331d828d8e89b22f19b304aa87d6da73c5d8c66fcef16e4c0b448b1a6f791#rd) - -1. **NIO简介**: - - Java NIO 是 java 1.4, 之后新出的一套IO接口NIO中的N可以理解为Non-blocking,不单纯是New。 - -2. **NIO的特性/NIO与IO区别:** - - 1)IO是面向流的,NIO是面向缓冲区的; - - 2)IO流是阻塞的,NIO流是不阻塞的; - - 3)NIO有选择器,而IO没有。 -3. **读数据和写数据方式:** - - 从通道进行数据读取 :创建一个缓冲区,然后请求通道读取数据。 - - - 从通道进行数据写入 :创建一个缓冲区,填充数据,并要求通道写入数据。 - -4. **NIO核心组件简单介绍** - - **Channels** - - **Buffers** - - **Selectors** - - -### [二 Java NIO 之 Buffer(缓冲区)](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247483961&idx=1&sn=f67bef4c279e78043ff649b6b03fdcbc&chksm=fd985458caefdd4e3317ccbdb2d0a5a70a5024d3255eebf38183919ed9c25ade536017c0a6ba#rd) - -1. **Buffer(缓冲区)介绍:** - - Java NIO Buffers用于和NIO Channel交互。 我们从Channel中读取数据到buffers里,从Buffer把数据写入到Channels; - - Buffer本质上就是一块内存区; - - 一个Buffer有三个属性是必须掌握的,分别是:capacity容量、position位置、limit限制。 -2. **Buffer的常见方法** - - Buffer clear() - - Buffer flip() - - Buffer rewind() - - Buffer position(int newPosition) -3. **Buffer的使用方式/方法介绍:** - - 分配缓冲区(Allocating a Buffer): - ```java - ByteBuffer buf = ByteBuffer.allocate(28);//以ByteBuffer为例子 - ``` - - 写入数据到缓冲区(Writing Data to a Buffer) - - **写数据到Buffer有两种方法:** - - 1.从Channel中写数据到Buffer - ```java - int bytesRead = inChannel.read(buf); //read into buffer. - ``` - 2.通过put写数据: - ```java - buf.put(127); - ``` - -4. **Buffer常用方法测试** - - 说实话,NIO编程真的难,通过后面这个测试例子,你可能才能勉强理解前面说的Buffer方法的作用。 - - -### [三 Java NIO 之 Channel(通道)](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247483966&idx=1&sn=d5cf18c69f5f9ec2aff149270422731f&chksm=fd98545fcaefdd49296e2c78000ce5da277435b90ba3c03b92b7cf54c6ccc71d61d13efbce63#rd) - - -1. **Channel(通道)介绍** - - 通常来说NIO中的所有IO都是从 Channel(通道) 开始的。 - - NIO Channel通道和流的区别: -2. **FileChannel的使用** -3. **SocketChannel和ServerSocketChannel的使用** -4. **️DatagramChannel的使用** -5. **Scatter / Gather** - - Scatter: 从一个Channel读取的信息分散到N个缓冲区中(Buufer). - - Gather: 将N个Buffer里面内容按照顺序发送到一个Channel. -6. **通道之间的数据传输** - - 在Java NIO中如果一个channel是FileChannel类型的,那么他可以直接把数据传输到另一个channel。 - - transferFrom() :transferFrom方法把数据从通道源传输到FileChannel - - transferTo() :transferTo方法把FileChannel数据传输到另一个channel - - -### [四 Java NIO之Selector(选择器)](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247483970&idx=1&sn=d5e2b133313b1d0f32872d54fbdf0aa7&chksm=fd985423caefdd354b587e57ce6cf5f5a7bec48b9ab7554f39a8d13af47660cae793956e0f46#rd) - - -1. **Selector(选择器)介绍** - - Selector 一般称 为选择器 ,当然你也可以翻译为 多路复用器 。它是Java NIO核心组件中的一个,用于检查一个或多个NIO Channel(通道)的状态是否处于可读、可写。如此可以实现单线程管理多个channels,也就是可以管理多个网络链接。 - - 使用Selector的好处在于: 使用更少的线程来就可以来处理通道了, 相比使用多个线程,避免了线程上下文切换带来的开销。 -2. **Selector(选择器)的使用方法介绍** - - Selector的创建 - ```java - Selector selector = Selector.open(); - ``` - - 注册Channel到Selector(Channel必须是非阻塞的) - ```java - channel.configureBlocking(false); - SelectionKey key = channel.register(selector, Selectionkey.OP_READ); - ``` - - SelectionKey介绍 - - 一个SelectionKey键表示了一个特定的通道对象和一个特定的选择器对象之间的注册关系。 - - 从Selector中选择channel(Selecting Channels via a Selector) - - 选择器维护注册过的通道的集合,并且这种注册关系都被封装在SelectionKey当中. - - 停止选择的方法 - - wakeup()方法 和close()方法。 -3. **模板代码** - - 有了模板代码我们在编写程序时,大多数时间都是在模板代码中添加相应的业务代码。 -4. **客户端与服务端简单交互实例** - - - -### [五 Java NIO之拥抱Path和Files](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247483976&idx=1&sn=2296c05fc1b840a64679e2ad7794c96d&chksm=fd985429caefdd3f48e2ee6fdd7b0f6fc419df90b3de46832b484d6d1ca4e74e7837689c8146&token=537240785&lang=zh_CN#rd) - -**一 文件I/O基石:Path:** -- 创建一个Path -- File和Path之间的转换,File和URI之间的转换 -- 获取Path的相关信息 -- 移除Path中的冗余项 - -**二 拥抱Files类:** -- Files.exists() 检测文件路径是否存在 -- Files.createFile() 创建文件 -- Files.createDirectories()和Files.createDirectory()创建文件夹 -- Files.delete()方法 可以删除一个文件或目录 -- Files.copy()方法可以吧一个文件从一个地址复制到另一个位置 -- 获取文件属性 -- 遍历一个文件夹 -- Files.walkFileTree()遍历整个目录 - -### [六 NIO学习总结以及NIO新特性介绍](https://blog.csdn.net/a953713428/article/details/64907250) - -- **内存映射:** - -这个功能主要是为了提高大文件的读写速度而设计的。内存映射文件(memory-mappedfile)能让你创建和修改那些大到无法读入内存的文件。有了内存映射文件,你就可以认为文件已经全部读进了内存,然后把它当成一个非常大的数组来访问了。将文件的一段区域映射到内存中,比传统的文件处理速度要快很多。内存映射文件它虽然最终也是要从磁盘读取数据,但是它并不需要将数据读取到OS内核缓冲区,而是直接将进程的用户私有地址空间中的一部分区域与文件对象建立起映射关系,就好像直接从内存中读、写文件一样,速度当然快了。 - -### [七 Java NIO AsynchronousFileChannel异步文件通](http://wiki.jikexueyuan.com/project/java-nio-zh/java-nio-asynchronousfilechannel.html) - -Java7中新增了AsynchronousFileChannel作为nio的一部分。AsynchronousFileChannel使得数据可以进行异步读写。 - -### [八 高并发Java(8):NIO和AIO](http://www.importnew.com/21341.html) - - - -## 推荐阅读 - -### [在 Java 7 中体会 NIO.2 异步执行的快乐](https://www.ibm.com/developerworks/cn/java/j-lo-nio2/index.html) - -### [Java AIO总结与示例](https://blog.csdn.net/x_i_y_u_e/article/details/52223406) -AIO是异步IO的缩写,虽然NIO在网络操作中,提供了非阻塞的方法,但是NIO的IO行为还是同步的。对于NIO来说,我们的业务线程是在IO操作准备好时,得到通知,接着就由这个线程自行进行IO操作,IO操作本身是同步的。 - - -**欢迎关注我的微信公众号:"Java面试通关手册"(一个有温度的微信公众号,期待与你共同进步~~~坚持原创,分享美文,分享各种Java学习资源):** diff --git a/docs/java/Multithread/Untitled.md b/docs/java/Multithread/Untitled.md deleted file mode 100644 index ca7411cb..00000000 --- a/docs/java/Multithread/Untitled.md +++ /dev/null @@ -1,422 +0,0 @@ -## synchronized / Lock - -1. JDK 1.5之前 - - ,Java通过 - - synchronized - - 关键字来实现 - - 锁 - - 功能 - - - synchronized是JVM实现的**内置锁**,锁的获取和释放都是由JVM**隐式**实现的 - -2. JDK 1.5 - - ,并发包中新增了 - - Lock接口 - - 来实现锁功能 - - - 提供了与synchronized类似的同步功能,但需要**显式**获取和释放锁 - -3. Lock同步锁是基于 - - Java - - 实现的,而synchronized是基于底层操作系统的 - - Mutex Lock - - 实现的 - - - 每次获取和释放锁都会带来**用户态和内核态的切换**,从而增加系统的**性能开销** - - 在锁竞争激烈的情况下,synchronized同步锁的性能很糟糕 - - 在**JDK 1.5**,在**单线程重复申请锁**的情况下,synchronized锁性能要比Lock的性能**差很多** - -4. **JDK 1.6**,Java对synchronized同步锁做了**充分的优化**,甚至在某些场景下,它的性能已经超越了Lock同步锁 - - - -## 实现原理 - -复制 - -``` -public class SyncTest { - public synchronized void method1() { - } - - public void method2() { - Object o = new Object(); - synchronized (o) { - } - } -} -``` - -复制 - -``` -$ javac -encoding UTF-8 SyncTest.java -$ javap -v SyncTest -``` - -### 修饰方法 - -复制 - -``` -public synchronized void method1(); - descriptor: ()V - flags: ACC_PUBLIC, ACC_SYNCHRONIZED - Code: - stack=0, locals=1, args_size=1 - 0: return -``` - -1. JVM使用**ACC_SYNCHRONIZED**访问标识来区分一个方法是否为**同步方法** - -2. 在方法调用时,会检查方法是否被设置了 - - ACC_SYNCHRONIZED - - 访问标识 - - - 如果是,执行线程会将先尝试**持有Monitor对象**,再执行方法,方法执行完成后,最后**释放Monitor对象** - -### 修饰代码块 - -复制 - -``` -public void method2(); - descriptor: ()V - flags: ACC_PUBLIC - Code: - stack=2, locals=4, args_size=1 - 0: new #2 // class java/lang/Object - 3: dup - 4: invokespecial #1 // Method java/lang/Object."":()V - 7: astore_1 - 8: aload_1 - 9: dup - 10: astore_2 - 11: monitorenter - 12: aload_2 - 13: monitorexit - 14: goto 22 - 17: astore_3 - 18: aload_2 - 19: monitorexit - 20: aload_3 - 21: athrow - 22: return -``` - -1. synchronized修饰同步代码块时,由**monitorenter**和**monitorexit**指令来实现同步 -2. 进入**monitorenter**指令后,线程将**持有**该**Monitor对象**,进入**monitorexit**指令,线程将**释放**该**Monitor对象** - -### 管程模型 - -1. JVM中的**同步**是基于进入和退出**管程**(**Monitor**)对象实现的 - -2. **每个Java对象实例都会有一个Monitor**,Monitor可以和Java对象实例一起被创建和销毁 - -3. Monitor是由**ObjectMonitor**实现的,对应[ObjectMonitor.hpp](https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/share/vm/runtime/objectMonitor.hpp) - -4. 当多个线程同时访问一段同步代码时,会先被放在**EntryList**中 - -5. 当线程获取到Java对象的Monitor时(Monitor是依靠 - - 底层操作系统 - - 的 - - Mutex Lock - - 来实现 - - 互斥 - - 的) - - - 线程申请Mutex成功,则持有该Mutex,其它线程将无法获取到该Mutex - -6. 进入 - - WaitSet - - - 竞争锁**失败**的线程会进入**WaitSet** - - 竞争锁**成功**的线程如果调用**wait**方法,就会**释放当前持有的Mutex**,并且该线程会进入**WaitSet** - - 进入**WaitSet**的进程会等待下一次唤醒,然后进入EntryList**重新排队** - -7. 如果当前线程顺利执行完方法,也会释放Mutex - -8. Monitor依赖于**底层操作系统**的实现,存在**用户态**和**内核态之间**的**切换**,所以增加了**性能开销** - -[![img](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-monitor.png)](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-monitor.png) - -复制 - -``` -ObjectMonitor() { - _header = NULL; - _count = 0; // 记录个数 - _waiters = 0, - _recursions = 0; - _object = NULL; - _owner = NULL; // 持有该Monitor的线程 - _WaitSet = NULL; // 处于wait状态的线程,会被加入 _WaitSet - _WaitSetLock = 0 ; - _Responsible = NULL ; - _succ = NULL ; - _cxq = NULL ; - FreeNext = NULL ; - _EntryList = NULL ; // 多个线程访问同步块或同步方法,会首先被加入 _EntryList - _SpinFreq = 0 ; - _SpinClock = 0 ; - OwnerIsThread = 0 ; - _previous_owner_tid = 0; -} -``` - -## 锁升级优化 - -1. 为了提升性能,在**JDK 1.6**引入**偏向锁、轻量级锁、重量级锁**,用来**减少锁竞争带来的上下文切换** -2. 借助JDK 1.6新增的**Java对象头**,实现了**锁升级**功能 - -### Java对象头 - -1. 在**JDK 1.6**的JVM中,对象实例在**堆内存**中被分为三部分:**对象头**、**实例数据**、**对齐填充** -2. 对象头的组成部分:**Mark Word**、**指向类的指针**、**数组长度**(可选,数组类型时才有) -3. Mark Word记录了**对象**和**锁**有关的信息,在64位的JVM中,Mark Word为**64 bit** -4. 锁升级功能主要依赖于Mark Word中**锁标志位**和**是否偏向锁标志位** -5. synchronized同步锁的升级优化路径:***偏向锁** -> **轻量级锁** -> **重量级锁*** - -[![img](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-mark-word.jpg)](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-mark-word.jpg) - -### 偏向锁 - -1. 偏向锁主要用来优化**同一线程多次申请同一个锁**的竞争,在某些情况下,大部分时间都是同一个线程竞争锁资源 - -2. 偏向锁的作用 - - - 当一个线程再次访问同一个同步代码时,该线程只需对该对象头的**Mark Word**中去判断是否有偏向锁指向它 - - **无需再进入Monitor去竞争对象**(避免用户态和内核态的**切换**) - -3. 当对象被当做同步锁,并有一个线程抢到锁时 - - - 锁标志位还是**01**,是否偏向锁标志位设置为**1**,并且记录抢到锁的**线程ID**,进入***偏向锁状态*** - -4. 偏向锁 - - **不会主动释放锁** - - - 当线程1再次获取锁时,会比较**当前线程的ID**与**锁对象头部的线程ID**是否一致,如果一致,无需CAS来抢占锁 - - - 如果不一致,需要查看 - - 锁对象头部记录的线程 - - 是否存活 - - - 如果**没有存活**,那么锁对象被重置为**无锁**状态(也是一种撤销),然后重新偏向线程2 - - - 如果 - - 存活 - - ,查找线程1的栈帧信息 - - - 如果线程1还是需要继续持有该锁对象,那么暂停线程1(**STW**),**撤销偏向锁**,**升级为轻量级锁** - - 如果线程1不再使用该锁对象,那么将该锁对象设为**无锁**状态(也是一种撤销),然后重新偏向线程2 - -5. 一旦出现其他线程竞争锁资源时,偏向锁就会被 - - 撤销 - - - 偏向锁的撤销**可能需要**等待**全局安全点**,暂停持有该锁的线程,同时检查该线程**是否还在执行该方法** - - 如果还没有执行完,说明此刻有**多个线程**竞争,升级为**轻量级锁**;如果已经执行完毕,唤醒其他线程继续**CAS**抢占 - -6. 在 - - 高并发 - - 场景下,当 - - 大量线程 - - 同时竞争同一个锁资源时,偏向锁会被 - - 撤销 - - ,发生 - - STW - - ,加大了 - - 性能开销 - - - 默认配置 - - - `-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=4000` - - 默认开启偏向锁,并且**延迟生效**,因为JVM刚启动时竞争非常激烈 - - - 关闭偏向锁 - - - `-XX:-UseBiasedLocking` - - - 直接 - - 设置为重量级锁 - - - `-XX:+UseHeavyMonitors` - -红线流程部分:偏向锁的**获取**和**撤销** -[![img](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-lock-upgrade-1.png)](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-lock-upgrade-1.png) - -### 轻量级锁 - -1. 当有另外一个线程竞争锁时,由于该锁处于**偏向锁**状态 - -2. 发现对象头Mark Word中的线程ID不是自己的线程ID,该线程就会执行 - - CAS - - 操作获取锁 - - - 如果获取**成功**,直接替换Mark Word中的线程ID为自己的线程ID,该锁会***保持偏向锁状态*** - - 如果获取**失败**,说明当前锁有一定的竞争,将偏向锁**升级**为轻量级锁 - -3. 线程获取轻量级锁时会有两步 - - - 先把**锁对象的Mark Word**复制一份到线程的**栈帧**中(**DisplacedMarkWord**),主要为了**保留现场**!! - - 然后使用**CAS**,把对象头中的内容替换为**线程栈帧中DisplacedMarkWord的地址** - -4. 场景 - - - 在线程1复制对象头Mark Word的同时(CAS之前),线程2也准备获取锁,也复制了对象头Mark Word - - 在线程2进行CAS时,发现线程1已经把对象头换了,线程2的CAS失败,线程2会尝试使用**自旋锁**来等待线程1释放锁 - -5. 轻量级锁的适用场景:线程**交替执行**同步块,***绝大部分的锁在整个同步周期内都不存在长时间的竞争*** - -红线流程部分:升级轻量级锁 -[![img](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-lock-upgrade-2.png)](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-lock-upgrade-2.png) - -### 自旋锁 / 重量级锁 - -1. 轻量级锁 - - CAS - - 抢占失败,线程将会被挂起进入 - - 阻塞 - - 状态 - - - 如果正在持有锁的线程在**很短的时间**内释放锁资源,那么进入**阻塞**状态的线程被**唤醒**后又要**重新抢占**锁资源 - -2. JVM提供了**自旋锁**,可以通过**自旋**的方式**不断尝试获取锁**,从而***避免线程被挂起阻塞*** - -3. 从 - - JDK 1.7 - - 开始, - - 自旋锁默认启用 - - ,自旋次数 - - 不建议设置过大 - - (意味着 - - 长时间占用CPU - - ) - - - `-XX:+UseSpinning -XX:PreBlockSpin=10` - -4. 自旋锁重试之后如果依然抢锁失败,同步锁会升级至 - - 重量级锁 - - ,锁标志位为 - - 10 - - - 在这个状态下,未抢到锁的线程都会**进入Monitor**,之后会被阻塞在**WaitSet**中 - -5. 在 - - 锁竞争不激烈 - - 且 - - 锁占用时间非常短 - - 的场景下,自旋锁可以提高系统性能 - - - 一旦锁竞争激烈或者锁占用的时间过长,自旋锁将会导致大量的线程一直处于**CAS重试状态**,**占用CPU资源** - -6. 在 - - 高并发 - - 的场景下,可以通过 - - 关闭自旋锁 - - 来优化系统性能 - - - ``` - -XX:-UseSpinning - ``` - - - 关闭自旋锁优化 - - - ``` - -XX:PreBlockSpin - ``` - - - 默认的自旋次数,在**JDK 1.7**后,**由JVM控制** - -[![img](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-lock-upgrade-3.png)](https://java-performance-1253868755.cos.ap-guangzhou.myqcloud.com/java-performance-synchronized-lock-upgrade-3.png) - -## 小结 - -1. JVM在**JDK 1.6**中引入了**分级锁**机制来优化synchronized - -2. 当一个线程获取锁时,首先对象锁成为一个 - - 偏向锁 - - - 这是为了避免在**同一线程重复获取同一把锁**时,**用户态和内核态频繁切换** - -3. 如果有多个线程竞争锁资源,锁将会升级为 - - 轻量级锁 - - - 这适用于在**短时间**内持有锁,且分锁**交替切换**的场景 - - 轻量级锁还结合了**自旋锁**来**避免线程用户态与内核态的频繁切换** - -4. 如果锁竞争太激烈(自旋锁失败),同步锁会升级为重量级锁 - -5. 优化synchronized同步锁的关键: - - 减少锁竞争 - - - 应该尽量使synchronized同步锁处于**轻量级锁**或**偏向锁**,这样才能提高synchronized同步锁的性能 - - 常用手段 - - **减少锁粒度**:降低锁竞争 - - **减少锁的持有时间**,提高synchronized同步锁在自旋时获取锁资源的成功率,**避免升级为重量级锁** - -6. 在**锁竞争激烈**时,可以考虑**禁用偏向锁**和**禁用自旋锁** \ No newline at end of file diff --git a/docs/java/basic/Arrays,CollectionsCommonMethods.md b/docs/java/basic/Arrays,CollectionsCommonMethods.md deleted file mode 100644 index 0710de44..00000000 --- a/docs/java/basic/Arrays,CollectionsCommonMethods.md +++ /dev/null @@ -1,383 +0,0 @@ - - -- [Collections 工具类和 Arrays 工具类常见方法](#collections-工具类和-arrays-工具类常见方法) - - [Collections](#collections) - - [排序操作](#排序操作) - - [查找,替换操作](#查找替换操作) - - [同步控制](#同步控制) - - [Arrays类的常见操作](#arrays类的常见操作) - - [排序 : `sort()`](#排序--sort) - - [查找 : `binarySearch()`](#查找--binarysearch) - - [比较: `equals()`](#比较-equals) - - [填充 : `fill()`](#填充--fill) - - [转列表 `asList()`](#转列表-aslist) - - [转字符串 `toString()`](#转字符串-tostring) - - [复制 `copyOf()`](#复制-copyof) - - -# Collections 工具类和 Arrays 工具类常见方法 - -## Collections - -Collections 工具类常用方法: - -1. 排序 -2. 查找,替换操作 -3. 同步控制(不推荐,需要线程安全的集合类型时请考虑使用 JUC 包下的并发集合) - -### 排序操作 - -```java -void reverse(List list)//反转 -void shuffle(List list)//随机排序 -void sort(List list)//按自然排序的升序排序 -void sort(List list, Comparator c)//定制排序,由Comparator控制排序逻辑 -void swap(List list, int i , int j)//交换两个索引位置的元素 -void rotate(List list, int distance)//旋转。当distance为正数时,将list后distance个元素整体移到前面。当distance为负数时,将 list的前distance个元素整体移到后面。 -``` - -**示例代码:** - -```java - ArrayList arrayList = new ArrayList(); - arrayList.add(-1); - arrayList.add(3); - arrayList.add(3); - arrayList.add(-5); - arrayList.add(7); - arrayList.add(4); - arrayList.add(-9); - arrayList.add(-7); - System.out.println("原始数组:"); - System.out.println(arrayList); - // void reverse(List list):反转 - Collections.reverse(arrayList); - System.out.println("Collections.reverse(arrayList):"); - System.out.println(arrayList); - - - Collections.rotate(arrayList, 4); - System.out.println("Collections.rotate(arrayList, 4):"); - System.out.println(arrayList); - - // void sort(List list),按自然排序的升序排序 - Collections.sort(arrayList); - System.out.println("Collections.sort(arrayList):"); - System.out.println(arrayList); - - // void shuffle(List list),随机排序 - Collections.shuffle(arrayList); - System.out.println("Collections.shuffle(arrayList):"); - System.out.println(arrayList); - - // void swap(List list, int i , int j),交换两个索引位置的元素 - Collections.swap(arrayList, 2, 5); - System.out.println("Collections.swap(arrayList, 2, 5):"); - System.out.println(arrayList); - - // 定制排序的用法 - Collections.sort(arrayList, new Comparator() { - - @Override - public int compare(Integer o1, Integer o2) { - return o2.compareTo(o1); - } - }); - System.out.println("定制排序后:"); - System.out.println(arrayList); -``` - -### 查找,替换操作 - -```java -int binarySearch(List list, Object key)//对List进行二分查找,返回索引,注意List必须是有序的 -int max(Collection coll)//根据元素的自然顺序,返回最大的元素。 类比int min(Collection coll) -int max(Collection coll, Comparator c)//根据定制排序,返回最大元素,排序规则由Comparatator类控制。类比int min(Collection coll, Comparator c) -void fill(List list, Object obj)//用指定的元素代替指定list中的所有元素。 -int frequency(Collection c, Object o)//统计元素出现次数 -int indexOfSubList(List list, List target)//统计target在list中第一次出现的索引,找不到则返回-1,类比int lastIndexOfSubList(List source, list target). -boolean replaceAll(List list, Object oldVal, Object newVal), 用新元素替换旧元素 -``` - -**示例代码:** - -```java - ArrayList arrayList = new ArrayList(); - arrayList.add(-1); - arrayList.add(3); - arrayList.add(3); - arrayList.add(-5); - arrayList.add(7); - arrayList.add(4); - arrayList.add(-9); - arrayList.add(-7); - ArrayList arrayList2 = new ArrayList(); - arrayList2.add(-3); - arrayList2.add(-5); - arrayList2.add(7); - System.out.println("原始数组:"); - System.out.println(arrayList); - - System.out.println("Collections.max(arrayList):"); - System.out.println(Collections.max(arrayList)); - - System.out.println("Collections.min(arrayList):"); - System.out.println(Collections.min(arrayList)); - - System.out.println("Collections.replaceAll(arrayList, 3, -3):"); - Collections.replaceAll(arrayList, 3, -3); - System.out.println(arrayList); - - System.out.println("Collections.frequency(arrayList, -3):"); - System.out.println(Collections.frequency(arrayList, -3)); - - System.out.println("Collections.indexOfSubList(arrayList, arrayList2):"); - System.out.println(Collections.indexOfSubList(arrayList, arrayList2)); - - System.out.println("Collections.binarySearch(arrayList, 7):"); - // 对List进行二分查找,返回索引,List必须是有序的 - Collections.sort(arrayList); - System.out.println(Collections.binarySearch(arrayList, 7)); -``` - -### 同步控制 - -Collections提供了多个`synchronizedXxx()`方法·,该方法可以将指定集合包装成线程同步的集合,从而解决多线程并发访问集合时的线程安全问题。 - -我们知道 HashSet,TreeSet,ArrayList,LinkedList,HashMap,TreeMap 都是线程不安全的。Collections提供了多个静态方法可以把他们包装成线程同步的集合。 - -**最好不要用下面这些方法,效率非常低,需要线程安全的集合类型时请考虑使用 JUC 包下的并发集合。** - -方法如下: - -```java -synchronizedCollection(Collection c) //返回指定 collection 支持的同步(线程安全的)collection。 -synchronizedList(List list)//返回指定列表支持的同步(线程安全的)List。 -synchronizedMap(Map m) //返回由指定映射支持的同步(线程安全的)Map。 -synchronizedSet(Set s) //返回指定 set 支持的同步(线程安全的)set。 -``` - -### Collections还可以设置不可变集合,提供了如下三类方法: - -```java -emptyXxx(): 返回一个空的、不可变的集合对象,此处的集合既可以是List,也可以是Set,还可以是Map。 -singletonXxx(): 返回一个只包含指定对象(只有一个或一个元素)的不可变的集合对象,此处的集合可以是:List,Set,Map。 -unmodifiableXxx(): 返回指定集合对象的不可变视图,此处的集合可以是:List,Set,Map。 -上面三类方法的参数是原有的集合对象,返回值是该集合的”只读“版本。 -``` - -**示例代码:** - -```java - ArrayList arrayList = new ArrayList(); - arrayList.add(-1); - arrayList.add(3); - arrayList.add(3); - arrayList.add(-5); - arrayList.add(7); - arrayList.add(4); - arrayList.add(-9); - arrayList.add(-7); - HashSet integers1 = new HashSet<>(); - integers1.add(1); - integers1.add(3); - integers1.add(2); - Map scores = new HashMap(); - scores.put("语文" , 80); - scores.put("Java" , 82); - - //Collections.emptyXXX();创建一个空的、不可改变的XXX对象 - List list = Collections.emptyList(); - System.out.println(list);//[] - Set objects = Collections.emptySet(); - System.out.println(objects);//[] - Map objectObjectMap = Collections.emptyMap(); - System.out.println(objectObjectMap);//{} - - //Collections.singletonXXX(); - List> arrayLists = Collections.singletonList(arrayList); - System.out.println(arrayLists);//[[-1, 3, 3, -5, 7, 4, -9, -7]] - //创建一个只有一个元素,且不可改变的Set对象 - Set> singleton = Collections.singleton(arrayList); - System.out.println(singleton);//[[-1, 3, 3, -5, 7, 4, -9, -7]] - Map nihao = Collections.singletonMap("1", "nihao"); - System.out.println(nihao);//{1=nihao} - - //unmodifiableXXX();创建普通XXX对象对应的不可变版本 - List integers = Collections.unmodifiableList(arrayList); - System.out.println(integers);//[-1, 3, 3, -5, 7, 4, -9, -7] - Set integers2 = Collections.unmodifiableSet(integers1); - System.out.println(integers2);//[1, 2, 3] - Map objectObjectMap2 = Collections.unmodifiableMap(scores); - System.out.println(objectObjectMap2);//{Java=82, 语文=80} - - //添加出现异常:java.lang.UnsupportedOperationException -// list.add(1); -// arrayLists.add(arrayList); -// integers.add(1); -``` - -## Arrays类的常见操作 -1. 排序 : `sort()` -2. 查找 : `binarySearch()` -3. 比较: `equals()` -4. 填充 : `fill()` -5. 转列表: `asList()` -6. 转字符串 : `toString()` -7. 复制: `copyOf()` - - -### 排序 : `sort()` - -```java - // *************排序 sort**************** - int a[] = { 1, 3, 2, 7, 6, 5, 4, 9 }; - // sort(int[] a)方法按照数字顺序排列指定的数组。 - Arrays.sort(a); - System.out.println("Arrays.sort(a):"); - for (int i : a) { - System.out.print(i); - } - // 换行 - System.out.println(); - - // sort(int[] a,int fromIndex,int toIndex)按升序排列数组的指定范围 - int b[] = { 1, 3, 2, 7, 6, 5, 4, 9 }; - Arrays.sort(b, 2, 6); - System.out.println("Arrays.sort(b, 2, 6):"); - for (int i : b) { - System.out.print(i); - } - // 换行 - System.out.println(); - - int c[] = { 1, 3, 2, 7, 6, 5, 4, 9 }; - // parallelSort(int[] a) 按照数字顺序排列指定的数组(并行的)。同sort方法一样也有按范围的排序 - Arrays.parallelSort(c); - System.out.println("Arrays.parallelSort(c):"); - for (int i : c) { - System.out.print(i); - } - // 换行 - System.out.println(); - - // parallelSort给字符数组排序,sort也可以 - char d[] = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' }; - Arrays.parallelSort(d); - System.out.println("Arrays.parallelSort(d):"); - for (char d2 : d) { - System.out.print(d2); - } - // 换行 - System.out.println(); - -``` - -在做算法面试题的时候,我们还可能会经常遇到对字符串排序的情况,`Arrays.sort()` 对每个字符串的特定位置进行比较,然后按照升序排序。 - -```java -String[] strs = { "abcdehg", "abcdefg", "abcdeag" }; -Arrays.sort(strs); -System.out.println(Arrays.toString(strs));//[abcdeag, abcdefg, abcdehg] -``` - -### 查找 : `binarySearch()` - -```java - // *************查找 binarySearch()**************** - char[] e = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' }; - // 排序后再进行二分查找,否则找不到 - Arrays.sort(e); - System.out.println("Arrays.sort(e)" + Arrays.toString(e)); - System.out.println("Arrays.binarySearch(e, 'c'):"); - int s = Arrays.binarySearch(e, 'c'); - System.out.println("字符c在数组的位置:" + s); -``` - -### 比较: `equals()` - -```java - // *************比较 equals**************** - char[] e = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' }; - char[] f = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' }; - /* - * 元素数量相同,并且相同位置的元素相同。 另外,如果两个数组引用都是null,则它们被认为是相等的 。 - */ - // 输出true - System.out.println("Arrays.equals(e, f):" + Arrays.equals(e, f)); -``` - -### 填充 : `fill()` - -```java - // *************填充fill(批量初始化)**************** - int[] g = { 1, 2, 3, 3, 3, 3, 6, 6, 6 }; - // 数组中所有元素重新分配值 - Arrays.fill(g, 3); - System.out.println("Arrays.fill(g, 3):"); - // 输出结果:333333333 - for (int i : g) { - System.out.print(i); - } - // 换行 - System.out.println(); - - int[] h = { 1, 2, 3, 3, 3, 3, 6, 6, 6, }; - // 数组中指定范围元素重新分配值 - Arrays.fill(h, 0, 2, 9); - System.out.println("Arrays.fill(h, 0, 2, 9);:"); - // 输出结果:993333666 - for (int i : h) { - System.out.print(i); - } -``` - -### 转列表 `asList()` - -```java - // *************转列表 asList()**************** - /* - * 返回由指定数组支持的固定大小的列表。 - * (将返回的列表更改为“写入数组”。)该方法作为基于数组和基于集合的API之间的桥梁,与Collection.toArray()相结合 。 - * 返回的列表是可序列化的,并实现RandomAccess 。 - * 此方法还提供了一种方便的方式来创建一个初始化为包含几个元素的固定大小的列表如下: - */ - List stooges = Arrays.asList("Larry", "Moe", "Curly"); - System.out.println(stooges); -``` - -### 转字符串 `toString()` - -```java - // *************转字符串 toString()**************** - /* - * 返回指定数组的内容的字符串表示形式。 - */ - char[] k = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' }; - System.out.println(Arrays.toString(k));// [a, f, b, c, e, A, C, B] -``` - -### 复制 `copyOf()` - -```java - // *************复制 copy**************** - // copyOf 方法实现数组复制,h为数组,6为复制的长度 - int[] h = { 1, 2, 3, 3, 3, 3, 6, 6, 6, }; - int i[] = Arrays.copyOf(h, 6); - System.out.println("Arrays.copyOf(h, 6);:"); - // 输出结果:123333 - for (int j : i) { - System.out.print(j); - } - // 换行 - System.out.println(); - // copyOfRange将指定数组的指定范围复制到新数组中 - int j[] = Arrays.copyOfRange(h, 6, 11); - System.out.println("Arrays.copyOfRange(h, 6, 11):"); - // 输出结果66600(h数组只有9个元素这里是从索引6到索引11复制所以不足的就为0) - for (int j2 : j) { - System.out.print(j2); - } - // 换行 - System.out.println(); -``` diff --git a/docs/java/BIO-NIO-AIO.md b/docs/java/basis/BIO,NIO,AIO总结.md similarity index 100% rename from docs/java/BIO-NIO-AIO.md rename to docs/java/basis/BIO,NIO,AIO总结.md diff --git a/docs/java/Java基础知识.md b/docs/java/basis/Java基础知识.md similarity index 96% rename from docs/java/Java基础知识.md rename to docs/java/basis/Java基础知识.md index 1a4b9da8..7d7568f7 100644 --- a/docs/java/Java基础知识.md +++ b/docs/java/basis/Java基础知识.md @@ -13,9 +13,8 @@ - [1.1.3. Oracle JDK 和 OpenJDK 的对比](#113-oracle-jdk-和-openjdk-的对比) - [1.1.4. Java 和 C++的区别?](#114-java-和-c的区别) - [1.1.5. 什么是 Java 程序的主类 应用程序和小程序的主类有何不同?](#115-什么是-java-程序的主类-应用程序和小程序的主类有何不同) - - [1.1.6. Java 应用程序与小程序之间有哪些差别?](#116-java-应用程序与小程序之间有哪些差别) - - [1.1.7. import java 和 javax 有什么区别?](#117-import-java-和-javax-有什么区别) - - [1.1.8. 为什么说 Java 语言“编译与解释并存”?](#118-为什么说-java-语言编译与解释并存) + - [1.1.6. import java 和 javax 有什么区别?](#116-import-java-和-javax-有什么区别) + - [1.1.7. 为什么说 Java 语言“编译与解释并存”?](#117-为什么说-java-语言编译与解释并存) - [1.2. Java 语法](#12-java-语法) - [1.2.1. 字符型常量和字符串常量的区别?](#121-字符型常量和字符串常量的区别) - [1.2.2. 关于注释?](#122-关于注释) @@ -34,8 +33,8 @@ - [1.4.1. 什么是方法的返回值?返回值在类的方法里的作用是什么?](#141-什么是方法的返回值返回值在类的方法里的作用是什么) - [1.4.2. 为什么 Java 中只有值传递?](#142-为什么-java-中只有值传递) - [1.4.3. 重载和重写的区别](#143-重载和重写的区别) - - [1.4.3.1. 重载](#1431-重载) - - [1.4.3.2. 重写](#1432-重写) + - [1.4.3.1. 重载](#1431-重载) + - [1.4.3.2. 重写](#1432-重写) - [1.4.4. 深拷贝 vs 浅拷贝](#144-深拷贝-vs-浅拷贝) - [1.4.5. 方法的四种类型](#145-方法的四种类型) - [2. Java 面向对象](#2-java-面向对象) @@ -70,8 +69,11 @@ - [2.5.5. Java 序列化中如果有些字段不想进行序列化,怎么办?](#255-java-序列化中如果有些字段不想进行序列化怎么办) - [2.5.6. 获取用键盘输入常用的两种方法](#256-获取用键盘输入常用的两种方法) - [3. Java 核心技术](#3-java-核心技术) - - [3.1. 集合](#31-集合) - - [3.1.1. Collections 工具类和 Arrays 工具类常见方法总结](#311-collections-工具类和-arrays-工具类常见方法总结) + - [3.1. 反射机制](#31-反射机制) + - [3.1.1. 静态编译和动态编译](#311静态编译和动态编译) + - [3.1.2. 反射机制优缺点](#312反射机制优缺点) + - [3.1.3. 反射的应用场景](#313反射的应用场景) + - [3.2. 异常](#32-异常) - [3.2.1. Java 异常类层次结构图](#321-java-异常类层次结构图) - [3.2.2. Throwable 类常用方法](#322-throwable-类常用方法) @@ -80,7 +82,7 @@ - [3.3. 多线程](#33-多线程) - [3.3.1. 简述线程、程序、进程的基本概念。以及他们之间关系是什么?](#331-简述线程程序进程的基本概念以及他们之间关系是什么) - [3.3.2. 线程有哪些基本状态?](#332-线程有哪些基本状态) - - [3.4. 文件与 I\O 流](#34-文件与-i\o-流) + - [3.4. 文件与 I\O 流](#34-文件与-io-流) - [3.4.1. Java 中 IO 流分为几种?](#341-java-中-io-流分为几种) - [3.4.1.1. 既然有了字节流,为什么还要有字符流?](#3411-既然有了字节流为什么还要有字符流) - [3.4.1.2. BIO,NIO,AIO 有什么区别?](#3412-bionioaio-有什么区别) @@ -169,17 +171,13 @@ JRE 是 Java 运行时环境。它是运行已编译 Java 程序所需的所有 一个程序中可以有多个类,但只能有一个类是主类。在 Java 应用程序中,这个主类是指包含 `main()` 方法的类。而在 Java 小程序中,这个主类是一个继承自系统类 JApplet 或 Applet 的子类。应用程序的主类不一定要求是 public 类,但小程序的主类要求必须是 public 类。主类是 Java 程序执行的入口点。 -#### 1.1.6. Java 应用程序与小程序之间有哪些差别? - -简单说应用程序是从主线程启动(也就是 `main()` 方法)。applet 小程序没有 `main()` 方法,主要是嵌在浏览器页面上运行(调用`init()`或者`run()`来启动),嵌入浏览器这点跟 flash 的小游戏类似。 - -#### 1.1.7. import java 和 javax 有什么区别? +#### 1.1.6. import java 和 javax 有什么区别? 刚开始的时候 JavaAPI 所必需的包是 java 开头的包,javax 当时只是扩展 API 包来使用。然而随着时间的推移,javax 逐渐地扩展成为 Java API 的组成部分。但是,将扩展从 javax 包移动到 java 包确实太麻烦了,最终会破坏一堆现有的代码。因此,最终决定 javax 包将成为标准 API 的一部分。 所以,实际上 java 和 javax 没有区别。这都是一个名字。 -#### 1.1.8. 为什么说 Java 语言“编译与解释并存”? +#### 1.1.7. 为什么说 Java 语言“编译与解释并存”? 高级编程语言按照程序的执行方式分为编译型和解释型两种。简单来说,编译型语言是指编译器针对特定的操作系统将源代码一次性翻译成可被该平台执行的机器码;解释型语言是指解释器对源程序逐行解释成特定平台的机器码并立即执行。比如,你想阅读一本英文名著,你可以找一个英文翻译人员帮助你阅读, 有两种选择方式,你可以先等翻译人员将全本的英文名著(也就是源码)都翻译成汉语,再去阅读,也可以让翻译人员翻译一段,你在旁边阅读一段,慢慢把书读完。 @@ -771,7 +769,7 @@ Java 程序设计语言对对象采用的不是引用调用,实际上,对象 > > 重写就是当子类继承自父类的相同方法,输入数据一样,但要做出有别于父类的响应时,你就要覆盖父类方法 -###### 1.4.3.1. 重载 +**重载:** 发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同。 @@ -779,9 +777,9 @@ Java 程序设计语言对对象采用的不是引用调用,实际上,对象 ![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/bg/desktopjava核心技术-重载.jpg) -**综上:重载就是同一个类中多个同名方法根据不同的传参来执行不同的逻辑处理。** +综上:重载就是同一个类中多个同名方法根据不同的传参来执行不同的逻辑处理。 -###### 1.4.3.2. 重写 +**重写:** 重写发生在运行期,是子类对父类的允许访问的方法的实现过程进行重新编写。 @@ -789,9 +787,9 @@ Java 程序设计语言对对象采用的不是引用调用,实际上,对象 2. 如果父类方法访问修饰符为 `private/final/static` 则子类就不能重写该方法,但是被 static 修饰的方法能够被再次声明。 3. 构造方法无法被重写 -**综上:重写就是子类对父类方法的重新改造,外部样子不能改变,内部逻辑可以改变** +综上:重写就是子类对父类方法的重新改造,外部样子不能改变,内部逻辑可以改变 -**暖心的 Guide 哥最后再来个图表总结一下!** +暖心的 Guide 哥最后再来个图表总结一下! | 区别点 | 重载方法 | 重写方法 | | :--------- | :------- | :----------------------------------------------------------- | @@ -1178,11 +1176,32 @@ String s = input.readLine(); ## 3. Java 核心技术 -### 3.1. 集合 +### 3.1. 反射机制 -#### 3.1.1. Collections 工具类和 Arrays 工具类常见方法总结 +JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。 -详见笔主的这篇文章: https://gitee.com/SnailClimb/JavaGuide/blob/master/docs/java/basic/Arrays,CollectionsCommonMethods.md +#### 3.1.1.静态编译和动态编译 + +- **静态编译:** 在编译时确定类型,绑定对象 +- **动态编译:** 运行时确定类型,绑定对象 + +#### 3.1.2.反射机制优缺点 + +- **优点:** 运行期类型的判断,动态加载类,提高代码灵活度。 +- **缺点:** 1,性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的 java 代码要慢很多。2,安全问题,让我们可以动态操作改变类的属性同时也增加了类的安全隐患。 + +#### 3.1.3.反射的应用场景 + +**反射是框架设计的灵魂。** + +在我们平时的项目开发过程中,基本上很少会直接使用到反射机制,但这不能说明反射机制没有用,实际上有很多设计、开发都与反射机制有关,例如模块化的开发,通过反射去调用对应的字节码;动态代理设计模式也采用了反射机制,还有我们日常使用的 Spring/Hibernate 等框架也大量使用到了反射机制。 + +举例: + +1. 我们在使用 JDBC 连接数据库时使用 `Class.forName()`通过反射加载数据库的驱动程序; +2. Spring 框架的 IOC(动态加载管理 Bean)创建对象以及 AOP(动态代理)功能都和反射有联系; +3. 动态配置实例的属性; +4. ...... ### 3.2. 异常 @@ -1334,7 +1353,7 @@ Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种 ![RUNNABLE-VS-RUNNING](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-3/RUNNABLE-VS-RUNNING.png) -当线程执行 `wait()`方法之后,线程进入 **WAITING(等待)**状态。进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态,而 **TIME_WAITING(超时等待)** 状态相当于在等待状态的基础上增加了超时限制,比如通过 `sleep(long millis)`方法或 `wait(long millis)`方法可以将 Java 线程置于 TIMED WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 **BLOCKED(阻塞)** 状态。线程在执行 Runnable 的`run()`方法之后将会进入到 **TERMINATED(终止)** 状态。 +当线程执行 `wait()`方法之后,线程进入 **WAITING(等待)** 状态。进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态,而 **TIME_WAITING(超时等待)** 状态相当于在等待状态的基础上增加了超时限制,比如通过 `sleep(long millis)`方法或 `wait(long millis)`方法可以将 Java 线程置于 TIMED WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 **BLOCKED(阻塞)** 状态。线程在执行 Runnable 的`run()`方法之后将会进入到 **TERMINATED(终止)** 状态。 ### 3.4. 文件与 I\O 流 diff --git a/docs/java/Java疑难点.md b/docs/java/basis/Java基础知识疑难点.md similarity index 100% rename from docs/java/Java疑难点.md rename to docs/java/basis/Java基础知识疑难点.md diff --git a/docs/java/basic/final,static,this,super.md b/docs/java/basis/Java常见关键字总结:final,static,this,super.md similarity index 100% rename from docs/java/basic/final,static,this,super.md rename to docs/java/basis/Java常见关键字总结:final,static,this,super.md diff --git a/docs/java/basic/reflection.md b/docs/java/basis/什么是反射机制?反射机制的应用场景有哪些?.md similarity index 84% rename from docs/java/basic/reflection.md rename to docs/java/basis/什么是反射机制?反射机制的应用场景有哪些?.md index ae1cc384..2f024c9c 100644 --- a/docs/java/basic/reflection.md +++ b/docs/java/basis/什么是反射机制?反射机制的应用场景有哪些?.md @@ -2,9 +2,9 @@ JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。 -### 获取 Class 对象的两种方式 +### 获取 Class 对象的四种方式 -如果我们动态获取到这些信息,我们需要依靠 Class 对象。Class 类对象将一个类的方法、变量等信息告诉运行的程序。Java 提供了三种方式获取 Class 对象: +如果我们动态获取到这些信息,我们需要依靠 Class 对象。Class 类对象将一个类的方法、变量等信息告诉运行的程序。Java 提供了四种方式获取 Class 对象: 1.知道具体类的情况下可以使用: @@ -12,18 +12,29 @@ JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道 Class alunbarClass = TargetObject.class; ``` -但是我们一般是不知道具体类的,基本都是通过遍历包下面的类来获取 Class 对象 +但是我们一般是不知道具体类的,基本都是通过遍历包下面的类来获取 Class 对象,通过此方式获取Class对象不会进行初始化 2.通过 `Class.forName()`传入类的路径获取: ```java Class alunbarClass1 = Class.forName("cn.javaguide.TargetObject"); ``` +Class.forName(className)方法,内部实际调用的是一个native方法 forName0(className, true, ClassLoader.getClassLoader(caller), caller); + +第2个boolean参数表示类是否需要初始化,Class.forName(className)默认是需要初始化。 + +一旦初始化,就会触发目标对象的 static块代码执行,static参数也会被再次初始化。 + 3.通过对象实例`instance.getClass()`获取: -``` -Employee e; +```java +Employee e = new Employee(); Class alunbarClass2 = e.getClass(); ``` +4.通过类加载器`xxxClassLoader.loadClass()`传入类路径获取 +```java +class clazz = ClassLoader.LoadClass("cn.javaguide.TargetObject"); +``` +通过类加载器获取Class对象不会进行初始化,意味着不进行包括初始化等一些列步骤,静态块和静态对象不会得到执行 ### 代码实例 diff --git a/docs/java/basic/用好Java中的枚举真的没有那么简单.md b/docs/java/basis/用好Java中的枚举真的没有那么简单.md similarity index 98% rename from docs/java/basic/用好Java中的枚举真的没有那么简单.md rename to docs/java/basis/用好Java中的枚举真的没有那么简单.md index bfd347aa..e88f0b5c 100644 --- a/docs/java/basic/用好Java中的枚举真的没有那么简单.md +++ b/docs/java/basis/用好Java中的枚举真的没有那么简单.md @@ -69,7 +69,7 @@ if(testPz.getStatus().equals(Pizza.PizzaStatus.DELIVERED)); if(testPz.getStatus() == Pizza.PizzaStatus.DELIVERED); ``` -对于编译时安全性,我们看另一个示例,两个不同枚举类型进行比较,使用equal方法比较结果确定为true,因为getStatus方法的枚举值与另一个类型枚举值一致,但逻辑上应该为false。这个问题可以使用==操作符避免。因为编译器会表示类型不兼容错误: +对于编译时安全性,我们看另一个示例,两个不同枚举类型进行比较,使用equal方法比较结果确定为true,因为`getStatus`方法的枚举值与另一个类型枚举值一致,但逻辑上应该为false。这个问题可以使用==操作符避免。因为编译器会表示类型不兼容错误: ```java if(testPz.getStatus().equals(TestColor.GREEN)); diff --git a/docs/java/basic/java-proxy.md b/docs/java/basis/静态代理+JDK,CGLIB动态代理实战.md similarity index 100% rename from docs/java/basic/java-proxy.md rename to docs/java/basis/静态代理+JDK,CGLIB动态代理实战.md diff --git a/docs/java/collection/ArrayList-Grow.md b/docs/java/collection/ArrayList-Grow.md deleted file mode 100644 index 2449190f..00000000 --- a/docs/java/collection/ArrayList-Grow.md +++ /dev/null @@ -1,361 +0,0 @@ - -## 一 先从 ArrayList 的构造函数说起 - -**ArrayList有三种方式来初始化,构造方法源码如下:** - -```java - /** - * 默认初始容量大小 - */ - private static final int DEFAULT_CAPACITY = 10; - - - private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; - - /** - *默认构造函数,使用初始容量10构造一个空列表(无参数构造) - */ - public ArrayList() { - this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; - } - - /** - * 带初始容量参数的构造函数。(用户自己指定容量) - */ - public ArrayList(int initialCapacity) { - if (initialCapacity > 0) {//初始容量大于0 - //创建initialCapacity大小的数组 - this.elementData = new Object[initialCapacity]; - } else if (initialCapacity == 0) {//初始容量等于0 - //创建空数组 - this.elementData = EMPTY_ELEMENTDATA; - } else {//初始容量小于0,抛出异常 - throw new IllegalArgumentException("Illegal Capacity: "+ - initialCapacity); - } - } - - - /** - *构造包含指定collection元素的列表,这些元素利用该集合的迭代器按顺序返回 - *如果指定的集合为null,throws NullPointerException。 - */ - public ArrayList(Collection c) { - elementData = c.toArray(); - if ((size = elementData.length) != 0) { - // c.toArray might (incorrectly) not return Object[] (see 6260652) - if (elementData.getClass() != Object[].class) - elementData = Arrays.copyOf(elementData, size, Object[].class); - } else { - // replace with empty array. - this.elementData = EMPTY_ELEMENTDATA; - } - } - -``` - -细心的同学一定会发现 :**以无参数构造方法创建 ArrayList 时,实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为10。** 下面在我们分析 ArrayList 扩容时会讲到这一点内容! - -## 二 一步一步分析 ArrayList 扩容机制 - -这里以无参构造函数创建的 ArrayList 为例分析 - -### 1. 先来看 `add` 方法 - -```java - /** - * 将指定的元素追加到此列表的末尾。 - */ - public boolean add(E e) { - //添加元素之前,先调用ensureCapacityInternal方法 - ensureCapacityInternal(size + 1); // Increments modCount!! - //这里看到ArrayList添加元素的实质就相当于为数组赋值 - elementData[size++] = e; - return true; - } -``` - -> **注意** :JDK11 移除了 `ensureCapacityInternal()` 和 `ensureExplicitCapacity()` 方法 - -### 2. 再来看看 `ensureCapacityInternal()` 方法 - -可以看到 `add` 方法 首先调用了`ensureCapacityInternal(size + 1)` - -```java - //得到最小扩容量 - private void ensureCapacityInternal(int minCapacity) { - if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { - // 获取默认的容量和传入参数的较大值 - minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); - } - - ensureExplicitCapacity(minCapacity); - } -``` -**当 要 add 进第1个元素时,minCapacity为1,在Math.max()方法比较后,minCapacity 为10。** - -### 3. `ensureExplicitCapacity()` 方法 - -如果调用 `ensureCapacityInternal()` 方法就一定会进过(执行)这个方法,下面我们来研究一下这个方法的源码! - -```java - //判断是否需要扩容 - private void ensureExplicitCapacity(int minCapacity) { - modCount++; - - // overflow-conscious code - if (minCapacity - elementData.length > 0) - //调用grow方法进行扩容,调用此方法代表已经开始扩容了 - grow(minCapacity); - } - -``` - -我们来仔细分析一下: - -- 当我们要 add 进第1个元素到 ArrayList 时,elementData.length 为0 (因为还是一个空的 list),因为执行了 `ensureCapacityInternal()` 方法 ,所以 minCapacity 此时为10。此时,`minCapacity - elementData.length > 0 `成立,所以会进入 `grow(minCapacity)` 方法。 -- 当add第2个元素时,minCapacity 为2,此时e lementData.length(容量)在添加第一个元素后扩容成 10 了。此时,`minCapacity - elementData.length > 0 ` 不成立,所以不会进入 (执行)`grow(minCapacity)` 方法。 -- 添加第3、4···到第10个元素时,依然不会执行grow方法,数组容量都为10。 - -直到添加第11个元素,minCapacity(为11)比elementData.length(为10)要大。进入grow方法进行扩容。 - -### 4. `grow()` 方法 - -```java - /** - * 要分配的最大数组大小 - */ - private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; - - /** - * ArrayList扩容的核心方法。 - */ - private void grow(int minCapacity) { - // oldCapacity为旧容量,newCapacity为新容量 - int oldCapacity = elementData.length; - //将oldCapacity 右移一位,其效果相当于oldCapacity /2, - //我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍, - int newCapacity = oldCapacity + (oldCapacity >> 1); - //然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量, - if (newCapacity - minCapacity < 0) - newCapacity = minCapacity; - // 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 MAX_ARRAY_SIZE, - //如果minCapacity大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`。 - if (newCapacity - MAX_ARRAY_SIZE > 0) - newCapacity = hugeCapacity(minCapacity); - // minCapacity is usually close to size, so this is a win: - elementData = Arrays.copyOf(elementData, newCapacity); - } -``` - -**int newCapacity = oldCapacity + (oldCapacity >> 1),所以 ArrayList 每次扩容之后容量都会变为原来的 1.5 倍左右(oldCapacity为偶数就是1.5倍,否则是1.5倍左右)!** 奇偶不同,比如 :10+10/2 = 15, 33+33/2=49。如果是奇数的话会丢掉小数. - -> ">>"(移位运算符):>>1 右移一位相当于除2,右移n位相当于除以 2 的 n 次方。这里 oldCapacity 明显右移了1位所以相当于oldCapacity /2。对于大数据的2进制运算,位移运算符比那些普通运算符的运算要快很多,因为程序仅仅移动一下而已,不去计算,这样提高了效率,节省了资源   - -**我们再来通过例子探究一下`grow()` 方法 :** - -- 当add第1个元素时,oldCapacity 为0,经比较后第一个if判断成立,newCapacity = minCapacity(为10)。但是第二个if判断不会成立,即newCapacity 不比 MAX_ARRAY_SIZE大,则不会进入 `hugeCapacity` 方法。数组容量为10,add方法中 return true,size增为1。 -- 当add第11个元素进入grow方法时,newCapacity为15,比minCapacity(为11)大,第一个if判断不成立。新容量没有大于数组最大size,不会进入hugeCapacity方法。数组容量扩为15,add方法中return true,size增为11。 -- 以此类推······ - -**这里补充一点比较重要,但是容易被忽视掉的知识点:** - -- java 中的 `length `属性是针对数组说的,比如说你声明了一个数组,想知道这个数组的长度则用到了 length 这个属性. -- java 中的 `length()` 方法是针对字符串说的,如果想看这个字符串的长度则用到 `length()` 这个方法. -- java 中的 `size()` 方法是针对泛型集合说的,如果想看这个泛型有多少个元素,就调用此方法来查看! - -### 5. `hugeCapacity()` 方法。 - -从上面 `grow()` 方法源码我们知道: 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 MAX_ARRAY_SIZE,如果minCapacity大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`。 - - -```java - private static int hugeCapacity(int minCapacity) { - if (minCapacity < 0) // overflow - throw new OutOfMemoryError(); - //对minCapacity和MAX_ARRAY_SIZE进行比较 - //若minCapacity大,将Integer.MAX_VALUE作为新数组的大小 - //若MAX_ARRAY_SIZE大,将MAX_ARRAY_SIZE作为新数组的大小 - //MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; - return (minCapacity > MAX_ARRAY_SIZE) ? - Integer.MAX_VALUE : - MAX_ARRAY_SIZE; - } -``` - - - -## 三 `System.arraycopy()` 和 `Arrays.copyOf()`方法 - - -阅读源码的话,我们就会发现 ArrayList 中大量调用了这两个方法。比如:我们上面讲的扩容操作以及`add(int index, E element)`、`toArray()` 等方法中都用到了该方法! - - -### 3.1 `System.arraycopy()` 方法 - -```java - /** - * 在此列表中的指定位置插入指定的元素。 - *先调用 rangeCheckForAdd 对index进行界限检查;然后调用 ensureCapacityInternal 方法保证capacity足够大; - *再将从index开始之后的所有成员后移一个位置;将element插入index位置;最后size加1。 - */ - public void add(int index, E element) { - rangeCheckForAdd(index); - - ensureCapacityInternal(size + 1); // Increments modCount!! - //arraycopy()方法实现数组自己复制自己 - //elementData:源数组;index:源数组中的起始位置;elementData:目标数组;index + 1:目标数组中的起始位置; size - index:要复制的数组元素的数量; - System.arraycopy(elementData, index, elementData, index + 1, size - index); - elementData[index] = element; - size++; - } -``` - -我们写一个简单的方法测试以下: - -```java -public class ArraycopyTest { - - public static void main(String[] args) { - // TODO Auto-generated method stub - int[] a = new int[10]; - a[0] = 0; - a[1] = 1; - a[2] = 2; - a[3] = 3; - System.arraycopy(a, 2, a, 3, 3); - a[2]=99; - for (int i = 0; i < a.length; i++) { - System.out.print(a[i] + " "); - } - } - -} -``` - -结果: - -``` -0 1 99 2 3 0 0 0 0 0 -``` - -### 3.2 `Arrays.copyOf()`方法 - -```java - /** - 以正确的顺序返回一个包含此列表中所有元素的数组(从第一个到最后一个元素); 返回的数组的运行时类型是指定数组的运行时类型。 - */ - public Object[] toArray() { - //elementData:要复制的数组;size:要复制的长度 - return Arrays.copyOf(elementData, size); - } -``` - -个人觉得使用 `Arrays.copyOf()`方法主要是为了给原有数组扩容,测试代码如下: - -```java -public class ArrayscopyOfTest { - - public static void main(String[] args) { - int[] a = new int[3]; - a[0] = 0; - a[1] = 1; - a[2] = 2; - int[] b = Arrays.copyOf(a, 10); - System.out.println("b.length"+b.length); - } -} -``` - -结果: - -``` -10 -``` - -### 3.3 两者联系和区别 - -**联系:** - -看两者源代码可以发现 copyOf() 内部实际调用了 `System.arraycopy()` 方法 - -**区别:** - -`arraycopy()` 需要目标数组,将原数组拷贝到你自己定义的数组里或者原数组,而且可以选择拷贝的起点和长度以及放入新数组中的位置 `copyOf()` 是系统自动在内部新建一个数组,并返回该数组。 - -## 四 `ensureCapacity`方法 - -ArrayList 源码中有一个 `ensureCapacity` 方法不知道大家注意到没有,这个方法 ArrayList 内部没有被调用过,所以很显然是提供给用户调用的,那么这个方法有什么作用呢? - -```java - /** - 如有必要,增加此 ArrayList 实例的容量,以确保它至少可以容纳由minimum capacity参数指定的元素数。 - * - * @param minCapacity 所需的最小容量 - */ - public void ensureCapacity(int minCapacity) { - int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) - // any size if not default element table - ? 0 - // larger than default for default empty table. It's already - // supposed to be at default size. - : DEFAULT_CAPACITY; - - if (minCapacity > minExpand) { - ensureExplicitCapacity(minCapacity); - } - } - -``` - -**最好在 add 大量元素之前用 `ensureCapacity` 方法,以减少增量重新分配的次数** - -我们通过下面的代码实际测试以下这个方法的效果: - -```java -public class EnsureCapacityTest { - public static void main(String[] args) { - ArrayList list = new ArrayList(); - final int N = 10000000; - long startTime = System.currentTimeMillis(); - for (int i = 0; i < N; i++) { - list.add(i); - } - long endTime = System.currentTimeMillis(); - System.out.println("使用ensureCapacity方法前:"+(endTime - startTime)); - - } -} -``` - -运行结果: - -``` -使用ensureCapacity方法前:2158 -``` - -```java -public class EnsureCapacityTest { - public static void main(String[] args) { - ArrayList list = new ArrayList(); - final int N = 10000000; - list = new ArrayList(); - long startTime1 = System.currentTimeMillis(); - list.ensureCapacity(N); - for (int i = 0; i < N; i++) { - list.add(i); - } - long endTime1 = System.currentTimeMillis(); - System.out.println("使用ensureCapacity方法后:"+(endTime1 - startTime1)); - } -} -``` - -运行结果: - -``` - -使用ensureCapacity方法前:1773 -``` - -通过运行结果,我们可以看出向 ArrayList 添加大量元素之前最好先使用`ensureCapacity` 方法,以减少增量重新分配的次数。 diff --git a/docs/java/collection/ArrayList.md b/docs/java/collection/ArrayList源码+扩容机制分析.md similarity index 58% rename from docs/java/collection/ArrayList.md rename to docs/java/collection/ArrayList源码+扩容机制分析.md index 43e81ba6..05cdb24a 100644 --- a/docs/java/collection/ArrayList.md +++ b/docs/java/collection/ArrayList源码+扩容机制分析.md @@ -1,34 +1,35 @@ - +## 1. ArrayList 简介 -- [ArrayList简介](#arraylist简介) -- [ArrayList核心源码](#arraylist核心源码) -- [ArrayList源码分析](#arraylist源码分析) - - [System.arraycopy\(\)和Arrays.copyOf\(\)方法](#systemarraycopy和arrayscopyof方法) - - [两者联系与区别](#两者联系与区别) - - [ArrayList核心扩容技术](#arraylist核心扩容技术) - - [内部类](#内部类) -- [ArrayList经典Demo](#arraylist经典demo) +`ArrayList` 的底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增长。在添加大量元素前,应用程序可以使用`ensureCapacity`操作来增加 `ArrayList` 实例的容量。这可以减少递增式再分配的数量。 - +`ArrayList`继承于 **`AbstractList`**,实现了 **`List`**, **`RandomAccess`**, **`Cloneable`**, **`java.io.Serializable`** 这些接口。 +```java -### ArrayList简介 -  ArrayList 的底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增长。在添加大量元素前,应用程序可以使用`ensureCapacity`操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。 - - 它继承于 **AbstractList**,实现了 **List**, **RandomAccess**, **Cloneable**, **java.io.Serializable** 这些接口。 - - 在我们学数据结构的时候就知道了线性表的顺序存储,插入删除元素的时间复杂度为**O(n)**,求表长以及增加元素,取第 i 元素的时间复杂度为**O(1)** +public class ArrayList extends AbstractList + implements List, RandomAccess, Cloneable, java.io.Serializable{ -  ArrayList 继承了AbstractList,实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。 + } +``` -  ArrayList 实现了**RandomAccess 接口**, RandomAccess 是一个标志接口,表明实现这个这个接口的 List 集合是支持**快速随机访问**的。在 ArrayList 中,我们即可以通过元素的序号快速获取元素对象,这就是快速随机访问。 +- `RandomAccess` 是一个标志接口,表明实现这个这个接口的 List 集合是支持**快速随机访问**的。在 `ArrayList` 中,我们即可以通过元素的序号快速获取元素对象,这就是快速随机访问。 +- `ArrayList` 实现了 **`Cloneable` 接口** ,即覆盖了函数`clone()`,能被克隆。 +- `ArrayList` 实现了 java.io.Serializable `接口,这意味着`ArrayList`支持序列化,能通过序列化去传输。 -  ArrayList 实现了**Cloneable 接口**,即覆盖了函数 clone(),**能被克隆**。 +### 1.1. Arraylist 和 Vector 的区别? -  ArrayList 实现**java.io.Serializable 接口**,这意味着ArrayList**支持序列化**,**能通过序列化去传输**。 +1. `ArrayList` 是 `List` 的主要实现类,底层使用 `Object[ ]`存储,适用于频繁的查找工作,线程不安全 ; +2. `Vector` 是 `List` 的古老实现类,底层使用 `Object[ ]`存储,线程安全的。 -  和 Vector 不同,**ArrayList 中的操作不是线程安全的**!所以,建议在单线程中才使用 ArrayList,而在多线程中可以选择 Vector 或者 CopyOnWriteArrayList。 -### ArrayList核心源码 +### 1.2. Arraylist 与 LinkedList 区别? + +1. **是否保证线程安全:** `ArrayList` 和 `LinkedList` 都是不同步的,也就是不保证线程安全; +2. **底层数据结构:** `Arraylist` 底层使用的是 **`Object` 数组**;`LinkedList` 底层使用的是 **双向链表** 数据结构(JDK1.6 之前为循环链表,JDK1.7 取消了循环。注意双向链表和双向循环链表的区别,下面有介绍到!) +3. **插入和删除是否受元素位置的影响:** ① **`ArrayList` 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。** 比如:执行`add(E e)`方法的时候, `ArrayList` 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是 O(1)。但是如果要在指定位置 i 插入和删除元素的话(`add(int index, E element)`)时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。 ② **`LinkedList` 采用链表存储,所以对于`add(E e)`方法的插入,删除元素时间复杂度不受元素位置的影响,近似 O(1),如果是要在指定位置`i`插入和删除元素的话(`(add(int index, E element)`) 时间复杂度近似为`o(n))`因为需要先移动到指定位置再插入。** +4. **是否支持快速随机访问:** `LinkedList` 不支持高效的随机元素访问,而 `ArrayList` 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于`get(int index)`方法)。 +5. **内存空间占用:** `ArrayList` 的空 间浪费主要体现在在 list 列表的结尾会预留一定的容量空间,而 `LinkedList` 的空间花费则体现在它的每一个元素都需要消耗比 `ArrayList` 更多的空间(因为要存放直接后继和直接前驱以及数据)。 + +## 2. ArrayList 核心源码解读 ```java package java.util; @@ -100,7 +101,7 @@ public class ArrayList extends AbstractList elementData = c.toArray(); //如果elementData数组的长度不为0 if ((size = elementData.length) != 0) { - // 如果elementData不是Object类型数据(c.toArray可能返回的不是Object类型的数组所以加上下面的语句用于判断) + // 如果elementData不是Object类型数据(c.toArray可能返回的不是Object类型的数组所以加上下面的语句用于判断) if (elementData.getClass() != Object[].class) //将原来不是Object类型的elementData数组的内容,赋值给新的Object类型的elementData数组 elementData = Arrays.copyOf(elementData, size, Object[].class); @@ -111,7 +112,7 @@ public class ArrayList extends AbstractList } /** - * 修改这个ArrayList实例的容量是列表的当前大小。 应用程序可以使用此操作来最小化ArrayList实例的存储。 + * 修改这个ArrayList实例的容量是列表的当前大小。 应用程序可以使用此操作来最小化ArrayList实例的存储。 */ public void trimToSize() { modCount++; @@ -195,7 +196,7 @@ public class ArrayList extends AbstractList } /** - *返回此列表中的元素数。 + *返回此列表中的元素数。 */ public int size() { return size; @@ -213,12 +214,12 @@ public class ArrayList extends AbstractList * 如果此列表包含指定的元素,则返回true 。 */ public boolean contains(Object o) { - //indexOf()方法:返回此列表中指定元素的首次出现的索引,如果此列表不包含此元素,则为-1 + //indexOf()方法:返回此列表中指定元素的首次出现的索引,如果此列表不包含此元素,则为-1 return indexOf(o) >= 0; } /** - *返回此列表中指定元素的首次出现的索引,如果此列表不包含此元素,则为-1 + *返回此列表中指定元素的首次出现的索引,如果此列表不包含此元素,则为-1 */ public int indexOf(Object o) { if (o == null) { @@ -251,7 +252,7 @@ public class ArrayList extends AbstractList } /** - * 返回此ArrayList实例的浅拷贝。 (元素本身不被复制。) + * 返回此ArrayList实例的浅拷贝。 (元素本身不被复制。) */ public Object clone() { try { @@ -267,7 +268,7 @@ public class ArrayList extends AbstractList } /** - *以正确的顺序(从第一个到最后一个元素)返回一个包含此列表中所有元素的数组。 + *以正确的顺序(从第一个到最后一个元素)返回一个包含此列表中所有元素的数组。 *返回的数组将是“安全的”,因为该列表不保留对它的引用。 (换句话说,这个方法必须分配一个新的数组)。 *因此,调用者可以自由地修改返回的数组。 此方法充当基于阵列和基于集合的API之间的桥梁。 */ @@ -276,11 +277,11 @@ public class ArrayList extends AbstractList } /** - * 以正确的顺序返回一个包含此列表中所有元素的数组(从第一个到最后一个元素); - *返回的数组的运行时类型是指定数组的运行时类型。 如果列表适合指定的数组,则返回其中。 - *否则,将为指定数组的运行时类型和此列表的大小分配一个新数组。 + * 以正确的顺序返回一个包含此列表中所有元素的数组(从第一个到最后一个元素); + *返回的数组的运行时类型是指定数组的运行时类型。 如果列表适合指定的数组,则返回其中。 + *否则,将为指定数组的运行时类型和此列表的大小分配一个新数组。 *如果列表适用于指定的数组,其余空间(即数组的列表数量多于此元素),则紧跟在集合结束后的数组中的元素设置为null 。 - *(这仅在调用者知道列表不包含任何空元素的情况下才能确定列表的长度。) + *(这仅在调用者知道列表不包含任何空元素的情况下才能确定列表的长度。) */ @SuppressWarnings("unchecked") public T[] toArray(T[] a) { @@ -311,7 +312,7 @@ public class ArrayList extends AbstractList } /** - * 用指定的元素替换此列表中指定位置的元素。 + * 用指定的元素替换此列表中指定位置的元素。 */ public E set(int index, E element) { //对index进行界限检查 @@ -324,7 +325,7 @@ public class ArrayList extends AbstractList } /** - * 将指定的元素追加到此列表的末尾。 + * 将指定的元素追加到此列表的末尾。 */ public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! @@ -334,7 +335,7 @@ public class ArrayList extends AbstractList } /** - * 在此列表中的指定位置插入指定的元素。 + * 在此列表中的指定位置插入指定的元素。 *先调用 rangeCheckForAdd 对index进行界限检查;然后调用 ensureCapacityInternal 方法保证capacity足够大; *再将从index开始之后的所有成员后移一个位置;将element插入index位置;最后size加1。 */ @@ -350,7 +351,7 @@ public class ArrayList extends AbstractList } /** - * 删除该列表中指定位置的元素。 将任何后续元素移动到左侧(从其索引中减去一个元素)。 + * 删除该列表中指定位置的元素。 将任何后续元素移动到左侧(从其索引中减去一个元素)。 */ public E remove(int index) { rangeCheck(index); @@ -363,7 +364,7 @@ public class ArrayList extends AbstractList System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work - //从列表中删除的元素 + //从列表中删除的元素 return oldValue; } @@ -402,7 +403,7 @@ public class ArrayList extends AbstractList } /** - * 从列表中删除所有元素。 + * 从列表中删除所有元素。 */ public void clear() { modCount++; @@ -488,7 +489,7 @@ public class ArrayList extends AbstractList } /** - * 从此列表中删除指定集合中包含的所有元素。 + * 从此列表中删除指定集合中包含的所有元素。 */ public boolean removeAll(Collection c) { Objects.requireNonNull(c); @@ -498,7 +499,7 @@ public class ArrayList extends AbstractList /** * 仅保留此列表中包含在指定集合中的元素。 - *换句话说,从此列表中删除其中不包含在指定集合中的所有元素。 + *换句话说,从此列表中删除其中不包含在指定集合中的所有元素。 */ public boolean retainAll(Collection c) { Objects.requireNonNull(c); @@ -508,8 +509,8 @@ public class ArrayList extends AbstractList /** * 从列表中的指定位置开始,返回列表中的元素(按正确顺序)的列表迭代器。 - *指定的索引表示初始调用将返回的第一个元素为next 。 初始调用previous将返回指定索引减1的元素。 - *返回的列表迭代器是fail-fast 。 + *指定的索引表示初始调用将返回的第一个元素为next 。 初始调用previous将返回指定索引减1的元素。 + *返回的列表迭代器是fail-fast 。 */ public ListIterator listIterator(int index) { if (index < 0 || index > size) @@ -518,7 +519,7 @@ public class ArrayList extends AbstractList } /** - *返回列表中的列表迭代器(按适当的顺序)。 + *返回列表中的列表迭代器(按适当的顺序)。 *返回的列表迭代器是fail-fast 。 */ public ListIterator listIterator() { @@ -526,21 +527,211 @@ public class ArrayList extends AbstractList } /** - *以正确的顺序返回该列表中的元素的迭代器。 - *返回的迭代器是fail-fast 。 + *以正确的顺序返回该列表中的元素的迭代器。 + *返回的迭代器是fail-fast 。 */ public Iterator iterator() { return new Itr(); } - + ``` -### ArrayList源码分析 -#### System.arraycopy()和Arrays.copyOf()方法 -  通过上面源码我们发现这两个实现数组复制的方法被广泛使用而且很多地方都特别巧妙。比如下面add(int index, E element)方法就很巧妙的用到了arraycopy()方法让数组自己复制自己实现让index开始之后的所有成员后移一个位置: -```java + +## 3. ArrayList 扩容机制分析 + +### 3.1. 先从 ArrayList 的构造函数说起 + +**ArrayList 有三种方式来初始化,构造方法源码如下:** + +```java + /** + * 默认初始容量大小 + */ + private static final int DEFAULT_CAPACITY = 10; + + + private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; + /** - * 在此列表中的指定位置插入指定的元素。 + *默认构造函数,使用初始容量10构造一个空列表(无参数构造) + */ + public ArrayList() { + this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; + } + + /** + * 带初始容量参数的构造函数。(用户自己指定容量) + */ + public ArrayList(int initialCapacity) { + if (initialCapacity > 0) {//初始容量大于0 + //创建initialCapacity大小的数组 + this.elementData = new Object[initialCapacity]; + } else if (initialCapacity == 0) {//初始容量等于0 + //创建空数组 + this.elementData = EMPTY_ELEMENTDATA; + } else {//初始容量小于0,抛出异常 + throw new IllegalArgumentException("Illegal Capacity: "+ + initialCapacity); + } + } + + + /** + *构造包含指定collection元素的列表,这些元素利用该集合的迭代器按顺序返回 + *如果指定的集合为null,throws NullPointerException。 + */ + public ArrayList(Collection c) { + elementData = c.toArray(); + if ((size = elementData.length) != 0) { + // c.toArray might (incorrectly) not return Object[] (see 6260652) + if (elementData.getClass() != Object[].class) + elementData = Arrays.copyOf(elementData, size, Object[].class); + } else { + // replace with empty array. + this.elementData = EMPTY_ELEMENTDATA; + } + } + +``` + +细心的同学一定会发现 :**以无参数构造方法创建 ArrayList 时,实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为 10。** 下面在我们分析 ArrayList 扩容时会讲到这一点内容! + +### 3.2. 一步一步分析 ArrayList 扩容机制 + +这里以无参构造函数创建的 ArrayList 为例分析 + +#### 3.2.1. 先来看 `add` 方法 + +```java + /** + * 将指定的元素追加到此列表的末尾。 + */ + public boolean add(E e) { + //添加元素之前,先调用ensureCapacityInternal方法 + ensureCapacityInternal(size + 1); // Increments modCount!! + //这里看到ArrayList添加元素的实质就相当于为数组赋值 + elementData[size++] = e; + return true; + } +``` + +> **注意** :JDK11 移除了 `ensureCapacityInternal()` 和 `ensureExplicitCapacity()` 方法 + +#### 3.2.2. 再来看看 `ensureCapacityInternal()` 方法 + +可以看到 `add` 方法 首先调用了`ensureCapacityInternal(size + 1)` + +```java + //得到最小扩容量 + private void ensureCapacityInternal(int minCapacity) { + if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { + // 获取默认的容量和传入参数的较大值 + minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); + } + + ensureExplicitCapacity(minCapacity); + } +``` + +**当 要 add 进第 1 个元素时,minCapacity 为 1,在 Math.max()方法比较后,minCapacity 为 10。** + +#### 3.2.3. `ensureExplicitCapacity()` 方法 + +如果调用 `ensureCapacityInternal()` 方法就一定会进过(执行)这个方法,下面我们来研究一下这个方法的源码! + +```java + //判断是否需要扩容 + private void ensureExplicitCapacity(int minCapacity) { + modCount++; + + // overflow-conscious code + if (minCapacity - elementData.length > 0) + //调用grow方法进行扩容,调用此方法代表已经开始扩容了 + grow(minCapacity); + } + +``` + +我们来仔细分析一下: + +- 当我们要 add 进第 1 个元素到 ArrayList 时,elementData.length 为 0 (因为还是一个空的 list),因为执行了 `ensureCapacityInternal()` 方法 ,所以 minCapacity 此时为 10。此时,`minCapacity - elementData.length > 0`成立,所以会进入 `grow(minCapacity)` 方法。 +- 当 add 第 2 个元素时,minCapacity 为 2,此时 e lementData.length(容量)在添加第一个元素后扩容成 10 了。此时,`minCapacity - elementData.length > 0` 不成立,所以不会进入 (执行)`grow(minCapacity)` 方法。 +- 添加第 3、4···到第 10 个元素时,依然不会执行 grow 方法,数组容量都为 10。 + +直到添加第 11 个元素,minCapacity(为 11)比 elementData.length(为 10)要大。进入 grow 方法进行扩容。 + +#### 3.2.4. `grow()` 方法 + +```java + /** + * 要分配的最大数组大小 + */ + private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + + /** + * ArrayList扩容的核心方法。 + */ + private void grow(int minCapacity) { + // oldCapacity为旧容量,newCapacity为新容量 + int oldCapacity = elementData.length; + //将oldCapacity 右移一位,其效果相当于oldCapacity /2, + //我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍, + int newCapacity = oldCapacity + (oldCapacity >> 1); + //然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量, + if (newCapacity - minCapacity < 0) + newCapacity = minCapacity; + // 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 MAX_ARRAY_SIZE, + //如果minCapacity大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`。 + if (newCapacity - MAX_ARRAY_SIZE > 0) + newCapacity = hugeCapacity(minCapacity); + // minCapacity is usually close to size, so this is a win: + elementData = Arrays.copyOf(elementData, newCapacity); + } +``` + +**int newCapacity = oldCapacity + (oldCapacity >> 1),所以 ArrayList 每次扩容之后容量都会变为原来的 1.5 倍左右(oldCapacity 为偶数就是 1.5 倍,否则是 1.5 倍左右)!** 奇偶不同,比如 :10+10/2 = 15, 33+33/2=49。如果是奇数的话会丢掉小数. + +> ">>"(移位运算符):>>1 右移一位相当于除 2,右移 n 位相当于除以 2 的 n 次方。这里 oldCapacity 明显右移了 1 位所以相当于 oldCapacity /2。对于大数据的 2 进制运算,位移运算符比那些普通运算符的运算要快很多,因为程序仅仅移动一下而已,不去计算,这样提高了效率,节省了资源 + +**我们再来通过例子探究一下`grow()` 方法 :** + +- 当 add 第 1 个元素时,oldCapacity 为 0,经比较后第一个 if 判断成立,newCapacity = minCapacity(为 10)。但是第二个 if 判断不会成立,即 newCapacity 不比 MAX_ARRAY_SIZE 大,则不会进入 `hugeCapacity` 方法。数组容量为 10,add 方法中 return true,size 增为 1。 +- 当 add 第 11 个元素进入 grow 方法时,newCapacity 为 15,比 minCapacity(为 11)大,第一个 if 判断不成立。新容量没有大于数组最大 size,不会进入 hugeCapacity 方法。数组容量扩为 15,add 方法中 return true,size 增为 11。 +- 以此类推······ + +**这里补充一点比较重要,但是容易被忽视掉的知识点:** + +- java 中的 `length`属性是针对数组说的,比如说你声明了一个数组,想知道这个数组的长度则用到了 length 这个属性. +- java 中的 `length()` 方法是针对字符串说的,如果想看这个字符串的长度则用到 `length()` 这个方法. +- java 中的 `size()` 方法是针对泛型集合说的,如果想看这个泛型有多少个元素,就调用此方法来查看! + +#### 3.2.5. `hugeCapacity()` 方法。 + +从上面 `grow()` 方法源码我们知道: 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 MAX_ARRAY_SIZE,如果 minCapacity 大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`。 + +```java + private static int hugeCapacity(int minCapacity) { + if (minCapacity < 0) // overflow + throw new OutOfMemoryError(); + //对minCapacity和MAX_ARRAY_SIZE进行比较 + //若minCapacity大,将Integer.MAX_VALUE作为新数组的大小 + //若MAX_ARRAY_SIZE大,将MAX_ARRAY_SIZE作为新数组的大小 + //MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + return (minCapacity > MAX_ARRAY_SIZE) ? + Integer.MAX_VALUE : + MAX_ARRAY_SIZE; + } +``` + +### 3.3. `System.arraycopy()` 和 `Arrays.copyOf()`方法 + +阅读源码的话,我们就会发现 ArrayList 中大量调用了这两个方法。比如:我们上面讲的扩容操作以及`add(int index, E element)`、`toArray()` 等方法中都用到了该方法! + +#### 3.3.1. `System.arraycopy()` 方法 + +```java + /** + * 在此列表中的指定位置插入指定的元素。 *先调用 rangeCheckForAdd 对index进行界限检查;然后调用 ensureCapacityInternal 方法保证capacity足够大; *再将从index开始之后的所有成员后移一个位置;将element插入index位置;最后size加1。 */ @@ -555,32 +746,87 @@ public class ArrayList extends AbstractList size++; } ``` -又如toArray()方法中用到了copyOf()方法 -```java - /** - *以正确的顺序(从第一个到最后一个元素)返回一个包含此列表中所有元素的数组。 - *返回的数组将是“安全的”,因为该列表不保留对它的引用。 (换句话说,这个方法必须分配一个新的数组)。 - *因此,调用者可以自由地修改返回的数组。 此方法充当基于阵列和基于集合的API之间的桥梁。 +我们写一个简单的方法测试以下: + +```java +public class ArraycopyTest { + + public static void main(String[] args) { + // TODO Auto-generated method stub + int[] a = new int[10]; + a[0] = 0; + a[1] = 1; + a[2] = 2; + a[3] = 3; + System.arraycopy(a, 2, a, 3, 3); + a[2]=99; + for (int i = 0; i < a.length; i++) { + System.out.print(a[i] + " "); + } + } + +} +``` + +结果: + +``` +0 1 99 2 3 0 0 0 0 0 +``` + +#### 3.3.2. `Arrays.copyOf()`方法 + +```java + /** + 以正确的顺序返回一个包含此列表中所有元素的数组(从第一个到最后一个元素); 返回的数组的运行时类型是指定数组的运行时类型。 */ public Object[] toArray() { //elementData:要复制的数组;size:要复制的长度 return Arrays.copyOf(elementData, size); } ``` -##### 两者联系与区别 -**联系:** -看两者源代码可以发现`copyOf()`内部调用了`System.arraycopy()`方法 -**区别:** -1. arraycopy()需要目标数组,将原数组拷贝到你自己定义的数组里,而且可以选择拷贝的起点和长度以及放入新数组中的位置 -2. copyOf()是系统自动在内部新建一个数组,并返回该数组。 -#### ArrayList 核心扩容技术 + +个人觉得使用 `Arrays.copyOf()`方法主要是为了给原有数组扩容,测试代码如下: + +```java +public class ArrayscopyOfTest { + + public static void main(String[] args) { + int[] a = new int[3]; + a[0] = 0; + a[1] = 1; + a[2] = 2; + int[] b = Arrays.copyOf(a, 10); + System.out.println("b.length"+b.length); + } +} +``` + +结果: + +``` +10 +``` + +#### 3.3.3. 两者联系和区别 + +**联系:** + +看两者源代码可以发现 `copyOf()`内部实际调用了 `System.arraycopy()` 方法 + +**区别:** + +`arraycopy()` 需要目标数组,将原数组拷贝到你自己定义的数组里或者原数组,而且可以选择拷贝的起点和长度以及放入新数组中的位置 `copyOf()` 是系统自动在内部新建一个数组,并返回该数组。 + +### 3.4. `ensureCapacity`方法 + +ArrayList 源码中有一个 `ensureCapacity` 方法不知道大家注意到没有,这个方法 ArrayList 内部没有被调用过,所以很显然是提供给用户调用的,那么这个方法有什么作用呢? + ```java -//下面是ArrayList的扩容机制 -//ArrayList的扩容机制提高了性能,如果每次只扩充一个, -//那么频繁的插入会导致频繁的拷贝,降低性能,而ArrayList的扩容机制避免了这种情况。 /** - * 如有必要,增加此ArrayList实例的容量,以确保它至少能容纳元素的数量 + 如有必要,增加此 ArrayList 实例的容量,以确保它至少可以容纳由minimum capacity参数指定的元素数。 + * * @param minCapacity 所需的最小容量 */ public void ensureCapacity(int minCapacity) { @@ -595,146 +841,56 @@ public class ArrayList extends AbstractList ensureExplicitCapacity(minCapacity); } } - //得到最小扩容量 - private void ensureCapacityInternal(int minCapacity) { - if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { - // 获取默认的容量和传入参数的较大值 - minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); + +``` + +**最好在 add 大量元素之前用 `ensureCapacity` 方法,以减少增量重新分配的次数** + +我们通过下面的代码实际测试以下这个方法的效果: + +```java +public class EnsureCapacityTest { + public static void main(String[] args) { + ArrayList list = new ArrayList(); + final int N = 10000000; + long startTime = System.currentTimeMillis(); + for (int i = 0; i < N; i++) { + list.add(i); + } + long endTime = System.currentTimeMillis(); + System.out.println("使用ensureCapacity方法前:"+(endTime - startTime)); + + } +} +``` + +运行结果: + +``` +使用ensureCapacity方法前:2158 +``` + +```java +public class EnsureCapacityTest { + public static void main(String[] args) { + ArrayList list = new ArrayList(); + final int N = 10000000; + list = new ArrayList(); + long startTime1 = System.currentTimeMillis(); + list.ensureCapacity(N); + for (int i = 0; i < N; i++) { + list.add(i); } - - ensureExplicitCapacity(minCapacity); - } - //判断是否需要扩容,上面两个方法都要调用 - private void ensureExplicitCapacity(int minCapacity) { - modCount++; - - // 如果说minCapacity也就是所需的最小容量大于保存ArrayList数据的数组的长度的话,就需要调用grow(minCapacity)方法扩容。 - //这个minCapacity到底为多少呢?举个例子在添加元素(add)方法中这个minCapacity的大小就为现在数组的长度加1 - if (minCapacity - elementData.length > 0) - //调用grow方法进行扩容,调用此方法代表已经开始扩容了 - grow(minCapacity); - } - -``` -```java - /** - * ArrayList扩容的核心方法。 - */ - private void grow(int minCapacity) { - //elementData为保存ArrayList数据的数组 - ///elementData.length求数组长度elementData.size是求数组中的元素个数 - // oldCapacity为旧容量,newCapacity为新容量 - int oldCapacity = elementData.length; - //将oldCapacity 右移一位,其效果相当于oldCapacity /2, - //我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍, - int newCapacity = oldCapacity + (oldCapacity >> 1); - //然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量, - if (newCapacity - minCapacity < 0) - newCapacity = minCapacity; - //再检查新容量是否超出了ArrayList所定义的最大容量, - //若超出了,则调用hugeCapacity()来比较minCapacity和 MAX_ARRAY_SIZE, - //如果minCapacity大于MAX_ARRAY_SIZE,则新容量则为Interger.MAX_VALUE,否则,新容量大小则为 MAX_ARRAY_SIZE。 - if (newCapacity - MAX_ARRAY_SIZE > 0) - newCapacity = hugeCapacity(minCapacity); - // minCapacity is usually close to size, so this is a win: - elementData = Arrays.copyOf(elementData, newCapacity); - } - -``` -  扩容机制代码已经做了详细的解释。另外值得注意的是大家很容易忽略的一个运算符:**移位运算符** -  **简介**:移位运算符就是在二进制的基础上对数字进行平移。按照平移的方向和填充数字的规则分为三种:<<(左移)>>(带符号右移)>>>(无符号右移)。 -  **作用**:**对于大数据的2进制运算,位移运算符比那些普通运算符的运算要快很多,因为程序仅仅移动一下而已,不去计算,这样提高了效率,节省了资源** -  比如这里:int newCapacity = oldCapacity + (oldCapacity >> 1); -右移一位相当于除2,右移n位相当于除以 2 的 n 次方。这里 oldCapacity 明显右移了1位所以相当于oldCapacity /2。 - -**另外需要注意的是:** - -1. java 中的**length 属性**是针对数组说的,比如说你声明了一个数组,想知道这个数组的长度则用到了 length 这个属性. - -2. java 中的**length()方法**是针对字 符串String说的,如果想看这个字符串的长度则用到 length()这个方法. - -3. .java 中的**size()方法**是针对泛型集合说的,如果想看这个泛型有多少个元素,就调用此方法来查看! - - -#### 内部类 -```java - (1)private class Itr implements Iterator - (2)private class ListItr extends Itr implements ListIterator - (3)private class SubList extends AbstractList implements RandomAccess - (4)static final class ArrayListSpliterator implements Spliterator -``` -  ArrayList有四个内部类,其中的**Itr是实现了Iterator接口**,同时重写了里面的**hasNext()**, **next()**, **remove()** 等方法;其中的**ListItr** 继承 **Itr**,实现了**ListIterator接口**,同时重写了**hasPrevious()**, **nextIndex()**, **previousIndex()**, **previous()**, **set(E e)**, **add(E e)** 等方法,所以这也可以看出了 **Iterator和ListIterator的区别:** ListIterator在Iterator的基础上增加了添加对象,修改对象,逆向遍历等方法,这些是Iterator不能实现的。 -### ArrayList经典Demo - -```java -package list; -import java.util.ArrayList; -import java.util.Iterator; - -public class ArrayListDemo { - - public static void main(String[] srgs){ - ArrayList arrayList = new ArrayList(); - - System.out.printf("Before add:arrayList.size() = %d\n",arrayList.size()); - - arrayList.add(1); - arrayList.add(3); - arrayList.add(5); - arrayList.add(7); - arrayList.add(9); - System.out.printf("After add:arrayList.size() = %d\n",arrayList.size()); - - System.out.println("Printing elements of arrayList"); - // 三种遍历方式打印元素 - // 第一种:通过迭代器遍历 - System.out.print("通过迭代器遍历:"); - Iterator it = arrayList.iterator(); - while(it.hasNext()){ - System.out.print(it.next() + " "); - } - System.out.println(); - - // 第二种:通过索引值遍历 - System.out.print("通过索引值遍历:"); - for(int i = 0; i < arrayList.size(); i++){ - System.out.print(arrayList.get(i) + " "); - } - System.out.println(); - - // 第三种:for循环遍历 - System.out.print("for循环遍历:"); - for(Integer number : arrayList){ - System.out.print(number + " "); - } - - // toArray用法 - // 第一种方式(最常用) - Integer[] integer = arrayList.toArray(new Integer[0]); - - // 第二种方式(容易理解) - Integer[] integer1 = new Integer[arrayList.size()]; - arrayList.toArray(integer1); - - // 抛出异常,java不支持向下转型 - //Integer[] integer2 = new Integer[arrayList.size()]; - //integer2 = arrayList.toArray(); - System.out.println(); - - // 在指定位置添加元素 - arrayList.add(2,2); - // 删除指定位置上的元素 - arrayList.remove(2); - // 删除指定元素 - arrayList.remove((Object)3); - // 判断arrayList是否包含5 - System.out.println("ArrayList contains 5 is: " + arrayList.contains(5)); - - // 清空ArrayList - arrayList.clear(); - // 判断ArrayList是否为空 - System.out.println("ArrayList is empty: " + arrayList.isEmpty()); + long endTime1 = System.currentTimeMillis(); + System.out.println("使用ensureCapacity方法后:"+(endTime1 - startTime1)); } } ``` +运行结果: + +``` +使用ensureCapacity方法前:1773 +``` + +通过运行结果,我们可以看出向 ArrayList 添加大量元素之前最好先使用`ensureCapacity` 方法,以减少增量重新分配的次数。 diff --git a/docs/java/collection/ConcurrentHashMap.md b/docs/java/collection/ConcurrentHashMap源码+底层数据结构分析.md similarity index 100% rename from docs/java/collection/ConcurrentHashMap.md rename to docs/java/collection/ConcurrentHashMap源码+底层数据结构分析.md diff --git a/docs/java/collection/HashMap.md b/docs/java/collection/HashMap(JDK1.8)源码+底层数据结构分析.md similarity index 100% rename from docs/java/collection/HashMap.md rename to docs/java/collection/HashMap(JDK1.8)源码+底层数据结构分析.md diff --git a/docs/java/collection/LinkedList.md b/docs/java/collection/LinkedList源码分析.md similarity index 99% rename from docs/java/collection/LinkedList.md rename to docs/java/collection/LinkedList源码分析.md index d26bc752..a8159a34 100644 --- a/docs/java/collection/LinkedList.md +++ b/docs/java/collection/LinkedList源码分析.md @@ -23,8 +23,10 @@ List list=Collections.synchronizedList(new LinkedList(...)); ``` ## 内部结构分析 **如下图所示:** -![LinkedList内部结构](https://user-gold-cdn.xitu.io/2018/3/19/1623e363fe0450b0?w=600&h=481&f=jpeg&s=18502) + +![LinkedList内部结构](images/linkedlist/LinkedList内部结构.png) 看完了图之后,我们再看LinkedList类中的一个**内部私有类Node**就很好理解了: + ```java private static class Node { E item;//节点值 diff --git a/docs/java/collection/images/linkedlist/LinkedList内部结构.png b/docs/java/collection/images/linkedlist/LinkedList内部结构.png new file mode 100644 index 00000000..b70a9372 Binary files /dev/null and b/docs/java/collection/images/linkedlist/LinkedList内部结构.png differ diff --git a/docs/java/java-naming-conventions.md b/docs/java/java-naming-conventions.md deleted file mode 100644 index d205f5f5..00000000 --- a/docs/java/java-naming-conventions.md +++ /dev/null @@ -1,416 +0,0 @@ -> 原文链接:https://www.cnblogs.com/liqiangchn/p/12000361.html - -简洁清爽的代码风格应该是大多数工程师所期待的。在工作中笔者常常因为起名字而纠结,夸张点可以说是编程5分钟,命名两小时!究竟为什么命名成为了工作中的拦路虎。 - -每个公司都有不同的标准,目的是为了保持统一,减少沟通成本,提升团队研发效能。所以本文中是笔者结合阿里巴巴开发规范,以及工作中的见闻针对Java领域相关命名进行整理和总结,仅供参考。 - -## 一,Java中的命名规范 - -好的命名能体现出代码的特征,含义或者是用途,让阅读者可以根据名称的含义快速厘清程序的脉络。不同语言中采用的命名形式大相径庭,Java中常用到的命名形式共有三种,既首字母大写的UpperCamelCase,首字母小写的lowerCamelCase以及全部大写的并用下划线分割单词的UPPER_CAMEL_UNSER_SCORE。通常约定,**类一般采用大驼峰命名,方法和局部变量使用小驼峰命名,而大写下划线命名通常是常量和枚举中使用。** - -| 类型 | 约束 | 例 | -| :----: | :----------------------------------------------------------: | :--------------------------------------------: | -| 项目名 | 全部小写,多个单词用中划线分隔‘-’ | spring-cloud | -| 包名 | 全部小写 | com.alibaba.fastjson | -| 类名 | 单词首字母大写 | Feature, ParserConfig,DefaultFieldDeserializer | -| 变量名 | 首字母小写,多个单词组成时,除首个单词,其他单词首字母都要大写 | password, userName | -| 常量名 | 全部大写,多个单词,用'_'分隔 | CACHE_EXPIRED_TIME | -| 方法 | 同变量 | read(), readObject(), getById() | - -## 二,包命名 - -**包名**统一使用**小写**,**点分隔符**之间有且仅有一个自然语义的英文单词或者多个单词自然连接到一块(如 springframework,deepspace不需要使用任何分割)。包名统一使用单数形式,如果类命有复数含义,则可以使用复数形式。 - -包名的构成可以分为以下几四部分【前缀】 【发起者名】【项目名】【模块名】。常见的前缀可以分为以下几种: - -| 前缀名 | 例 | 含义 | -| :-------------: | :----------------------------: | :----------------------------------------------------------: | -| indi(或onem ) | indi.发起者名.项目名.模块名.…… | 个体项目,指个人发起,但非自己独自完成的项目,可公开或私有项目,copyright主要属于发起者。 | -| pers | pers.个人名.项目名.模块名.…… | 个人项目,指个人发起,独自完成,可分享的项目,copyright主要属于个人 | -| priv | priv.个人名.项目名.模块名.…… | 私有项目,指个人发起,独自完成,非公开的私人使用的项目,copyright属于个人。 | -| team | team.团队名.项目名.模块名.…… | 团队项目,指由团队发起,并由该团队开发的项目,copyright属于该团队所有 | -| 顶级域名 | com.公司名.项目名.模块名.…… | 公司项目,copyright由项目发起的公司所有 | - -## 三,类命名 - -**类名使用大驼峰命名形式**,类命通常时**名词或名词短语**,接口名除了用名词和名词短语以外,还可以使用形容词或形容词短语,如Cloneable,Callable等,表示实现该接口的类有某种功能或能力。对于测试类则以它要测试的类开头,以Test结尾,如HashMapTest。 - -对于一些特殊特有名词缩写也可以使用全大写命名,比如XMLHttpRequest,不过笔者认为缩写三个字母以内都大写,超过三个字母则按照要给单词算。这个没有标准如阿里巴巴中fastjson用JSONObject作为类命,而google则使用JsonObjectRequest命名,对于这种特殊的缩写,原则是统一就好。 - -| 属性 | 约束 | 例 | -| -------------- | ----------------------------------------- | ------------------------------------------------------------ | -| 抽象类 | Abstract 或者 Base 开头 | BaseUserService | -| 枚举类 | Enum 作为后缀 | GenderEnum | -| 工具类 | Utils作为后缀 | StringUtils | -| 异常类 | Exception结尾 | RuntimeException | -| 接口实现类 | 接口名+ Impl | UserServiceImpl | -| 领域模型相关 | /DO/DTO/VO/DAO | 正例:UserDAO 反例: UserDo, UserDao | -| 设计模式相关类 | Builder,Factory等 | 当使用到设计模式时,需要使用对应的设计模式作为后缀,如ThreadFactory | -| 处理特定功能的 | Handler,Predicate, Validator | 表示处理器,校验器,断言,这些类工厂还有配套的方法名如handle,predicate,validate | -| 测试类 | Test结尾 | UserServiceTest, 表示用来测试UserService类的 | -| MVC分层 | Controller,Service,ServiceImpl,DAO后缀 | UserManageController,UserManageDAO | - -## 四,方法 - -**方法命名采用小驼峰的形式**,首字小写,往后的每个单词首字母都要大写。 和类名不同的是,方法命名一般为**动词或动词短语**,与参数或参数名共同组成动宾短语,即动词 + 名词。一个好的函数名一般能通过名字直接获知该函数实现什么样的功能。 - -### 4.1 返回真伪值的方法 - -注:Prefix-前缀,Suffix-后缀,Alone-单独使用 - -| 位置 | 单词 | 意义 | 例 | -| ------ | ------ | ------------------------------------------------------------ | ------------- | -| Prefix | is | 对象是否符合期待的状态 | isValid | -| Prefix | can | 对象**能否执行**所期待的动作 | canRemove | -| Prefix | should | 调用方执行某个命令或方法是**好还是不好**,**应不应该**,或者说**推荐还是不推荐** | shouldMigrate | -| Prefix | has | 对象**是否持有**所期待的数据和属性 | hasObservers | -| Prefix | needs | 调用方**是否需要**执行某个命令或方法 | needsMigrate | - -### 4.2 用来检查的方法 - -| 单词 | 意义 | 例 | -| -------- | ---------------------------------------------------- | -------------- | -| ensure | 检查是否为期待的状态,不是则抛出异常或返回error code | ensureCapacity | -| validate | 检查是否为正确的状态,不是则抛出异常或返回error code | validateInputs | - -### 4.3 按需求才执行的方法 - -| 位置 | 单词 | 意义 | 例 | -| ------ | --------- | ----------------------------------------- | ---------------------- | -| Suffix | IfNeeded | 需要的时候执行,不需要的时候什么都不做 | drawIfNeeded | -| Prefix | might | 同上 | mightCreate | -| Prefix | try | 尝试执行,失败时抛出异常或是返回errorcode | tryCreate | -| Suffix | OrDefault | 尝试执行,失败时返回默认值 | getOrDefault | -| Suffix | OrElse | 尝试执行、失败时返回实际参数中指定的值 | getOrElse | -| Prefix | force | 强制尝试执行。error抛出异常或是返回值 | forceCreate, forceStop | - -### 4.4 异步相关方法 - -| 位置 | 单词 | 意义 | 例 | -| --------------- | ------------ | -------------------------------------------- | --------------------- | -| Prefix | blocking | 线程阻塞方法 | blockingGetUser | -| Suffix | InBackground | 执行在后台的线程 | doInBackground | -| Suffix | Async | 异步方法 | sendAsync | -| Suffix | Sync | 对应已有异步方法的同步方法 | sendSync | -| Prefix or Alone | schedule | Job和Task放入队列 | schedule, scheduleJob | -| Prefix or Alone | post | 同上 | postJob | -| Prefix or Alone | execute | 执行异步方法(注:我一般拿这个做同步方法名) | execute, executeTask | -| Prefix or Alone | start | 同上 | start, startJob | -| Prefix or Alone | cancel | 停止异步方法 | cancel, cancelJob | -| Prefix or Alone | stop | 同上 | stop, stopJob | - -### 4.5 回调方法 - -| 位置 | 单词 | 意义 | 例 | -| ------ | ------ | -------------------------- | ------------ | -| Prefix | on | 事件发生时执行 | onCompleted | -| Prefix | before | 事件发生前执行 | beforeUpdate | -| Prefix | pre | 同上 | preUpdate | -| Prefix | will | 同上 | willUpdate | -| Prefix | after | 事件发生后执行 | afterUpdate | -| Prefix | post | 同上 | postUpdate | -| Prefix | did | 同上 | didUpdate | -| Prefix | should | 确认事件是否可以发生时执行 | shouldUpdate | - -### 4.6 操作对象生命周期的方法 - -| 单词 | 意义 | 例 | -| ---------- | ------------------------------ | --------------- | -| initialize | 初始化。也可作为延迟初始化使用 | initialize | -| pause | 暂停 | onPause ,pause | -| stop | 停止 | onStop,stop | -| abandon | 销毁的替代 | abandon | -| destroy | 同上 | destroy | -| dispose | 同上 | dispose | - -### 4.7 与集合操作相关的方法 - -| 单词 | 意义 | 例 | -| -------- | ---------------------------- | ---------- | -| contains | 是否持有与指定对象相同的对象 | contains | -| add | 添加 | addJob | -| append | 添加 | appendJob | -| insert | 插入到下标n | insertJob | -| put | 添加与key对应的元素 | putJob | -| remove | 移除元素 | removeJob | -| enqueue | 添加到队列的最末位 | enqueueJob | -| dequeue | 从队列中头部取出并移除 | dequeueJob | -| push | 添加到栈头 | pushJob | -| pop | 从栈头取出并移除 | popJob | -| peek | 从栈头取出但不移除 | peekJob | -| find | 寻找符合条件的某物 | findById | - -### 4.8 与数据相关的方法 - -| 单词 | 意义 | 例 | -| ------ | -------------------------------------- | ------------- | -| create | 新创建 | createAccount | -| new | 新创建 | newAccount | -| from | 从既有的某物新建,或是从其他的数据新建 | fromConfig | -| to | 转换 | toString | -| update | 更新既有某物 | updateAccount | -| load | 读取 | loadAccount | -| fetch | 远程读取 | fetchAccount | -| delete | 删除 | deleteAccount | -| remove | 删除 | removeAccount | -| save | 保存 | saveAccount | -| store | 保存 | storeAccount | -| commit | 保存 | commitChange | -| apply | 保存或应用 | applyChange | -| clear | 清除数据或是恢复到初始状态 | clearAll | -| reset | 清除数据或是恢复到初始状态 | resetAll | - -### 4.9 成对出现的动词 - -| 单词 | 意义 | -| -------------- | ----------------- | -| get获取 | set 设置 | -| add 增加 | remove 删除 | -| create 创建 | destory 移除 | -| start 启动 | stop 停止 | -| open 打开 | close 关闭 | -| read 读取 | write 写入 | -| load 载入 | save 保存 | -| create 创建 | destroy 销毁 | -| begin 开始 | end 结束 | -| backup 备份 | restore 恢复 | -| import 导入 | export 导出 | -| split 分割 | merge 合并 | -| inject 注入 | extract 提取 | -| attach 附着 | detach 脱离 | -| bind 绑定 | separate 分离 | -| view 查看 | browse 浏览 | -| edit 编辑 | modify 修改 | -| select 选取 | mark 标记 | -| copy 复制 | paste 粘贴 | -| undo 撤销 | redo 重做 | -| insert 插入 | delete 移除 | -| add 加入 | append 添加 | -| clean 清理 | clear 清除 | -| index 索引 | sort 排序 | -| find 查找 | search 搜索 | -| increase 增加 | decrease 减少 | -| play 播放 | pause 暂停 | -| launch 启动 | run 运行 | -| compile 编译 | execute 执行 | -| debug 调试 | trace 跟踪 | -| observe 观察 | listen 监听 | -| build 构建 | publish 发布 | -| input 输入 | output 输出 | -| encode 编码 | decode 解码 | -| encrypt 加密 | decrypt 解密 | -| compress 压缩 | decompress 解压缩 | -| pack 打包 | unpack 解包 | -| parse 解析 | emit 生成 | -| connect 连接 | disconnect 断开 | -| send 发送 | receive 接收 | -| download 下载 | upload 上传 | -| refresh 刷新 | synchronize 同步 | -| update 更新 | revert 复原 | -| lock 锁定 | unlock 解锁 | -| check out 签出 | check in 签入 | -| submit 提交 | commit 交付 | -| push 推 | pull 拉 | -| expand 展开 | collapse 折叠 | -| begin 起始 | end 结束 | -| start 开始 | finish 完成 | -| enter 进入 | exit 退出 | -| abort 放弃 | quit 离开 | -| obsolete 废弃 | depreciate 废旧 | -| collect 收集 | aggregate 聚集 | - -## 五,变量&常量命名 - -### 5.1 变量命名 - -变量是指在程序运行中可以改变其值的量,包括成员变量和局部变量。变量名由多单词组成时,第一个单词的首字母小写,其后单词的首字母大写,俗称骆驼式命名法(也称驼峰命名法),如 computedValues,index、变量命名时,尽量简短且能清楚的表达变量的作用,命名体现具体的业务含义即可。 - -变量名不应以下划线或美元符号开头,尽管这在语法上是允许的。变量名应简短且富于描述。变量名的选用应该易于记忆,即,能够指出其用途。尽量避免单个字符的变量名,除非是一次性的临时变量。pojo中的布尔变量,都不要加is(数据库中的布尔字段全都要加 is_ 前缀)。 - -### 5.2 常量命名 - -常量命名CONSTANT_CASE,一般采用全部大写(作为方法参数时除外),单词间用下划线分割。那么什么是常量呢? - -常量是在作用域内保持不变的值,一般使用final进行修饰。一般分为三种,全局常量(public static final修饰),类内常量(private static final 修饰)以及局部常量(方法内,或者参数中的常量),局部常量比较特殊,通常采用小驼峰命名即可。 - -```java -/** - * 一个demo - * - * @author Jann Lee - * @date 2019-12-07 00:25 - **/ -public class HelloWorld { - - /** - * 局部常量(正例) - */ - public static final long USER_MESSAGE_CACHE_EXPIRE_TIME = 3600; - - /** - * 局部常量(反例,命名不清晰) - */ - public static final long MESSAGE_CACHE_TIME = 3600; - - /** - * 全局常量 - */ - private static final String ERROR_MESSAGE = " error message"; - - /** - * 成员变量 - */ - private int currentUserId; - - /** - * 控制台打印 {@code message} 信息 - * - * @param message 消息体,局部常量 - */ - public void sayHello(final String message){ - System.out.println("Hello world!"); - } - -} -``` - -常量一般都有自己的业务含义,**不要害怕长度过长而进行省略或者缩写**。如,用户消息缓存过期时间的表示,那种方式更佳清晰,交给你来评判。 - -## 通用命名规则[#](https://www.cnblogs.com/liqiangchn/p/12000361.html#450918152) - -1. 尽量不要使用拼音;杜绝拼音和英文混用。对于一些通用的表示或者难以用英文描述的可以采用拼音,一旦采用拼音就坚决不能和英文混用。 - 正例: BeiJing, HangZhou - 反例: validateCanShu -2. 命名过程中尽量不要出现特殊的字符,常量除外。 -3. 尽量不要和jdk或者框架中已存在的类重名,也不能使用java中的关键字命名。 -4. 妙用介词,如for(可以用同音的4代替), to(可用同音的2代替), from, with,of等。 - 如类名采用User4RedisDO,方法名getUserInfoFromRedis,convertJson2Map等。 - -## 六,代码注解 - -### 6.1 注解的原则 - -好的命名增加代码阅读性,代码的命名往往有严格的限制。而注解不同,程序员往往可以自由发挥,单并不意味着可以为所欲为之胡作非为。优雅的注解通常要满足三要素。 - -1. Nothing is strange - 没有注解的代码对于阅读者非常不友好,哪怕代码写的在清除,阅读者至少从心理上会有抵触,更何况代码中往往有许多复杂的逻辑,所以一定要写注解,不仅要记录代码的逻辑,还有说清楚修改的逻辑。 -2. Less is more - 从代码维护角度来讲,代码中的注解一定是精华中的精华。合理清晰的命名能让代码易于理解,对于逻辑简单且命名规范,能够清楚表达代码功能的代码不需要注解。滥用注解会增加额外的负担,更何况大部分都是废话。 - -```java -// 根据id获取信息【废话注解】 -getMessageById(id) -``` - -1. Advance with the time - 注解应该随着代码的变动而改变,注解表达的信息要与代码中完全一致。通常情况下修改代码后一定要修改注解。 - -### 6.2 注解格式 - -注解大体上可以分为两种,一种是javadoc注解,另一种是简单注解。javadoc注解可以生成JavaAPI为外部用户提供有效的支持javadoc注解通常在使用IDEA,或者Eclipse等开发工具时都可以自动生成,也支持自定义的注解模板,仅需要对对应的字段进行解释。参与同一项目开发的同学,尽量设置成相同的注解模板。 - -#### a. 包注解 - -包注解在工作中往往比较特殊,通过包注解可以快速知悉当前包下代码是用来实现哪些功能,强烈建议工作中加上,尤其是对于一些比较复杂的包,包注解一般在包的根目录下,名称统一为package-info.java。 - -```java -/** - * 落地也质量检测 - * 1. 用来解决什么问题 - * 对广告主投放的广告落地页进行性能检测,模拟不同的系统,如Android,IOS等; 模拟不同的网络:2G,3G,4G,wifi等 - * - * 2. 如何实现 - * 基于chrome浏览器,用chromedriver驱动浏览器,设置对应的网络,OS参数,获取到浏览器返回结果。 - * - * 注意: 网络环境配置信息{@link cn.mycookies.landingpagecheck.meta.NetWorkSpeedEnum}目前使用是常规速度,可以根据实际情况进行调整 - * - * @author cruder - * @time 2019/12/7 20:3 下午 - */ -package cn.mycookies.landingpagecheck; -``` - -#### b. 类注接 - -javadoc注解中,每个类都必须有注解。 - -```java -/** -* Copyright (C), 2019-2020, Jann balabala... -* -* 类的介绍:这是一个用来做什么事情的类,有哪些功能,用到的技术..... -* -* @author 类创建者姓名 保持对齐 -* @date 创建日期 保持对齐 -* @version 版本号 保持对齐 -*/ -``` - -#### c. 属性注解 - -在每个属性前面必须加上属性注释,通常有以下两种形式,至于怎么选择,你高兴就好,不过一个项目中要保持统一。 - -```java -/** 提示信息 */ -private String userName; -/** - * 密码 - */ -private String password; -``` - -#### d. 方法注释 - -在每个方法前面必须加上方法注释,对于方法中的每个参数,以及返回值都要有说明。 - -```java -/** - * 方法的详细说明,能干嘛,怎么实现的,注意事项... - * - * @param xxx 参数1的使用说明, 能否为null - * @return 返回结果的说明, 不同情况下会返回怎样的结果 - * @throws 异常类型 注明从此类方法中抛出异常的说明 - */ -``` - -#### e. 构造方法注释 - -在每个构造方法前面必须加上注释,注释模板如下: - -```java - /** - * 构造方法的详细说明 - * - * @param xxx 参数1的使用说明, 能否为null - * @throws 异常类型 注明从此类方法中抛出异常的说明 - */ -``` - -而简单注解往往是需要工程师字节定义,在使用注解时应该注意以下几点: - -1. 枚举类的各个属性值都要使用注解,枚举可以理解为是常量,通常不会发生改变,通常会被在多个地方引用,对枚举的修改和添加属性通常会带来很大的影响。 -2. 保持排版整洁,不要使用行尾注释;双斜杠和星号之后要用1个空格分隔。 - -```java -id = 1;// 反例:不要使用行尾注释 -//反例:换行符与注释之间没有缩进 -int age = 18; -// 正例:姓名 -String name; -/** - * 1. 多行注释 - * - * 2. 对于不同的逻辑说明,可以用空行分隔 - */ -``` - -## 总结 - -无论是命名和注解,他们的目的都是为了让代码和工程师进行对话,增强代码的可读性,可维护性。优秀的代码往往能够见名知意,注解往往是对命名的补充和完善。命名太南了! - -参考文献: - -- 《码出高效》 -- https://www.cnblogs.com/wangcp-2014/p/10215620.html -- https://qiita.com/KeithYokoma/items/2193cf79ba76563e3db6 -- https://google.github.io/styleguide/javaguide.html#s2.1-file-name \ No newline at end of file diff --git a/docs/java/java-programming-problem/Java程序设计题.md b/docs/java/java-programming-problem/Java程序设计题.md deleted file mode 100644 index 112c1bff..00000000 --- a/docs/java/java-programming-problem/Java程序设计题.md +++ /dev/null @@ -1,130 +0,0 @@ - - -- [0.0.1. 泛型的实际应用:实现最小值函数](#001-%e6%b3%9b%e5%9e%8b%e7%9a%84%e5%ae%9e%e9%99%85%e5%ba%94%e7%94%a8%e5%ae%9e%e7%8e%b0%e6%9c%80%e5%b0%8f%e5%80%bc%e5%87%bd%e6%95%b0) -- [0.0.2. 使用数组实现栈](#002-%e4%bd%bf%e7%94%a8%e6%95%b0%e7%bb%84%e5%ae%9e%e7%8e%b0%e6%a0%88) -- [0.0.3. 实现线程安全的 LRU 缓存](#003-%e5%ae%9e%e7%8e%b0%e7%ba%bf%e7%a8%8b%e5%ae%89%e5%85%a8%e7%9a%84-lru-%e7%bc%93%e5%ad%98) - - - -### 0.0.1. 泛型的实际应用:实现最小值函数 - -自己设计一个泛型的获取数组最小值的函数.并且这个方法只能接受Number的子类并且实现了Comparable接口。 - -```java -//注意:Number并没有实现Comparable -private static > T min(T[] values) { - if (values == null || values.length == 0) return null; - T min = values[0]; - for (int i = 1; i < values.length; i++) { - if (min.compareTo(values[i]) > 0) min = values[i]; - } - return min; -} -``` - -测试: - -```java -int minInteger = min(new Integer[]{1, 2, 3});//result:1 -double minDouble = min(new Double[]{1.2, 2.2, -1d});//result:-1d -String typeError = min(new String[]{"1","3"});//报错 -``` -### 0.0.2. 使用数组实现栈 - -**自己实现一个栈,要求这个栈具有`push()`、`pop()`(返回栈顶元素并出栈)、`peek()` (返回栈顶元素不出栈)、`isEmpty()`、`size()`这些基本的方法。** - -提示:每次入栈之前先判断栈的容量是否够用,如果不够用就用`Arrays.copyOf()`进行扩容; - -```java -public class MyStack { - private int[] storage;//存放栈中元素的数组 - private int capacity;//栈的容量 - private int count;//栈中元素数量 - private static final int GROW_FACTOR = 2; - - //不带初始容量的构造方法。默认容量为8 - public MyStack() { - this.capacity = 8; - this.storage=new int[8]; - this.count = 0; - } - - //带初始容量的构造方法 - public MyStack(int initialCapacity) { - if (initialCapacity < 1) - throw new IllegalArgumentException("Capacity too small."); - - this.capacity = initialCapacity; - this.storage = new int[initialCapacity]; - this.count = 0; - } - - //入栈 - public void push(int value) { - if (count == capacity) { - ensureCapacity(); - } - storage[count++] = value; - } - - //确保容量大小 - private void ensureCapacity() { - int newCapacity = capacity * GROW_FACTOR; - storage = Arrays.copyOf(storage, newCapacity); - capacity = newCapacity; - } - - //返回栈顶元素并出栈 - private int pop() { - if (count == 0) - throw new IllegalArgumentException("Stack is empty."); - count--; - return storage[count]; - } - - //返回栈顶元素不出栈 - private int peek() { - if (count == 0){ - throw new IllegalArgumentException("Stack is empty."); - }else { - return storage[count-1]; - } - } - - //判断栈是否为空 - private boolean isEmpty() { - return count == 0; - } - - //返回栈中元素的个数 - private int size() { - return count; - } - -} - -``` - -验证 - -```java -MyStack myStack = new MyStack(3); -myStack.push(1); -myStack.push(2); -myStack.push(3); -myStack.push(4); -myStack.push(5); -myStack.push(6); -myStack.push(7); -myStack.push(8); -System.out.println(myStack.peek());//8 -System.out.println(myStack.size());//8 -for (int i = 0; i < 8; i++) { - System.out.println(myStack.pop()); -} -System.out.println(myStack.isEmpty());//true -myStack.pop();//报错:java.lang.IllegalArgumentException: Stack is empty. -``` - - - diff --git a/docs/java/java-programming-problem/a-thread-safe-implementation-of-lru-cache.md b/docs/java/java-programming-problem/a-thread-safe-implementation-of-lru-cache.md deleted file mode 100644 index 7c367e11..00000000 --- a/docs/java/java-programming-problem/a-thread-safe-implementation-of-lru-cache.md +++ /dev/null @@ -1,441 +0,0 @@ - - -- [1. LRU 缓存介绍](#1-lru-%e7%bc%93%e5%ad%98%e4%bb%8b%e7%bb%8d) -- [2. ConcurrentLinkedQueue简单介绍](#2-concurrentlinkedqueue%e7%ae%80%e5%8d%95%e4%bb%8b%e7%bb%8d) -- [3. ReadWriteLock简单介绍](#3-readwritelock%e7%ae%80%e5%8d%95%e4%bb%8b%e7%bb%8d) -- [4. ScheduledExecutorService 简单介绍](#4-scheduledexecutorservice-%e7%ae%80%e5%8d%95%e4%bb%8b%e7%bb%8d) -- [5. 徒手撸一个线程安全的 LRU 缓存](#5-%e5%be%92%e6%89%8b%e6%92%b8%e4%b8%80%e4%b8%aa%e7%ba%bf%e7%a8%8b%e5%ae%89%e5%85%a8%e7%9a%84-lru-%e7%bc%93%e5%ad%98) - - [5.1. 实现方法](#51-%e5%ae%9e%e7%8e%b0%e6%96%b9%e6%b3%95) - - [5.2. 原理](#52-%e5%8e%9f%e7%90%86) - - [5.3. put方法具体流程分析](#53-put%e6%96%b9%e6%b3%95%e5%85%b7%e4%bd%93%e6%b5%81%e7%a8%8b%e5%88%86%e6%9e%90) - - [5.4. 源码](#54-%e6%ba%90%e7%a0%81) -- [6. 实现一个线程安全并且带有过期时间的 LRU 缓存](#6-%e5%ae%9e%e7%8e%b0%e4%b8%80%e4%b8%aa%e7%ba%bf%e7%a8%8b%e5%ae%89%e5%85%a8%e5%b9%b6%e4%b8%94%e5%b8%a6%e6%9c%89%e8%bf%87%e6%9c%9f%e6%97%b6%e9%97%b4%e7%9a%84-lru-%e7%bc%93%e5%ad%98) - - - -最近被读者问到“不用LinkedHashMap的话,如何实现一个线程安全的 LRU 缓存?网上的代码太杂太乱,Guide哥哥能不能帮忙写一个?”。 - -*划重点,手写一个 LRU 缓存在面试中还是挺常见的!* - -很多人就会问了:“网上已经有这么多现成的缓存了!为什么面试官还要我们自己实现一个呢?” 。咳咳咳,当然是为了面试需要。哈哈!开个玩笑,我个人觉得更多地是为了学习吧!今天Guide哥教大家: - -1. 实现一个线程安全的 LRU 缓存 -2. 实现一个线程安全并且带有过期时间的 LRU 缓存 - -考虑到了线程安全性我们使用了 `ConcurrentHashMap` 、`ConcurrentLinkedQueue` 这两个线程安全的集合。另外,还用到 `ReadWriteLock`(读写锁)。为了实现带有过期时间的缓存,我们用到了 `ScheduledExecutorService`来做定时任务执行。 - -如果有任何不对或者需要完善的地方,请帮忙指出! - -### 1. LRU 缓存介绍 - -**LRU (Least Recently Used,最近最少使用)是一种缓存淘汰策略。** - -LRU缓存指的是当缓存大小已达到最大分配容量的时候,如果再要去缓存新的对象数据的话,就需要将缓存中最近访问最少的对象删除掉以便给新来的数据腾出空间。 - -### 2. ConcurrentLinkedQueue简单介绍 - -**ConcurrentLinkedQueue是一个基于单向链表的无界无锁线程安全的队列,适合在高并发环境下使用,效率比较高。** 我们在使用的时候,可以就把它理解为我们经常接触的数据结构——队列,不过是增加了多线程下的安全性保证罢了。**和普通队列一样,它也是按照先进先出(FIFO)的规则对接点进行排序。** 另外,队列元素中不可以放置null元素。 - -`ConcurrentLinkedQueue` 整个继承关系如下图所示: - -![](./../../../media/pictures/java/my-lru-cache/ConcurrentLinkedQueue-Diagram.png) - -`ConcurrentLinkedQueue中`最主要的两个方法是:`offer(value)`和`poll()`,分别实现队列的两个重要的操作:入队和出队(`offer(value)`等价于 `add(value)`)。 - -我们添加一个元素到队列的时候,它会添加到队列的尾部,当我们获取一个元素时,它会返回队列头部的元素。 - -![单链表](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/单链表2.png) - -利用`ConcurrentLinkedQueue`队列先进先出的特性,每当我们 `put`/`get`(缓存被使用)元素的时候,我们就将这个元素存放在队列尾部,这样就能保证队列头部的元素是最近最少使用的。 - -### 3. ReadWriteLock简单介绍 - -`ReadWriteLock` 是一个接口,位于`java.util.concurrent.locks`包下,里面只有两个方法分别返回读锁和写锁: - -```java -public interface ReadWriteLock { - /** - * 返回读锁 - */ - Lock readLock(); - - /** - * 返回写锁 - */ - Lock writeLock(); -} -``` - -`ReentrantReadWriteLock` 是`ReadWriteLock`接口的具体实现类。 - -**读写锁还是比较适合缓存这种读多写少的场景。读写锁可以保证多个线程和同时读取,但是只有一个线程可以写入。** - -读写锁的特点是:写锁和写锁互斥,读锁和写锁互斥,读锁之间不互斥。也就说:同一时刻只能有一个线程写,但是可以有多个线程 -读。读写之间是互斥的,两者不能同时发生(当进行写操作时,同一时刻其他线程的读操作会被阻塞;当进行读操作时,同一时刻所有线程的写操作会被阻塞)。 - -另外,**同一个线程持有写锁时是可以申请读锁,但是持有读锁的情况下不可以申请写锁。** - -### 4. ScheduledExecutorService 简单介绍 - -`ScheduledExecutorService` 是一个接口,`ScheduledThreadPoolExecutor` 是其主要实现类。 - -![](./../../../media/pictures/java/my-lru-cache/ScheduledThreadPoolExecutor-diagram.png) - -**`ScheduledThreadPoolExecutor` 主要用来在给定的延迟后运行任务,或者定期执行任务。** 这个在实际项目用到的比较少,因为有其他方案选择比如`quartz`。但是,在一些需求比较简单的场景下还是非常有用的! - -**`ScheduledThreadPoolExecutor` 使用的任务队列 `DelayQueue` 封装了一个 `PriorityQueue`,`PriorityQueue` 会对队列中的任务进行排序,执行所需时间短的放在前面先被执行,如果执行所需时间相同则先提交的任务将被先执行。** - -### 5. 徒手撸一个线程安全的 LRU 缓存 - -#### 5.1. 实现方法 - - `ConcurrentHashMap` + `ConcurrentLinkedQueue` +`ReadWriteLock` - -#### 5.2. 原理 - -`ConcurrentHashMap` 是线程安全的Map,我们可以利用它缓存 key,value形式的数据。`ConcurrentLinkedQueue`是一个线程安全的基于链表的队列(先进先出),我们可以用它来维护 key 。每当我们put/get(缓存被使用)元素的时候,我们就将这个元素对应的 key 存放在队列尾部,这样就能保证队列头部的元素是最近最少使用的。当我们的缓存容量不够的时候,我们直接移除队列头部对应的key以及这个key对应的缓存即可! - -另外,我们用到了`ReadWriteLock`(读写锁)来保证线程安全。 - -#### 5.3. put方法具体流程分析 - -为了方便大家理解,我将代码中比较重要的 `put(key,value)`方法的原理图画了出来,如下图所示: - -![](./../../../media/pictures/java/my-lru-cache/MyLRUCachePut.png) - - - -#### 5.4. 源码 - -```java -/** - * @author shuang.kou - *

- * 使用 ConcurrentHashMap+ConcurrentLinkedQueue+ReadWriteLock实现线程安全的 LRU 缓存 - * 这里只是为了学习使用,本地缓存推荐使用 Guava 自带的,使用 Spring 的话,推荐使用Spring Cache - */ -public class MyLruCache { - - /** - * 缓存的最大容量 - */ - private final int maxCapacity; - - private ConcurrentHashMap cacheMap; - private ConcurrentLinkedQueue keys; - /** - * 读写锁 - */ - private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); - private Lock writeLock = readWriteLock.writeLock(); - private Lock readLock = readWriteLock.readLock(); - - public MyLruCache(int maxCapacity) { - if (maxCapacity < 0) { - throw new IllegalArgumentException("Illegal max capacity: " + maxCapacity); - } - this.maxCapacity = maxCapacity; - cacheMap = new ConcurrentHashMap<>(maxCapacity); - keys = new ConcurrentLinkedQueue<>(); - } - - public V put(K key, V value) { - // 加写锁 - writeLock.lock(); - try { - //1.key是否存在于当前缓存 - if (cacheMap.containsKey(key)) { - moveToTailOfQueue(key); - cacheMap.put(key, value); - return value; - } - //2.是否超出缓存容量,超出的话就移除队列头部的元素以及其对应的缓存 - if (cacheMap.size() == maxCapacity) { - System.out.println("maxCapacity of cache reached"); - removeOldestKey(); - } - //3.key不存在于当前缓存。将key添加到队列的尾部并且缓存key及其对应的元素 - keys.add(key); - cacheMap.put(key, value); - return value; - } finally { - writeLock.unlock(); - } - } - - public V get(K key) { - //加读锁 - readLock.lock(); - try { - //key是否存在于当前缓存 - if (cacheMap.containsKey(key)) { - // 存在的话就将key移动到队列的尾部 - moveToTailOfQueue(key); - return cacheMap.get(key); - } - //不存在于当前缓存中就返回Null - return null; - } finally { - readLock.unlock(); - } - } - - public V remove(K key) { - writeLock.lock(); - try { - //key是否存在于当前缓存 - if (cacheMap.containsKey(key)) { - // 存在移除队列和Map中对应的Key - keys.remove(key); - return cacheMap.remove(key); - } - //不存在于当前缓存中就返回Null - return null; - } finally { - writeLock.unlock(); - } - } - - /** - * 将元素添加到队列的尾部(put/get的时候执行) - */ - private void moveToTailOfQueue(K key) { - keys.remove(key); - keys.add(key); - } - - /** - * 移除队列头部的元素以及其对应的缓存 (缓存容量已满的时候执行) - */ - private void removeOldestKey() { - K oldestKey = keys.poll(); - if (oldestKey != null) { - cacheMap.remove(oldestKey); - } - } - - public int size() { - return cacheMap.size(); - } - -} -``` - -**非并发环境测试:** - -```java -MyLruCache myLruCache = new MyLruCache<>(3); -myLruCache.put(1, "Java"); -System.out.println(myLruCache.get(1));// Java -myLruCache.remove(1); -System.out.println(myLruCache.get(1));// null -myLruCache.put(2, "C++"); -myLruCache.put(3, "Python"); -System.out.println(myLruCache.get(2));//C++ -myLruCache.put(4, "C"); -myLruCache.put(5, "PHP"); -System.out.println(myLruCache.get(2));// C++ -``` - -**并发环境测试:** - -我们初始化了一个固定容量为 10 的线程池和count为10的`CountDownLatch`。我们将1000000次操作分10次添加到线程池,然后我们等待线程池执行完成这10次操作。 - - -```java -int threadNum = 10; -int batchSize = 100000; -//init cache -MyLruCache myLruCache = new MyLruCache<>(batchSize * 10); -//init thread pool with 10 threads -ExecutorService fixedThreadPool = Executors.newFixedThreadPool(threadNum); -//init CountDownLatch with 10 count -CountDownLatch latch = new CountDownLatch(threadNum); -AtomicInteger atomicInteger = new AtomicInteger(0); -long startTime = System.currentTimeMillis(); -for (int t = 0; t < threadNum; t++) { - fixedThreadPool.submit(() -> { - for (int i = 0; i < batchSize; i++) { - int value = atomicInteger.incrementAndGet(); - myLruCache.put("id" + value, value); - } - latch.countDown(); - }); -} -//wait for 10 threads to complete the task -latch.await(); -fixedThreadPool.shutdown(); -System.out.println("Cache size:" + myLruCache.size());//Cache size:1000000 -long endTime = System.currentTimeMillis(); -long duration = endTime - startTime; -System.out.println(String.format("Time cost:%dms", duration));//Time cost:511ms -``` - -### 6. 实现一个线程安全并且带有过期时间的 LRU 缓存 - -实际上就是在我们上面时间的LRU缓存的基础上加上一个定时任务去删除缓存,单纯利用 JDK 提供的类,我们实现定时任务的方式有很多种: - -1. `Timer` :不被推荐,多线程会存在问题。 -2. `ScheduledExecutorService` :定时器线程池,可以用来替代 `Timer` -3. `DelayQueue` :延时队列 -4. `quartz` :一个很火的开源任务调度框架,很多其他框架都是基于 `quartz` 开发的,比如当当网的`elastic-job `就是基于`quartz`二次开发之后的分布式调度解决方案 -5. ...... - -最终我们选择了 `ScheduledExecutorService`,主要原因是它易用(基于`DelayQueue`做了很多封装)并且基本能满足我们的大部分需求。 - -我们在我们上面实现的线程安全的 LRU 缓存基础上,简单稍作修改即可!我们增加了一个方法: - -```java -private void removeAfterExpireTime(K key, long expireTime) { - scheduledExecutorService.schedule(() -> { - //过期后清除该键值对 - cacheMap.remove(key); - keys.remove(key); - }, expireTime, TimeUnit.MILLISECONDS); -} -``` -我们put元素的时候,如果通过这个方法就能直接设置过期时间。 - - -**完整源码如下:** - -```java -/** - * @author shuang.kou - *

- * 使用 ConcurrentHashMap+ConcurrentLinkedQueue+ReadWriteLock+ScheduledExecutorService实现线程安全的 LRU 缓存 - * 这里只是为了学习使用,本地缓存推荐使用 Guava 自带的,使用 Spring 的话,推荐使用Spring Cache - */ -public class MyLruCacheWithExpireTime { - - /** - * 缓存的最大容量 - */ - private final int maxCapacity; - - private ConcurrentHashMap cacheMap; - private ConcurrentLinkedQueue keys; - /** - * 读写锁 - */ - private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); - private Lock writeLock = readWriteLock.writeLock(); - private Lock readLock = readWriteLock.readLock(); - - private ScheduledExecutorService scheduledExecutorService; - - public MyLruCacheWithExpireTime(int maxCapacity) { - if (maxCapacity < 0) { - throw new IllegalArgumentException("Illegal max capacity: " + maxCapacity); - } - this.maxCapacity = maxCapacity; - cacheMap = new ConcurrentHashMap<>(maxCapacity); - keys = new ConcurrentLinkedQueue<>(); - scheduledExecutorService = Executors.newScheduledThreadPool(3); - } - - public V put(K key, V value, long expireTime) { - // 加写锁 - writeLock.lock(); - try { - //1.key是否存在于当前缓存 - if (cacheMap.containsKey(key)) { - moveToTailOfQueue(key); - cacheMap.put(key, value); - return value; - } - //2.是否超出缓存容量,超出的话就移除队列头部的元素以及其对应的缓存 - if (cacheMap.size() == maxCapacity) { - System.out.println("maxCapacity of cache reached"); - removeOldestKey(); - } - //3.key不存在于当前缓存。将key添加到队列的尾部并且缓存key及其对应的元素 - keys.add(key); - cacheMap.put(key, value); - if (expireTime > 0) { - removeAfterExpireTime(key, expireTime); - } - return value; - } finally { - writeLock.unlock(); - } - } - - public V get(K key) { - //加读锁 - readLock.lock(); - try { - //key是否存在于当前缓存 - if (cacheMap.containsKey(key)) { - // 存在的话就将key移动到队列的尾部 - moveToTailOfQueue(key); - return cacheMap.get(key); - } - //不存在于当前缓存中就返回Null - return null; - } finally { - readLock.unlock(); - } - } - - public V remove(K key) { - writeLock.lock(); - try { - //key是否存在于当前缓存 - if (cacheMap.containsKey(key)) { - // 存在移除队列和Map中对应的Key - keys.remove(key); - return cacheMap.remove(key); - } - //不存在于当前缓存中就返回Null - return null; - } finally { - writeLock.unlock(); - } - } - - /** - * 将元素添加到队列的尾部(put/get的时候执行) - */ - private void moveToTailOfQueue(K key) { - keys.remove(key); - keys.add(key); - } - - /** - * 移除队列头部的元素以及其对应的缓存 (缓存容量已满的时候执行) - */ - private void removeOldestKey() { - K oldestKey = keys.poll(); - if (oldestKey != null) { - cacheMap.remove(oldestKey); - } - } - - private void removeAfterExpireTime(K key, long expireTime) { - scheduledExecutorService.schedule(() -> { - //过期后清除该键值对 - cacheMap.remove(key); - keys.remove(key); - }, expireTime, TimeUnit.MILLISECONDS); - } - - public int size() { - return cacheMap.size(); - } - -} - -``` - -**测试效果:** - -```java -MyLruCacheWithExpireTime myLruCache = new MyLruCacheWithExpireTime<>(3); -myLruCache.put(1,"Java",3000); -myLruCache.put(2,"C++",3000); -myLruCache.put(3,"Python",1500); -System.out.println(myLruCache.size());//3 -Thread.sleep(2000); -System.out.println(myLruCache.size());//2 -``` diff --git a/docs/java/jvm/JDK监控和故障处理工具总结.md b/docs/java/jvm/JDK监控和故障处理工具总结.md index d5cb29de..c8263de0 100644 --- a/docs/java/jvm/JDK监控和故障处理工具总结.md +++ b/docs/java/jvm/JDK监控和故障处理工具总结.md @@ -1,5 +1,3 @@ -点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。 - - [JDK 监控和故障处理工具总结](#jdk-监控和故障处理工具总结) @@ -325,13 +323,3 @@ VisualVM 提供在 Java 虚拟机 (Java Virutal Machine, JVM) 上运行的 Java - - - -## 公众号 - -如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。 - -**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本公众号后台回复 **"Java面试突击"** 即可免费领取! - -**Java工程师必备学习资源:** 一些Java工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。 - -![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png) diff --git a/docs/java/jvm/JVM垃圾回收.md b/docs/java/jvm/JVM垃圾回收.md index 95363151..f6ede785 100644 --- a/docs/java/jvm/JVM垃圾回收.md +++ b/docs/java/jvm/JVM垃圾回收.md @@ -1,5 +1,3 @@ -点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。 - - [JVM 垃圾回收](#jvm-垃圾回收) @@ -334,7 +332,7 @@ JDK1.2 以后,Java 对引用的概念进行了扩充,将引用分为强引 为了解决效率问题,“复制”收集算法出现了。它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。 -公众号 +![复制算法](./pictures/jvm垃圾回收/90984624.png) ### 3.3 标记-整理算法 @@ -401,7 +399,7 @@ Parallel Scavenge 收集器也是使用复制算法的多线程收集器,它 ``` -**Parallel Scavenge 收集器关注点是吞吐量(高效率的利用 CPU)。CMS 等垃圾收集器的关注点更多的是用户线程的停顿时间(提高用户体验)。所谓吞吐量就是 CPU 中用于运行用户代码的时间与 CPU 总消耗时间的比值。** Parallel Scavenge 收集器提供了很多参数供用户找到最合适的停顿时间或最大吞吐量,如果对于收集器运作不太了解的话,手工优化存在困难的话可以选择把内存管理优化交给虚拟机去完成也是一个不错的选择。 +**Parallel Scavenge 收集器关注点是吞吐量(高效率的利用 CPU)。CMS 等垃圾收集器的关注点更多的是用户线程的停顿时间(提高用户体验)。所谓吞吐量就是 CPU 中用于运行用户代码的时间与 CPU 总消耗时间的比值。** Parallel Scavenge 收集器提供了很多参数供用户找到最合适的停顿时间或最大吞吐量,如果对于收集器运作不太了解,手工优化存在困难的时候,使用Parallel Scavenge收集器配合自适应调节策略,把内存管理优化交给虚拟机去完成也是一个不错的选择。 **新生代采用复制算法,老年代采用标记-整理算法。** ![Parallel Scavenge 收集器 ](./pictures/jvm垃圾回收/parllel-scavenge收集器.png) @@ -473,16 +471,6 @@ G1 收集器的运作大致分为以下几个步骤: - https://my.oschina.net/hosee/blog/644618 - -## 公众号 - -如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。 - -**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"Java面试突击"** 即可免费领取! - -**Java工程师必备学习资源:** 一些Java工程师常用学习资源[公众号](#公众号)后台回复关键字 **“1”** 即可免费无套路获取。 - -![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png) - diff --git a/docs/java/jvm/Java内存区域.md b/docs/java/jvm/Java内存区域.md index 0cae9f0a..42dc7b7c 100644 --- a/docs/java/jvm/Java内存区域.md +++ b/docs/java/jvm/Java内存区域.md @@ -1,5 +1,3 @@ -点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。 - - [Java 内存区域详解](#java-内存区域详解) @@ -492,13 +490,3 @@ i4=i5+i6 true - - - 深入解析String#intern - -## 公众号 - -如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。 - -**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"Java面试突击"** 即可免费领取! - -**Java工程师必备学习资源:** 一些Java工程师常用学习资源[公众号](#公众号)后台回复关键字 **“1”** 即可免费无套路获取。 - -![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png) diff --git a/docs/java/jvm/类加载器.md b/docs/java/jvm/类加载器.md index 1d0a826f..37394a92 100644 --- a/docs/java/jvm/类加载器.md +++ b/docs/java/jvm/类加载器.md @@ -1,5 +1,3 @@ -点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。 - - [回顾一下类加载过程](#回顾一下类加载过程) @@ -134,13 +132,5 @@ protected Class loadClass(String name, boolean resolve) - - -### 公众号 -如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。 - -**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"Java面试突击"** 即可免费领取! - -**Java工程师必备学习资源:** 一些Java工程师常用学习资源[公众号](#公众号)后台回复关键字 **“1”** 即可免费无套路获取。 - -![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png) diff --git a/docs/java/jvm/类加载过程.md b/docs/java/jvm/类加载过程.md index 9330c581..947e1ad1 100644 --- a/docs/java/jvm/类加载过程.md +++ b/docs/java/jvm/类加载过程.md @@ -1,7 +1,3 @@ -点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。 - -> 公众号JavaGuide 后台回复关键字“1”,免费获取JavaGuide配套的Java工程师必备学习资源(文末有公众号二维码)。 - - [类的生命周期](#类的生命周期) diff --git a/docs/java/jvm/类文件结构.md b/docs/java/jvm/类文件结构.md index d766aa80..8620fab9 100644 --- a/docs/java/jvm/类文件结构.md +++ b/docs/java/jvm/类文件结构.md @@ -212,13 +212,3 @@ Class 文件存储格式中对方法的描述与对字段的描述几乎采用 - - - 《实战 Java 虚拟机》 - -## 公众号 - -如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。 - -**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"Java面试突击"** 即可免费领取! - -**Java工程师必备学习资源:** 一些Java工程师常用学习资源[公众号](#公众号)后台回复关键字 **“1”** 即可免费无套路获取。 - -![我的公众号](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/167598cd2e17b8ec.png) diff --git a/docs/java/Multithread/JavaConcurrencyBasicsCommonInterviewQuestionsSummary.md b/docs/java/multi-thread/2020最新Java并发基础常见面试题总结.md similarity index 100% rename from docs/java/Multithread/JavaConcurrencyBasicsCommonInterviewQuestionsSummary.md rename to docs/java/multi-thread/2020最新Java并发基础常见面试题总结.md diff --git a/docs/java/Multithread/JavaConcurrencyAdvancedCommonInterviewQuestions.md b/docs/java/multi-thread/2020最新Java并发进阶常见面试题总结.md similarity index 100% rename from docs/java/Multithread/JavaConcurrencyAdvancedCommonInterviewQuestions.md rename to docs/java/multi-thread/2020最新Java并发进阶常见面试题总结.md diff --git a/docs/java/Multithread/AQS.md b/docs/java/multi-thread/AQS原理以及AQS同步组件总结.md similarity index 100% rename from docs/java/Multithread/AQS.md rename to docs/java/multi-thread/AQS原理以及AQS同步组件总结.md diff --git a/docs/java/Multithread/Atomic.md b/docs/java/multi-thread/Atomic原子类总结.md similarity index 100% rename from docs/java/Multithread/Atomic.md rename to docs/java/multi-thread/Atomic原子类总结.md diff --git a/docs/java/Multithread/ThreadLocal(未完成).md b/docs/java/multi-thread/ThreadLocal(未完成).md similarity index 100% rename from docs/java/Multithread/ThreadLocal(未完成).md rename to docs/java/multi-thread/ThreadLocal(未完成).md diff --git a/docs/java/Multithread/images/ThreadLocal内部类.png b/docs/java/multi-thread/images/ThreadLocal内部类.png similarity index 100% rename from docs/java/Multithread/images/ThreadLocal内部类.png rename to docs/java/multi-thread/images/ThreadLocal内部类.png diff --git a/docs/java/Multithread/images/interview-questions/synchronized关键字.png b/docs/java/multi-thread/images/interview-questions/synchronized关键字.png similarity index 100% rename from docs/java/Multithread/images/interview-questions/synchronized关键字.png rename to docs/java/multi-thread/images/interview-questions/synchronized关键字.png diff --git a/docs/java/multi-thread/images/java线程池学习总结/CachedThreadPool-execute.png b/docs/java/multi-thread/images/java线程池学习总结/CachedThreadPool-execute.png new file mode 100644 index 00000000..8b2ede8a Binary files /dev/null and b/docs/java/multi-thread/images/java线程池学习总结/CachedThreadPool-execute.png differ diff --git a/docs/java/multi-thread/images/java线程池学习总结/Executors工具类.png b/docs/java/multi-thread/images/java线程池学习总结/Executors工具类.png new file mode 100644 index 00000000..87658aa3 Binary files /dev/null and b/docs/java/multi-thread/images/java线程池学习总结/Executors工具类.png differ diff --git a/docs/java/multi-thread/images/java线程池学习总结/Executor框架的使用示意图.png b/docs/java/multi-thread/images/java线程池学习总结/Executor框架的使用示意图.png new file mode 100644 index 00000000..5cc148dd Binary files /dev/null and b/docs/java/multi-thread/images/java线程池学习总结/Executor框架的使用示意图.png differ diff --git a/docs/java/multi-thread/images/java线程池学习总结/FixedThreadPool.png b/docs/java/multi-thread/images/java线程池学习总结/FixedThreadPool.png new file mode 100644 index 00000000..fc1c7034 Binary files /dev/null and b/docs/java/multi-thread/images/java线程池学习总结/FixedThreadPool.png differ diff --git a/docs/java/multi-thread/images/java线程池学习总结/ScheduledThreadPoolExecutor执行周期任务步骤.png b/docs/java/multi-thread/images/java线程池学习总结/ScheduledThreadPoolExecutor执行周期任务步骤.png new file mode 100644 index 00000000..c56521d2 Binary files /dev/null and b/docs/java/multi-thread/images/java线程池学习总结/ScheduledThreadPoolExecutor执行周期任务步骤.png differ diff --git a/docs/java/multi-thread/images/java线程池学习总结/ScheduledThreadPoolExecutor机制.png b/docs/java/multi-thread/images/java线程池学习总结/ScheduledThreadPoolExecutor机制.png new file mode 100644 index 00000000..bae0dc5b Binary files /dev/null and b/docs/java/multi-thread/images/java线程池学习总结/ScheduledThreadPoolExecutor机制.png differ diff --git a/docs/java/multi-thread/images/java线程池学习总结/SingleThreadExecutor.png b/docs/java/multi-thread/images/java线程池学习总结/SingleThreadExecutor.png new file mode 100644 index 00000000..c933674f Binary files /dev/null and b/docs/java/multi-thread/images/java线程池学习总结/SingleThreadExecutor.png differ diff --git a/docs/java/multi-thread/images/java线程池学习总结/threadpoolexecutor构造函数.png b/docs/java/multi-thread/images/java线程池学习总结/threadpoolexecutor构造函数.png new file mode 100644 index 00000000..30c29859 Binary files /dev/null and b/docs/java/multi-thread/images/java线程池学习总结/threadpoolexecutor构造函数.png differ diff --git a/docs/java/multi-thread/images/java线程池学习总结/任务的执行相关接口.png b/docs/java/multi-thread/images/java线程池学习总结/任务的执行相关接口.png new file mode 100644 index 00000000..6aebd60b Binary files /dev/null and b/docs/java/multi-thread/images/java线程池学习总结/任务的执行相关接口.png differ diff --git a/docs/java/multi-thread/images/java线程池学习总结/图解线程池实现原理.png b/docs/java/multi-thread/images/java线程池学习总结/图解线程池实现原理.png new file mode 100644 index 00000000..bc661944 Binary files /dev/null and b/docs/java/multi-thread/images/java线程池学习总结/图解线程池实现原理.png differ diff --git a/docs/java/multi-thread/images/java线程池学习总结/线程池各个参数之间的关系.png b/docs/java/multi-thread/images/java线程池学习总结/线程池各个参数之间的关系.png new file mode 100644 index 00000000..d609943b Binary files /dev/null and b/docs/java/multi-thread/images/java线程池学习总结/线程池各个参数之间的关系.png differ diff --git a/docs/java/Multithread/images/thread-local/1.png b/docs/java/multi-thread/images/thread-local/1.png similarity index 100% rename from docs/java/Multithread/images/thread-local/1.png rename to docs/java/multi-thread/images/thread-local/1.png diff --git a/docs/java/Multithread/images/thread-local/10.png b/docs/java/multi-thread/images/thread-local/10.png similarity index 100% rename from docs/java/Multithread/images/thread-local/10.png rename to docs/java/multi-thread/images/thread-local/10.png diff --git a/docs/java/Multithread/images/thread-local/11.png b/docs/java/multi-thread/images/thread-local/11.png similarity index 100% rename from docs/java/Multithread/images/thread-local/11.png rename to docs/java/multi-thread/images/thread-local/11.png diff --git a/docs/java/Multithread/images/thread-local/12.png b/docs/java/multi-thread/images/thread-local/12.png similarity index 100% rename from docs/java/Multithread/images/thread-local/12.png rename to docs/java/multi-thread/images/thread-local/12.png diff --git a/docs/java/Multithread/images/thread-local/13.png b/docs/java/multi-thread/images/thread-local/13.png similarity index 100% rename from docs/java/Multithread/images/thread-local/13.png rename to docs/java/multi-thread/images/thread-local/13.png diff --git a/docs/java/Multithread/images/thread-local/14.png b/docs/java/multi-thread/images/thread-local/14.png similarity index 100% rename from docs/java/Multithread/images/thread-local/14.png rename to docs/java/multi-thread/images/thread-local/14.png diff --git a/docs/java/Multithread/images/thread-local/15.png b/docs/java/multi-thread/images/thread-local/15.png similarity index 100% rename from docs/java/Multithread/images/thread-local/15.png rename to docs/java/multi-thread/images/thread-local/15.png diff --git a/docs/java/Multithread/images/thread-local/16.png b/docs/java/multi-thread/images/thread-local/16.png similarity index 100% rename from docs/java/Multithread/images/thread-local/16.png rename to docs/java/multi-thread/images/thread-local/16.png diff --git a/docs/java/Multithread/images/thread-local/17.png b/docs/java/multi-thread/images/thread-local/17.png similarity index 100% rename from docs/java/Multithread/images/thread-local/17.png rename to docs/java/multi-thread/images/thread-local/17.png diff --git a/docs/java/Multithread/images/thread-local/18.png b/docs/java/multi-thread/images/thread-local/18.png similarity index 100% rename from docs/java/Multithread/images/thread-local/18.png rename to docs/java/multi-thread/images/thread-local/18.png diff --git a/docs/java/Multithread/images/thread-local/19.png b/docs/java/multi-thread/images/thread-local/19.png similarity index 100% rename from docs/java/Multithread/images/thread-local/19.png rename to docs/java/multi-thread/images/thread-local/19.png diff --git a/docs/java/Multithread/images/thread-local/2.png b/docs/java/multi-thread/images/thread-local/2.png similarity index 100% rename from docs/java/Multithread/images/thread-local/2.png rename to docs/java/multi-thread/images/thread-local/2.png diff --git a/docs/java/Multithread/images/thread-local/20.png b/docs/java/multi-thread/images/thread-local/20.png similarity index 100% rename from docs/java/Multithread/images/thread-local/20.png rename to docs/java/multi-thread/images/thread-local/20.png diff --git a/docs/java/Multithread/images/thread-local/21.png b/docs/java/multi-thread/images/thread-local/21.png similarity index 100% rename from docs/java/Multithread/images/thread-local/21.png rename to docs/java/multi-thread/images/thread-local/21.png diff --git a/docs/java/Multithread/images/thread-local/22.png b/docs/java/multi-thread/images/thread-local/22.png similarity index 100% rename from docs/java/Multithread/images/thread-local/22.png rename to docs/java/multi-thread/images/thread-local/22.png diff --git a/docs/java/Multithread/images/thread-local/23.png b/docs/java/multi-thread/images/thread-local/23.png similarity index 100% rename from docs/java/Multithread/images/thread-local/23.png rename to docs/java/multi-thread/images/thread-local/23.png diff --git a/docs/java/Multithread/images/thread-local/24.png b/docs/java/multi-thread/images/thread-local/24.png similarity index 100% rename from docs/java/Multithread/images/thread-local/24.png rename to docs/java/multi-thread/images/thread-local/24.png diff --git a/docs/java/Multithread/images/thread-local/25.png b/docs/java/multi-thread/images/thread-local/25.png similarity index 100% rename from docs/java/Multithread/images/thread-local/25.png rename to docs/java/multi-thread/images/thread-local/25.png diff --git a/docs/java/Multithread/images/thread-local/26.png b/docs/java/multi-thread/images/thread-local/26.png similarity index 100% rename from docs/java/Multithread/images/thread-local/26.png rename to docs/java/multi-thread/images/thread-local/26.png diff --git a/docs/java/Multithread/images/thread-local/27.png b/docs/java/multi-thread/images/thread-local/27.png similarity index 100% rename from docs/java/Multithread/images/thread-local/27.png rename to docs/java/multi-thread/images/thread-local/27.png diff --git a/docs/java/Multithread/images/thread-local/28.png b/docs/java/multi-thread/images/thread-local/28.png similarity index 100% rename from docs/java/Multithread/images/thread-local/28.png rename to docs/java/multi-thread/images/thread-local/28.png diff --git a/docs/java/Multithread/images/thread-local/29.png b/docs/java/multi-thread/images/thread-local/29.png similarity index 100% rename from docs/java/Multithread/images/thread-local/29.png rename to docs/java/multi-thread/images/thread-local/29.png diff --git a/docs/java/Multithread/images/thread-local/3.png b/docs/java/multi-thread/images/thread-local/3.png similarity index 100% rename from docs/java/Multithread/images/thread-local/3.png rename to docs/java/multi-thread/images/thread-local/3.png diff --git a/docs/java/Multithread/images/thread-local/30.png b/docs/java/multi-thread/images/thread-local/30.png similarity index 100% rename from docs/java/Multithread/images/thread-local/30.png rename to docs/java/multi-thread/images/thread-local/30.png diff --git a/docs/java/Multithread/images/thread-local/31.png b/docs/java/multi-thread/images/thread-local/31.png similarity index 100% rename from docs/java/Multithread/images/thread-local/31.png rename to docs/java/multi-thread/images/thread-local/31.png diff --git a/docs/java/Multithread/images/thread-local/4.png b/docs/java/multi-thread/images/thread-local/4.png similarity index 100% rename from docs/java/Multithread/images/thread-local/4.png rename to docs/java/multi-thread/images/thread-local/4.png diff --git a/docs/java/Multithread/images/thread-local/5.png b/docs/java/multi-thread/images/thread-local/5.png similarity index 100% rename from docs/java/Multithread/images/thread-local/5.png rename to docs/java/multi-thread/images/thread-local/5.png diff --git a/docs/java/Multithread/images/thread-local/6.png b/docs/java/multi-thread/images/thread-local/6.png similarity index 100% rename from docs/java/Multithread/images/thread-local/6.png rename to docs/java/multi-thread/images/thread-local/6.png diff --git a/docs/java/Multithread/images/thread-local/7.png b/docs/java/multi-thread/images/thread-local/7.png similarity index 100% rename from docs/java/Multithread/images/thread-local/7.png rename to docs/java/multi-thread/images/thread-local/7.png diff --git a/docs/java/Multithread/images/thread-local/8.png b/docs/java/multi-thread/images/thread-local/8.png similarity index 100% rename from docs/java/Multithread/images/thread-local/8.png rename to docs/java/multi-thread/images/thread-local/8.png diff --git a/docs/java/Multithread/images/thread-local/9.png b/docs/java/multi-thread/images/thread-local/9.png similarity index 100% rename from docs/java/Multithread/images/thread-local/9.png rename to docs/java/multi-thread/images/thread-local/9.png diff --git a/docs/java/Multithread/images/thread-pool/19a0255a-6ef3-4835-98d1-a839d1983332.png b/docs/java/multi-thread/images/thread-pool/19a0255a-6ef3-4835-98d1-a839d1983332.png similarity index 100% rename from docs/java/Multithread/images/thread-pool/19a0255a-6ef3-4835-98d1-a839d1983332.png rename to docs/java/multi-thread/images/thread-pool/19a0255a-6ef3-4835-98d1-a839d1983332.png diff --git a/docs/java/Multithread/images/thread-pool/1bc44c67-26ba-42ab-bcb8-4e29e6fd99b9.png b/docs/java/multi-thread/images/thread-pool/1bc44c67-26ba-42ab-bcb8-4e29e6fd99b9.png similarity index 100% rename from docs/java/Multithread/images/thread-pool/1bc44c67-26ba-42ab-bcb8-4e29e6fd99b9.png rename to docs/java/multi-thread/images/thread-pool/1bc44c67-26ba-42ab-bcb8-4e29e6fd99b9.png diff --git a/docs/java/Multithread/images/thread-pool/5b9b814d-722a-4116-b066-43dc80fc1dc4.png b/docs/java/multi-thread/images/thread-pool/5b9b814d-722a-4116-b066-43dc80fc1dc4.png similarity index 100% rename from docs/java/Multithread/images/thread-pool/5b9b814d-722a-4116-b066-43dc80fc1dc4.png rename to docs/java/multi-thread/images/thread-pool/5b9b814d-722a-4116-b066-43dc80fc1dc4.png diff --git a/docs/java/Multithread/images/thread-pool/7888fb0d-4699-4d3a-8885-405cb5415617.png b/docs/java/multi-thread/images/thread-pool/7888fb0d-4699-4d3a-8885-405cb5415617.png similarity index 100% rename from docs/java/Multithread/images/thread-pool/7888fb0d-4699-4d3a-8885-405cb5415617.png rename to docs/java/multi-thread/images/thread-pool/7888fb0d-4699-4d3a-8885-405cb5415617.png diff --git a/docs/java/Multithread/images/thread-pool/b6fd95a7-4c9d-4fc6-ad26-890adb3f6c4c.png b/docs/java/multi-thread/images/thread-pool/b6fd95a7-4c9d-4fc6-ad26-890adb3f6c4c.png similarity index 100% rename from docs/java/Multithread/images/thread-pool/b6fd95a7-4c9d-4fc6-ad26-890adb3f6c4c.png rename to docs/java/multi-thread/images/thread-pool/b6fd95a7-4c9d-4fc6-ad26-890adb3f6c4c.png diff --git a/docs/java/Multithread/images/thread-pool/ddf22709-bff5-45b4-acb7-a3f2e6798608.png b/docs/java/multi-thread/images/thread-pool/ddf22709-bff5-45b4-acb7-a3f2e6798608.png similarity index 100% rename from docs/java/Multithread/images/thread-pool/ddf22709-bff5-45b4-acb7-a3f2e6798608.png rename to docs/java/multi-thread/images/thread-pool/ddf22709-bff5-45b4-acb7-a3f2e6798608.png diff --git a/docs/java/Multithread/images/threadlocal数据结构.png b/docs/java/multi-thread/images/threadlocal数据结构.png similarity index 100% rename from docs/java/Multithread/images/threadlocal数据结构.png rename to docs/java/multi-thread/images/threadlocal数据结构.png diff --git a/docs/java/Multithread/images/多线程学习指南/Java并发编程的艺术.png b/docs/java/multi-thread/images/多线程学习指南/Java并发编程的艺术.png similarity index 100% rename from docs/java/Multithread/images/多线程学习指南/Java并发编程的艺术.png rename to docs/java/multi-thread/images/多线程学习指南/Java并发编程的艺术.png diff --git a/docs/java/Multithread/images/多线程学习指南/javaguide-并发.png b/docs/java/multi-thread/images/多线程学习指南/javaguide-并发.png similarity index 100% rename from docs/java/Multithread/images/多线程学习指南/javaguide-并发.png rename to docs/java/multi-thread/images/多线程学习指南/javaguide-并发.png diff --git a/docs/java/Multithread/images/多线程学习指南/java并发编程之美.png b/docs/java/multi-thread/images/多线程学习指南/java并发编程之美.png similarity index 100% rename from docs/java/Multithread/images/多线程学习指南/java并发编程之美.png rename to docs/java/multi-thread/images/多线程学习指南/java并发编程之美.png diff --git a/docs/java/Multithread/images/多线程学习指南/实战Java高并发程序设计.png b/docs/java/multi-thread/images/多线程学习指南/实战Java高并发程序设计.png similarity index 100% rename from docs/java/Multithread/images/多线程学习指南/实战Java高并发程序设计.png rename to docs/java/multi-thread/images/多线程学习指南/实战Java高并发程序设计.png diff --git a/docs/java/Multithread/images/多线程学习指南/深入浅出Java多线程.png b/docs/java/multi-thread/images/多线程学习指南/深入浅出Java多线程.png similarity index 100% rename from docs/java/Multithread/images/多线程学习指南/深入浅出Java多线程.png rename to docs/java/multi-thread/images/多线程学习指南/深入浅出Java多线程.png diff --git a/docs/java/Multithread/java线程池学习总结.md b/docs/java/multi-thread/java线程池学习总结.md similarity index 96% rename from docs/java/Multithread/java线程池学习总结.md rename to docs/java/multi-thread/java线程池学习总结.md index 54f4b650..6a946dd0 100644 --- a/docs/java/Multithread/java线程池学习总结.md +++ b/docs/java/multi-thread/java线程池学习总结.md @@ -97,7 +97,7 @@ public class ScheduledThreadPoolExecutor implements ScheduledExecutorService ``` -![任务的执行相关接口](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-7/任务的执行相关接口.png) +![任务的执行相关接口](images/java线程池学习总结/任务的执行相关接口.png) #### 3) 异步计算的结果(`Future`) @@ -107,7 +107,7 @@ public class ScheduledThreadPoolExecutor ### 2.3 Executor 框架的使用示意图 -![Executor 框架的使用示意图](https://imgconvert.csdnimg.cn/aHR0cDovL215LWJsb2ctdG8tdXNlLm9zcy1jbi1iZWlqaW5nLmFsaXl1bmNzLmNvbS8xOC01LTMwLzg0ODIzMzMwLmpwZw?x-oss-process=image/format,png) +![Executor 框架的使用示意图](images/java线程池学习总结/Executor框架的使用示意图.png) 1. **主线程首先要创建实现 `Runnable` 或者 `Callable` 接口的任务对象。** 2. **把创建完成的实现 `Runnable`/`Callable`接口的 对象直接交给 `ExecutorService` 执行**: `ExecutorService.execute(Runnable command)`)或者也可以把 `Runnable` 对象或`Callable` 对象提交给 `ExecutorService` 执行(`ExecutorService.submit(Runnable task)`或 `ExecutorService.submit(Callable task)`)。 @@ -167,7 +167,7 @@ public class ScheduledThreadPoolExecutor 下面这张图可以加深你对线程池中各个参数的相互关系的理解(图片来源:《Java 性能调优实战》): -![线程池各个参数的关系](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-7/线程池各个参数的关系.jpg) +![线程池各个参数的关系](images/java线程池学习总结/线程池各个参数之间的关系.png) **`ThreadPoolExecutor` 饱和策略定义:** @@ -198,7 +198,7 @@ public class ScheduledThreadPoolExecutor > - **CachedThreadPool 和 ScheduledThreadPool** : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致 OOM。 **方式一:通过`ThreadPoolExecutor`构造函数实现(推荐)** -![通过构造方法实现](https://imgconvert.csdnimg.cn/aHR0cDovL215LWJsb2ctdG8tdXNlLm9zcy1jbi1iZWlqaW5nLmFsaXl1bmNzLmNvbS8xOC00LTE2LzE3ODU4MjMwLmpwZw?x-oss-process=image/format,png) +![通过构造方法实现](images/java线程池学习总结/threadpoolexecutor构造函数.png) **方式二:通过 Executor 框架的工具类 Executors 来实现** 我们可以创建三种类型的 ThreadPoolExecutor: @@ -207,7 +207,7 @@ public class ScheduledThreadPoolExecutor - **CachedThreadPool** 对应 Executors 工具类中的方法如图所示: -![通过Executor 框架的工具类Executors来实现](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/format,png.jpeg) +![通过Executor 框架的工具类Executors来实现](images/java线程池学习总结/Executors工具类.png) ## 四 (重要)ThreadPoolExecutor 使用示例 @@ -388,7 +388,7 @@ pool-1-thread-2 End. Time = Sun Apr 12 11:14:47 CST 2020 通过下图可以更好的对上面这 3 步做一个展示,下图是我为了省事直接从网上找到,原地址不明。 -![图解线程池实现原理](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-7/图解线程池实现原理.png) +![图解线程池实现原理](images/java线程池学习总结/图解线程池实现原理.png) @@ -705,7 +705,7 @@ Wed Nov 13 13:40:43 CST 2019::pool-1-thread-5 `FixedThreadPool` 的 `execute()` 方法运行示意图(该图片来源:《Java 并发编程的艺术》): -![FixedThreadPool的execute()方法运行示意图](https://imgconvert.csdnimg.cn/aHR0cDovL215LWJsb2ctdG8tdXNlLm9zcy1jbi1iZWlqaW5nLmFsaXl1bmNzLmNvbS8xOC00LTE2LzcxMzc1OTYzLmpwZw?x-oss-process=image/format,png) +![FixedThreadPool的execute()方法运行示意图](images/java线程池学习总结/FixedThreadPool.png) **上图说明:** @@ -755,7 +755,7 @@ Wed Nov 13 13:40:43 CST 2019::pool-1-thread-5 #### 5.2.2 执行任务过程介绍 **`SingleThreadExecutor` 的运行示意图(该图片来源:《Java 并发编程的艺术》):** -![SingleThreadExecutor的运行示意图](https://imgconvert.csdnimg.cn/aHR0cDovL215LWJsb2ctdG8tdXNlLm9zcy1jbi1iZWlqaW5nLmFsaXl1bmNzLmNvbS8xOC00LTE2LzgyMjc2NDU4LmpwZw?x-oss-process=image/format,png) +![SingleThreadExecutor的运行示意图](images/java线程池学习总结/SingleThreadExecutor.png) **上图说明;** @@ -799,7 +799,7 @@ Wed Nov 13 13:40:43 CST 2019::pool-1-thread-5 #### 5.3.2 执行任务过程介绍 **CachedThreadPool 的 execute()方法的执行示意图(该图片来源:《Java 并发编程的艺术》):** -![CachedThreadPool的execute()方法的执行示意图](https://imgconvert.csdnimg.cn/aHR0cDovL215LWJsb2ctdG8tdXNlLm9zcy1jbi1iZWlqaW5nLmFsaXl1bmNzLmNvbS8xOC00LTE2LzE4NjExNzY3LmpwZw?x-oss-process=image/format,png) +![CachedThreadPool的execute()方法的执行示意图](images/java线程池学习总结/CachedThreadPool-execute.png) **上图说明:** @@ -830,7 +830,7 @@ Wed Nov 13 13:40:43 CST 2019::pool-1-thread-5 ### 6.2 运行机制 -![ScheduledThreadPoolExecutor运行机制](https://imgconvert.csdnimg.cn/aHR0cDovL215LWJsb2ctdG8tdXNlLm9zcy1jbi1iZWlqaW5nLmFsaXl1bmNzLmNvbS8xOC00LTE2LzkyNTk0Njk4LmpwZw?x-oss-process=image/format,png) +![ScheduledThreadPoolExecutor运行机制](images/java线程池学习总结/ScheduledThreadPoolExecutor机制.png) **`ScheduledThreadPoolExecutor` 的执行主要分为两大部分:** @@ -845,7 +845,7 @@ Wed Nov 13 13:40:43 CST 2019::pool-1-thread-5 ### 6.3 ScheduledThreadPoolExecutor 执行周期任务的步骤 -![ScheduledThreadPoolExecutor执行周期任务的步骤](https://imgconvert.csdnimg.cn/aHR0cDovL215LWJsb2ctdG8tdXNlLm9zcy1jbi1iZWlqaW5nLmFsaXl1bmNzLmNvbS8xOC01LTMwLzU5OTE2Mzg5LmpwZw?x-oss-process=image/format,png) +![ScheduledThreadPoolExecutor执行周期任务的步骤](images/java线程池学习总结/ScheduledThreadPoolExecutor执行周期任务步骤.png) 1. 线程 1 从 `DelayQueue` 中获取已到期的 `ScheduledFutureTask(DelayQueue.take())`。到期任务是指 `ScheduledFutureTask`的 time 大于等于当前系统的时间; 2. 线程 1 执行这个 `ScheduledFutureTask`; diff --git a/docs/java/Multithread/synchronized在JDK1.6之后的底层优化.md b/docs/java/multi-thread/synchronized在JDK1.6之后的底层优化.md similarity index 100% rename from docs/java/Multithread/synchronized在JDK1.6之后的底层优化.md rename to docs/java/multi-thread/synchronized在JDK1.6之后的底层优化.md diff --git a/docs/java/Multithread/ThreadLocal.md b/docs/java/multi-thread/万字详解ThreadLocal关键字.md similarity index 100% rename from docs/java/Multithread/ThreadLocal.md rename to docs/java/multi-thread/万字详解ThreadLocal关键字.md diff --git a/docs/java/Multithread/创建线程的几种方式总结.md b/docs/java/multi-thread/创建线程的几种方式总结.md similarity index 100% rename from docs/java/Multithread/创建线程的几种方式总结.md rename to docs/java/multi-thread/创建线程的几种方式总结.md diff --git a/docs/java/Multithread/多线程学习指南.md b/docs/java/multi-thread/多线程学习指南.md similarity index 100% rename from docs/java/Multithread/多线程学习指南.md rename to docs/java/multi-thread/多线程学习指南.md diff --git a/docs/java/Multithread/并发容器总结.md b/docs/java/multi-thread/并发容器总结.md similarity index 100% rename from docs/java/Multithread/并发容器总结.md rename to docs/java/multi-thread/并发容器总结.md diff --git a/docs/java/Multithread/best-practice-of-threadpool.md b/docs/java/multi-thread/拿来即用的线程池最佳实践.md similarity index 100% rename from docs/java/Multithread/best-practice-of-threadpool.md rename to docs/java/multi-thread/拿来即用的线程池最佳实践.md diff --git a/docs/java/What's New in JDK8/Java8foreach指南.md b/docs/java/new-features/Java8foreach指南.md similarity index 100% rename from docs/java/What's New in JDK8/Java8foreach指南.md rename to docs/java/new-features/Java8foreach指南.md diff --git a/docs/java/What's New in JDK8/Java8教程推荐.md b/docs/java/new-features/Java8教程推荐.md similarity index 100% rename from docs/java/What's New in JDK8/Java8教程推荐.md rename to docs/java/new-features/Java8教程推荐.md diff --git a/docs/java/What's New in JDK8/Java8Tutorial.md b/docs/java/new-features/Java8新特性总结.md similarity index 100% rename from docs/java/What's New in JDK8/Java8Tutorial.md rename to docs/java/new-features/Java8新特性总结.md diff --git a/docs/java/jdk-new-features/images/fc66979f-7974-40e8-88ae-6dbff15ac9ef.png b/docs/java/new-features/images/一文带你看遍JDK9~14 的重要新特性/java版本发布.png similarity index 100% rename from docs/java/jdk-new-features/images/fc66979f-7974-40e8-88ae-6dbff15ac9ef.png rename to docs/java/new-features/images/一文带你看遍JDK9~14 的重要新特性/java版本发布.png diff --git a/docs/java/jdk-new-features/new-features-from-jdk8-to-jdk14.md b/docs/java/new-features/一文带你看遍JDK9到14的重要新特性.md similarity index 99% rename from docs/java/jdk-new-features/new-features-from-jdk8-to-jdk14.md rename to docs/java/new-features/一文带你看遍JDK9到14的重要新特性.md index 5b193cd5..64d70b3a 100644 --- a/docs/java/jdk-new-features/new-features-from-jdk8-to-jdk14.md +++ b/docs/java/new-features/一文带你看遍JDK9到14的重要新特性.md @@ -109,7 +109,7 @@ Java 10 在现有的 CDS 功能基础上再次拓展,以允许应用类放置 Java11 于 2018 年 9 月 25 日正式发布,这是很重要的一个版本!Java 11 和 2017 年 9 月份发布的 Java 9 以及 2018 年 3 月份发布的 Java 10 相比,其最大的区别就是:在长期支持(Long-Term-Support)方面,**Oracle 表示会对 Java 11 提供大力支持,这一支持将会持续至 2026 年 9 月。这是据 Java 8 以后支持的首个长期版本。** -![](images/fc66979f-7974-40e8-88ae-6dbff15ac9ef.png) +![](images/一文带你看遍JDK9~14 的重要新特性/java版本发布.png) ### 字符串加强 diff --git a/docs/network/images/七层体系结构图.png b/docs/network/images/七层体系结构图.png new file mode 100644 index 00000000..a2d24300 Binary files /dev/null and b/docs/network/images/七层体系结构图.png differ diff --git a/docs/network/images/传输层.png b/docs/network/images/传输层.png new file mode 100644 index 00000000..192af245 Binary files /dev/null and b/docs/network/images/传输层.png differ diff --git a/docs/network/images/应用层.png b/docs/network/images/应用层.png new file mode 100644 index 00000000..31e1e447 Binary files /dev/null and b/docs/network/images/应用层.png differ diff --git a/docs/network/images/数据链路层.png b/docs/network/images/数据链路层.png new file mode 100644 index 00000000..c2b51a7c Binary files /dev/null and b/docs/network/images/数据链路层.png differ diff --git a/docs/network/images/物理层.png b/docs/network/images/物理层.png new file mode 100644 index 00000000..abb97926 Binary files /dev/null and b/docs/network/images/物理层.png differ diff --git a/docs/network/images/网络层.png b/docs/network/images/网络层.png new file mode 100644 index 00000000..376479d7 Binary files /dev/null and b/docs/network/images/网络层.png differ diff --git a/docs/network/images/计算机网络概述.png b/docs/network/images/计算机网络概述.png new file mode 100644 index 00000000..999c2362 Binary files /dev/null and b/docs/network/images/计算机网络概述.png differ diff --git a/docs/network/images/计算机网络知识点总结/万维网的大致工作工程.png b/docs/network/images/计算机网络知识点总结/万维网的大致工作工程.png new file mode 100644 index 00000000..6af03daa Binary files /dev/null and b/docs/network/images/计算机网络知识点总结/万维网的大致工作工程.png differ diff --git a/docs/network/images/计算机网络第七版.png b/docs/network/images/计算机网络第七版.png new file mode 100644 index 00000000..1ea61963 Binary files /dev/null and b/docs/network/images/计算机网络第七版.png differ diff --git a/docs/network/干货:计算机网络知识总结.md b/docs/network/干货:计算机网络知识总结.md deleted file mode 100644 index a20e6f85..00000000 --- a/docs/network/干货:计算机网络知识总结.md +++ /dev/null @@ -1,420 +0,0 @@ -### 1. [计算机概述 ](#一计算机概述) -### 2. [物理层 ](#二物理层) -### 3. [数据链路层 ](#三数据链路层 ) -### 4. [网络层 ](#四网络层 ) -### 5. [运输层 ](#五运输层 ) -### 6. [应用层](#六应用层) - - -## 一计算机概述 -### (1),基本术语 - -#### 结点 (node): - - 网络中的结点可以是计算机,集线器,交换机或路由器等。 -#### 链路(link ): - - 从一个结点到另一个结点的一段物理线路。中间没有任何其他交点。 -#### 主机(host): - 连接在因特网上的计算机. -#### ISP(Internet Service Provider): - 因特网服务提供者(提供商). -#### IXP(Internet eXchange Point): - 互联网交换点IXP的主要作用就是允许两个网络直接相连并交换分组,而不需要再通过第三个网络来转发分组。. -#### RFC(Request For Comments) - 意思是“请求评议”,包含了关于Internet几乎所有的重要的文字资料。 -#### 广域网WAN(Wide Area Network) - 任务是通过长距离运送主机发送的数据 -#### 城域网MAN(Metropolitan Area Network) - 用来将多个局域网进行互连 - -#### 局域网LAN(Local Area Network) - 学校或企业大多拥有多个互连的局域网 -#### 个人区域网PAN(Personal Area Network) - 在个人工作的地方把属于个人使用的电子设备用无线技术连接起来的网络 -#### 端系统(end system): - 处在因特网边缘的部分即是连接在因特网上的所有的主机. -#### 分组(packet ): - 因特网中传送的数据单元。由首部header和数据段组成。分组又称为包,首部可称为包头。 -#### 存储转发(store and forward ): - 路由器收到一个分组,先存储下来,再检查其首部,查找转发表,按照首部中的目的地址,找到合适的接口转发出去。 -#### 带宽(bandwidth): - 在计算机网络中,表示在单位时间内从网络中的某一点到另一点所能通过的“最高数据率”。常用来表示网络的通信线路所能传送数据的能力。单位是“比特每秒”,记为b/s。 -#### 吞吐量(throughput ): - 表示在单位时间内通过某个网络(或信道、接口)的数据量。吞吐量更经常地用于对现实世界中的网络的一种测量,以便知道实际上到底有多少数据量能够通过网络。吞吐量受网络的带宽或网络的额定速率的限制。 - -### (2),重要知识点总结 - - 1,计算机网络(简称网络)把许多计算机连接在一起,而互联网把许多网络连接在一起,是网络的网络。 - - 2,小写字母i开头的internet(互联网)是通用名词,它泛指由多个计算机网络相互连接而成的网络。在这些网络之间的通信协议(即通信规则)可以是任意的。 - - 大写字母I开头的Internet(互联网)是专用名词,它指全球最大的,开放的,由众多网络相互连接而成的特定的互联网,并采用TCP/IP协议作为通信规则,其前身为ARPANET。Internet的推荐译名为因特网,现在一般流行称为互联网。 - - 3,路由器是实现分组交换的关键构件,其任务是转发收到的分组,这是网络核心部分最重要的功能。分组交换采用存储转发技术,表示把一个报文(要发送的整块数据)分为几个分组后再进行传送。在发送报文之前,先把较长的报文划分成为一个个更小的等长数据段。在每个数据端的前面加上一些由必要的控制信息组成的首部后,就构成了一个分组。分组又称为包。分组是在互联网中传送的数据单元,正是由于分组的头部包含了诸如目的地址和源地址等重要控制信息,每一个分组才能在互联网中独立的选择传输路径,并正确地交付到分组传输的终点。 - -4,互联网按工作方式可划分为边缘部分和核心部分。主机在网络的边缘部分,其作用是进行信息处理。由大量网络和连接这些网络的路由器组成核心部分,其作用是提供连通性和交换。 - - 5,计算机通信是计算机中进程(即运行着的程序)之间的通信。计算机网络采用的通信方式是客户-服务器方式(C/S方式)和对等连接方式(P2P方式)。 - - 6,客户和服务器都是指通信中所涉及的应用进程。客户是服务请求方,服务器是服务提供方。 - -7,按照作用范围的不同,计算机网络分为广域网WAN,城域网MAN,局域网LAN,个人区域网PAN。 - - 8,计算机网络最常用的性能指标是:速率,带宽,吞吐量,时延(发送时延,处理时延,排队时延),时延带宽积,往返时间和信道利用率。 - - 9,网络协议即协议,是为进行网络中的数据交换而建立的规则。计算机网络的各层以及其协议集合,称为网络的体系结构。 - - 10,五层体系结构由应用层,运输层,网络层(网际层),数据链路层,物理层组成。运输层最主要的协议是TCP和UDP协议,网络层最重要的协议是IP协议。 - -## 二物理层 -### (1),基本术语 -#### 数据(data): - 运送消息的实体。 -#### 信号(signal): - 数据的电气的或电磁的表现。或者说信号是适合在传输介质上传输的对象。 -#### 码元( code): - 在使用时间域(或简称为时域)的波形来表示数字信号时,代表不同离散数值的基本波形。 -#### 单工(simplex ): - 只能有一个方向的通信而没有反方向的交互。 -#### 半双工(half duplex ): - 通信的双方都可以发送信息,但不能双方同时发送(当然也就不能同时接收)。 -#### 全双工(full duplex): - 通信的双方可以同时发送和接收信息。 -#### 奈氏准则: - 在任何信道中,码元的传输的效率是有上限的,传输速率超过此上限,就会出现严重的码间串扰问题,使接收端对码元的判决(即识别)成为不可能。 -#### 基带信号(baseband signal): - 来自信源的信号。指没有经过调制的数字信号或模拟信号。 -#### 带通(频带)信号(bandpass signal): - 把基带信号经过载波调制后,把信号的频率范围搬移到较高的频段以便在信道中传输(即仅在一段频率范围内能够通过信道),这里调制过后的信号就是带通信号。 -#### 调制(modulation ): - 对信号源的信息进行处理后加到载波信号上,使其变为适合在信道传输的形式的过程。 -#### 信噪比(signal-to-noise ratio ): - 指信号的平均功率和噪声的平均功率之比,记为S/N。信噪比(dB)=10*log10(S/N) -#### 信道复用(channel multiplexing ): - 指多个用户共享同一个信道。(并不一定是同时) -#### 比特率(bit rate ): - 单位时间(每秒)内传送的比特数。 -#### 波特率(baud rate): - 单位时间载波调制状态改变的次数。针对数据信号对载波的调制速率。 -#### 复用(multiplexing): - 共享信道的方法 -#### ADSL(Asymmetric Digital Subscriber Line ): - 非对称数字用户线。 -#### 光纤同轴混合网(HFC网): - 在目前覆盖范围很广的有线电视网的基础上开发的一种居民宽带接入网 - -### (2),重要知识点总结 - - 1,物理层的主要任务就是确定与传输媒体接口有关的一些特性,如机械特性,电气特性,功能特性,过程特性。 - - 2,一个数据通信系统可划分为三大部分,即源系统,传输系统,目的系统。源系统包括源点(或源站,信源)和发送器,目的系统包括接收器和终点。 - - 3,通信的目的是传送消息。如话音,文字,图像等都是消息,数据是运送消息的实体。信号则是数据的电器或电磁的表现。 - - 4,根据信号中代表消息的参数的取值方式不同,信号可分为模拟信号(或连续信号)和数字信号(或离散信号)。在使用时间域(简称时域)的波形表示数字信号时,代表不同离散数值的基本波形称为码元。 - - 5,根据双方信息交互的方式,通信可划分为单向通信(或单工通信),双向交替通信(或半双工通信),双向同时通信(全双工通信)。 - - 6,来自信源的信号称为基带信号。信号要在信道上传输就要经过调制。调制有基带调制和带通调制之分。最基本的带通调制方法有调幅,调频和调相。还有更复杂的调制方法,如正交振幅调制。 - - 7,要提高数据在信道上的传递速率,可以使用更好的传输媒体,或使用先进的调制技术。但数据传输速率不可能任意被提高。 - - 8,传输媒体可分为两大类,即导引型传输媒体(双绞线,同轴电缆,光纤)和非导引型传输媒体(无线,红外,大气激光)。 - - 9,为了有效利用光纤资源,在光纤干线和用户之间广泛使用无源光网络PON。无源光网络无需配备电源,其长期运营成本和管理成本都很低。最流行的无源光网络是以太网无源光网络EPON和吉比特无源光网络GPON。 - -### (3),最重要的知识点 -#### **①,物理层的任务** - 透明地传送比特流。也可以将物理层的主要任务描述为确定与传输媒体的接口的一些特性,即:机械特性(接口所用接线器的一些物理属性如形状尺寸),电气特性(接口电缆的各条线上出现的电压的范围),功能特性(某条线上出现的某一电平的电压的意义),过程特性(对于不同功能能的各种可能事件的出现顺序)。 - -#### 拓展: - 物理层考虑的是怎样才能在连接各种计算机的传输媒体上传输数据比特流,而不是指具体的传输媒体。现有的计算机网络中的硬件设备和传输媒体的种类非常繁多,而且通信手段也有许多不同的方式。物理层的作用正是尽可能地屏蔽掉这些传输媒体和通信手段的差异,使物理层上面的数据链路层感觉不到这些差异,这样就可以使数据链路层只考虑完成本层的协议和服务,而不必考虑网络的具体传输媒体和通信手段是什么。 - -#### **②,几种常用的信道复用技术** -![这里写图片描述](https://user-gold-cdn.xitu.io/2018/4/1/1627f7a170ec6611?w=1247&h=425&f=png&s=36746) - -### **③,几种常用的宽带接入技术,主要是ADSL和FTTx** - 用户到互联网的宽带接入方法有非对称数字用户线ADSL(用数字技术对现有的模拟电话线进行改造,而不需要重新布线。ASDL的快速版本是甚高速数字用户线VDSL。),光纤同轴混合网HFC(是在目前覆盖范围很广的有线电视网的基础上开发的一种居民宽带接入网)和FTTx(即光纤到······)。 - -## 三数据链路层 -### (1),基本术语 - -#### 链路(link): - 一个结点到相邻结点的一段物理链路 -#### 数据链路(data link): - 把实现控制数据运输的协议的硬件和软件加到链路上就构成了数据链路 -#### 循环冗余检验CRC(Cyclic Redundancy Check): - 为了保证数据传输的可靠性,CRC是数据链路层广泛使用的一种检错技术 -#### 帧(frame): - 一个数据链路层的传输单元,由一个数据链路层首部和其携带的封包所组成协议数据单元。 -#### MTU(Maximum Transfer Uint ): - 最大传送单元。帧的数据部分的的长度上限。 -#### 误码率BER(Bit Error Rate ): - 在一段时间内,传输错误的比特占所传输比特总数的比率。 -#### PPP(Point-to-Point Protocol ): - 点对点协议。即用户计算机和ISP进行通信时所使用的数据链路层协议。以下是PPP帧的示意图: -![PPP](https://user-gold-cdn.xitu.io/2018/4/1/1627f8291c6b032c?w=624&h=359&f=jpeg&s=44271) -#### MAC地址(Media Access Control或者Medium Access Control): - 意译为媒体访问控制,或称为物理地址、硬件地址,用来定义网络设备的位置。 - 在OSI模型中,第三层网络层负责 IP地址,第二层数据链路层则负责 MAC地址。 - 因此一个主机会有一个MAC地址,而每个网络位置会有一个专属于它的IP地址 。 - 地址是识别某个系统的重要标识符,“名字指出我们所要寻找的资源,地址指出资源所在的地方,路由告诉我们如何到达该处” -#### 网桥(bridge): - 一种用于数据链路层实现中继,连接两个或多个局域网的网络互连设备。 -#### 交换机(switch ): - 广义的来说,交换机指的是一种通信系统中完成信息交换的设备。这里工作在数据链路层的交换机指的是交换式集线器,其实质是一个多接口的网桥 - - -### (2),重要知识点总结 - -1,链路是从一个结点到相邻节点的一段物理链路,数据链路则在链路的基础上增加了一些必要的硬件(如网络适配器)和软件(如协议的实现) - -2,数据链路层使用的主要是**点对点信道**和**广播信道**两种。 - -3,数据链路层传输的协议数据单元是帧。数据链路层的三个基本问题是:**封装成帧**,**透明传输**和**差错检测** - -4,**循环冗余检验CRC**是一种检错方法,而帧检验序列FCS是添加在数据后面的冗余码 - -5,**点对点协议PPP**是数据链路层使用最多的一种协议,它的特点是:简单,只检测差错而不去纠正差错,不使用序号,也不进行流量控制,可同时支持多种网络层协议 - - 6,PPPoE是为宽带上网的主机使用的链路层协议 - -7,局域网的优点是:具有广播功能,从一个站点可方便地访问全网;便于系统的扩展和逐渐演变;提高了系统的可靠性,可用性和生存性。 - -8,共向媒体通信资源的方法有二:一是静态划分信道(各种复用技术),而是动态媒体接入控制,又称为多点接入(随即接入或受控接入) - -9,计算机与外接局域网通信需要通过通信适配器(或网络适配器),它又称为网络接口卡或网卡。**计算器的硬件地址就在适配器的ROM中**。 - -10,以太网采用的无连接的工作方式,对发送的数据帧不进行编号,也不要求对方发回确认。目的站收到有差错帧就把它丢掉,其他什么也不做 - -11,以太网采用的协议是具有冲突检测的**载波监听多点接入CSMA/CD**。协议的特点是:**发送前先监听,边发送边监听,一旦发现总线上出现了碰撞,就立即停止发送。然后按照退避算法等待一段随机时间后再次发送。** 因此,每一个站点在自己发送数据之后的一小段时间内,存在这遭遇碰撞的可能性。以太网上的各站点平等的争用以太网信道 - -12,以太网的适配器具有过滤功能,它只接收单播帧,广播帧和多播帧。 - -13,使用集线器可以在物理层扩展以太网(扩展后的以太网仍然是一个网络) -### (3),最重要的知识点 -#### ① 数据链路层的点对点信道和广播信道的特点,以及这两种信道所使用的协议(PPP协议以及CSMA/CD协议)的特点 -#### ② 数据链路层的三个基本问题:**封装成帧**,**透明传输**,**差错检测** -#### ③ 以太网的MAC层硬件地址 -#### ④ 适配器,转发器,集线器,网桥,以太网交换机的作用以及适用场合 - -## 四网络层 -### (1),基本术语 - -#### 虚电路(Virtual Circuit): - 在两个终端设备的逻辑或物理端口之间,通过建立的双向的透明传输通道。虚电路表示这只是一条逻辑上的连接,分组都沿着这条逻辑连接按照存储转发方式传送,而并不是真正建立了一条物理连接。 -#### IP(Internet Protocol ): - 网际协议 IP 是 TCP/IP体系中两个最主要的协议之一,是TCP/IP体系结构网际层的核心。配套的有ARP,RARP,ICMP,IGMP。 - ![这里写图片描述](https://user-gold-cdn.xitu.io/2018/4/1/1627f92f98436286?w=453&h=331&f=jpeg&s=27535) -#### ARP(Address Resolution Protocol): - 地址解析协议 -#### ICMP(Internet Control Message Protocol ): - 网际控制报文协议 (ICMP 允许主机或路由器报告差错情况和提供有关异常情况的报告。) -#### 子网掩码(subnet mask ): - 它是一种用来指明一个IP地址的哪些位标识的是主机所在的子网以及哪些位标识的是主机的位掩码。子网掩码不能单独存在,它必须结合IP地址一起使用。 -#### CIDR( Classless Inter-Domain Routing ): - 无分类域间路由选择 (特点是消除了传统的 A 类、B 类和 C 类地址以及划分子网的概念,并使用各种长度的“网络前缀”(network-prefix)来代替分类地址中的网络号和子网号) -#### 默认路由(default route): - 当在路由表中查不到能到达目的地址的路由时,路由器选择的路由。默认路由还可以减小路由表所占用的空间和搜索路由表所用的时间。 -#### 路由选择算法(Virtual Circuit): - 路由选择协议的核心部分。因特网采用自适应的,分层次的路由选择协议。 - -### (2),重要知识点总结 -1,TCP/IP协议中的网络层向上只提供简单灵活的,无连接的,尽最大努力交付的数据报服务。网络层不提供服务质量的承诺,不保证分组交付的时限所传送的分组可能出错,丢失,重复和失序。进程之间通信的可靠性由运输层负责 - -2,在互联网的交付有两种,一是在本网络直接交付不用经过路由器,另一种是和其他网络的间接交付,至少经过一个路由器,但最后一次一定是直接交付 - -3,分类的IP地址由网络号字段(指明网络)和主机号字段(指明主机)组成。网络号字段最前面的类别指明IP地址的类别。IP地址是一种分等级的地址结构。IP地址管理机构分配IP地址时只分配网络号,主机号由得到该网络号的单位自行分配。路由器根据目的主机所连接的网络号来转发分组。一个路由器至少连接到两个网络,所以一个路由器至少应当有两个不同的IP地址 - -4,IP数据报分为首部和数据两部分。首部的前一部分是固定长度,共20字节,是所有IP数据包必须具有的(源地址,目的地址,总长度等重要地段都固定在首部)。一些长度可变的可选字段固定在首部的后面。IP首部中的生存时间给出了IP数据报在互联网中所能经过的最大路由器数。可防止IP数据报在互联网中无限制的兜圈子。 - -5,地址解析协议ARP把IP地址解析为硬件地址。ARP的高速缓存可以大大减少网络上的通信量。因为这样可以使主机下次再与同样地址的主机通信时,可以直接从高速缓存中找到所需要的硬件地址而不需要再去广播方式发送ARP请求分组 - -6,无分类域间路由选择CIDR是解决目前IP地址紧缺的一个好办法。CIDR记法把IP地址后面加上斜线“/”,然后写上前缀所所占的位数。前缀(或网络前缀用来指明网络),前缀后面的部分是后缀,用来指明主机。CIDR把前缀都相同的连续的IP地址组成一个“CIDR地址块”,IP地址分配都以CIDR地址块为单位。 - -7, 网际控制报文协议是IP层的协议.ICMP报文作为IP数据报的数据,加上首部后组成IP数据报发送出去。使用ICMP数据报并不是为了实现可靠传输。ICMP允许主机或路由器报告差错情况和提供有关异常情况的报告。ICMP报文的种类有两种 ICMP差错报告报文和ICMP询问报文。 - -8,要解决IP地址耗尽的问题,最根本的办法是采用具有更大地址空间的新版本IP协议-IPv6。IPv6所带来的变化有①更大的地址空间(采用128位地址)②灵活的首部格式③改进的选项④支持即插即用⑤支持资源的预分配⑥IPv6的首部改为8字节对齐。另外IP数据报的目的地址可以是以下三种基本类型地址之一:单播,多播和任播 - -9,虚拟专用网络VPN利用公用的互联网作为本机构专用网之间的通信载体。VPN内使用互联网的专用地址。一个VPN至少要有一个路由器具有合法的全球IP地址,这样才能和本系统的另一个VPN通过互联网进行通信。所有通过互联网传送的数据都需要加密 - -10, MPLS的特点是:①支持面向连接的服务质量②支持流量工程,平衡网络负载③有效的支持虚拟专用网VPN。MPLS在入口节点给每一个IP数据报打上固定长度的“标记”,然后根据标记在第二层(链路层)用硬件进行转发(在标记交换路由器中进行标记交换),因而转发速率大大加快。 - -### (3),最重要知识点 -#### ① 虚拟互联网络的概念 -#### ② IP地址和物理地址的关系 -#### ③ 传统的分类的IP地址(包括子网掩码)和无分类域间路由选择CIDR -#### ④ 路由选择协议的工作原理 - -## 五运输层 - -### (1),基本术语 - -#### 进程(process): - 指计算机中正在运行的程序实体 -#### 应用进程互相通信: - 一台主机的进程和另一台主机中的一个进程交换数据的过程(另外注意通信真正的端点不是主机而是主机中的进程,也就是说端到端的通信是应用进程之间的通信) -#### 传输层的复用与分用: - 复用指发送方不同的进程都可以通过统一个运输层协议传送数据。分用指接收方的运输层在剥去报文的首部后能把这些数据正确的交付到目的应用进程。 -#### TCP(Transmission Control Protocol): - 传输控制协议 -#### UDP(User Datagram Protocol): - 用户数据报协议 -#### 端口(port)(link): - 端口的目的是为了确认对方机器是那个进程在于自己进行交互,比如MSN和QQ的端口不同,如果没有端口就可能出现QQ进程和MSN交互错误。端口又称协议端口号。 -#### 停止等待协议(link): - 指发送方每发送完一个分组就停止发送,等待对方确认,在收到确认之后在发送下一个分组。 -#### 流量控制(link): - 就是让发送方的发送速率不要太快,既要让接收方来得及接收,也不要使网络发生拥塞。 -#### 拥塞控制(link): - 防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。 - - -### (2),重要知识点总结 - -1,运输层提供应用进程之间的逻辑通信,也就是说,运输层之间的通信并不是真正在两个运输层之间直接传输数据。运输层向应用层屏蔽了下面网络的细节(如网络拓补,所采用的路由选择协议等),它使应用进程之间看起来好像两个运输层实体之间有一条端到端的逻辑通信信道。 - -2,网络层为主机提供逻辑通信,而运输层为应用进程之间提供端到端的逻辑通信。 - -3,运输层的两个重要协议是用户数据报协议UDP和传输控制协议TCP。按照OSI的术语,两个对等运输实体在通信时传送的数据单位叫做运输协议数据单元TPDU(Transport Protocol Data Unit)。但在TCP/IP体系中,则根据所使用的协议是TCP或UDP,分别称之为TCP报文段或UDP用户数据报。 - -4,UDP在传送数据之前不需要先建立连接,远地主机在收到UDP报文后,不需要给出任何确认。虽然UDP不提供可靠交付,但在某些情况下UDP确是一种最有效的工作方式。 TCP提供面向连接的服务。在传送数据之前必须先建立连接,数据传送结束后要释放连接。TCP不提供广播或多播服务。由于TCP要提供可靠的,面向连接的传输服务,这一难以避免增加了许多开销,如确认,流量控制,计时器以及连接管理等。这不仅使协议数据单元的首部增大很多,还要占用许多处理机资源。 - -5,硬件端口是不同硬件设备进行交互的接口,而软件端口是应用层各种协议进程与运输实体进行层间交互的一种地址。UDP和TCP的首部格式中都有源端口和目的端口这两个重要字段。当运输层收到IP层交上来的运输层报文时,就能够 根据其首部中的目的端口号把数据交付应用层的目的应用层。(两个进程之间进行通信不光要知道对方IP地址而且要知道对方的端口号(为了找到对方计算机中的应用进程)) - -6,运输层用一个16位端口号标志一个端口。端口号只有本地意义,它只是为了标志计算机应用层中的各个进程在和运输层交互时的层间接口。在互联网的不同计算机中,相同的端口号是没有关联的。协议端口号简称端口。虽然通信的终点是应用进程,但只要把所发送的报文交到目的主机的某个合适端口,剩下的工作(最后交付目的进程)就由TCP和UDP来完成。 - -7,运输层的端口号分为服务器端使用的端口号(0~1023指派给熟知端口,1024~49151是登记端口号)和客户端暂时使用的端口号(49152~65535) - -8,UDP的主要特点是①无连接②尽最大努力交付③面向报文④无拥塞控制⑤支持一对一,一对多,多对一和多对多的交互通信⑥首部开销小(只有四个字段:源端口,目的端口,长度和检验和) - -9,TCP的主要特点是①面向连接②每一条TCP连接只能是一对一的③提供可靠交付④提供全双工通信⑤面向字节流 - -10,TCP用主机的IP地址加上主机上的端口号作为TCP连接的端点。这样的端点就叫做套接字(socket)或插口。套接字用(IP地址:端口号)来表示。每一条TCP连接唯一被通信两端的两个端点所确定。 - - 11,停止等待协议是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认。在收到确认后再发下一个分组。 - -12,为了提高传输效率,发送方可以不使用低效率的停止等待协议,而是采用流水线传输。流水线传输就是发送方可连续发送多个分组,不必每发完一个分组就停下来等待对方确认。这样可使信道上一直有数据不间断的在传送。这种传输方式可以明显提高信道利用率。 - -13,停止等待协议中超时重传是指只要超过一段时间仍然没有收到确认,就重传前面发送过的分组(认为刚才发送过的分组丢失了)。因此每发送完一个分组需要设置一个超时计时器,其重转时间应比数据在分组传输的平均往返时间更长一些。这种自动重传方式常称为自动重传请求ARQ。另外在停止等待协议中若收到重复分组,就丢弃该分组,但同时还要发送确认。连续ARQ协议可提高信道利用率。发送维持一个发送窗口,凡位于发送窗口内的分组可连续发送出去,而不需要等待对方确认。接收方一般采用累积确认,对按序到达的最后一个分组发送确认,表明到这个分组位置的所有分组都已经正确收到了。 - -14,TCP报文段的前20个字节是固定的,后面有4n字节是根据需要增加的选项。因此,TCP首部的最小长度是20字节。 - -15,TCP使用滑动窗口机制。发送窗口里面的序号表示允许发送的序号。发送窗口后沿的后面部分表示已发送且已收到确认,而发送窗口前沿的前面部分表示不允许发送。发送窗口后沿的变化情况有两种可能,即不动(没有收到新的确认)和前移(收到了新的确认)。发送窗口的前沿通常是不断向前移动的。一般来说,我们总是希望数据传输更快一些。但如果发送方把数据发送的过快,接收方就可能来不及接收,这就会造成数据的丢失。所谓流量控制就是让发送方的发送速率不要太快,要让接收方来得及接收。 - -16,在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏。这种情况就叫拥塞。拥塞控制就是为了防止过多的数据注入到网络中,这样就可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机,所有的路由器,以及与降低网络传输性能有关的所有因素。相反,流量控制往往是点对点通信量的控制,是个端到端的问题。流量控制所要做到的就是抑制发送端发送数据的速率,以便使接收端来得及接收。 - -17,为了进行拥塞控制,TCP发送方要维持一个拥塞窗口cwnd的状态变量。拥塞控制窗口的大小取决于网络的拥塞程度,并且动态变化。发送方让自己的发送窗口取为拥塞窗口和接收方的接受窗口中较小的一个。 - -18,TCP的拥塞控制采用了四种算法,即慢开始,拥塞避免,快重传和快恢复。在网络层也可以使路由器采用适当的分组丢弃策略(如主动队列管理AQM),以减少网络拥塞的发生。 - -19,运输连接的三个阶段,即:连接建立,数据传送和连接释放。 - -20,主动发起TCP连接建立的应用进程叫做客户,而被动等待连接建立的应用进程叫做服务器。TCP连接采用三报文握手机制。服务器要确认用户的连接请求,然后客户要对服务器的确认进行确认。 - -21,TCP的连接释放采用四报文握手机制。任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送时,则发送连接释放通知,对方确认后就完全关闭了TCP连接 -### (3),最重要的知识点 -#### ① 端口和套接字的意义 - -#### ② 无连接UDP的特点 - -#### ③ 面向连接TCP的特点 - -#### ④ 在不可靠的网络上实现可靠传输的工作原理,停止等待协议和ARQ协议 - -#### ① TCP的滑动窗口,流量控制,拥塞控制和连接管理 - -## 六应用层 -### (1),基本术语 -#### 域名系统(DNS): - DNS(Domain Name System,域名系统),万维网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。 - 通过域名,最终得到该域名对应的IP地址的过程叫做域名解析(或主机名解析)。DNS协议运行在UDP协议之上,使用端口号53。在RFC文档中RFC 2181对DNS有规范说明,RFC 2136对DNS的动态更新进行说明,RFC 2308对DNS查询的反向缓存进行说明。 -#### 文件传输协议(FTP): - FTP 是File TransferProtocol(文件传输协议)的英文简称,而中文简称为“文传协议”。用于Internet上的控制文件的双向传输。同时,它也是一个应用程序(Application)。 - 基于不同的操作系统有不同的FTP应用程序,而所有这些应用程序都遵守同一种协议以传输文件。在FTP的使用当中,用户经常遇到两个概念:"下载"(Download)和"上传"(Upload)。 - "下载"文件就是从远程主机拷贝文件至自己的计算机上;"上传"文件就是将文件从自己的计算机中拷贝至远程主机上。用Internet语言来说,用户可通过客户机程序向(从)远程主机上传(下载)文件。 - -#### 简单文件传输协议(TFTP): - TFTP(Trivial File Transfer Protocol,简单文件传输协议)是TCP/IP协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议,提供不复杂、开销不大的文件传输服务。端口号为69。 - -#### 远程终端协议(TELENET): - Telnet协议是TCP/IP协议族中的一员,是Internet远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。 - 在终端使用者的电脑上使用telnet程序,用它连接到服务器。终端使用者可以在telnet程序中输入命令,这些命令会在服务器上运行,就像直接在服务器的控制台上输入一样。 - 可以在本地就能控制服务器。要开始一个telnet会话,必须输入用户名和密码来登录服务器。Telnet是常用的远程控制Web服务器的方法。 - - -#### 万维网(WWW): - WWW是环球信息网的缩写,(亦作“Web”、“WWW”、“'W3'”,英文全称为“World Wide Web”),中文名字为“万维网”,"环球网"等,常简称为Web。分为Web客户端和Web服务器程序。 - WWW可以让Web客户端(常用浏览器)访问浏览Web服务器上的页面。是一个由许多互相链接的超文本组成的系统,通过互联网访问。在这个系统中,每个有用的事物,称为一样“资源”;并且由一个全局“统一资源标识符”(URI)标识;这些资源通过超文本传输协议(Hypertext Transfer Protocol)传送给用户,而后者通过点击链接来获得资源。 - 万维网联盟(英语:World Wide Web Consortium,简称W3C),又称W3C理事会。1994年10月在麻省理工学院(MIT)计算机科学实验室成立。万维网联盟的创建者是万维网的发明者蒂姆·伯纳斯-李。 - 万维网并不等同互联网,万维网只是互联网所能提供的服务其中之一,是靠着互联网运行的一项服务。 -#### 万维网的大致工作工程: -![万维网的大致工作工程](https://user-gold-cdn.xitu.io/2018/4/1/1627ff96a96087af?w=839&h=610&f=jpeg&s=86703) - -#### 统一资源定位符(URL): - 统一资源定位符是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。 - -#### 超文本传输协议(HTTP): - 超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。所有的WWW文件都必须遵守这个标准。 - 设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。1960年美国人Ted Nelson构思了一种通过计算机处理文本信息的方法,并称之为超文本(hypertext),这成为了HTTP超文本传输协议标准架构的发展根基。 - -#### 代理服务器(Proxy Server): - 代理服务器(Proxy Server)是一种网络实体,它又称为万维网高速缓存。 - 代理服务器把最近的一些请求和响应暂存在本地磁盘中。当新请求到达时,若代理服务器发现这个请求与暂时存放的的请求相同,就返回暂存的响应,而不需要按URL的地址再次去互联网访问该资源。 - 代理服务器可在客户端或服务器工作,也可以在中间系统工作。 - -#### http请求头: - http请求头,HTTP客户程序(例如浏览器),向服务器发送请求的时候必须指明请求类型(一般是GET或者POST)。如有必要,客户程序还可以选择发送其他的请求头。 - - Accept:浏览器可接受的MIME类型。 - - Accept-Charset:浏览器可接受的字符集。 - - Accept-Encoding:浏览器能够进行解码的数据编码方式,比如gzip。Servlet能够向支持gzip的浏览器返回经gzip编码的HTML页面。许多情形下这可以减少5到10倍的下载时间。 - - Accept-Language:浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到。 - - Authorization:授权信息,通常出现在对服务器发送的WWW-Authenticate头的应答中。 - - Connection:表示是否需要持久连接。如果Servlet看到这里的值为“Keep-Alive”,或者看到请求使用的是HTTP 1.1(HTTP 1.1默认进行持久连接),它就可以利用持久连接的优点,当页面包含多个元素时(例如Applet,图片),显著地减少下载所需要的时间。要实现这一点,Servlet需要在应答中发送一个Content-Length头,最简单的实现方法是:先把内容写入ByteArrayOutputStream,然后在正式写出内容之前计算它的大小。 - - Content-Length:表示请求消息正文的长度。 - - Cookie:这是最重要的请求头信息之一 - - From:请求发送者的email地址,由一些特殊的Web客户程序使用,浏览器不会用到它。 - - Host:初始URL中的主机和端口。 - - If-Modified-Since:只有当所请求的内容在指定的日期之后又经过修改才返回它,否则返回304“Not Modified”应答。 - - Pragma:指定“no-cache”值表示服务器必须返回一个刷新后的文档,即使它是代理服务器而且已经有了页面的本地拷贝。 - - Referer:包含一个URL,用户从该URL代表的页面出发访问当前请求的页面。 - - User-Agent:浏览器类型,如果Servlet返回的内容与浏览器类型有关则该值非常有用。 -#### 简单邮件传输协议(SMTP): - SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。 - SMTP协议属于TCP/IP协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。 - 通过SMTP协议所指定的服务器,就可以把E-mail寄到收信人的服务器上了,整个过程只要几分钟。SMTP服务器则是遵循SMTP协议的发送邮件服务器,用来发送或中转发出的电子邮件。 - -#### 搜索引擎: - 搜索引擎(Search Engine)是指根据一定的策略、运用特定的计算机程序从互联网上搜集信息,在对信息进行组织和处理后,为用户提供检索服务,将用户检索相关的信息展示给用户的系统。 - 搜索引擎包括全文索引、目录索引、元搜索引擎、垂直搜索引擎、集合式搜索引擎、门户搜索引擎与免费链接列表等。 -#### 全文索引: - 全文索引技术是目前搜索引擎的关键技术。 - 试想在1M大小的文件中搜索一个词,可能需要几秒,在100M的文件中可能需要几十秒,如果在更大的文件中搜索那么就需要更大的系统开销,这样的开销是不现实的。 - 所以在这样的矛盾下出现了全文索引技术,有时候有人叫倒排文档技术。 -#### 目录索引: - 目录索引( search index/directory),顾名思义就是将网站分门别类地存放在相应的目录中,因此用户在查询信息时,可选择关键词搜索,也可按分类目录逐层查找。 - - -#### 垂直搜索引擎: - 垂直搜索引擎是针对某一个行业的专业搜索引擎,是搜索引擎的细分和延伸,是对网页库中的某类专门的信息进行一次整合,定向分字段抽取出需要的数据进行处理后再以某种形式返回给用户。 - 垂直搜索是相对通用搜索引擎的信息量大、查询不准确、深度不够等提出来的新的搜索引擎服务模式,通过针对某一特定领域、某一特定人群或某一特定需求提供的有一定价值的信息和相关服务。 - 其特点就是“专、精、深”,且具有行业色彩,相比较通用搜索引擎的海量信息无序化,垂直搜索引擎则显得更加专注、具体和深入。 - -### (2),重要知识点总结 -1,文件传输协议(FTP)使用TCP可靠的运输服务。FTP使用客户服务器方式。一个FTP服务器进程可以同时为多个用户提供服务。在进进行文件传输时,FTP的客户和服务器之间要先建立两个并行的TCP连接:控制连接和数据连接。实际用于传输文件的是数据连接。 - -2,万维网客户程序与服务器之间进行交互使用的协议是超文本传输协议HTTP。HTTP使用TCP连接进行可靠传输。但HTTP本身是无连接、无状态的。HTTP/1.1协议使用了持续连接(分为非流水线方式和流水线方式) - -3,电子邮件把邮件发送到收件人使用的邮件服务器,并放在其中的收件人邮箱中,收件人可随时上网到自己使用的邮件服务器读取,相当于电子邮箱。 - -4,一个电子邮件系统有三个重要组成构件:用户代理、邮件服务器、邮件协议(包括邮件发送协议,如SMTP,和邮件读取协议,如POP3和IMAP)。用户代理和邮件服务器都要运行这些协议。 - - - -### (3),最重要知识点总结 - -#### ① 域名系统-从域名解析出IP地址 -#### ② 访问一个网站大致的过程 -#### ③ 系统调用和应用编程接口概念 - diff --git a/docs/network/计算机网络.md b/docs/network/计算机网络.md index 1cfe2695..5c0e3e25 100644 --- a/docs/network/计算机网络.md +++ b/docs/network/计算机网络.md @@ -44,11 +44,12 @@ **数据链路层(data link layer)通常简称为链路层。两台主机之间的数据传输,总是在一段一段的链路上传送的,这就需要使用专门的链路层的协议。** 在两个相邻节点之间传送数据时,**数据链路层将网络层交下来的 IP 数据报组装成帧**,在两个相邻节点间的链路上传送帧。每一帧包括数据和必要的控制信息(如同步信息,地址信息,差错控制等)。 在接收数据时,控制信息使接收端能够知道一个帧从哪个比特开始和到哪个比特结束。这样,数据链路层在收到一个帧后,就可从中提出数据部分,上交给网络层。 -控制信息还使接收端能够检测到所收到的帧中有误差错。如果发现差错,数据链路层就简单地丢弃这个出了差错的帧,以避免继续在网络中传送下去白白浪费网络资源。如果需要改正数据在链路层传输时出现差错(这就是说,数据链路层不仅要检错,而且还要纠错),那么就要采用可靠性传输协议来纠正出现的差错。这种方法会使链路层的协议复杂些。 +控制信息还使接收端能够检测到所收到的帧中有无差错。如果发现差错,数据链路层就简单地丢弃这个出了差错的帧,以避免继续在网络中传送下去白白浪费网络资源。如果需要改正数据在链路层传输时出现差错(这就是说,数据链路层不仅要检错,而且还要纠错),那么就要采用可靠性传输协议来纠正出现的差错。这种方法会使链路层的协议复杂些。 ### 1.5 物理层 在物理层上所传送的数据单位是比特。 - **物理层(physical layer)的作用是实现相邻计算机节点之间比特流的透明传送,尽可能屏蔽掉具体传输介质和物理设备的差异。** 使其上面的数据链路层不必考虑网络的具体传输介质是什么。“透明传送比特流”表示经实际电路传送后的比特流没有发生变化,对传送的比特流来说,这个电路好像是看不见的。 + + **物理层(physical layer)的作用是实现相邻计算机节点之间比特流的透明传送,尽可能屏蔽掉具体传输介质和物理设备的差异,** 使其上面的数据链路层不必考虑网络的具体传输介质是什么。“透明传送比特流”表示经实际电路传送后的比特流没有发生变化,对传送的比特流来说,这个电路好像是看不见的。 在互联网使用的各种协中最重要和最著名的就是 TCP/IP 两个协议。现在人们经常提到的TCP/IP并不一定单指TCP和IP这两个具体的协议,而往往表示互联网所使用的整个TCP/IP协议族。 @@ -56,7 +57,7 @@ 上面我们对计算机网络的五层体系结构有了初步的了解,下面附送一张七层体系结构图总结一下(图片来源于网络)。 -![七层体系结构图](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019/7/七层体系结构图.png) +![七层体系结构图](images/七层体系结构图.png) ## 二 TCP 三次握手和四次挥手(面试常客) diff --git a/docs/network/计算机网络知识总结.md b/docs/network/计算机网络知识总结.md new file mode 100644 index 00000000..7fa60005 --- /dev/null +++ b/docs/network/计算机网络知识总结.md @@ -0,0 +1,288 @@ +本文是我在大二学习计算机网络期间整理的, 大部分内容都来自于谢希仁老师的《计算机网络》这本书。 + +![](images/计算机网络第七版.png) + + + + + +- [1. 计算机网络概述](#1-计算机网络概述) + - [1.1. 基本术语](#11-基本术语) + - [1.2. 重要知识点总结](#12-重要知识点总结) +- [2. 物理层(Physical Layer)](#2-物理层physical-layer) + - [2.1. 基本术语](#21-基本术语) + - [2.2. 重要知识点总结](#22-重要知识点总结) + - [2.3. 补充](#23-补充) + - [2.3.1. 物理层主要做啥?](#231-物理层主要做啥) + - [2.3.2. 几种常用的信道复用技术](#232-几种常用的信道复用技术) + - [2.3.3. 几种常用的宽带接入技术,主要是 ADSL 和 FTTx](#233-几种常用的宽带接入技术主要是-adsl-和-fttx) +- [3. 数据链路层(Data Link Layer)](#3-数据链路层data-link-layer) + - [3.1. 基本术语](#31-基本术语) + - [3.2. 重要知识点总结](#32-重要知识点总结) + - [3.3. 补充](#33-补充) +- [4. 网络层(Network Layer)](#4-网络层network-layer) + - [4.1. 基本术语](#41-基本术语) + - [4.2. 重要知识点总结](#42-重要知识点总结) +- [5. 传输层(Transport Layer)](#5-传输层transport-layer) + - [5.1. 基本术语](#51-基本术语) + - [5.2. 重要知识点总结](#52-重要知识点总结) + - [5.3. 补充(重要)](#53-补充重要) +- [6. 应用层(Application Layer)](#6-应用层application-layer) + - [6.1. 基本术语](#61-基本术语) + - [6.2. 重要知识点总结](#62-重要知识点总结) + - [6.3. 补充(重要)](#63-补充重要) + + + +## 1. 计算机网络概述 + +![计算机网络概述](images/计算机网络概述.png) + +### 1.1. 基本术语 + +1. **结点 (node)** :网络中的结点可以是计算机,集线器,交换机或路由器等。 +2. **链路(link )** : 从一个结点到另一个结点的一段物理线路。中间没有任何其他交点。 +3. **主机(host)** :连接在因特网上的计算机。 +4. **ISP(Internet Service Provider)** :因特网服务提供者(提供商)。 +5. **IXP(Internet eXchange Point)** : 互联网交换点 IXP 的主要作用就是允许两个网络直接相连并交换分组,而不需要再通过第三个网络来转发分组。 +6. **RFC(Request For Comments)** :意思是“请求评议”,包含了关于 Internet 几乎所有的重要的文字资料。 +7. **广域网 WAN(Wide Area Network)** :任务是通过长距离运送主机发送的数据。 +8. **城域网 MAN(Metropolitan Area Network)**:用来将多个局域网进行互连。 +9. **局域网 LAN(Local Area Network)** : 学校或企业大多拥有多个互连的局域网。 +10. **个人区域网 PAN(Personal Area Network)** :在个人工作的地方把属于个人使用的电子设备用无线技术连接起来的网络 。 +11. **端系统(end system)** :处在因特网边缘的部分即是连接在因特网上的所有的主机。 +12. **分组(packet )** :因特网中传送的数据单元。由首部 header 和数据段组成。分组又称为包,首部可称为包头。 +13. **存储转发(store and forward )** :路由器收到一个分组,先存储下来,再检查其首部,查找转发表,按照首部中的目的地址,找到合适的接口转发出去。 +14. **带宽(bandwidth)** :在计算机网络中,表示在单位时间内从网络中的某一点到另一点所能通过的“最高数据率”。常用来表示网络的通信线路所能传送数据的能力。单位是“比特每秒”,记为 b/s。 +15. **吞吐量(throughput )** :表示在单位时间内通过某个网络(或信道、接口)的数据量。吞吐量更经常地用于对现实世界中的网络的一种测量,以便知道实际上到底有多少数据量能够通过网络。吞吐量受网络的带宽或网络的额定速率的限制。 + +### 1.2. 重要知识点总结 + +1. **计算机网络(简称网络)把许多计算机连接在一起,而互联网把许多网络连接在一起,是网络的网络。** +2. 小写字母 i 开头的 internet(互联网)是通用名词,它泛指由多个计算机网络相互连接而成的网络。在这些网络之间的通信协议(即通信规则)可以是任意的。大写字母 I 开头的 Internet(互联网)是专用名词,它指全球最大的,开放的,由众多网络相互连接而成的特定的互联网,并采用 TCP/IP 协议作为通信规则,其前身为 ARPANET。Internet 的推荐译名为因特网,现在一般流行称为互联网。 +3. 路由器是实现分组交换的关键构件,其任务是转发收到的分组,这是网络核心部分最重要的功能。分组交换采用存储转发技术,表示把一个报文(要发送的整块数据)分为几个分组后再进行传送。在发送报文之前,先把较长的报文划分成为一个个更小的等长数据段。在每个数据端的前面加上一些由必要的控制信息组成的首部后,就构成了一个分组。分组又称为包。分组是在互联网中传送的数据单元,正是由于分组的头部包含了诸如目的地址和源地址等重要控制信息,每一个分组才能在互联网中独立的选择传输路径,并正确地交付到分组传输的终点。 +4. 互联网按工作方式可划分为边缘部分和核心部分。主机在网络的边缘部分,其作用是进行信息处理。由大量网络和连接这些网络的路由器组成核心部分,其作用是提供连通性和交换。 +5. 计算机通信是计算机中进程(即运行着的程序)之间的通信。计算机网络采用的通信方式是客户-服务器方式(C/S 方式)和对等连接方式(P2P 方式)。 +6. 客户和服务器都是指通信中所涉及的应用进程。客户是服务请求方,服务器是服务提供方。 +7. 按照作用范围的不同,计算机网络分为广域网 WAN,城域网 MAN,局域网 LAN,个人区域网 PAN。 +8. **计算机网络最常用的性能指标是:速率,带宽,吞吐量,时延(发送时延,处理时延,排队时延),时延带宽积,往返时间和信道利用率。** +9. 网络协议即协议,是为进行网络中的数据交换而建立的规则。计算机网络的各层以及其协议集合,称为网络的体系结构。 +10. **五层体系结构由应用层,运输层,网络层(网际层),数据链路层,物理层组成。运输层最主要的协议是 TCP 和 UDP 协议,网络层最重要的协议是 IP 协议。** + +![七层体系结构图](images/七层体系结构图.png) + +下面的内容会介绍计算机网络的五层体系结构:**物理层+数据链路层+网络层(网际层)+运输层+应用层**。 + +## 2. 物理层(Physical Layer) + +![物理层](images/物理层.png) + +### 2.1. 基本术语 + +1. **数据(data)** :运送消息的实体。 +2. **信号(signal)** :数据的电气的或电磁的表现。或者说信号是适合在传输介质上传输的对象。 +3. **码元( code)** :在使用时间域(或简称为时域)的波形来表示数字信号时,代表不同离散数值的基本波形。 +4. **单工(simplex )** : 只能有一个方向的通信而没有反方向的交互。 +5. **半双工(half duplex )** :通信的双方都可以发送信息,但不能双方同时发送(当然也就不能同时接收)。 +6. **全双工(full duplex)** : 通信的双方可以同时发送和接收信息。 +7. **奈氏准则** : 在任何信道中,码元的传输的效率是有上限的,传输速率超过此上限,就会出现严重的码间串扰问题,使接收端对码元的判决(即识别)成为不可能。 +8. **基带信号(baseband signal)** : 来自信源的信号。指没有经过调制的数字信号或模拟信号。 +9. **带通(频带)信号(bandpass signal)** :把基带信号经过载波调制后,把信号的频率范围搬移到较高的频段以便在信道中传输(即仅在一段频率范围内能够通过信道),这里调制过后的信号就是带通信号。 +10. **调制(modulation )** : 对信号源的信息进行处理后加到载波信号上,使其变为适合在信道传输的形式的过程。 +11. **信噪比(signal-to-noise ratio )** : 指信号的平均功率和噪声的平均功率之比,记为 S/N。信噪比(dB)=10\*log10(S/N)。 +12. **信道复用(channel multiplexing )** :指多个用户共享同一个信道。(并不一定是同时)。 +13. **比特率(bit rate )** :单位时间(每秒)内传送的比特数。 +14. **波特率(baud rate)** :单位时间载波调制状态改变的次数。针对数据信号对载波的调制速率。 +15. **复用(multiplexing)** :共享信道的方法。 +16. **ADSL(Asymmetric Digital Subscriber Line )** :非对称数字用户线。 +17. **光纤同轴混合网(HFC 网)** :在目前覆盖范围很广的有线电视网的基础上开发的一种居民宽带接入网 + +### 2.2. 重要知识点总结 + +1. **物理层的主要任务就是确定与传输媒体接口有关的一些特性,如机械特性,电气特性,功能特性,过程特性。** +2. 一个数据通信系统可划分为三大部分,即源系统,传输系统,目的系统。源系统包括源点(或源站,信源)和发送器,目的系统包括接收器和终点。 +3. **通信的目的是传送消息。如话音,文字,图像等都是消息,数据是运送消息的实体。信号则是数据的电器或电磁的表现。** +4. 根据信号中代表消息的参数的取值方式不同,信号可分为模拟信号(或连续信号)和数字信号(或离散信号)。在使用时间域(简称时域)的波形表示数字信号时,代表不同离散数值的基本波形称为码元。 +5. 根据双方信息交互的方式,通信可划分为单向通信(或单工通信),双向交替通信(或半双工通信),双向同时通信(全双工通信)。 +6. 来自信源的信号称为基带信号。信号要在信道上传输就要经过调制。调制有基带调制和带通调制之分。最基本的带通调制方法有调幅,调频和调相。还有更复杂的调制方法,如正交振幅调制。 +7. 要提高数据在信道上的传递速率,可以使用更好的传输媒体,或使用先进的调制技术。但数据传输速率不可能任意被提高。 +8. 传输媒体可分为两大类,即导引型传输媒体(双绞线,同轴电缆,光纤)和非导引型传输媒体(无线,红外,大气激光)。 +9. 了有效利用光纤资源,在光纤干线和用户之间广泛使用无源光网络 PON。无源光网络无需配备电源,其长期运营成本和管理成本都很低。最流行的无源光网络是以太网无源光网络 EPON 和吉比特无源光网络 GPON。 + +### 2.3. 补充 + +#### 2.3.1. 物理层主要做啥? + +物理层主要做的事情就是 **透明地传送比特流**。也可以将物理层的主要任务描述为确定与传输媒体的接口的一些特性,即:机械特性(接口所用接线器的一些物理属性如形状尺寸),电气特性(接口电缆的各条线上出现的电压的范围),功能特性(某条线上出现的某一电平的电压的意义),过程特性(对于不同功能能的各种可能事件的出现顺序)。 + +**物理层考虑的是怎样才能在连接各种计算机的传输媒体上传输数据比特流,而不是指具体的传输媒体。** 现有的计算机网络中的硬件设备和传输媒体的种类非常繁多,而且通信手段也有许多不同的方式。物理层的作用正是尽可能地屏蔽掉这些传输媒体和通信手段的差异,使物理层上面的数据链路层感觉不到这些差异,这样就可以使数据链路层只考虑完成本层的协议和服务,而不必考虑网络的具体传输媒体和通信手段是什么。 + +#### 2.3.2. 几种常用的信道复用技术 + +1. **频分复用(FDM)** :所有用户在同样的时间占用不同的带宽资源。 +2. **时分复用(TDM)** :所有用户在不同的时间占用同样的频带宽度(分时不分频)。 +3. **统计时分复用 (Statistic TDM)** :改进的时分复用,能够明显提高信道的利用率。 +4. **码分复用(CDM)** : 用户使用经过特殊挑选的不同码型,因此各用户之间不会造成干扰。这种系统发送的信号有很强的抗干扰能力,其频谱类似于白噪声,不易被敌人发现。 +5. **波分复用( WDM)** :波分复用就是光的频分复用。 + +#### 2.3.3. 几种常用的宽带接入技术,主要是 ADSL 和 FTTx + +用户到互联网的宽带接入方法有非对称数字用户线 ADSL(用数字技术对现有的模拟电话线进行改造,而不需要重新布线。ASDL 的快速版本是甚高速数字用户线 VDSL。),光纤同轴混合网 HFC(是在目前覆盖范围很广的有线电视网的基础上开发的一种居民宽带接入网)和 FTTx(即光纤到······)。 + +## 3. 数据链路层(Data Link Layer) + +![数据链路层](images/数据链路层.png) + +### 3.1. 基本术语 + +1. **链路(link)** :一个结点到相邻结点的一段物理链路。 +2. **数据链路(data link)** :把实现控制数据运输的协议的硬件和软件加到链路上就构成了数据链路。 +3. **循环冗余检验 CRC(Cyclic Redundancy Check)** :为了保证数据传输的可靠性,CRC 是数据链路层广泛使用的一种检错技术。 +4. **帧(frame)** :一个数据链路层的传输单元,由一个数据链路层首部和其携带的封包所组成协议数据单元。 +5. **MTU(Maximum Transfer Uint )** :最大传送单元。帧的数据部分的的长度上限。 +6. **误码率 BER(Bit Error Rate )** :在一段时间内,传输错误的比特占所传输比特总数的比率。 +7. **PPP(Point-to-Point Protocol )** :点对点协议。即用户计算机和 ISP 进行通信时所使用的数据链路层协议。以下是 PPP 帧的示意图: + ![PPP](https://user-gold-cdn.xitu.io/2018/4/1/1627f8291c6b032c?w=624&h=359&f=jpeg&s=44271) +8. **MAC 地址(Media Access Control 或者 Medium Access Control)** :意译为媒体访问控制,或称为物理地址、硬件地址,用来定义网络设备的位置。在 OSI 模型中,第三层网络层负责 IP 地址,第二层数据链路层则负责 MAC 地址。因此一个主机会有一个 MAC 地址,而每个网络位置会有一个专属于它的 IP 地址 。地址是识别某个系统的重要标识符,“名字指出我们所要寻找的资源,地址指出资源所在的地方,路由告诉我们如何到达该处。 +9. **网桥(bridge)** :一种用于数据链路层实现中继,连接两个或多个局域网的网络互连设备。 +10. **交换机(switch )** :广义的来说,交换机指的是一种通信系统中完成信息交换的设备。这里工作在数据链路层的交换机指的是交换式集线器,其实质是一个多接口的网桥 + +### 3.2. 重要知识点总结 + +1. 链路是从一个结点到相邻节点的一段物理链路,数据链路则在链路的基础上增加了一些必要的硬件(如网络适配器)和软件(如协议的实现) +2. 数据链路层使用的主要是**点对点信道**和**广播信道**两种。 +3. 数据链路层传输的协议数据单元是帧。数据链路层的三个基本问题是:**封装成帧**,**透明传输**和**差错检测** +4. **循环冗余检验 CRC** 是一种检错方法,而帧检验序列 FCS 是添加在数据后面的冗余码 +5. **点对点协议 PPP** 是数据链路层使用最多的一种协议,它的特点是:简单,只检测差错而不去纠正差错,不使用序号,也不进行流量控制,可同时支持多种网络层协议 +6. PPPoE 是为宽带上网的主机使用的链路层协议 +7. **局域网的优点是:具有广播功能,从一个站点可方便地访问全网;便于系统的扩展和逐渐演变;提高了系统的可靠性,可用性和生存性。** +8. 计算机与外接局域网通信需要通过通信适配器(或网络适配器),它又称为网络接口卡或网卡。**计算器的硬件地址就在适配器的 ROM 中**。 +9. 以太网采用的无连接的工作方式,对发送的数据帧不进行编号,也不要求对方发回确认。目的站收到有差错帧就把它丢掉,其他什么也不做 +10. 以太网采用的协议是具有冲突检测的**载波监听多点接入 CSMA/CD**。协议的特点是:**发送前先监听,边发送边监听,一旦发现总线上出现了碰撞,就立即停止发送。然后按照退避算法等待一段随机时间后再次发送。** 因此,每一个站点在自己发送数据之后的一小段时间内,存在这遭遇碰撞的可能性。以太网上的各站点平等的争用以太网信道 +11. 以太网的适配器具有过滤功能,它只接收单播帧,广播帧和多播帧。 +12. 使用集线器可以在物理层扩展以太网(扩展后的以太网仍然是一个网络) + +### 3.3. 补充 + +1. 数据链路层的点对点信道和广播信道的特点,以及这两种信道所使用的协议(PPP 协议以及 CSMA/CD 协议)的特点 +2. 数据链路层的三个基本问题:**封装成帧**,**透明传输**,**差错检测** +3. 以太网的 MAC 层硬件地址 +4. 适配器,转发器,集线器,网桥,以太网交换机的作用以及适用场合 + +## 4. 网络层(Network Layer) + +![网络层](images/网络层.png) + +### 4.1. 基本术语 + +1. **虚电路(Virtual Circuit)** : 在两个终端设备的逻辑或物理端口之间,通过建立的双向的透明传输通道。虚电路表示这只是一条逻辑上的连接,分组都沿着这条逻辑连接按照存储转发方式传送,而并不是真正建立了一条物理连接。 +2. **IP(Internet Protocol )** : 网际协议 IP 是 TCP/IP 体系中两个最主要的协议之一,是 TCP/IP 体系结构网际层的核心。配套的有 ARP,RARP,ICMP,IGMP。 +3. **ARP(Address Resolution Protocol)** : 地址解析协议。地址解析协议 ARP 把 IP 地址解析为硬件地址。 +4. **ICMP(Internet Control Message Protocol )** :网际控制报文协议 (ICMP 允许主机或路由器报告差错情况和提供有关异常情况的报告)。 +5. **子网掩码(subnet mask )** :它是一种用来指明一个 IP 地址的哪些位标识的是主机所在的子网以及哪些位标识的是主机的位掩码。子网掩码不能单独存在,它必须结合 IP 地址一起使用。 +6. **CIDR( Classless Inter-Domain Routing ) **:无分类域间路由选择 (特点是消除了传统的 A 类、B 类和 C 类地址以及划分子网的概念,并使用各种长度的“网络前缀”(network-prefix)来代替分类地址中的网络号和子网号)。 +7. **默认路由(default route)** :当在路由表中查不到能到达目的地址的路由时,路由器选择的路由。默认路由还可以减小路由表所占用的空间和搜索路由表所用的时间。 +8. **路由选择算法(Virtual Circuit)** :路由选择协议的核心部分。因特网采用自适应的,分层次的路由选择协议。 + +### 4.2. 重要知识点总结 + +1. **TCP/IP 协议中的网络层向上只提供简单灵活的,无连接的,尽最大努力交付的数据报服务。网络层不提供服务质量的承诺,不保证分组交付的时限所传送的分组可能出错,丢失,重复和失序。进程之间通信的可靠性由运输层负责** +2. 在互联网的交付有两种,一是在本网络直接交付不用经过路由器,另一种是和其他网络的间接交付,至少经过一个路由器,但最后一次一定是直接交付 +3. 分类的 IP 地址由网络号字段(指明网络)和主机号字段(指明主机)组成。网络号字段最前面的类别指明 IP 地址的类别。IP 地址是一种分等级的地址结构。IP 地址管理机构分配 IP 地址时只分配网络号,主机号由得到该网络号的单位自行分配。路由器根据目的主机所连接的网络号来转发分组。一个路由器至少连接到两个网络,所以一个路由器至少应当有两个不同的 IP 地址 +4. IP 数据报分为首部和数据两部分。首部的前一部分是固定长度,共 20 字节,是所有 IP 数据包必须具有的(源地址,目的地址,总长度等重要地段都固定在首部)。一些长度可变的可选字段固定在首部的后面。IP 首部中的生存时间给出了 IP 数据报在互联网中所能经过的最大路由器数。可防止 IP 数据报在互联网中无限制的兜圈子。 +5. **地址解析协议 ARP 把 IP 地址解析为硬件地址。ARP 的高速缓存可以大大减少网络上的通信量。因为这样可以使主机下次再与同样地址的主机通信时,可以直接从高速缓存中找到所需要的硬件地址而不需要再去广播方式发送 ARP 请求分组** +6. 无分类域间路由选择 CIDR 是解决目前 IP 地址紧缺的一个好办法。CIDR 记法把 IP 地址后面加上斜线“/”,然后写上前缀所所占的位数。前缀(或网络前缀用来指明网络),前缀后面的部分是后缀,用来指明主机。CIDR 把前缀都相同的连续的 IP 地址组成一个“CIDR 地址块”,IP 地址分配都以 CIDR 地址块为单位。 +7. 网际控制报文协议是 IP 层的协议。ICMP 报文作为 IP 数据报的数据,加上首部后组成 IP 数据报发送出去。使用 ICMP 数据报并不是为了实现可靠传输。ICMP 允许主机或路由器报告差错情况和提供有关异常情况的报告。ICMP 报文的种类有两种 ICMP 差错报告报文和 ICMP 询问报文。 +8. **要解决 IP 地址耗尽的问题,最根本的办法是采用具有更大地址空间的新版本 IP 协议-IPv6。** IPv6 所带来的变化有 ① 更大的地址空间(采用 128 位地址)② 灵活的首部格式 ③ 改进的选项 ④ 支持即插即用 ⑤ 支持资源的预分配 ⑥IPv6 的首部改为 8 字节对齐。 +9. **虚拟专用网络 VPN 利用公用的互联网作为本机构专用网之间的通信载体。VPN 内使用互联网的专用地址。一个 VPN 至少要有一个路由器具有合法的全球 IP 地址,这样才能和本系统的另一个 VPN 通过互联网进行通信。所有通过互联网传送的数据都需要加密。** +10. MPLS 的特点是:① 支持面向连接的服务质量 ② 支持流量工程,平衡网络负载 ③ 有效的支持虚拟专用网 VPN。MPLS 在入口节点给每一个 IP 数据报打上固定长度的“标记”,然后根据标记在第二层(链路层)用硬件进行转发(在标记交换路由器中进行标记交换),因而转发速率大大加快。 + +## 5. 传输层(Transport Layer) + +![传输层](images/传输层.png) + +### 5.1. 基本术语 + +1. **进程(process)** :指计算机中正在运行的程序实体。 +2. **应用进程互相通信** :一台主机的进程和另一台主机中的一个进程交换数据的过程(另外注意通信真正的端点不是主机而是主机中的进程,也就是说端到端的通信是应用进程之间的通信)。 +3. **传输层的复用与分用** :复用指发送方不同的进程都可以通过统一个运输层协议传送数据。分用指接收方的运输层在剥去报文的首部后能把这些数据正确的交付到目的应用进程。 +4. **TCP(Transmission Control Protocol)** :传输控制协议。 +5. **UDP(User Datagram Protocol)** :用户数据报协议。 +6. **端口(port) ** :端口的目的是为了确认对方机器是那个进程在于自己进行交互,比如 MSN 和 QQ 的端口不同,如果没有端口就可能出现 QQ 进程和 MSN 交互错误。端口又称协议端口号。 +7. **停止等待协议(stop-and-wait)** :指发送方每发送完一个分组就停止发送,等待对方确认,在收到确认之后在发送下一个分组。 +8. **流量控制** : 就是让发送方的发送速率不要太快,既要让接收方来得及接收,也不要使网络发生拥塞。 +9. **拥塞控制** :防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。 + +### 5.2. 重要知识点总结 + +1. **运输层提供应用进程之间的逻辑通信,也就是说,运输层之间的通信并不是真正在两个运输层之间直接传输数据。运输层向应用层屏蔽了下面网络的细节(如网络拓补,所采用的路由选择协议等),它使应用进程之间看起来好像两个运输层实体之间有一条端到端的逻辑通信信道。** +2. **网络层为主机提供逻辑通信,而运输层为应用进程之间提供端到端的逻辑通信。** +3. 运输层的两个重要协议是用户数据报协议 UDP 和传输控制协议 TCP。按照 OSI 的术语,两个对等运输实体在通信时传送的数据单位叫做运输协议数据单元 TPDU(Transport Protocol Data Unit)。但在 TCP/IP 体系中,则根据所使用的协议是 TCP 或 UDP,分别称之为 TCP 报文段或 UDP 用户数据报。 +4. **UDP 在传送数据之前不需要先建立连接,远地主机在收到 UDP 报文后,不需要给出任何确认。虽然 UDP 不提供可靠交付,但在某些情况下 UDP 确是一种最有效的工作方式。 TCP 提供面向连接的服务。在传送数据之前必须先建立连接,数据传送结束后要释放连接。TCP 不提供广播或多播服务。由于 TCP 要提供可靠的,面向连接的传输服务,这一难以避免增加了许多开销,如确认,流量控制,计时器以及连接管理等。这不仅使协议数据单元的首部增大很多,还要占用许多处理机资源。** +5. 硬件端口是不同硬件设备进行交互的接口,而软件端口是应用层各种协议进程与运输实体进行层间交互的一种地址。UDP 和 TCP 的首部格式中都有源端口和目的端口这两个重要字段。当运输层收到 IP 层交上来的运输层报文时,就能够 根据其首部中的目的端口号把数据交付应用层的目的应用层。(两个进程之间进行通信不光要知道对方 IP 地址而且要知道对方的端口号(为了找到对方计算机中的应用进程)) +6. 运输层用一个 16 位端口号标志一个端口。端口号只有本地意义,它只是为了标志计算机应用层中的各个进程在和运输层交互时的层间接口。在互联网的不同计算机中,相同的端口号是没有关联的。协议端口号简称端口。虽然通信的终点是应用进程,但只要把所发送的报文交到目的主机的某个合适端口,剩下的工作(最后交付目的进程)就由 TCP 和 UDP 来完成。 +7. 运输层的端口号分为服务器端使用的端口号(0~1023 指派给熟知端口,1024~49151 是登记端口号)和客户端暂时使用的端口号(49152~65535) +8. **UDP 的主要特点是 ① 无连接 ② 尽最大努力交付 ③ 面向报文 ④ 无拥塞控制 ⑤ 支持一对一,一对多,多对一和多对多的交互通信 ⑥ 首部开销小(只有四个字段:源端口,目的端口,长度和检验和)** +9. **TCP 的主要特点是 ① 面向连接 ② 每一条 TCP 连接只能是一对一的 ③ 提供可靠交付 ④ 提供全双工通信 ⑤ 面向字节流** +10. **TCP 用主机的 IP 地址加上主机上的端口号作为 TCP 连接的端点。这样的端点就叫做套接字(socket)或插口。套接字用(IP 地址:端口号)来表示。每一条 TCP 连接唯一被通信两端的两个端点所确定。** +11. 停止等待协议是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认。在收到确认后再发下一个分组。 +12. 为了提高传输效率,发送方可以不使用低效率的停止等待协议,而是采用流水线传输。流水线传输就是发送方可连续发送多个分组,不必每发完一个分组就停下来等待对方确认。这样可使信道上一直有数据不间断的在传送。这种传输方式可以明显提高信道利用率。 +13. 停止等待协议中超时重传是指只要超过一段时间仍然没有收到确认,就重传前面发送过的分组(认为刚才发送过的分组丢失了)。因此每发送完一个分组需要设置一个超时计时器,其重转时间应比数据在分组传输的平均往返时间更长一些。这种自动重传方式常称为自动重传请求 ARQ。另外在停止等待协议中若收到重复分组,就丢弃该分组,但同时还要发送确认。连续 ARQ 协议可提高信道利用率。发送维持一个发送窗口,凡位于发送窗口内的分组可连续发送出去,而不需要等待对方确认。接收方一般采用累积确认,对按序到达的最后一个分组发送确认,表明到这个分组位置的所有分组都已经正确收到了。 +14. TCP 报文段的前 20 个字节是固定的,后面有 4n 字节是根据需要增加的选项。因此,TCP 首部的最小长度是 20 字节。 +15. **TCP 使用滑动窗口机制。发送窗口里面的序号表示允许发送的序号。发送窗口后沿的后面部分表示已发送且已收到确认,而发送窗口前沿的前面部分表示不允许发送。发送窗口后沿的变化情况有两种可能,即不动(没有收到新的确认)和前移(收到了新的确认)。发送窗口的前沿通常是不断向前移动的。一般来说,我们总是希望数据传输更快一些。但如果发送方把数据发送的过快,接收方就可能来不及接收,这就会造成数据的丢失。所谓流量控制就是让发送方的发送速率不要太快,要让接收方来得及接收。** +16. 在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏。这种情况就叫拥塞。拥塞控制就是为了防止过多的数据注入到网络中,这样就可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机,所有的路由器,以及与降低网络传输性能有关的所有因素。相反,流量控制往往是点对点通信量的控制,是个端到端的问题。流量控制所要做到的就是抑制发送端发送数据的速率,以便使接收端来得及接收。 +17. **为了进行拥塞控制,TCP 发送方要维持一个拥塞窗口 cwnd 的状态变量。拥塞控制窗口的大小取决于网络的拥塞程度,并且动态变化。发送方让自己的发送窗口取为拥塞窗口和接收方的接受窗口中较小的一个。** +18. **TCP 的拥塞控制采用了四种算法,即慢开始,拥塞避免,快重传和快恢复。在网络层也可以使路由器采用适当的分组丢弃策略(如主动队列管理 AQM),以减少网络拥塞的发生。** +19. 运输连接的三个阶段,即:连接建立,数据传送和连接释放。 +20. **主动发起 TCP 连接建立的应用进程叫做客户,而被动等待连接建立的应用进程叫做服务器。TCP 连接采用三报文握手机制。服务器要确认用户的连接请求,然后客户要对服务器的确认进行确认。** +21. TCP 的连接释放采用四报文握手机制。任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送时,则发送连接释放通知,对方确认后就完全关闭了 TCP 连接 + +### 5.3. 补充(重要) + +以下知识点需要重点关注: + +1. 端口和套接字的意义 +2. UDP 和 TCP 的区别以及两者的应用场景 +3. 在不可靠的网络上实现可靠传输的工作原理,停止等待协议和 ARQ 协议 +4. TCP 的滑动窗口,流量控制,拥塞控制和连接管理 +5. TCP 的三次握手,四次挥手机制 + +## 6. 应用层(Application Layer) + +![应用层](./images/应用层.png) + +### 6.1. 基本术语 + +1. **域名系统(DNS)** :DNS(Domain Name System,域名系统),万维网上作为域名和 IP 地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的 IP 数串。通过域名,最终得到该域名对应的 IP 地址的过程叫做域名解析(或主机名解析)。DNS 协议运行在 UDP 协议之上,使用端口号 53。在 RFC 文档中 RFC 2181 对 DNS 有规范说明,RFC 2136 对 DNS 的动态更新进行说明,RFC 2308 对 DNS 查询的反向缓存进行说明。 +2. **文件传输协议(FTP)** :FTP 是 File TransferProtocol(文件传输协议)的英文简称,而中文简称为“文传协议”。用于 Internet 上的控制文件的双向传输。同时,它也是一个应用程序(Application)。基于不同的操作系统有不同的 FTP 应用程序,而所有这些应用程序都遵守同一种协议以传输文件。在 FTP 的使用当中,用户经常遇到两个概念:"下载"(Download)和"上传"(Upload)。 "下载"文件就是从远程主机拷贝文件至自己的计算机上;"上传"文件就是将文件从自己的计算机中拷贝至远程主机上。用 Internet 语言来说,用户可通过客户机程序向(从)远程主机上传(下载)文件。 +3. **简单文件传输协议(TFTP)** :TFTP(Trivial File Transfer Protocol,简单文件传输协议)是 TCP/IP 协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议,提供不复杂、开销不大的文件传输服务。端口号为 69。 +4. **远程终端协议(TELENET)** :Telnet 协议是 TCP/IP 协议族中的一员,是 Internet 远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。在终端使用者的电脑上使用 telnet 程序,用它连接到服务器。终端使用者可以在 telnet 程序中输入命令,这些命令会在服务器上运行,就像直接在服务器的控制台上输入一样。可以在本地就能控制服务器。要开始一个 telnet 会话,必须输入用户名和密码来登录服务器。Telnet 是常用的远程控制 Web 服务器的方法。 +5. **万维网(WWW)** :WWW 是环球信息网的缩写,(亦作“Web”、“WWW”、“'W3'”,英文全称为“World Wide Web”),中文名字为“万维网”,"环球网"等,常简称为 Web。分为 Web 客户端和 Web 服务器程序。WWW 可以让 Web 客户端(常用浏览器)访问浏览 Web 服务器上的页面。是一个由许多互相链接的超文本组成的系统,通过互联网访问。在这个系统中,每个有用的事物,称为一样“资源”;并且由一个全局“统一资源标识符”(URI)标识;这些资源通过超文本传输协议(Hypertext Transfer Protocol)传送给用户,而后者通过点击链接来获得资源。万维网联盟(英语:World Wide Web Consortium,简称 W3C),又称 W3C 理事会。1994 年 10 月在麻省理工学院(MIT)计算机科学实验室成立。万维网联盟的创建者是万维网的发明者蒂姆·伯纳斯-李。万维网并不等同互联网,万维网只是互联网所能提供的服务其中之一,是靠着互联网运行的一项服务。 +6. **万维网的大致工作工程:** + +![万维网的大致工作工程](images/计算机网络知识点总结/万维网的大致工作工程.png) + +7. **统一资源定位符(URL)** :统一资源定位符是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的 URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。 +8. **超文本传输协议(HTTP)** :超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。所有的 WWW 文件都必须遵守这个标准。设计 HTTP 最初的目的是为了提供一种发布和接收 HTML 页面的方法。1960 年美国人 Ted Nelson 构思了一种通过计算机处理文本信息的方法,并称之为超文本(hypertext),这成为了 HTTP 超文本传输协议标准架构的发展根基。 +9. **代理服务器(Proxy Server)** : 代理服务器(Proxy Server)是一种网络实体,它又称为万维网高速缓存。 代理服务器把最近的一些请求和响应暂存在本地磁盘中。当新请求到达时,若代理服务器发现这个请求与暂时存放的的请求相同,就返回暂存的响应,而不需要按 URL 的地址再次去互联网访问该资源。代理服务器可在客户端或服务器工作,也可以在中间系统工作。 +10. **简单邮件传输协议(SMTP)** : SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。 SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。 通过 SMTP 协议所指定的服务器,就可以把 E-mail 寄到收信人的服务器上了,整个过程只要几分钟。SMTP 服务器则是遵循 SMTP 协议的发送邮件服务器,用来发送或中转发出的电子邮件。 +11. **搜索引擎** :搜索引擎(Search Engine)是指根据一定的策略、运用特定的计算机程序从互联网上搜集信息,在对信息进行组织和处理后,为用户提供检索服务,将用户检索相关的信息展示给用户的系统。搜索引擎包括全文索引、目录索引、元搜索引擎、垂直搜索引擎、集合式搜索引擎、门户搜索引擎与免费链接列表等。 +12. **垂直搜索引擎** :垂直搜索引擎是针对某一个行业的专业搜索引擎,是搜索引擎的细分和延伸,是对网页库中的某类专门的信息进行一次整合,定向分字段抽取出需要的数据进行处理后再以某种形式返回给用户。垂直搜索是相对通用搜索引擎的信息量大、查询不准确、深度不够等提出来的新的搜索引擎服务模式,通过针对某一特定领域、某一特定人群或某一特定需求提供的有一定价值的信息和相关服务。其特点就是“专、精、深”,且具有行业色彩,相比较通用搜索引擎的海量信息无序化,垂直搜索引擎则显得更加专注、具体和深入。 +13. **全文索引** :全文索引技术是目前搜索引擎的关键技术。试想在 1M 大小的文件中搜索一个词,可能需要几秒,在 100M 的文件中可能需要几十秒,如果在更大的文件中搜索那么就需要更大的系统开销,这样的开销是不现实的。所以在这样的矛盾下出现了全文索引技术,有时候有人叫倒排文档技术。 +14. **目录索引** :目录索引( search index/directory),顾名思义就是将网站分门别类地存放在相应的目录中,因此用户在查询信息时,可选择关键词搜索,也可按分类目录逐层查找。 + +### 6.2. 重要知识点总结 + +1. 文件传输协议(FTP)使用 TCP 可靠的运输服务。FTP 使用客户服务器方式。一个 FTP 服务器进程可以同时为多个用户提供服务。在进进行文件传输时,FTP 的客户和服务器之间要先建立两个并行的 TCP 连接:控制连接和数据连接。实际用于传输文件的是数据连接。 +2. 万维网客户程序与服务器之间进行交互使用的协议是超文本传输协议 HTTP。HTTP 使用 TCP 连接进行可靠传输。但 HTTP 本身是无连接、无状态的。HTTP/1.1 协议使用了持续连接(分为非流水线方式和流水线方式) +3. 电子邮件把邮件发送到收件人使用的邮件服务器,并放在其中的收件人邮箱中,收件人可随时上网到自己使用的邮件服务器读取,相当于电子邮箱。 +4. 一个电子邮件系统有三个重要组成构件:用户代理、邮件服务器、邮件协议(包括邮件发送协议,如 SMTP,和邮件读取协议,如 POP3 和 IMAP)。用户代理和邮件服务器都要运行这些协议。 + +### 6.3. 补充(重要) + +以下知识点需要重点关注: + +1. 应用层的常见协议(重点关注 HTTP 协议) +2. 域名系统-从域名解析出 IP 地址 +3. 访问一个网站大致的过程 +4. 系统调用和应用编程接口概念 \ No newline at end of file diff --git a/docs/operating-system/Linux_IO.md b/docs/operating-system/Linux_IO.md deleted file mode 100644 index 99478c22..00000000 --- a/docs/operating-system/Linux_IO.md +++ /dev/null @@ -1,160 +0,0 @@ - - -- [Linux IO](#linux-io) - - [操作系统的内核](#操作系统的内核) - - [操作系统的用户态与内核态](#操作系统的用户态与内核态) - - [为什么要有用户态与内核态?](#为什么要有用户态与内核态) - - [用户态切换到内核态的几种方式](#用户态切换到内核态的几种方式) - - [阻塞和非阻塞](#阻塞和非阻塞) - - [同步与异步](#同步与异步) - - [Linux IO 模型](#linux-io模型) - - [阻塞 IO](#阻塞io) - - [非阻塞 IO(网络 IO 模型)](#非阻塞io网络io模型) - - [IO 多路复用(网络 IO 模型)](#io多路复用网络io模型) - - [信号驱动 IO(网络 IO 模型)](#信号驱动io网络io模型) - - [异步 IO](#异步io) - - - -# Linux IO - -> 图源: https://www.jianshu.com/p/85e931636f27 (如有侵权,请联系俺,俺会立刻删除) - -### 操作系统的内核 - -**操作系统的内核是操作系统的核心部分。它负责系统的内存,硬件设备,文件系统以及应用程序的管理。** - -#### 操作系统的用户态与内核态 - -unix 与 linux 的体系架构:用户态与内核态。 - -用户态与内核态与内核态是操作系统对执行权限进行分级后的不同的运行模式。 - -![用户态与内核态](../../media/pictures/java/linux_io/用户态与内核态.png) - -#### 为什么要有用户态与内核态? - -在 cpu 的所有指令中,有些指令是非常危险的,如果使用不当,将会造成系统崩溃等后果。为了避免这种情况发生,cpu 将指令划分为**特权级(内核态)指令**和**非特权级(用户态)指令。** - -**对于那些危险的指令只允许内核及其相关模块调用,对于那些不会造成危险的指令,就允许用户应用程序调用。** - -- 内核态(核心态,特权态): **内核态是操作系统内核运行的模式。** 内核态控制计算机的硬件资源,如硬件设备,文件系统等等,并为上层应用程序提供执行环境。 -- 用户态: **用户态是用户应用程序运行的状态。** 应用程序必须依托于内核态运行,因此用户态的态的操作权限比内核态是要低的,如磁盘,文件等,访问操作都是受限的。 -- 系统调用: 系统调用是操作系统为应用程序提供能够访问到内核态的资源的接口。 - -#### 用户态切换到内核态的几种方式 - -- 系统调用: 系统调用是用户态主动要求切换到内核态的一种方式,用户应用程序通过操作系统调用内核为上层应用程序开放的接口来执行程序。 -- 异常: 当 cpu 在执行用户态的应用程序时,发生了某些不可知的异常。于是当前用户态的应用进程切换到处理此异常的内核的程序中去。 -- 硬件设备的中断: 当硬件设备完成用户请求后,会向 cpu 发出相应的中断信号,这时 cpu 会暂停执行下一条即将要执行的指令,转而去执行与中断信号对应的应用程序,如果先前执行的指令是用户态下程序的指令,那么这个转换过程也是用户态到内核台的转换。 - -#### 阻塞和非阻塞 - -1. 阻塞: 一个线程调用一个方法计算 1 - 100 的和,如果该方法没有返回结果, - 那么调用方法的线程就一直等待直到该方法执行完毕。 -2. 非阻塞: 一个线程调用一个方法计算 1 - 100 的和,该方法立刻返回,如果方法返回没有结果, - 调用者线程也无需一直等待该方法的结果,可以执行其他任务,但是在方法返回结果之前, - **线程仍然需要轮询的检查方法是否已经有结果。** - -**结论: 阻塞与非阻塞针对调用者的立场而言。** - -#### 同步与异步 - -1. **同步**: 一个线程调用一个方法计算 1 - 100 的和,如果方法没有计算完,就不返回。 -2. **异步**: 一个线程调用一个方法计算 1 - 100 的和,该方法立刻返回,但是由于方法没有返回结果, - 所以就需要被调用的这个方法来通知调用线程 1 - 100 的结果, - 或者线程在调用方法的时候指定一个回调函数来告诉被调用的方法执行完后就执行回调函数。 - -**结论:同步和异步是针对被调用者的立场而言的。** - -### Linux IO 模型 - -Linux 下共有 5 种 IO 模型: - -1. 阻塞 IO -2. 非阻塞 IO -3. IO 多路复用 -4. 信号驱动 IO -5. 异步 IO - -#### 阻塞 IO - -阻塞 IO 是很常见的一种 IO 模型。在这种模型中,**用户态的应用程序会执行一个操作系统的调用,检查内核的数据是否准备好。如果内核的数据已经准备好,就把数据复制到用户应用进程。如果内核没有准备好数据,那么用户应用进程(线程)就阻塞,直到内核准备好数据并把数据从内核复制到用户应用进程,** 最后应用程序再处理数据。 - -![BIO原理](../../media/pictures/java/linux_io/BIO原理.png) - -**阻塞 IO 是同步阻塞的。** - -1. 阻塞 IO 的同步体现在: **内核只有准备好数据并把数据复制到用户应用进程才会返回。** - -2. 阻塞 IO 的阻塞体现在:**用户应用进程等待内核准备数据和把数据从用户态拷贝到内核态的这整个过程, - 用户应用进程都必须一直等待。** 当然,如果是本地磁盘 IO,内核准备数据的时间可能会很短。但网络 IO 就不一样了,因为服务端不知道客户端何时发送数据,内核就仍需要等待 socket 数据,时间就可能会很长。 - -**阻塞 IO 的优点是对于数据是能够保证无延时的,因为应用程序进程会一直阻塞直到 IO 完成。**但应用程序的阻塞就意味着应用程序进程无法执行其他任务,这会大大降低程序性能。一个不太可行的办法是为每个客户端 socket 都分配一个线程,这样就会提升 server 处理请求的能力。不过操作系统的线程资源是有限的,如果请求过多,可能造成线程资源耗尽,系统卡死等后果。 - -#### 非阻塞 IO(网络 IO 模型) - -在非阻塞 IO 模型中,用户态的应用程序也会执行一个操作系统的调用,检查内核的数据是否准备完成。**如果内核没有准备好数据, -内核会立刻返回结果,用户应用进程不用一直阻塞等待内核准备数据,而是可以执行其他任务,但仍需要不断的向内核发起系统调用,检测数据是否准备好,这个过程就叫轮询。** 轮询直到内核准备好数据,然后内核把数据拷贝到用户应用进程,再进行数据处理。 - -![NIO原理](../../media/pictures/java/linux_io/NIO原理.png) - -非阻塞 IO 的非阻塞体现在: **用户应用进程不用阻塞在对内核的系统调用上** - -非阻塞 IO 的优点在于用户应用进程在轮询阶段可以执行其它任务。但这也是它的缺点,轮询就代表着用户应用进程不是时刻都会发起系统调用。 -**可能数据准备好了,而用户应用进程可能等待其它任务执行完毕才会发起系统调用,这就意味着数据可能会被延时获取。** - -#### IO 多路复用(网络 IO 模型) - -在 IO 多路复用模型中,**用户应用进程会调用操作系统的 select/poll/epoll 函数,它会使内核同步的轮询指定的 socket, -(在 NIO 中,socket 就是注册到 Selector 上的 SocketChannel,可以允许多个)直至监听的 socket 有数据可读或可写,select/poll/epoll 函数才会返回,用户应用进程也会阻塞的等待 select/poll/epoll 函数返回。** - -当 select/poll/epoll 函数返回后,即某个 socket 有事件发生了,用户应用进程就会发起系统调用,处理事件,将 socket 数据复制到用户进程内,然后进行数据处理。 - -![IO多路复用原理](../../media/pictures/java/linux_io/IO多路复用原理.png) - -**IO 多路复用模型是同步阻塞的** - -1. IO 多路复用模型的同步体现在: **select 函数只有监听到某个 socket 有事件才会返回。** - -2. IO 多路复用模型的阻塞体现在: **用户应用进程会阻塞在对 select 函数上的调用上。** - -**IO 多路复用的优点在于内核可以处理多个 socket,相当于一个用户进程(线程)就可以处理多个 socket 连接。** - -这样不仅降低了系统的开销,并且对于需要高并发的应用是非常有利的。而非阻塞 IO 和阻塞 IO 的一个用户应用进程只能处理一个 socket,要想处理多 socket,只能新开进程或线程,但这样很消耗系统资源。 - -**PS: -在 IO 多路复用模型中, socket 一般应该为非阻塞的,这就是 Java 中 NIO 被称为非阻塞 IO 的原因。但实际上 NIO 属于 IO 多路复用,它是同步阻塞的 IO。具体原因见 [知乎讨论](https://www.zhihu.com/question/37271342)** - -**PS: -select/poll/epoll 函数是 IO 多路复用模型的基础,所以如果想深入了解 IO 多路复用模型,就需要了解这 3 个函数以及它们的优缺点。** - -#### 信号驱动 IO(网络 IO 模型) - -在信号驱动 IO 模型中,**用户应用进程发起 sigaction 系统调用,内核收到并立即返回。用户应用进程可以继续执行其他任务,不会阻塞。当内核准备好数据后向用户应用进程发送 SIGIO 信号,应用进程收到信号后,发起系统调用,将数据从内核拷贝到用户进程,** 然后进行数据处理。 - -![信号驱动IO原理](../../media/pictures/java/linux_io/信号驱动IO原理.png) - -个人感觉在内核收到系统调用就立刻返回这一点很像异步 IO 的方式了,不过与异步 IO 仍有很大差别。 - -#### 异步 IO - -在异步 IO 模型中,**用户进程发起 aio_read 系统调用,无论内核的数据是否准备好,都会立即返回。用户应用进程不会阻塞,可以继续执行其他任务。当内核准备好数据,会直接把数据复制到用户应用进程。最后内核会通知用户应用进程 IO 完成。** - -![异步IO原理](../../media/pictures/java/linux_io/异步IO原理.png) - -**异步 IO 的异步体现在:内核不用等待数据准备好就立刻返回,所以内核肯定需要在 IO 完成后通知用户应用进程。** - ---- - -```text -弄清楚了阻塞与非阻塞,同步与异步和上面5种IO模型,相信再看 -Java中的IO模型,也只是换汤不换药。 -``` - -- BIO : 阻塞 IO -- NIO : IO 多路复用 -- AIO : 异步 IO - -本来打算写 Java 中的 IO 模型的,发现上面几乎讲完了(剩 API 使用吧),没啥要写的了, -所以暂时就这样吧。如果各位同学有好的建议,欢迎 pr 或 issue。 diff --git a/docs/operating-system/Linux_performance/image-20200604180850391.png b/docs/operating-system/Linux_performance/image-20200604180850391.png deleted file mode 100755 index 20b6a534..00000000 Binary files a/docs/operating-system/Linux_performance/image-20200604180850391.png and /dev/null differ diff --git a/docs/operating-system/Linux_performance/image-20200604180851790.png b/docs/operating-system/Linux_performance/image-20200604180851790.png deleted file mode 100755 index 20b6a534..00000000 Binary files a/docs/operating-system/Linux_performance/image-20200604180851790.png and /dev/null differ diff --git a/docs/operating-system/Linux_performance/image-20200604181133355.png b/docs/operating-system/Linux_performance/image-20200604181133355.png deleted file mode 100755 index 47d74f36..00000000 Binary files a/docs/operating-system/Linux_performance/image-20200604181133355.png and /dev/null differ diff --git a/docs/operating-system/Linux_performance/image-20200604203027136.png b/docs/operating-system/Linux_performance/image-20200604203027136.png deleted file mode 100755 index 753a72f6..00000000 Binary files a/docs/operating-system/Linux_performance/image-20200604203027136.png and /dev/null differ diff --git a/docs/operating-system/Linux_performance/image-20200605104607007.png b/docs/operating-system/Linux_performance/image-20200605104607007.png deleted file mode 100755 index 95b1791f..00000000 Binary files a/docs/operating-system/Linux_performance/image-20200605104607007.png and /dev/null differ diff --git a/docs/operating-system/Linux_performance/iostat.png b/docs/operating-system/Linux_performance/iostat.png deleted file mode 100755 index 4e8c446f..00000000 Binary files a/docs/operating-system/Linux_performance/iostat.png and /dev/null differ diff --git a/docs/operating-system/Linux_performance/linux_xn.png b/docs/operating-system/Linux_performance/linux_xn.png deleted file mode 100755 index 0eb28c01..00000000 Binary files a/docs/operating-system/Linux_performance/linux_xn.png and /dev/null differ diff --git a/docs/operating-system/Linux_performance/tcp_close.jpg b/docs/operating-system/Linux_performance/tcp_close.jpg deleted file mode 100755 index f58c76cd..00000000 Binary files a/docs/operating-system/Linux_performance/tcp_close.jpg and /dev/null differ diff --git a/docs/operating-system/Linux_performance/tcp_conn.jpg b/docs/operating-system/Linux_performance/tcp_conn.jpg deleted file mode 100755 index fae229db..00000000 Binary files a/docs/operating-system/Linux_performance/tcp_conn.jpg and /dev/null differ diff --git a/docs/operating-system/Linux_performance/tcpclose.png b/docs/operating-system/Linux_performance/tcpclose.png deleted file mode 100755 index 80d17430..00000000 Binary files a/docs/operating-system/Linux_performance/tcpclose.png and /dev/null differ diff --git a/docs/operating-system/Linux_performance/tcpconn.png b/docs/operating-system/Linux_performance/tcpconn.png deleted file mode 100755 index 1985847c..00000000 Binary files a/docs/operating-system/Linux_performance/tcpconn.png and /dev/null differ diff --git a/docs/operating-system/Linux性能分析工具合集.md b/docs/operating-system/Linux性能分析工具合集.md deleted file mode 100755 index 41e17b97..00000000 --- a/docs/operating-system/Linux性能分析工具合集.md +++ /dev/null @@ -1,634 +0,0 @@ -# Linux性能分析工具合集 - -> 本文由读者投稿,原文地址:[https://ysshao.cn/Linux/Linux_performance/](https://ysshao.cn/Linux/Linux_performance/) 。 - -## 1. 背景 - -有时候会遇到一些疑难杂症,并且监控插件并不能一眼立马发现问题的根源。这时候就需要登录服务器进一步深入分析问题的根源。那么分析问题需要有一定的技术经验积累,并且有些问题涉及到的领域非常广,才能定位到问题。所以,分析问题和踩坑是非常锻炼一个人的成长和提升自我能力。如果我们有一套好的分析工具,那将是事半功倍,能够帮助大家快速定位问题,节省大家很多时间做更深入的事情。 - -## 2. 说明 - -本篇文章主要介绍各种问题定位的工具以及会结合案例分析问题。 - -## 3. 分析问题的方法论 - -套用5W2H方法,可以提出性能分析的几个问题 - -- What-现象是什么样的 -- When-什么时候发生 -- Why-为什么会发生 -- Where-哪个地方发生的问题 -- How much-耗费了多少资源 -- How to do-怎么解决问题 - -## 4.性能分析工具合集 - -img - -### CPU - -针对应用程序,我们通常关注的是内核CPU调度器功能和性能。 - -线程的状态分析主要是分析线程的时间用在什么地方,而线程状态的分类一般分为: - -a. on-CPU:执行中,执行中的时间通常又分为用户态时间user和系统态时间sys。 - b. off-CPU:等待下一轮上CPU,或者等待I/O、锁、换页等等,其状态可以细分为可执行、匿名换页、睡 眠、锁、空闲等状态。 - -#### **分析工具** - -| 工具 | 描述 | -| -------- | ------------------------------ | -| uptime/w | 查看服务器运行时间、平均负载 | -| top | 监控每个进程的CPU用量分解 | -| vmstat | 系统的CPU平均负载情况 | -| mpstat | 查看多核CPU信息 | -| sar -u | 查看CPU过去或未来时点CPU利用率 | -| pidstat | 查看每个进程的用量分解 | - -#### uptime - -uptime 命令可以用来查看服务器已经运行了多久,当前登录的用户有多少,以及服务器在过去的1分钟、5分钟、15分钟的系统平均负载值 - -image-20200604180851790 - -第一项是当前时间,up 表示系统正在运行,6:47是系统启动的总时间,最后是系统的负载load信息 - -w 同上,增加了具体登陆了那些用户及登陆时间。 - -#### top - -常用来监控[Linux](http://lib.csdn.net/base/linux)的系统状况,比如cpu、内存的使用,显示系统上正在运行的进程。 - -image-20200604181133355 - -1. **系统运行时间和平均负载:** - - top命令的顶部显示与uptime命令相似的输出。 - - 这些字段显示: - - - 当前时间 - - 系统已运行的时间 - - 当前登录用户的数量 - - 相应最近5、10和15分钟内的平均负载。 - -2. **任务** - - 第二行显示的是任务或者进程的总结。进程可以处于不同的状态。这里显示了全部进程的数量。除此之外,还有正在运行、睡眠、停止、僵尸进程的数量(僵尸是一种进程的状态)。这些进程概括信息可以用’t’切换显示。 - -3. **CPU状态** - - 下一行显示的是CPU状态。 这里显示了不同模式下的所占CPU时间的百分比。这些不同的CPU时间表示: - - - us, user: 运行(未调整优先级的) 用户进程的CPU时间 - - sy,system: 运行内核进程的CPU时间 - - ni,niced:运行已调整优先级的用户进程的CPU时间 - - wa,IO wait: 用于等待IO完成的CPU时间 - - hi:处理硬件中断的CPU时间 - - si: 处理软件中断的CPU时间 - - st:这个虚拟机被hypervisor偷去的CPU时间(译注:如果当前处于一个hypervisor下的vm,实际上hypervisor也是要消耗一部分CPU处理时间的)。 - -4. **内存使用** - - 接下来两行显示内存使用率,有点像’free’命令。第一行是物理内存使用,第二行是虚拟内存使用(交换空间)。 - - 物理内存显示如下:全部可用内存、已使用内存、空闲内存、缓冲内存。相似地:交换部分显示的是:全部、已使用、空闲和缓冲交换空间。 - - > 这里要说明的是不能用windows的内存概念理解这些数据,如果按windows的方式此台服务器“危矣”:8G的内存总量只剩下530M的可用内存。Linux的内存管理有其特殊性,复杂点需要一本书来说明,这里只是简单说点和我们传统概念(windows)的不同。 - > - > 第四行中使用中的内存总量(used)指的是现在系统内核控制的内存数,空闲内存总量(free)是内核还未纳入其管控范围的数量。纳入内核管理的内存不见得都在使用中,还包括过去使用过的现在可以被重复利用的内存,内核并不把这些可被重新使用的内存交还到free中去,因此在[linux](http://lib.csdn.net/base/linux)上free内存会越来越少,但不用为此担心。 - > - > 如果出于习惯去计算可用内存数,这里有个近似的计算公式: - > - > ​ **第四行的free + 第四行的buffers + 第五行的cached。** - > - > 对于内存监控,在top里我们要时刻监控第五行swap交换分区的used,如果这个数值在不断的变化,说明内核在不断进行内存和swap的数据交换,这是真正的内存不够用了。 - -5. **字段/列** - - | 进程的属性 | 属性含义 | - | ---------- | ------------------------------------------------------------ | - | PID | 进程ID,进程的唯一标识符 | - | USER | 进程所有者的实际用户名。 | - | PR | 进程的调度优先级。这个字段的一些值是’rt’。这意味这这些进程运行在实时态。 | - | NI | 进程的nice值(优先级)。越小的值意味着越高的优先级。 | - | VIRT | 进程使用的虚拟内存。 | - | RES | 驻留内存大小。驻留内存是任务使用的非交换物理内存大小。 | - | SHR | SHR是进程使用的共享内存。 | - | S | 这个是进程的状态。它有以下不同的值:
D–不可中断的睡眠态、R–运行态、S–睡眠态、T–被跟踪或已停止、Z – 僵尸态 | - | %CPU | 自从上一次更新时到现在任务所使用的CPU时间百分比。 | - | %MEM | 进程使用的可用物理内存百分比。 | - | TIME+ | 任务启动后到现在所使用的全部CPU时间,精确到百分之一秒。 | - | COMMAND | 运行进程所使用的命令。 | - - 还有许多在默认情况下不会显示的输出,它们可以显示进程的页错误、有效组和组ID和其他更多的信息。 - - 常用交互命令: - - ‘B’:一些重要信息会以加粗字体显示(高亮)。这个命令可以切换粗体显示。 - - ‘b’: - - ‘D’或’S‘: 你将被提示输入一个值(以秒为单位),它会以设置的值作为刷新间隔。如果你这里输入了1,top将会每秒刷新。 top默认为3秒刷新 - - ‘l’、‘t’、‘m’: 切换负载、任务、内存信息的显示,这会相应地切换顶部的平均负载、任务/CPU状态和内存信息的概况显示。 - - ‘z’ : 切换彩色显示 - - ‘x’ 或者 ‘y’ - - 切换高亮信息:’x’将排序字段高亮显示(纵列);’y’将运行进程高亮显示(横行)。依赖于你的显示设置,你可能需要让输出彩色来看到这些高亮。 - - ‘u’: 特定用户的进程 - - ‘n’ 或 ‘#’: 任务的数量 - - ‘k’: 结束任务 - - **命令行选项** - - > top //每隔3秒显式所有进程的资源占用情况 - > - > top -u oracle -c //按照用户显示进程、并显示完整命令 - > - > top -d 2 //每隔2秒显式所有进程的资源占用情况 - > - > top -c //每隔3秒显式进程的资源占用情况,并显示进程的命令行参数(默认只有进程名) - > - > top -p 12345 -p 6789//每隔3秒显示pid是12345和pid是6789的两个进程的资源占用情况 - > - > top -d 2 -c -p 123456 //每隔2秒显示pid是12345的进程的资源使用情况,并显式该进程启动的命令行参数 - > - > top -n 设置显示多少次后就退出 - - **补充** - - top命令是Linux上进行系统监控的首选命令,但有时候却达不到我们的要求,比如当前这台服务器,top监控有很大的局限性。这台服务器运行着websphere集群,有两个节点服务,就是【top视图 01】中的老大、老二两个java进程,top命令的监控最小单位是进程,所以看不到我关心的java线程数和客户连接数,而这两个指标是java的web服务非常重要的指标,通常我用ps和netstate两个命令来补充top的不足。 - -#### vmstat - -​ vmstat命令是最常见的Linux/Unix监控工具,可以展现给定时间间隔的服务器的状态值,包括服务器的CPU使用率,内存使用,虚拟内存交换情况,IO读写情况。 - -​ 一般vmstat工具的使用是通过两个数字参数来完成的,第一个参数是采样的时间间隔数,单位是秒,第二个参数是采样的次数,如: - -image-20200604203027136 - -每个参数的含义: - -**Procs(进程)** - -| r: | 运行队列中进程数量,这个值也可以判断是否需要增加CPU。(长期大于1) | -| ---- | ------------------------------------------------------------ | -| b | 等待IO的进程数量。 | - -**Memory(内存)** - -| swpd | 使用虚拟内存大小,如果swpd的值不为0,但是SI,SO的值长期为0,这种情况不会影响系统性能。 | -| ----- | ------------------------------------------------------------ | -| free | 空闲物理内存大小。 | -| buff | 用作缓冲的内存大小。 | -| cache | 用作缓存的内存大小,如果cache的值大的时候,说明cache处的文件数多,如果频繁访问到的文件都能被cache处,那么磁盘的读IO bi会非常小。 | - -**Swap** - -| si | 每秒从交换区写到内存的大小,由磁盘调入内存。 | -| ---- | -------------------------------------------- | -| so | 每秒写入交换区的内存大小,由内存调入磁盘。 | - -注意:内存够用的时候,这2个值都是0,如果这2个值长期大于0时,系统性能会受到影响,磁盘IO和CPU资源都会被消耗。有些朋友看到空闲内存(free)很少的或接近于0时,就认为内存不够用了,不能光看这一点,还要结合si和so,如果free很少,但是si和so也很少(大多时候是0),那么不用担心,系统性能这时不会受到影响的。因为linux总是先把内存用光. - -**IO** - -| bi | 每秒读取的块数 | -| ---- | -------------- | -| bo | 每秒写入的块数 | - -注意:随机磁盘读写的时候,这2个值越大(如超出1024k),能看到CPU在IO等待的值也会越大。 - -**system(系统)** - -| in | 每秒中断数,包括时钟中断。 | -| ---- | -------------------------- | -| cs | 每秒上下文切换数。 | - -注意:上面2个值越大,会看到由内核消耗的CPU时间会越大。 - -**CPU(以百分比表示)** - -| us | 用户进程执行时间百分比(user time) us的值比较高时,说明用户进程消耗的CPU时间多,但是如果长期超50%的使用,那么我们就该考虑优化程序算法或者进行加速。 | -| ---- | ------------------------------------------------------------ | -| sy: | 内核系统进程执行时间百分比(system time) sy的值高时,说明系统内核消耗的CPU资源多,这并不是良性表现,我们应该检查原因。 | -| wa | IO等待时间百分比 wa的值高时,说明IO等待比较严重,这可能由于磁盘大量作随机访问造成,也有可能磁盘出现瓶颈(块操作)。 | -| id | 空闲时间百分比 | - -#### mpstat - -​ mpstat是一个实时监控工具,主要报告与CPU相关统计信息,在多核心cpu系统中,不仅可以查看cpu平均信息,还可以查看指定cpu信息。 - -> mpstat -P ALL //查看全部CPU的负载情况。 -> -> mpstat 2 5 //可指定间隔时间和次数。 - -| CPU: 处理器编号。关键字all表示统计信息计算为所有处理器之间的平均值。 | -| ------------------------------------------------------------ | -| %usr: 显示在用户级(应用程序)执行时发生的CPU利用率百分比。 | -| %nice: 显示以优先级较高的用户级别执行时发生的CPU利用率百分比。 | -| %sys: 显示在系统级(内核)执行时发生的CPU利用率百分比。请注意,这不包括维护硬件和软件的时间中断。 | -| %iowait: 显示系统具有未完成磁盘I / O请求的CPU或CPU空闲的时间百分比。 | -| %irq: 显示CPU或CPU用于服务硬件中断的时间百分比。 | -| %soft: 显示CPU或CPU用于服务软件中断的时间百分比。 | -| %steal: 显示虚拟CPU或CPU在管理程序为另一个虚拟处理器提供服务时非自愿等待的时间百分比。 | -| %guest: 显示CPU或CPU运行虚拟处理器所花费的时间百分比。 | - -#### sar - -系统活动情况报告,可以从多方面对系统的活动进行报告,包括:文件的读写情况、系统调用的使用情况、磁盘I/O、CPU效率、内存使用状况、进程活动及IPC有关的活动等 - -CPU相关: - -sar -p (查看全天) - -sar -u 1 10 (1:每隔一秒,10:写入10次) - -CPU输出项-详细说明 - -CPU:all 表示统计信息为所有 CPU 的平均值。 - -%user:显示在用户级别(application)运行使用 CPU 总时间的百分比。 - -%nice:显示在用户级别,用于nice操作,所占用 CPU 总时间的百分比。 - -%system:在核心级别(kernel)运行所使用 CPU 总时间的百分比。 - -%iowait:显示用于等待I/O操作占用 CPU 总时间的百分比。 - -%steal:管理程序(hypervisor)为另一个虚拟进程提供服务而等待虚拟 CPU 的百分比。 - -%idle:显示 CPU 空闲时间占用 CPU 总时间的百分比。 - -#### pidstat - -用于监控全部或指定进程的cpu、内存、线程、设备IO等系统资源的占用情况。 - -pidstat 和 pidstat -u -p ALL 是等效的。 - pidstat 默认显示了所有进程的cpu使用率。 - -详细说明 - -PID:进程ID - -%usr:进程在用户空间占用cpu的百分比 - -%system:进程在内核空间占用cpu的百分比 - -%guest:进程在虚拟机占用cpu的百分比 - -%CPU:进程占用cpu的百分比 - -CPU:处理进程的cpu编号 - -Command:当前进程对应的命令 - -### 内存 - -内存是为提高效率而生,实际分析问题的时候,内存出现问题可能不只是影响性能,而是影响服务或者引起其他问题。同样对于内存有些概念需要清楚: - -- 主存 -- 虚拟内存 -- 常驻内存 -- 地址空间 -- OOM -- 页缓存 -- 缺页 -- 换页 -- 交换空间 -- 交换 -- 用户分配器libc、glibc、libmalloc和mtmalloc -- LINUX内核级SLUB分配器 - -#### 分析工具 - -| 工具 | 描述 | -| ------- | ------------------------------ | -| free | 查看内存的使用情况 | -| top | 监控每个进程的内存使用情况 | -| vmstat | 虚拟内存统计信息 | -| sar -r | 查看内存 | -| sar | 查看CPU过去或未来时点CPU利用率 | -| pidstat | 查看每个进程的内存使用情况 | - -#### free - -free 命令显示系统内存的使用情况,包括物理内存、交换内存(swap)和内核缓冲区内存。 - -Mem 行(第二行)是内存的使用情况。 - Swap 行(第三行)是交换空间的使用情况。 - total 列显示系统总的可用物理内存和交换空间大小。 - used 列显示已经被使用的物理内存和交换空间。 - free 列显示还有多少物理内存和交换空间可用使用。 - shared 列显示被共享使用的物理内存大小。 - buff/cache 列显示被 buffer 和 cache 使用的物理内存大小。 - available 列显示还可以被应用程序使用的物理内存大小。 - -常用命令: - -> free -> -> free -g 以GB显示 -> -> free -m 以MB显示 -> -> free -h 自动转换展示 -> -> free -h -s 3 有时我们需要持续的观察内存的状况,此时可以使用 -s 选项并指定间隔的秒数 - -所以从应用程序的角度来说,**available = free + buffer + cache** - -可用内存=系统free memory+buffers+cached。 - -#### top - -请参考上面top的详解 - -#### vmstat - -请参考上面vmstat的详解 - -#### sar - -sar -r #查看内存使用情况 - -详解: - -kbmemfree 空闲的物理内存大小 - -kbmemused 使用中的物理内存大小 - -%memused 物理内存使用率 - -kbbuffers 内核中作为缓冲区使用的物理内存大小,kbbuffers和kbcached:这两个值就是free命令中的buffer 和cache. - -kbcached 缓存的文件大小 - -kbcommit 保证当前系统正常运行所需要的最小内存,即为了确保内存不溢出而需要的最少内存(物理内存 +Swap分区) - -commt 这个值是kbcommit与内存总量(物理内存+swap分区)的一个百分比的值 - -#### pidstat - -pidstat -r 查看内存使用情况 pidstat将显示各活动进程的内存使用统计 - -PID:进程标识符 - -Minflt/s:任务每秒发生的次要错误,不需要从磁盘中加载页 - -Majflt/s:任务每秒发生的主要错误,需要从磁盘中加载页 - -VSZ:虚拟地址大小,虚拟内存的使用KB - -RSS:常驻集合大小,非交换区五里内存使用KB - -Command:task命令名 - -### 磁盘IO - -磁盘通常是计算机最慢的子系统,也是最容易出现性能瓶颈的地方,因为磁盘离 CPU 距离最远而且 CPU 访问磁盘要涉及到机械操作,比如转轴、寻轨等。访问硬盘和访问内存之间的速度差别是以数量级来计算的,就像1天和1分钟的差别一样。要监测 IO 性能,有必要了解一下基本原理和 Linux 是如何处理硬盘和内存之间的 IO 的。 - -在理解磁盘IO之前,同样我们需要理解一些概念,例如: - -- 文件系统 -- VFS -- 文件系统缓存 -- 页缓存page cache -- 缓冲区高速缓存buffer cache -- 目录缓存 -- inode -- inode缓存 -- noop调用策略 - -#### 分析工具 - -| 工具 | 描述 | -| ------- | ---------------------------- | -| iostat | 磁盘详细统计信息 | -| iotop | 按进程查看磁盘IO统计信息 | -| pidstat | 查看每个进程的磁盘IO使用情况 | - -#### iostat - -iostat工具将对系统的磁盘操作活动进行监视。它的特点是汇报磁盘活动统计情况,同时也会汇报出CPU使用情况 - -image-20200604203027136 - -CPU属性 - -%user:CPU处在用户模式下的时间百分比。 - -%nice:CPU处在带NICE值的用户模式下的时间百分比。 - -%system:CPU处在系统模式下的时间百分比。 - -%iowait:CPU等待输入输出完成时间的百分比。 - -%steal:管理程序维护另一个虚拟处理器时,虚拟CPU的无意识等待时间百分比。 - -%idle:CPU空闲时间百分比。 - -备注: - -如果%iowait的值过高,表示硬盘存在I/O瓶颈 - -如果%idle值高,表示CPU较空闲 - -如果%idle值高但系统响应慢时,可能是CPU等待分配内存,应加大内存容量。 - -如果%idle值持续低于10,表明CPU处理能力相对较低,系统中最需要解决的资源是CPU。 - -Device属性 - -tps:该设备每秒的传输次数 - -kB_read/s:每秒从设备(drive expressed)读取的数据量; - -kB_wrtn/s:每秒向设备(drive expressed)写入的数据量; - -kB_read: 读取的总数据量; - -kB_wrtn:写入的总数量数据量 - -常用命令: - -iostat 2 3 每隔2秒刷新显示,且显示3次 - -iostat -m 以M为单位显示所有信息 - -查看设备使用率(%util)、响应时间(await) - -iostat -d -x -k 1 1 - -#### iotop - -在一般运维工作中经常会遇到这么一个场景,服务器的IO负载很高(iostat中的util),但是无法快速的定位到IO负载的来源进程和来源文件导致无法进行相应的策略来解决问题。 - -如果你想检查那个进程实际在做 I/O,那么运行 `iotop` 命令加上 `-o` 或者 `--only` 参数。 - -iotop --only - -#### pidstat - -显示各个进程的IO使用情况 - -pidstat -d - -报告IO统计显示以下信息: - -- PID:进程id -- kB_rd/s:每秒从磁盘读取的KB -- kB_wr/s:每秒写入磁盘KB -- kB_ccwr/s:任务取消的写入磁盘的KB。当任务截断脏的pagecache的时候会发生。 -- COMMAND:task的命令名 - -### 网络 - -#### 分析工具 - -| ping | 测试网络的连通性 | -| -------- | ---------------------------- | -| netstat | 检验本机各端口的网络连接情况 | -| hostname | 查看主机和域名 | - -#### ping - -常用命令参数: - --d 使用Socket的SO_DEBUG功能。 - --f 极限检测。大量且快速地送网络封包给一台机器,看它的回应。 - --n 只输出数值。 - --q 不显示任何传送封包的信息,只显示最后的结果。 - --r 忽略普通的Routing Table,直接将数据包送到远端主机上。通常是查看本机的网络接口是否有问题。 - --R 记录路由过程。 - --v 详细显示指令的执行过程。 - -

-c 数目:在发送指定数目的包后停止。 - --i 秒数:设定间隔几秒送一个网络封包给一台机器,预设值是一秒送一次。 - --I 网络界面:使用指定的网络界面送出数据包。 - --l 前置载入:设置在送出要求信息之前,先行发出的数据包。 - --p 范本样式:设置填满数据包的范本样式。 - --s 字节数:指定发送的数据字节数,预设值是56,加上8字节的ICMP头,一共是64ICMP数据字节。 - --t 存活数值:设置存活数值TTL的大小。 - -> ping -b 192.168.120.1 --ping网关 -> -> ping -c 10 192.168.120.206 --ping指定次数 -> -> ping -c 10 -i 0.5 192.168.120.206 --时间间隔和次数限制的ping - -#### netstat - -netstat命令是一个监控TCP/IP网络的非常有用的工具,它可以显示路由表、实际的网络连接以及每一个网络接口设备的状态信息。 - -netstat [选项] - -``` --a或--all:显示所有连线中的Socket; --a (all) 显示所有选项,默认不显示LISTEN相关。 --t (tcp) 仅显示tcp相关选项。 --u (udp) 仅显示udp相关选项。 --n 拒绝显示别名,能显示数字的全部转化成数字。 --l 仅列出有在 Listen (监听) 的服务状态。 --p 显示建立相关链接的程序名 --r 显示路由信息,路由表 --e 显示扩展信息,例如uid等 --s 按各个协议进行统计 --c 每隔一个固定时间,执行该netstat命令。 -``` - -常用命令: - -列出所有端口情况 - -``` -netstat -a # 列出所有端口 -netstat -at # 列出所有TCP端口 -netstat -au # 列出所有UDP端口 -``` - -列出所有处于监听状态的 Sockets - -``` -netstat -l # 只显示监听端口 -netstat -lt # 显示监听TCP端口 -netstat -lu # 显示监听UDP端口 -netstat -lx # 显示监听UNIX端口 -``` - -显示每个协议的统计信息 - -``` -netstat -s # 显示所有端口的统计信息 -netstat -st # 显示所有TCP的统计信息 -netstat -su # 显示所有UDP的统计信息 -``` - -显示 PID 和进程名称 - -``` -netstat -p -``` - -显示网络统计信息 - -``` -netstat -s -``` - -统计机器中网络连接各个状态个数 - -``` -netstat` `-an | ``awk` `'/^tcp/ {++S[$NF]} END {for (a in S) print a,S[a]} ' -``` - -**补充netstat网络状态详解:** - -一个正常的TCP连接,都会有三个阶段:1、TCP三次握手;2、数据传送;3、TCP四次挥手 - -在这里插入图片描述 - - - -**TCP的连接释放** - -在这里插入图片描述 - -``` -LISTEN:侦听来自远方的TCP端口的连接请求 -SYN-SENT:再发送连接请求后等待匹配的连接请求(如果有大量这样的状态包,检查是否中招了) -SYN-RECEIVED:再收到和发送一个连接请求后等待对方对连接请求的确认(如有大量此状态估计被flood攻击了) -ESTABLISHED:代表一个打开的连接 -FIN-WAIT-1:等待远程TCP连接中断请求,或先前的连接中断请求的确认 -FIN-WAIT-2:从远程TCP等待连接中断请求 -CLOSE-WAIT:等待从本地用户发来的连接中断请求 -CLOSING:等待远程TCP对连接中断的确认 -LAST-ACK:等待原来的发向远程TCP的连接中断请求的确认(不是什么好东西,此项出现,检查是否被攻击) -TIME-WAIT:等待足够的时间以确保远程TCP接收到连接中断请求的确认 -CLOSED:没有任何连接状态 -``` - ------- - -本文参考的文章: https://rdc.hundsun.com/portal/article/731.html?ref=myread - - - diff --git a/docs/operating-system/完全使用GNU_Linux学习.md b/docs/operating-system/完全使用GNU_Linux学习.md deleted file mode 100644 index d9e171da..00000000 --- a/docs/operating-system/完全使用GNU_Linux学习.md +++ /dev/null @@ -1,252 +0,0 @@ - - - * [完全使用GNU/Linux学习](#完全使用gnulinux学习) - * [为什么要写这篇文章?](#为什么要写这篇文章) - * [为什么我要从Windows切换到Linux?](#为什么我要从windows切换到linux) - * [Linux作为日常使用的缺点](#linux作为日常使用的缺点) - * [硬件驱动问题](#硬件驱动问题) - * [软件问题](#软件问题) - * [你真的需要完全使用Linux吗?](#你真的需要完全使用linux吗) - * [结尾](#结尾) - * [我使用Debian/Ubuntu时遇到的问题](#我使用debianubuntu时遇到的问题) - * [IDEA编辑Markdown预渲染问题](#idea编辑markdown预渲染问题) - * [wifi适配器找不到](#wifi适配器找不到) - * [XMind安装](#xmind安装) - * [Fcitx候选框的定位问题](#fcitx候选框的定位问题) - - - -# 完全使用GNU/Linux学习 - -喔,看到这个标题千万不要以为我要写和王垠前辈一样的内容啊,嘿嘿。不过在这里还是献上王垠前辈的那篇文章的链接吧:[完全用Linux工作](https://www.douban.com/group/topic/12121637/)。 - -## 为什么要写这篇文章? - -首先介绍本篇文章产出的时间,现在是2020/04/06。在三,四天之前,我其实并没有写这篇文章的打算,但是这三,四天以来,我一直在忙活从Ubuntu18换到Debian10 Buster的事情,没有时间写代码,手确实有些痒了。你可能想象不到,我这个之前一直使用Ubuntu的人,只是切换到Debian就花这么长时间,你可能以为我是在劝退各位同学,其实不是的,我只是想表达:我对Linux并不熟悉,这其中一部分原因是我使用的是对用户较为友好的发行版Ubuntu,另一部分原因是我仍然没有那么大的动力去学习Linux,即使它一直作为我的日常使用。 - -这篇文章并不是吹嘘或贬低Windows和Linux系统,而是想记录一下我一直以来使用Linux作为日常学习的心得,以及这几天再度折腾Debian以来的感触。 - - - -## 为什么我要从Windows切换到Linux? - -Windows是商业软件,这使它具备易用的性质。Linux是自由软件,这使得它拥有开源的性质。 - -易用软件通常带来的是对用户的友好度,以致于Windows发展至今,被许许多多的普通用户所采用。自由软件通常带来的是其社区的发展,所以你现在可以在网上看到许多如 ask ubuntu 这样的论坛。 - -我非常赞同《完全用Linux工作》中的一个观点: **UNIX 不是计算机专家的专利。** - -我对这句话的理解就是:即使你学习或工作的方向不是计算机,但你仍然可以去学习Unix/Linux,如果你是计算机方向的同学,那么,你就更应该去学习Unix/Linux了。 - -但这只是我从Win切换到Linux的一部分原因,另一个很重要的原因是我受够了Windows的 “易用性”。这里的易用性并不是说我排斥Windows的人性化,而是因为人性化给我带来了很多学习上的困难。举个很简单的栗子:你在学习一项技术的时候,无论是否有面试造火箭的需要,你是否都会好奇想了解其原理和实现,即使你可能知道它很复杂。 - -**为什么你会好奇一个事物的源头?** - -我个人认为的答案是:有趣的事情就在眼前,为什么不去了解它呢? - -而Windows只是有趣,但它并不在“眼前”。 - -我个人的体验哈,不知道有没有同学和我一样的经历,在很多时候,你的Windows可能会出现一些莫名奇妙的问题,但你却不知道如何解决它,你只能求助搜索引擎,当你解决完问题后,你不会想要去了解为什么会发生这种问题,因为Windows太庞大了。 - -就比如: 我现在安装了Git,使用起来没有任何问题。但等到过一段时间后,Git莫名奇妙的不能使用了,明明你啥都没干。更甚之,有一些流氓问题或流氓软件不能被解决和被屏蔽。 - -问题出现了,总得要解决吧,所以此时你开始在互联网上查询相关问题的解决方法,如果你的运气好,那么有人可能遇到过和你出现相同的问题,你也因此可能会得到答案。不过一般的答案只是教你怎么解决的,如打开注册表,添加或删除某个key,你不会想要知道为什么做,因为对于初学者来说,当你看到注册表那么多的内容时,你只想着不出错就行了,哪还有心思去学习这玩意啊。如果你的运气不好,且并没有更换系统的打算,那么你可能会将就着使用,但此时,你的心里可能已经衍生了对Windows的厌烦情绪。 - -我对流氓软件的定义是:当你想让一个软件如你的想法停止运行或停止弹出广告的时候,这个软件不能或不能做的很好的达到你的要求时,这就是一个流氓软件。你也许会说,每个人都有不同的要求,软件怎么可能达到每个人的标准呢?但我指的是停止和停止弹出广告等这样最基本的诉求,如果一个软件连最基本的诉求都实现不了,又何必再使用它呢? - -综上所述,我从Window切换到Linux的最主要的原因有:**学习和自由。** - -是的,你不得不承认Linux是你学习计算机的非常好的环境,与C/C++天然的集成,比你在Windows上冷冰冰的安装一个IDE就开始敲起代码来,显得多了那么一点感觉。 - -还有一点,可能有的同学和我一样,刚接触Linux的时候,是在Windows上安装一个虚拟机环境或使用Docker来进行学习。不可否认,这确实是在Windows上学习Linux的主要途径了,但是你有没有感觉到,你在采取这种方式学习的时候,对Linux始终有种陌生感,似乎我只是在为了学习而学习。 - -产生这种想法的主要原因就是你没有融入到Linux环境之中,当你融入到Linux环境之中时,你不再只是需要学习那些操作命令,你会不可避免的遇到某个你从来没有接触过的问题,这个问题不是你在Windows上“丢失图标”的那种烦人问题,而可能是令你有些害怕的因为Nvidia的驱动而黑屏的问题。你也会在互联网上查询为什么会出现这种问题,但你得到的并不是“修改注册表”这种答案,而是会学习到:为什么Nvidia在Linux上会出现这种问题?我怎么做才能解决驱动问题?其他驱动是否也有类似Nvidia这种问题? 当你解决问题后,你的电脑开始正常工作了,你便开始使用它作为你的日常使用... - -关于使用Linux学习的原因的最后一点是我认为自己不够慎独,不够克制。当我使用Windows的时候,并不能完全克制住自己接触那些新鲜游戏的念头,我玩起游戏来,通常会连续很长时间,可能是一天-_-。不过我并不是说Linux上没有游戏,相反,Linux是对很多游戏的支持是很好的,你可以玩到很多游戏,但你是否会因为使用Linux对游戏不再那么执着,至少我是如此了。这一点可以归结为“使用Linux对戒游戏有帮助吧” ,哈哈。 - -再谈谈自由: - -我对自由的理解是:软件在你的掌控之中,你可以了解它的每一部分,你可以去到你想到达的地方,不受任何限制,这只取决于你愿不愿意。 - -来看看基本的Linux目录吧: - -![Linux目录](../../media/pictures/linux/Linux目录.png) - -这些目录你可能有很多都不认识,但没关系,因为这就是Linux系统(大部分)所有的目录了,你稍微了解下,就知道这些目录里放的是什么文件了。 - -这也是我个人的体验而已,总之,Linux的自由是一种开源精神,比我描述的可大的多。至于Windows,我到现在连C盘的目录放了些什么都不太熟悉,但我并不是在贬低Windows,因为这就是Windows易用性的代价,相应的,Linux作为自由软件,它也有很多缺点。 - - - -## Linux作为日常使用的缺点 - -### 硬件驱动问题 - -硬件驱动问题一般是在安装Linux时会出现的问题,根据个人电脑配置的不同,你的电脑的硬件驱动可能与要安装的Linux发行版不兼容,导致系统出现相应的问题。我这几天对驱动问题最深刻的体会就明白了为啥Linus大神会吐槽: “Nvidia Fuck You”。很多驱动厂商对Linux系统是闭源的,你可以下载这些厂商的驱动,但是能不能用,或者用起来有什么毛病都得你自己买单。 - -随着Linux开始在普通用户中变得流行起来,我相信今后Linux的生态会发展的越来越好,且现在很多Linux发行版对各种硬件的兼容性也越来越好,就以我之前使用的Ubuntu18来说,Nvidia,Wifi,蓝牙等驱动使用都是没啥问题的。我现在使用的Debian10 Buster对Nvidia的支持可能还不是那么好,使用起来总有一些小毛病,不过无伤大雅,其实没毛病我还有点不适应,不是说Debian是Ubuntu的爸爸吗,哈哈。 - - - -### 软件问题 - -不得不承认的一点是Linux的软件生态确实没有Windows那么丰富,你在考虑切换系统之前,必须先调查清楚Linux上是否有你必需的软件,你所需的软件是否支持跨平台或者是否有可替代的应用。我个人对软件要求较为简单,大部分都是生产力工具,其他的应用如娱乐软件之类的都可以使用网页版作为替代。如果你在Linux系统上想尝试游戏的话,我认为也是OK的,因为我也尝试过Linux Dota2 ,体验非常好(不是广告-_-)。不过大多数国内游戏厂商对Linux的支持都是很差的,所以如果过不了这道坎,也不要切换系统了。 - -软件问题其实可以分为2部分看待,一部分就是刚刚介绍过的生态问题,另一部分就是当你在使用某些软件的时候,总会出现某些小Bug。 - -就以Fcitx来说,Fcitx是一款通用的Linux输入法框架,被称为小企鹅输入法,很多输入法都是在Fcitx之上开发的,如搜狗,Googlepinyin,Sunpinyin等。使用过Fcitx的同学可能会遇到这种问题:当你在使用Fcitx在某些软件上打字时,候选框并不会跟随你光标的位置,而是总会固定在某一个位置,并且你无法改变,这个问题是我目前见过的最大Bug。不过这个Bug只在部分软件上有,在Chrome,Typora上都没有这个问题,这让我怀疑是软件的国际化问题,而非Fcitx问题。 - -所以第二个部分总结起来就是某些软件可能会出现某些未知的Bug,你得寻求解决的办法,或者忍耐使用,使用Linux也是得牺牲一些代价的。 - - - -## 你真的需要完全使用Linux吗? - -说到这里,其实我想借用知乎某位前辈的话来表达一下我的真实想法: “**Linux最好的地方在与开放自由,最大的毛病也是在这里。普通人没有能力去选择,也没有时间做选择。透明就一定好么?也有很多人喜欢被安排啊!**“ ([知乎 - 汉卿](https://www.zhihu.com/question/309704636)) - -就像我开头说过的: “我对Linux并不熟悉,这其中一部分原因是我使用的是对用户较为友好的发行版Ubuntu,另一部分原因是我仍然没有那么大的动力去学习Linux,即使它一直作为我的日常使用。” - -我完全使用Linux是为了学习和自由,我确实在Linux上感受到了自由,且学到了很多东西,但我却一直沉溺在这种使用Linux带来的满足感之中,并不能真正理解Linux给我们带来的到底是什么。 - -这次从Ubuntu切换到Debian的原因是我想尝试换个新的环境,但是当我花了3,4天后,我明白了:我只是呆在一个地方久了,想换个新地方而已,但老地方不一定坏,因为我都没怎么了解过这个老地方,就像当初我从Windows换到Linux那样,我都没有深入的了解过Windows就换了,那一段时间我还抱怨Windows的各种缺点,现在看来,非常可笑。 - - - -#### 结尾 - -一文把想说的话几乎都给说了,个人文笔有限,且本文主观意识太强,如果觉得本文不符合您的胃口,就当看个笑话吧。 - - ---- - -## 我使用Debian/Ubuntu时遇到的问题 - -**以下内容是我在Debian10 Buster下遇到的问题以及相关解决办法, -使用Ubuntu和Debian其他版本的同学也可借鉴。** - -PS:欢迎各位同学在此处写下你遇到的问题和解决办法。 - -### IDEA编辑Markdown预渲染问题 -这个问题花了我很长时间。 - -当我安装IDEA后,使用它编辑markdown文件的时候,就出现了如下图所示的情况: - -![Debian10下IDEA的Markdown预渲染问题](../../media/pictures/linux/Debian10下IDEA的Markdown预渲染问题.png) - -你可以看到右边渲染的画面明显有问题。刚开始的时候我一度怀疑是IDEA版本的问题, -于是我又安装IDEA其他版本,但也没有任何作用,这时我怀疑是显卡的原因: - -![我的电脑配置](../../media/pictures/linux/我的电脑配置.png) - -可以看到使用的是Intel的核显,于是当我查询相关资料,使用脚本将核显换为了独显,这里没留截图,当你换到独显后, -图形会显示独显的配置,使用nvidia-smi命令可以查看独显使用状态。 -于是我满怀期待的打开IDEA,但还是无济于事。当我以为真的是Debian的Bug的时候, -我又发现Bumblebee可以管理显卡,何不一试?于是我安装Bumblebee后,使用optirun命令启动IDEA,没想到啊, -还真是可以: - -![Debian10下IDEA的Markdown预渲染解决后](../../media/pictures/linux/Debian10下IDEA的Markdown预渲染解决后.png) - -我真的就很奇怪,同样是使用了独显,为什么optirun启动就可以正常显示。 -于是我后来又查询optirun是否开启了gpu加速,但很可惜,我并没有得到相关答案,不过这让我确定了这个问题出现在 -显卡上。如果有知道原因的同学,敬请告之,感激不尽。 - - -### wifi适配器找不到 -我猜(不确定)这个问题应该发生在大多数使用联想笔记本的同学的电脑上,不止Debian,且Ubuntu也有这个问题。 -当安装完系统后,我们打开设置会发现wifi一栏显示 “wifi适配器找不到” 此类的错误信息。 -这个问题的大概原因是:无线网络适配器被阻塞了,需要手动将电脑上的wifi开关打开,而在我的笔记本上并wifi开关, -所以可以猜测是联想网络驱动的问题。 -可以使用 rfkill list all命令查询你的wlan是否被阻塞了,没有此命令的同学可以使用 - -````text -sudo apt-get install rfkill -```` - -安装,当wlan显示Hard blocked: true , 就证明你的无线驱动被阻塞了。 -解决办法是将阻塞无限驱动的那个模块从内核中移除掉,直接在 /etc/modprobe.d -目录下编辑 blacklist.conf文件,其内容为: - -````text -blacklist ideapad_laptop -```` - -文件名不一定要与我的一致,但是要以.conf结尾。 -你可以将modprobe.d目录下的文件理解为黑名单文件, -当Linux启动时就不会加载conf文件指定的模块, -这里的 ideapad_laptop 就是我们需要移除的那个无线模块。 - -**后遗症: -当我们移除 ideapad_laptop 模块后,以后开机的时候,有时会出现 -蓝牙适配器找不到的情况,之前在Ubuntu上却并未发现这种问题, -看来Debian在驱动方面没有Ubuntu做的好,不过这也是可以理解的, -而且大多数时候还是可以正常使用的-_-。** - - -### XMind安装 -XMind是使用Java编写的,依赖于Openjdk8。所以在Linux上使用XMind, -首先需要有Openjdk8的环境。 -其次启动的时候需要编写Shell脚本来启动(不是唯一办法,但却是非常简单的办法),没想到吧,我也没想到, -这也是我趟过很多坑才玩出来的。 - -首先我们需要准备一张XMind的软件启动图片:XMind.png, -这个我已经放到[目录](https://github.com/guang19/framework-learning/tree/dev/img/linux) -下了,需要的同学请自取。 - -其次我们进入XMind_amd64目录下,32位系统的同学进入XMind_i386目录, -我们创建并编辑 start.sh 脚本,其内容为: - -````text -#!/bin/bash -cd /home/guang19/SDK/xmind/XMind_amd64 (这个路径为你的XMind脚本的路径) -./XMind -```` - -这个脚本的内容很简单吧,当启动脚本的时候,进入目录,直接启动XMind。 - -脚本写完后需要让它能够被执行,使用 - -````text -chmod +x start.sh -```` - -命令让start.sh可以被执行。 - -此时你可以尝试执行 ./start.sh 命令来启动XMind,启动成功的话, -就已经完成了99%了,如果启动不成功,可以再检测下前面的步骤是否有误。 - -如果以后你只想用Shell启动XMind的话,那么到此也就为止了,连上面所说的图片都不需要了。 -如果你想更方便的启动的话,那么就需要创建桌面文件启动。 -在Debian/Ubuntu下,你所看到的桌面文件,都存储在 /usr/share/applications -目录下面(也有的在.local/share/applications目录下),这个目录下文件全是以.desktop结尾。 -我们现在就需要在这个目录下创建xmind.desktop文件(名字可以不叫xmind)。 - -其内容为: - -````text -[Desktop Entry] -Encoding=UTF-8 -Name=XMind -Type=Application -Exec=sh /home/guang19/SDK/xmind/XMind_amd64/start.sh -Icon=/home/guang19/SDK/xmind/XMind.png -```` - -我们暂时只需要理解Icon和Exec属性。 -Icon就是你在桌面上看到的应用的图标,把Icon的路径改为你XMind.png的路径就行了。 -再看Exec属性,当我们在桌面上点击XMind的图标的时候,就会执行Exec对应的命令或脚本, -我们把Exec改为start.sh文件的路径就行了,别掉了sh命令,因为start.sh是脚本, -需要sh命令启动。 - -以上步骤完成,保存desktop文件后,你就可以在桌面上看到XMind应用了。 - - -### Fcitx候选框的定位问题 -这个问题贴一张我处境的截图就明白了: - -![Fcitx候选框定位问题](../../media/pictures/linux/Fcitx候选框定位问题.png) - -可以看到我的光标定位在第207行,但是我输入法的候选框停留在IDEA的左下角。 -为什么我要说停留在IDEA的左下角?因为就目前我的使用而言,这个问题只在IDEA下存在, -不仅是Debian,Ubuntu也存在这种问题,我个人认为这应该是IDEA的问题, -查到的相关文章大部分都是说Swing的问题,看来这个问题还真是比较困难了。 -如果有同学知道解决办法,还请不吝分享,非常感谢。 \ No newline at end of file diff --git a/docs/system-design/authority-certification/JWT-advantages-and-disadvantages.md b/docs/system-design/authority-certification/JWT优缺点分析以及常见问题解决方案.md similarity index 100% rename from docs/system-design/authority-certification/JWT-advantages-and-disadvantages.md rename to docs/system-design/authority-certification/JWT优缺点分析以及常见问题解决方案.md diff --git a/docs/system-design/authority-certification/sso.md b/docs/system-design/authority-certification/SSO单点登录看这一篇就够了.md similarity index 100% rename from docs/system-design/authority-certification/sso.md rename to docs/system-design/authority-certification/SSO单点登录看这一篇就够了.md diff --git a/docs/system-design/authority-certification/basis-of-authority-certification.md b/docs/system-design/authority-certification/basis-of-authority-certification.md index 8441bc0f..b576a28c 100644 --- a/docs/system-design/authority-certification/basis-of-authority-certification.md +++ b/docs/system-design/authority-certification/basis-of-authority-certification.md @@ -6,11 +6,11 @@ **认证 (Authentication):** 你是谁。 - +![](./images/basis-of-authority-certification/authentication.png) **授权 (Authorization):** 你有权限干什么。 - +![](./images/basis-of-authority-certification/authorization.png) 稍微正式点(啰嗦点)的说法就是: @@ -21,7 +21,7 @@ ## 2. 什么是Cookie ? Cookie的作用是什么?如何在服务端使用 Cookie ? -![](../pictures/cookie-sessionId.png) +![](./images/basis-of-authority-certification/cookie-sessionId.png) ### 2.1 什么是Cookie ? Cookie的作用是什么? @@ -90,7 +90,7 @@ public String readAllCookies(HttpServletRequest request) { 很多时候我们都是通过 SessionID 来实现特定的用户,SessionID 一般会选择存放在 Redis 中。举个例子:用户成功登陆系统,然后返回给客户端具有 SessionID 的 Cookie,当用户向后端发起请求的时候会把 SessionID 带上,这样后端就知道你的身份状态了。关于这种认证方式更详细的过程如下: -![Session Based Authentication flow](../pictures/Session-Based-Authentication-flow.png) +![Session Based Authentication flow](./images/basis-of-authority-certification/Session-Based-Authentication-flow.png) 1. 用户向服务器发送用户名和密码用于登陆系统。 2. 服务器验证通过后,服务器为用户创建一个 Session,并将 Session信息存储 起来。 @@ -105,7 +105,7 @@ public String readAllCookies(HttpServletRequest request) { 花了个图简单总结了一下Session认证涉及的一些东西。 - +![](./images/basis-of-authority-certification/session-cookie-intro.jpeg) 另外,Spring Session提供了一种跨多个应用程序或实例管理用户会话信息的机制。如果想详细了解可以查看下面几篇很不错的文章: @@ -143,9 +143,7 @@ public String readAllCookies(HttpServletRequest request) { XSS中攻击者会用各种方式将恶意代码注入到其他用户的页面中。就可以通过脚本盗用信息比如cookie。 -推荐阅读: - -1. [如何防止CSRF攻击?—美团技术团队](https://tech.meituan.com/2018/10/11/fe-security-csrf.html) +推荐阅读:[如何防止CSRF攻击?—美团技术团队](https://tech.meituan.com/2018/10/11/fe-security-csrf.html) ## 6. 什么是 Token?什么是 JWT?如何基于Token进行身份验证? @@ -167,7 +165,7 @@ JWT 由 3 部分构成: 在基于 Token 进行身份验证的的应用程序中,服务器通过`Payload`、`Header`和一个密钥(`secret`)创建令牌(`Token`)并将 `Token` 发送给客户端,客户端将 `Token` 保存在 Cookie 或者 localStorage 里面,以后客户端发出的所有请求都会携带这个令牌。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP Header 的 Authorization字段中:` Authorization: Bearer Token`。 -![Token Based Authentication flow](../pictures/Token-Based-Authentication.png) +![Token Based Authentication flow](./images/basis-of-authority-certification/Token-Based-Authentication.png) 1. 用户向服务器发送用户名和密码用于登陆系统。 2. 身份验证服务响应并返回了签名的 JWT,上面包含了用户是谁的内容。 @@ -194,7 +192,7 @@ OAuth 2.0 比较常用的场景就是第三方登录,当你的网站接入了 微信支付账户相关参数: - +![](./images/basis-of-authority-certification/微信支付-fnglfdlgdfj.png) **推荐阅读:** diff --git a/docs/system-design/pictures/Session-Based-Authentication-flow.png b/docs/system-design/authority-certification/images/basis-of-authority-certification/Session-Based-Authentication-flow.png similarity index 100% rename from docs/system-design/pictures/Session-Based-Authentication-flow.png rename to docs/system-design/authority-certification/images/basis-of-authority-certification/Session-Based-Authentication-flow.png diff --git a/docs/system-design/pictures/Token-Based-Authentication.png b/docs/system-design/authority-certification/images/basis-of-authority-certification/Token-Based-Authentication.png similarity index 100% rename from docs/system-design/pictures/Token-Based-Authentication.png rename to docs/system-design/authority-certification/images/basis-of-authority-certification/Token-Based-Authentication.png diff --git a/docs/system-design/pictures/authentication.png b/docs/system-design/authority-certification/images/basis-of-authority-certification/authentication.png similarity index 100% rename from docs/system-design/pictures/authentication.png rename to docs/system-design/authority-certification/images/basis-of-authority-certification/authentication.png diff --git a/docs/system-design/pictures/authorization.png b/docs/system-design/authority-certification/images/basis-of-authority-certification/authorization.png similarity index 100% rename from docs/system-design/pictures/authorization.png rename to docs/system-design/authority-certification/images/basis-of-authority-certification/authorization.png diff --git a/docs/system-design/pictures/cookie-sessionId.png b/docs/system-design/authority-certification/images/basis-of-authority-certification/cookie-sessionId.png similarity index 100% rename from docs/system-design/pictures/cookie-sessionId.png rename to docs/system-design/authority-certification/images/basis-of-authority-certification/cookie-sessionId.png diff --git a/docs/system-design/authority-certification/images/basis-of-authority-certification/session-cookie-intro.jpeg b/docs/system-design/authority-certification/images/basis-of-authority-certification/session-cookie-intro.jpeg new file mode 100644 index 00000000..feca0114 Binary files /dev/null and b/docs/system-design/authority-certification/images/basis-of-authority-certification/session-cookie-intro.jpeg differ diff --git a/docs/system-design/pictures/微信支付-fnglfdlgdfj.png b/docs/system-design/authority-certification/images/basis-of-authority-certification/微信支付-fnglfdlgdfj.png similarity index 100% rename from docs/system-design/pictures/微信支付-fnglfdlgdfj.png rename to docs/system-design/authority-certification/images/basis-of-authority-certification/微信支付-fnglfdlgdfj.png diff --git a/docs/system-design/restful-api.md b/docs/system-design/coding-way/RESTfulAPI简明教程.md similarity index 100% rename from docs/system-design/restful-api.md rename to docs/system-design/coding-way/RESTfulAPI简明教程.md diff --git a/docs/system-design/data-communication/Kafka入门看这一篇就够了.md b/docs/system-design/data-communication/Kafka入门看这一篇就够了.md deleted file mode 100644 index 3fd42c5e..00000000 --- a/docs/system-design/data-communication/Kafka入门看这一篇就够了.md +++ /dev/null @@ -1,323 +0,0 @@ -> 本文由 JavaGuide 读者推荐,JavaGuide 对文章进行了整理排版!原文地址:https://www.wmyskxz.com/2019/07/17/kafka-ru-men-jiu-zhe-yi-pian/ , 作者:我没有三颗心脏。 - -# 一、Kafka 简介 - ------- - -## Kafka 创建背景 - -**Kafka** 是一个消息系统,原本开发自 LinkedIn,用作 LinkedIn 的活动流(Activity Stream)和运营数据处理管道(Pipeline)的基础。现在它已被[多家不同类型的公司](https://cwiki.apache.org/confluence/display/KAFKA/Powered+By) 作为多种类型的数据管道和消息系统使用。 - -**活动流数据**是几乎所有站点在对其网站使用情况做报表时都要用到的数据中最常规的部分。活动数据包括页面访问量(Page View)、被查看内容方面的信息以及搜索情况等内容。这种数据通常的处理方式是先把各种活动以日志的形式写入某种文件,然后周期性地对这些文件进行统计分析。**运营数据**指的是服务器的性能数据(CPU、IO 使用率、请求时间、服务日志等等数据)。运营数据的统计方法种类繁多。 - -近年来,活动和运营数据处理已经成为了网站软件产品特性中一个至关重要的组成部分,这就需要一套稍微更加复杂的基础设施对其提供支持。 - -## Kafka 简介 - -**Kafka 是一种分布式的,基于发布 / 订阅的消息系统。** - -主要设计目标如下: - -- 以时间复杂度为 O(1) 的方式提供消息持久化能力,即使对 TB 级以上数据也能保证常数时间复杂度的访问性能。 -- 高吞吐率。即使在非常廉价的商用机器上也能做到单机支持每秒 100K 条以上消息的传输。 -- 支持 Kafka Server 间的消息分区,及分布式消费,同时保证每个 Partition 内的消息顺序传输。 -- 同时支持离线数据处理和实时数据处理。 -- Scale out:支持在线水平扩展。 - -## Kafka 基础概念 - -### 概念一:生产者与消费者 - -![生产者与消费者](./../../../media/pictures/kafka/生产者和消费者.png) - -对于 Kafka 来说客户端有两种基本类型: - -1. **生产者(Producer)** -2. **消费者(Consumer)**。 - -除此之外,还有用来做数据集成的 Kafka Connect API 和流式处理的 Kafka Streams 等高阶客户端,但这些高阶客户端底层仍然是生产者和消费者API,它们只不过是在上层做了封装。 - -这很容易理解,生产者(也称为发布者)创建消息,而消费者(也称为订阅者)负责消费or读取消息。 - -### 概念二:主题(Topic)与分区(Partition) - -![主题(Topic)与分区(Partition)](./../../../media/pictures/kafka/主题与分区.png) - -在 Kafka 中,消息以**主题(Topic)**来分类,每一个主题都对应一个 **「消息队列」**,这有点儿类似于数据库中的表。但是如果我们把所有同类的消息都塞入到一个“中心”队列中,势必缺少可伸缩性,无论是生产者/消费者数目的增加,还是消息数量的增加,都可能耗尽系统的性能或存储。 - -我们使用一个生活中的例子来说明:现在 A 城市生产的某商品需要运输到 B 城市,走的是公路,那么单通道的高速公路不论是在「A 城市商品增多」还是「现在 C 城市也要往 B 城市运输东西」这样的情况下都会出现「吞吐量不足」的问题。所以我们现在引入**分区(Partition)**的概念,类似“允许多修几条道”的方式对我们的主题完成了水平扩展。 - -### 概念三:Broker 和集群(Cluster) - -一个 Kafka 服务器也称为 Broker,它接受生产者发送的消息并存入磁盘;Broker 同时服务消费者拉取分区消息的请求,返回目前已经提交的消息。使用特定的机器硬件,一个 Broker 每秒可以处理成千上万的分区和百万量级的消息。(现在动不动就百万量级..我特地去查了一把,好像确实集群的情况下吞吐量挺高的..嗯..) - -若干个 Broker 组成一个集群(Cluster),其中集群内某个 Broker 会成为集群控制器(Cluster Controller),它负责管理集群,包括分配分区到 Broker、监控 Broker 故障等。在集群内,一个分区由一个 Broker 负责,这个 Broker 也称为这个分区的 Leader;当然一个分区可以被复制到多个 Broker 上来实现冗余,这样当存在 Broker 故障时可以将其分区重新分配到其他 Broker 来负责。下图是一个样例: - -![Broker和集群](./../../../media/pictures/kafka/Broker和集群.png) - -Kafka 的一个关键性质是日志保留(retention),我们可以配置主题的消息保留策略,譬如只保留一段时间的日志或者只保留特定大小的日志。当超过这些限制时,老的消息会被删除。我们也可以针对某个主题单独设置消息过期策略,这样对于不同应用可以实现个性化。 - -### 概念四:多集群 - -随着业务发展,我们往往需要多集群,通常处于下面几个原因: - -- 基于数据的隔离; -- 基于安全的隔离; -- 多数据中心(容灾) - -当构建多个数据中心时,往往需要实现消息互通。举个例子,假如用户修改了个人资料,那么后续的请求无论被哪个数据中心处理,这个更新需要反映出来。又或者,多个数据中心的数据需要汇总到一个总控中心来做数据分析。 - -上面说的分区复制冗余机制只适用于同一个 Kafka 集群内部,对于多个 Kafka 集群消息同步可以使用 Kafka 提供的 MirrorMaker 工具。本质上来说,MirrorMaker 只是一个 Kafka 消费者和生产者,并使用一个队列连接起来而已。它从一个集群中消费消息,然后往另一个集群生产消息。 - - -# 二、Kafka 的设计与实现 - ------- - -上面我们知道了 Kafka 中的一些基本概念,但作为一个成熟的「消息队列」中间件,其中有许多有意思的设计值得我们思考,下面我们简单列举一些。 - -## 讨论一:Kafka 存储在文件系统上 - -是的,**您首先应该知道 Kafka 的消息是存在于文件系统之上的**。Kafka 高度依赖文件系统来存储和缓存消息,一般的人认为 “磁盘是缓慢的”,所以对这样的设计持有怀疑态度。实际上,磁盘比人们预想的快很多也慢很多,这取决于它们如何被使用;一个好的磁盘结构设计可以使之跟网络速度一样快。 - -现代的操作系统针对磁盘的读写已经做了一些优化方案来加快磁盘的访问速度。比如,**预读**会提前将一个比较大的磁盘快读入内存。**后写**会将很多小的逻辑写操作合并起来组合成一个大的物理写操作。并且,操作系统还会将主内存剩余的所有空闲内存空间都用作**磁盘缓存**,所有的磁盘读写操作都会经过统一的磁盘缓存(除了直接 I/O 会绕过磁盘缓存)。综合这几点优化特点,**如果是针对磁盘的顺序访问,某些情况下它可能比随机的内存访问都要快,甚至可以和网络的速度相差无几。** - -**上述的 Topic 其实是逻辑上的概念,面相消费者和生产者,物理上存储的其实是 Partition**,每一个 Partition 最终对应一个目录,里面存储所有的消息和索引文件。默认情况下,每一个 Topic 在创建时如果不指定 Partition 数量时只会创建 1 个 Partition。比如,我创建了一个 Topic 名字为 test ,没有指定 Partition 的数量,那么会默认创建一个 test-0 的文件夹,这里的命名规则是:`-`。 - -![主题(Topic)与分区(Partition)](./../../../media/pictures/kafka/kafka存在文件系统上.png) - -任何发布到 Partition 的消息都会被追加到 Partition 数据文件的尾部,这样的顺序写磁盘操作让 Kafka 的效率非常高(经验证,顺序写磁盘效率比随机写内存还要高,这是 Kafka 高吞吐率的一个很重要的保证)。 - -每一条消息被发送到 Broker 中,会根据 Partition 规则选择被存储到哪一个 Partition。如果 Partition 规则设置的合理,所有消息可以均匀分布到不同的 Partition中。 - -## 讨论二:Kafka 中的底层存储设计 - -假设我们现在 Kafka 集群只有一个 Broker,我们创建 2 个 Topic 名称分别为:「topic1」和「topic2」,Partition 数量分别为 1、2,那么我们的根目录下就会创建如下三个文件夹: - -```shell - | --topic1-0 - | --topic2-0 - | --topic2-1 -``` - -在 Kafka 的文件存储中,同一个 Topic 下有多个不同的 Partition,每个 Partition 都为一个目录,而每一个目录又被平均分配成多个大小相等的 **Segment File** 中,Segment File 又由 index file 和 data file 组成,他们总是成对出现,后缀 “.index” 和 “.log” 分表表示 Segment 索引文件和数据文件。 - -现在假设我们设置每个 Segment 大小为 500 MB,并启动生产者向 topic1 中写入大量数据,topic1-0 文件夹中就会产生类似如下的一些文件: - -```shell - | --topic1-0 - | --00000000000000000000.index - | --00000000000000000000.log - | --00000000000000368769.index - | --00000000000000368769.log - | --00000000000000737337.index - | --00000000000000737337.log - | --00000000000001105814.index - | --00000000000001105814.log - | --topic2-0 - | --topic2-1 - -``` - -**Segment 是 Kafka 文件存储的最小单位。**Segment 文件命名规则:Partition 全局的第一个 Segment 从 0 开始,后续每个 Segment 文件名为上一个 Segment 文件最后一条消息的 offset 值。数值最大为 64 位 long 大小,19 位数字字符长度,没有数字用0填充。如 00000000000000368769.index 和 00000000000000368769.log。 - -以上面的一对 Segment File 为例,说明一下索引文件和数据文件对应关系: - -![索引文件和数据文件](./../../../media/pictures/kafka/segment是kafka文件存储的最小单位.png) - - - -其中以索引文件中元数据 `<3, 497>` 为例,依次在数据文件中表示第 3 个 message(在全局 Partition 表示第 368769 + 3 = 368772 个 message)以及该消息的物理偏移地址为 497。 - -注意该 index 文件并不是从0开始,也不是每次递增1的,这是因为 Kafka 采取稀疏索引存储的方式,每隔一定字节的数据建立一条索引,它减少了索引文件大小,使得能够把 index 映射到内存,降低了查询时的磁盘 IO 开销,同时也并没有给查询带来太多的时间消耗。 - -因为其文件名为上一个 Segment 最后一条消息的 offset ,所以当需要查找一个指定 offset 的 message 时,通过在所有 segment 的文件名中进行二分查找就能找到它归属的 segment ,再在其 index 文件中找到其对应到文件上的物理位置,就能拿出该 message 。 - -由于消息在 Partition 的 Segment 数据文件中是顺序读写的,且消息消费后不会删除(删除策略是针对过期的 Segment 文件),这种顺序磁盘 IO 存储设计师 Kafka 高性能很重要的原因。 - -> Kafka 是如何准确的知道 message 的偏移的呢?这是因为在 Kafka 定义了标准的数据存储结构,在 Partition 中的每一条 message 都包含了以下三个属性: -> -> - offset:表示 message 在当前 Partition 中的偏移量,是一个逻辑上的值,唯一确定了 Partition 中的一条 message,可以简单的认为是一个 id; -> - MessageSize:表示 message 内容 data 的大小; -> - data:message 的具体内容 - -## 讨论三:生产者设计概要 - -当我们发送消息之前,先问几个问题:每条消息都是很关键且不能容忍丢失么?偶尔重复消息可以么?我们关注的是消息延迟还是写入消息的吞吐量? - -举个例子,有一个信用卡交易处理系统,当交易发生时会发送一条消息到 Kafka,另一个服务来读取消息并根据规则引擎来检查交易是否通过,将结果通过 Kafka 返回。对于这样的业务,消息既不能丢失也不能重复,由于交易量大因此吞吐量需要尽可能大,延迟可以稍微高一点。 - -再举个例子,假如我们需要收集用户在网页上的点击数据,对于这样的场景,少量消息丢失或者重复是可以容忍的,延迟多大都不重要只要不影响用户体验,吞吐则根据实时用户数来决定。 - -不同的业务需要使用不同的写入方式和配置。具体的方式我们在这里不做讨论,现在先看下生产者写消息的基本流程: - -![生产者设计概要](./../../../media/pictures/kafka/生产者设计概要.png) - -图片来源:[http://www.dengshenyu.com/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F/2017/11/12/kafka-producer.html](http://www.dengshenyu.com/分布式系统/2017/11/12/kafka-producer.html) - -流程如下: - -1. 首先,我们需要创建一个ProducerRecord,这个对象需要包含消息的主题(topic)和值(value),可以选择性指定一个键值(key)或者分区(partition)。 -2. 发送消息时,生产者会对键值和值序列化成字节数组,然后发送到分配器(partitioner)。 -3. 如果我们指定了分区,那么分配器返回该分区即可;否则,分配器将会基于键值来选择一个分区并返回。 -4. 选择完分区后,生产者知道了消息所属的主题和分区,它将这条记录添加到相同主题和分区的批量消息中,另一个线程负责发送这些批量消息到对应的Kafka broker。 -5. 当broker接收到消息后,如果成功写入则返回一个包含消息的主题、分区及位移的RecordMetadata对象,否则返回异常。 -6. 生产者接收到结果后,对于异常可能会进行重试。 - - - -## 讨论四:消费者设计概要 - -### 消费者与消费组 - -假设这么个场景:我们从Kafka中读取消息,并且进行检查,最后产生结果数据。我们可以创建一个消费者实例去做这件事情,但如果生产者写入消息的速度比消费者读取的速度快怎么办呢?这样随着时间增长,消息堆积越来越严重。对于这种场景,我们需要增加多个消费者来进行水平扩展。 - -Kafka消费者是**消费组**的一部分,当多个消费者形成一个消费组来消费主题时,每个消费者会收到不同分区的消息。假设有一个T1主题,该主题有4个分区;同时我们有一个消费组G1,这个消费组只有一个消费者C1。那么消费者C1将会收到这4个分区的消息,如下所示: - -![生产者设计概要](./../../../media/pictures/kafka/消费者设计概要1.png) - -如果我们增加新的消费者C2到消费组G1,那么每个消费者将会分别收到两个分区的消息,如下所示: - -![生产者设计概要](./../../../media/pictures/kafka/消费者设计概要2.png) - -如果增加到4个消费者,那么每个消费者将会分别收到一个分区的消息,如下所示: - -![生产者设计概要](./../../../media/pictures/kafka/消费者设计概要3.png) - -但如果我们继续增加消费者到这个消费组,剩余的消费者将会空闲,不会收到任何消息: - -![生产者设计概要](./../../../media/pictures/kafka/消费者设计概要4.png) - -总而言之,我们可以通过增加消费组的消费者来进行水平扩展提升消费能力。这也是为什么建议创建主题时使用比较多的分区数,这样可以在消费负载高的情况下增加消费者来提升性能。另外,消费者的数量不应该比分区数多,因为多出来的消费者是空闲的,没有任何帮助。 - -**Kafka一个很重要的特性就是,只需写入一次消息,可以支持任意多的应用读取这个消息。**换句话说,每个应用都可以读到全量的消息。为了使得每个应用都能读到全量消息,应用需要有不同的消费组。对于上面的例子,假如我们新增了一个新的消费组G2,而这个消费组有两个消费者,那么会是这样的: - -![生产者设计概要](./../../../media/pictures/kafka/消费者设计概要5.png) - -在这个场景中,消费组G1和消费组G2都能收到T1主题的全量消息,在逻辑意义上来说它们属于不同的应用。 - -最后,总结起来就是:如果应用需要读取全量消息,那么请为该应用设置一个消费组;如果该应用消费能力不足,那么可以考虑在这个消费组里增加消费者。 - -### 消费组与分区重平衡 - -可以看到,当新的消费者加入消费组,它会消费一个或多个分区,而这些分区之前是由其他消费者负责的;另外,当消费者离开消费组(比如重启、宕机等)时,它所消费的分区会分配给其他分区。这种现象称为**重平衡(rebalance)**。重平衡是 Kafka 一个很重要的性质,这个性质保证了高可用和水平扩展。**不过也需要注意到,在重平衡期间,所有消费者都不能消费消息,因此会造成整个消费组短暂的不可用。**而且,将分区进行重平衡也会导致原来的消费者状态过期,从而导致消费者需要重新更新状态,这段期间也会降低消费性能。后面我们会讨论如何安全的进行重平衡以及如何尽可能避免。 - -消费者通过定期发送心跳(heartbeat)到一个作为组协调者(group coordinator)的 broker 来保持在消费组内存活。这个 broker 不是固定的,每个消费组都可能不同。当消费者拉取消息或者提交时,便会发送心跳。 - -如果消费者超过一定时间没有发送心跳,那么它的会话(session)就会过期,组协调者会认为该消费者已经宕机,然后触发重平衡。可以看到,从消费者宕机到会话过期是有一定时间的,这段时间内该消费者的分区都不能进行消息消费;通常情况下,我们可以进行优雅关闭,这样消费者会发送离开的消息到组协调者,这样组协调者可以立即进行重平衡而不需要等待会话过期。 - -在 0.10.1 版本,Kafka 对心跳机制进行了修改,将发送心跳与拉取消息进行分离,这样使得发送心跳的频率不受拉取的频率影响。另外更高版本的 Kafka 支持配置一个消费者多长时间不拉取消息但仍然保持存活,这个配置可以避免活锁(livelock)。活锁,是指应用没有故障但是由于某些原因不能进一步消费。 - -### Partition 与消费模型 - -上面提到,Kafka 中一个 topic 中的消息是被打散分配在多个 Partition(分区) 中存储的, Consumer Group 在消费时需要从不同的 Partition 获取消息,那最终如何重建出 Topic 中消息的顺序呢? - -答案是:没有办法。Kafka 只会保证在 Partition 内消息是有序的,而不管全局的情况。 - -下一个问题是:Partition 中的消息可以被(不同的 Consumer Group)多次消费,那 Partition中被消费的消息是何时删除的? Partition 又是如何知道一个 Consumer Group 当前消费的位置呢? - -无论消息是否被消费,除非消息到期 Partition 从不删除消息。例如设置保留时间为 2 天,则消息发布 2 天内任何 Group 都可以消费,2 天后,消息自动被删除。 -Partition 会为每个 Consumer Group 保存一个偏移量,记录 Group 消费到的位置。 如下图: -![生产者设计概要](./../../../media/pictures/kafka/Partition与消费模型.png) - - - - -### 为什么 Kafka 是 pull 模型 - -消费者应该向 Broker 要数据(pull)还是 Broker 向消费者推送数据(push)?作为一个消息系统,Kafka 遵循了传统的方式,选择由 Producer 向 broker push 消息并由 Consumer 从 broker pull 消息。一些 logging-centric system,比如 Facebook 的[Scribe](https://github.com/facebookarchive/scribe)和 Cloudera 的[Flume](https://flume.apache.org/),采用 push 模式。事实上,push 模式和 pull 模式各有优劣。 - -**push 模式很难适应消费速率不同的消费者,因为消息发送速率是由 broker 决定的。**push 模式的目标是尽可能以最快速度传递消息,但是这样很容易造成 Consumer 来不及处理消息,典型的表现就是拒绝服务以及网络拥塞。**而 pull 模式则可以根据 Consumer 的消费能力以适当的速率消费消息。** - -**对于 Kafka 而言,pull 模式更合适。**pull 模式可简化 broker 的设计,Consumer 可自主控制消费消息的速率,同时 Consumer 可以自己控制消费方式——即可批量消费也可逐条消费,同时还能选择不同的提交方式从而实现不同的传输语义。 - -## 讨论五:Kafka 如何保证可靠性 - -当我们讨论**可靠性**的时候,我们总会提到*保证**这个词语。可靠性保证是基础,我们基于这些基础之上构建我们的应用。比如关系型数据库的可靠性保证是ACID,也就是原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。 - -Kafka 中的可靠性保证有如下四点: - -- 对于一个分区来说,它的消息是有序的。如果一个生产者向一个分区先写入消息A,然后写入消息B,那么消费者会先读取消息A再读取消息B。 -- 当消息写入所有in-sync状态的副本后,消息才会认为**已提交(committed)**。这里的写入有可能只是写入到文件系统的缓存,不一定刷新到磁盘。生产者可以等待不同时机的确认,比如等待分区主副本写入即返回,后者等待所有in-sync状态副本写入才返回。 -- 一旦消息已提交,那么只要有一个副本存活,数据不会丢失。 -- 消费者只能读取到已提交的消息。 - -使用这些基础保证,我们构建一个可靠的系统,这时候需要考虑一个问题:究竟我们的应用需要多大程度的可靠性?可靠性不是无偿的,它与系统可用性、吞吐量、延迟和硬件价格息息相关,得此失彼。因此,我们往往需要做权衡,一味的追求可靠性并不实际。 - -> 想了解更多戳这里:http://www.dengshenyu.com/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F/2017/11/21/kafka-data-delivery.html - -# 三、动手搭一个 Kafka - -通过上面的描述,我们已经大致了解到了「Kafka」是何方神圣了,现在我们开始尝试自己动手本地搭一个来实际体验一把。 - -## 第一步:下载 Kafka - -这里以 Mac OS 为例,在安装了 Homebrew 的情况下执行下列代码: - -```shell -brew install kafka -``` - -由于 Kafka 依赖了 Zookeeper,所以在下载的时候会自动下载。 - -## 第二步:启动服务 - -我们在启动之前首先需要修改 Kafka 的监听地址和端口为 `localhost:9092`: - -```shell -vi /usr/local/etc/kafka/server.properties -``` - - -然后修改成下图的样子: - -![启动服务](./../../../media/pictures/kafka/启动服务.png) -依次启动 Zookeeper 和 Kafka: - -```shell -brew services start zookeeper -brew services start kafka -``` - -然后执行下列语句来创建一个名字为 “test” 的 Topic: - -```shell -kafka-topics --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test -``` - -我们可以通过下列的命令查看我们的 Topic 列表: - -```shell -kafka-topics --list --zookeeper localhost:2181 -``` - -## 第三步:发送消息 - -然后我们新建一个控制台,运行下列命令创建一个消费者关注刚才创建的 Topic: - -```shell -kafka-console-consumer --bootstrap-server localhost:9092 --topic test --from-beginning -``` - -用控制台往刚才创建的 Topic 中添加消息,并观察刚才创建的消费者窗口: - -```shel -kafka-console-producer --broker-list localhost:9092 --topic test -``` - -能通过消费者窗口观察到正确的消息: - -![发送消息](./../../../media/pictures/kafka/发送消息.png) - -# 参考资料 - ------- - -1. https://www.infoq.cn/article/kafka-analysis-part-1 - Kafka 设计解析(一):Kafka 背景及架构介绍 -2. [http://www.dengshenyu.com/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F/2017/11/06/kafka-Meet-Kafka.html](http://www.dengshenyu.com/分布式系统/2017/11/06/kafka-Meet-Kafka.html) - Kafka系列(一)初识Kafka -3. https://lotabout.me/2018/kafka-introduction/ - Kafka 入门介绍 -4. https://www.zhihu.com/question/28925721 - Kafka 中的 Topic 为什么要进行分区? - 知乎 -5. https://blog.joway.io/posts/kafka-design-practice/ - Kafka 的设计与实践思考 -6. [http://www.dengshenyu.com/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F/2017/11/21/kafka-data-delivery.html](http://www.dengshenyu.com/分布式系统/2017/11/21/kafka-data-delivery.html) - Kafka系列(六)可靠的数据传输 - - diff --git a/docs/system-design/data-communication/kafka-inverview.md b/docs/system-design/distributed-system/message-queue/Kafka常见面试题总结.md similarity index 100% rename from docs/system-design/data-communication/kafka-inverview.md rename to docs/system-design/distributed-system/message-queue/Kafka常见面试题总结.md diff --git a/docs/system-design/data-communication/rabbitmq.md b/docs/system-design/distributed-system/message-queue/RabbitMQ入门看这一篇就够了.md similarity index 100% rename from docs/system-design/data-communication/rabbitmq.md rename to docs/system-design/distributed-system/message-queue/RabbitMQ入门看这一篇就够了.md diff --git a/docs/system-design/data-communication/RocketMQ-Questions.md b/docs/system-design/distributed-system/message-queue/RocketMQ-Questions.md similarity index 100% rename from docs/system-design/data-communication/RocketMQ-Questions.md rename to docs/system-design/distributed-system/message-queue/RocketMQ-Questions.md diff --git a/docs/system-design/data-communication/RocketMQ.md b/docs/system-design/distributed-system/message-queue/RocketMQ.md similarity index 100% rename from docs/system-design/data-communication/RocketMQ.md rename to docs/system-design/distributed-system/message-queue/RocketMQ.md diff --git a/docs/system-design/data-communication/message-queue.md b/docs/system-design/distributed-system/message-queue/message-queue.md similarity index 100% rename from docs/system-design/data-communication/message-queue.md rename to docs/system-design/distributed-system/message-queue/message-queue.md diff --git a/docs/system-design/data-communication/dubbo.md b/docs/system-design/distributed-system/rpc/关于Dubbo的重要知识点.md similarity index 100% rename from docs/system-design/data-communication/dubbo.md rename to docs/system-design/distributed-system/rpc/关于Dubbo的重要知识点.md diff --git a/docs/system-design/data-communication/why-use-rpc.md b/docs/system-design/distributed-system/rpc/服务之间的调用为啥不直接用HTTP而用RPC.md similarity index 99% rename from docs/system-design/data-communication/why-use-rpc.md rename to docs/system-design/distributed-system/rpc/服务之间的调用为啥不直接用HTTP而用RPC.md index f295e13b..4a139911 100644 --- a/docs/system-design/data-communication/why-use-rpc.md +++ b/docs/system-design/distributed-system/rpc/服务之间的调用为啥不直接用HTTP而用RPC.md @@ -41,7 +41,7 @@ http://www.importnew.com/22003.html ## 既有 HTTP ,为啥用 RPC 进行服务调用? -###RPC 只是一种设计而已 +### RPC 只是一种设计而已 RPC 只是一种概念、一种设计,就是为了解决 **不同服务之间的调用问题**, 它一般会包含有 **传输协议** 和 **序列化协议** 这两个。 diff --git a/docs/system-design/website-architecture/分布式.md b/docs/system-design/distributed-system/分布式.md similarity index 100% rename from docs/system-design/website-architecture/分布式.md rename to docs/system-design/distributed-system/分布式.md diff --git a/docs/system-design/framework/mybatis/mybatis-interview.md b/docs/system-design/framework/mybatis/mybatis-interview.md index 877d79dd..0c4d4b9d 100644 --- a/docs/system-design/framework/mybatis/mybatis-interview.md +++ b/docs/system-design/framework/mybatis/mybatis-interview.md @@ -1,12 +1,12 @@ > 本篇文章是JavaGuide收集自网络,原出处不明。 -Mybatis 技术内幕系列博客,从原理和源码角度,介绍了其内部实现细节,无论是写的好与不好,我确实是用心写了,由于并不是介绍如何使用 Mybatis 的文章,所以,一些参数使用细节略掉了,我们的目标是介绍 Mybatis 的技术架构和重要组成部分,以及基本运行原理。 +MyBatis 技术内幕系列博客,从原理和源码角度,介绍了其内部实现细节,无论是写的好与不好,我确实是用心写了,由于并不是介绍如何使用 MyBatis 的文章,所以,一些参数使用细节略掉了,我们的目标是介绍 MyBatis 的技术架构和重要组成部分,以及基本运行原理。 博客写的很辛苦,但是写出来却不一定好看,所谓开始很兴奋,过程很痛苦,结束很遗憾。要求不高,只要读者能从系列博客中,学习到一点其他博客所没有的技术点,作为作者,我就很欣慰了,我也读别人写的博客,通常对自己当前研究的技术,是很有帮助的。 尽管还有很多可写的内容,但是,我认为再写下去已经没有意义,任何其他小的功能点,都是在已经介绍的基本框架和基本原理下运行的,只有结束,才能有新的开始。写博客也积攒了一些经验,源码多了感觉就是复制黏贴,源码少了又觉得是空谈原理,将来再写博客,我希望是“精炼博文”,好读好懂美观读起来又不累,希望自己能再写一部开源分布式框架原理系列博客。 -有胆就来,我出几道 Mybatis 面试题,看你能回答上来几道(都是我出的,可不是网上找的)。 +有胆就来,我出几道 MyBatis 面试题,看你能回答上来几道(都是我出的,可不是网上找的)。 #### 1、#{}和\${}的区别是什么? @@ -15,7 +15,7 @@ Mybatis 技术内幕系列博客,从原理和源码角度,介绍了其内部 答: - `${}`是 Properties 文件中的变量占位符,它可以用于标签属性值和 sql 内部,属于静态文本替换,比如\${driver}会被静态替换为`com.mysql.jdbc.Driver`。 -- `#{}`是 sql 的参数占位符,Mybatis 会将 sql 中的`#{}`替换为?号,在 sql 执行前会使用 PreparedStatement 的参数设置方法,按序给 sql 的?号占位符设置参数值,比如 ps.setInt(0, parameterValue),`#{item.name}` 的取值方式为使用反射从参数对象中获取 item 对象的 name 属性值,相当于 `param.getItem().getName()`。 +- `#{}`是 sql 的参数占位符,MyBatis 会将 sql 中的`#{}`替换为?号,在 sql 执行前会使用 PreparedStatement 的参数设置方法,按序给 sql 的?号占位符设置参数值,比如 ps.setInt(0, parameterValue),`#{item.name}` 的取值方式为使用反射从参数对象中获取 item 对象的 name 属性值,相当于 `param.getItem().getName()`。 #### 2、Xml 映射文件中,除了常见的 select|insert|updae|delete 标签之外,还有哪些标签? @@ -27,65 +27,65 @@ Mybatis 技术内幕系列博客,从原理和源码角度,介绍了其内部 注:这道题也是京东面试官面试我时问的。 -答:Dao 接口,就是人们常说的 `Mapper`接口,接口的全限名,就是映射文件中的 namespace 的值,接口的方法名,就是映射文件中`MappedStatement`的 id 值,接口方法内的参数,就是传递给 sql 的参数。`Mapper`接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为 key 值,可唯一定位一个`MappedStatement`,举例:`com.mybatis3.mappers.StudentDao.findStudentById`,可以唯一找到 namespace 为`com.mybatis3.mappers.StudentDao`下面`id = findStudentById`的`MappedStatement`。在 Mybatis 中,每一个``、``、``、``标签,都会被解析为一个`MappedStatement`对象。 Dao 接口里的方法,是不能重载的,因为是全限名+方法名的保存和寻找策略。 -Dao 接口的工作原理是 JDK 动态代理,Mybatis 运行时会使用 JDK 动态代理为 Dao 接口生成代理 proxy 对象,代理对象 proxy 会拦截接口方法,转而执行`MappedStatement`所代表的 sql,然后将 sql 执行结果返回。 +Dao 接口的工作原理是 JDK 动态代理,MyBatis 运行时会使用 JDK 动态代理为 Dao 接口生成代理 proxy 对象,代理对象 proxy 会拦截接口方法,转而执行`MappedStatement`所代表的 sql,然后将 sql 执行结果返回。 -#### 4、Mybatis 是如何进行分页的?分页插件的原理是什么? +#### 4、MyBatis 是如何进行分页的?分页插件的原理是什么? 注:我出的。 -答:Mybatis 使用 RowBounds 对象进行分页,它是针对 ResultSet 结果集执行的内存分页,而非物理分页,可以在 sql 内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。 +答:MyBatis 使用 RowBounds 对象进行分页,它是针对 ResultSet 结果集执行的内存分页,而非物理分页,可以在 sql 内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。 -分页插件的基本原理是使用 Mybatis 提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 sql,然后重写 sql,根据 dialect 方言,添加对应的物理分页语句和物理分页参数。 +分页插件的基本原理是使用 MyBatis 提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 sql,然后重写 sql,根据 dialect 方言,添加对应的物理分页语句和物理分页参数。 举例:`select _ from student`,拦截 sql 后重写为:`select t._ from (select \* from student)t limit 0,10` -#### 5、简述 Mybatis 的插件运行原理,以及如何编写一个插件。 +#### 5、简述 MyBatis 的插件运行原理,以及如何编写一个插件。 注:我出的。 -答:Mybatis 仅可以编写针对 `ParameterHandler`、`ResultSetHandler`、`StatementHandler`、`Executor` 这 4 种接口的插件,Mybatis 使用 JDK 的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这 4 种接口对象的方法时,就会进入拦截方法,具体就是 `InvocationHandler` 的 `invoke()`方法,当然,只会拦截那些你指定需要拦截的方法。 +答:MyBatis 仅可以编写针对 `ParameterHandler`、`ResultSetHandler`、`StatementHandler`、`Executor` 这 4 种接口的插件,MyBatis 使用 JDK 的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这 4 种接口对象的方法时,就会进入拦截方法,具体就是 `InvocationHandler` 的 `invoke()`方法,当然,只会拦截那些你指定需要拦截的方法。 -实现 Mybatis 的 Interceptor 接口并复写` intercept()`方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。 +实现 MyBatis 的 Interceptor 接口并复写` intercept()`方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。 -#### 6、Mybatis 执行批量插入,能返回数据库主键列表吗? +#### 6、MyBatis 执行批量插入,能返回数据库主键列表吗? 注:我出的。 -答:能,JDBC 都能,Mybatis 当然也能。 +答:能,JDBC 都能,MyBatis 当然也能。 -#### 7、Mybatis 动态 sql 是做什么的?都有哪些动态 sql?能简述一下动态 sql 的执行原理不? +#### 7、MyBatis 动态 sql 是做什么的?都有哪些动态 sql?能简述一下动态 sql 的执行原理不? 注:我出的。 -答:Mybatis 动态 sql 可以让我们在 Xml 映射文件内,以标签的形式编写动态 sql,完成逻辑判断和动态拼接 sql 的功能,Mybatis 提供了 9 种动态 sql 标签 `trim|where|set|foreach|if|choose|when|otherwise|bind`。 +答:MyBatis 动态 sql 可以让我们在 Xml 映射文件内,以标签的形式编写动态 sql,完成逻辑判断和动态拼接 sql 的功能,MyBatis 提供了 9 种动态 sql 标签 `trim|where|set|foreach|if|choose|when|otherwise|bind`。 其执行原理为,使用 OGNL 从 sql 参数对象中计算表达式的值,根据表达式的值动态拼接 sql,以此来完成动态 sql 的功能。 -#### 8、Mybatis 是如何将 sql 执行结果封装为目标对象并返回的?都有哪些映射形式? +#### 8、MyBatis 是如何将 sql 执行结果封装为目标对象并返回的?都有哪些映射形式? 注:我出的。 -答:第一种是使用``标签,逐一定义列名和对象属性名之间的映射关系。第二种是使用 sql 列的别名功能,将列别名书写为对象属性名,比如 T_NAME AS NAME,对象属性名一般是 name,小写,但是列名不区分大小写,Mybatis 会忽略列名大小写,智能找到与之对应对象属性名,你甚至可以写成 T_NAME AS NaMe,Mybatis 一样可以正常工作。 +答:第一种是使用``标签,逐一定义列名和对象属性名之间的映射关系。第二种是使用 sql 列的别名功能,将列别名书写为对象属性名,比如 T_NAME AS NAME,对象属性名一般是 name,小写,但是列名不区分大小写,MyBatis 会忽略列名大小写,智能找到与之对应对象属性名,你甚至可以写成 T_NAME AS NaMe,MyBatis 一样可以正常工作。 -有了列名与属性名的映射关系后,Mybatis 通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。 +有了列名与属性名的映射关系后,MyBatis 通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。 -#### 9、Mybatis 能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别。 +#### 9、MyBatis 能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别。 注:我出的。 -答:能,Mybatis 不仅可以执行一对一、一对多的关联查询,还可以执行多对一,多对多的关联查询,多对一查询,其实就是一对一查询,只需要把 `selectOne()`修改为 `selectList()`即可;多对多查询,其实就是一对多查询,只需要把 `selectOne()`修改为 `selectList()`即可。 +答:能,MyBatis 不仅可以执行一对一、一对多的关联查询,还可以执行多对一,多对多的关联查询,多对一查询,其实就是一对一查询,只需要把 `selectOne()`修改为 `selectList()`即可;多对多查询,其实就是一对多查询,只需要把 `selectOne()`修改为 `selectList()`即可。 关联对象查询,有两种实现方式,一种是单独发送一个 sql 去查询关联对象,赋给主对象,然后返回主对象。另一种是使用嵌套查询,嵌套查询的含义为使用 join 查询,一部分列是 A 对象的属性值,另外一部分列是关联对象 B 的属性值,好处是只发一个 sql 查询,就可以把主对象和其关联对象查出来。 -那么问题来了,join 查询出来 100 条记录,如何确定主对象是 5 个,而不是 100 个?其去重复的原理是``标签内的``子标签,指定了唯一确定一条记录的 id 列,Mybatis 根据列值来完成 100 条记录的去重复功能,``可以有多个,代表了联合主键的语意。 +那么问题来了,join 查询出来 100 条记录,如何确定主对象是 5 个,而不是 100 个?其去重复的原理是``标签内的``子标签,指定了唯一确定一条记录的 id 列,MyBatis 根据列值来完成 100 条记录的去重复功能,``可以有多个,代表了联合主键的语意。 同样主对象的关联对象,也是根据这个原理去重复的,尽管一般情况下,只有主对象会有重复记录,关联对象一般不会重复。 -举例:下面 join 查询出来 6 条记录,一、二列是 Teacher 对象列,第三列为 Student 对象列,Mybatis 去重复处理后,结果为 1 个老师 6 个学生,而不是 6 个老师 6 个学生。 +举例:下面 join 查询出来 6 条记录,一、二列是 Teacher 对象列,第三列为 Student 对象列,MyBatis 去重复处理后,结果为 1 个老师 6 个学生,而不是 6 个老师 6 个学生。 t_id t_name s_id @@ -96,17 +96,17 @@ Dao 接口的工作原理是 JDK 动态代理,Mybatis 运行时会使用 JDK | 1 | teacher | 42 | | 1 | teacher | 43 | -#### 10、Mybatis 是否支持延迟加载?如果支持,它的实现原理是什么? +#### 10、MyBatis 是否支持延迟加载?如果支持,它的实现原理是什么? 注:我出的。 -答:Mybatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,association 指的就是一对一,collection 指的就是一对多查询。在 Mybatis 配置文件中,可以配置是否启用延迟加载 `lazyLoadingEnabled=true|false。` +答:MyBatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,association 指的就是一对一,collection 指的就是一对多查询。在 MyBatis 配置文件中,可以配置是否启用延迟加载 `lazyLoadingEnabled=true|false。` 它的原理是,使用` CGLIB` 创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用 `a.getB().getName()`,拦截器 `invoke()`方法发现 `a.getB()`是 null 值,那么就会单独发送事先保存好的查询关联 B 对象的 sql,把 B 查询上来,然后调用 a.setB(b),于是 a 的对象 b 属性就有值了,接着完成 `a.getB().getName()`方法的调用。这就是延迟加载的基本原理。 -当然了,不光是 Mybatis,几乎所有的包括 Hibernate,支持延迟加载的原理都是一样的。 +当然了,不光是 MyBatis,几乎所有的包括 Hibernate,支持延迟加载的原理都是一样的。 -#### 11、Mybatis 的 Xml 映射文件中,不同的 Xml 映射文件,id 是否可以重复? +#### 11、MyBatis 的 Xml 映射文件中,不同的 Xml 映射文件,id 是否可以重复? 注:我出的。 @@ -114,17 +114,17 @@ Dao 接口的工作原理是 JDK 动态代理,Mybatis 运行时会使用 JDK 原因就是 namespace+id 是作为 `Map`的 key 使用的,如果没有 namespace,就剩下 id,那么,id 重复会导致数据互相覆盖。有了 namespace,自然 id 就可以重复,namespace 不同,namespace+id 自然也就不同。 -#### 12、Mybatis 中如何执行批处理? +#### 12、MyBatis 中如何执行批处理? 注:我出的。 答:使用 BatchExecutor 完成批处理。 -#### 13、Mybatis 都有哪些 Executor 执行器?它们之间的区别是什么? +#### 13、MyBatis 都有哪些 Executor 执行器?它们之间的区别是什么? 注:我出的 -答:Mybatis 有三种基本的 Executor 执行器,**`SimpleExecutor`、`ReuseExecutor`、`BatchExecutor`。** +答:MyBatis 有三种基本的 Executor 执行器,**`SimpleExecutor`、`ReuseExecutor`、`BatchExecutor`。** **`SimpleExecutor`:**每执行一次 update 或 select,就开启一个 Statement 对象,用完立刻关闭 Statement 对象。 @@ -134,36 +134,36 @@ Dao 接口的工作原理是 JDK 动态代理,Mybatis 运行时会使用 JDK 作用范围:Executor 的这些特点,都严格限制在 SqlSession 生命周期范围内。 -#### 14、Mybatis 中如何指定使用哪一种 Executor 执行器? +#### 14、MyBatis 中如何指定使用哪一种 Executor 执行器? 注:我出的 -答:在 Mybatis 配置文件中,可以指定默认的 ExecutorType 执行器类型,也可以手动给 `DefaultSqlSessionFactory` 的创建 SqlSession 的方法传递 ExecutorType 类型参数。 +答:在 MyBatis 配置文件中,可以指定默认的 ExecutorType 执行器类型,也可以手动给 `DefaultSqlSessionFactory` 的创建 SqlSession 的方法传递 ExecutorType 类型参数。 -#### 15、Mybatis 是否可以映射 Enum 枚举类? +#### 15、MyBatis 是否可以映射 Enum 枚举类? 注:我出的 -答:Mybatis 可以映射枚举类,不单可以映射枚举类,Mybatis 可以映射任何对象到表的一列上。映射方式为自定义一个 `TypeHandler`,实现 `TypeHandler` 的 `setParameter()`和 `getResult()`接口方法。`TypeHandler` 有两个作用,一是完成从 javaType 至 jdbcType 的转换,二是完成 jdbcType 至 javaType 的转换,体现为 `setParameter()`和 `getResult()`两个方法,分别代表设置 sql 问号占位符参数和获取列查询结果。 +答:MyBatis 可以映射枚举类,不单可以映射枚举类,MyBatis 可以映射任何对象到表的一列上。映射方式为自定义一个 `TypeHandler`,实现 `TypeHandler` 的 `setParameter()`和 `getResult()`接口方法。`TypeHandler` 有两个作用,一是完成从 javaType 至 jdbcType 的转换,二是完成 jdbcType 至 javaType 的转换,体现为 `setParameter()`和 `getResult()`两个方法,分别代表设置 sql 问号占位符参数和获取列查询结果。 -#### 16、Mybatis 映射文件中,如果 A 标签通过 include 引用了 B 标签的内容,请问,B 标签能否定义在 A 标签的后面,还是说必须定义在 A 标签的前面? +#### 16、MyBatis 映射文件中,如果 A 标签通过 include 引用了 B 标签的内容,请问,B 标签能否定义在 A 标签的后面,还是说必须定义在 A 标签的前面? 注:我出的 -答:虽然 Mybatis 解析 Xml 映射文件是按照顺序解析的,但是,被引用的 B 标签依然可以定义在任何地方,Mybatis 都可以正确识别。 +答:虽然 MyBatis 解析 Xml 映射文件是按照顺序解析的,但是,被引用的 B 标签依然可以定义在任何地方,MyBatis 都可以正确识别。 -原理是,Mybatis 解析 A 标签,发现 A 标签引用了 B 标签,但是 B 标签尚未解析到,尚不存在,此时,Mybatis 会将 A 标签标记为未解析状态,然后继续解析余下的标签,包含 B 标签,待所有标签解析完毕,Mybatis 会重新解析那些被标记为未解析的标签,此时再解析 A 标签时,B 标签已经存在,A 标签也就可以正常解析完成了。 +原理是,MyBatis 解析 A 标签,发现 A 标签引用了 B 标签,但是 B 标签尚未解析到,尚不存在,此时,MyBatis 会将 A 标签标记为未解析状态,然后继续解析余下的标签,包含 B 标签,待所有标签解析完毕,MyBatis 会重新解析那些被标记为未解析的标签,此时再解析 A 标签时,B 标签已经存在,A 标签也就可以正常解析完成了。 -#### 17、简述 Mybatis 的 Xml 映射文件和 Mybatis 内部数据结构之间的映射关系? +#### 17、简述 MyBatis 的 Xml 映射文件和 MyBatis 内部数据结构之间的映射关系? 注:我出的 -答:Mybatis 将所有 Xml 配置信息都封装到 All-In-One 重量级对象 Configuration 内部。在 Xml 映射文件中,``标签会被解析为 `ParameterMap` 对象,其每个子元素会被解析为 ParameterMapping 对象。``标签会被解析为 `ResultMap` 对象,其每个子元素会被解析为 `ResultMapping` 对象。每一个``标签均会被解析为 `MappedStatement` 对象,标签内的 sql 会被解析为 BoundSql 对象。 -#### 18、为什么说 Mybatis 是半自动 ORM 映射工具?它与全自动的区别在哪里? +#### 18、为什么说 MyBatis 是半自动 ORM 映射工具?它与全自动的区别在哪里? 注:我出的 -答:Hibernate 属于全自动 ORM 映射工具,使用 Hibernate 查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而 Mybatis 在查询关联对象或关联集合对象时,需要手动编写 sql 来完成,所以,称之为半自动 ORM 映射工具。 +答:Hibernate 属于全自动 ORM 映射工具,使用 Hibernate 查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而 MyBatis 在查询关联对象或关联集合对象时,需要手动编写 sql 来完成,所以,称之为半自动 ORM 映射工具。 -面试题看似都很简单,但是想要能正确回答上来,必定是研究过源码且深入的人,而不是仅会使用的人或者用的很熟的人,以上所有面试题及其答案所涉及的内容,在我的 Mybatis 系列博客中都有详细讲解和原理分析。 \ No newline at end of file +面试题看似都很简单,但是想要能正确回答上来,必定是研究过源码且深入的人,而不是仅会使用的人或者用的很熟的人,以上所有面试题及其答案所涉及的内容,在我的 MyBatis 系列博客中都有详细讲解和原理分析。 diff --git a/docs/system-design/framework/spring/Spring-Design-Patterns.md b/docs/system-design/framework/spring/Spring-Design-Patterns.md index b37e318b..c5d56ff6 100644 --- a/docs/system-design/framework/spring/Spring-Design-Patterns.md +++ b/docs/system-design/framework/spring/Spring-Design-Patterns.md @@ -154,8 +154,6 @@ AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无 模板方法模式是一种行为设计模式,它定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。 模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤的实现方式。 -![模板方法UML图](https://ws1.sinaimg.cn/large/006rNwoDgy1g3a73vdbojj30vo0iwdgc.jpg) - ```java public abstract class Template { //这是我们的模板方法 diff --git a/docs/system-design/framework/spring/SpringBean.md b/docs/system-design/framework/spring/SpringBean.md deleted file mode 100644 index 3291e7fa..00000000 --- a/docs/system-design/framework/spring/SpringBean.md +++ /dev/null @@ -1,451 +0,0 @@ - - -- [前言](#前言) -- [一 bean的作用域](#一-bean的作用域) - - [1. singleton——唯一 bean 实例](#1-singleton——唯一-bean-实例) - - [2. prototype——每次请求都会创建一个新的 bean 实例](#2-prototype——每次请求都会创建一个新的-bean-实例) - - [3. request——每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效](#3-request——每一次http请求都会产生一个新的bean,该bean仅在当前http-request内有效) - - [4. session——每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效](#4-session——每一次http请求都会产生一个新的-bean,该bean仅在当前-http-session-内有效) - - [5. globalSession](#5-globalsession) -- [二 bean的生命周期](#二-bean的生命周期) - - [initialization 和 destroy](#initialization-和-destroy) - - [实现*Aware接口 在Bean中使用Spring框架的一些对象](#实现aware接口-在bean中使用spring框架的一些对象) - - [BeanPostProcessor](#beanpostprocessor) - - [总结](#总结) - - [单例管理的对象](#单例管理的对象) - - [非单例管理的对象](#非单例管理的对象) -- [三 说明](#三-说明) - - - -# 前言 -在 Spring 中,那些组成应用程序的主体及由 Spring IOC 容器所管理的对象,被称之为 bean。简单地讲,bean 就是由 IOC 容器初始化、装配及管理的对象,除此之外,bean 就与应用程序中的其他对象没有什么区别了。而 bean 的定义以及 bean 相互间的依赖关系将通过配置元数据来描述。 - -**Spring中的bean默认都是单例的,这些单例Bean在多线程程序下如何保证线程安全呢?** 例如对于Web应用来说,Web容器对于每个用户请求都创建一个单独的Sevlet线程来处理请求,引入Spring框架之后,每个Action都是单例的,那么对于Spring托管的单例Service Bean,如何保证其安全呢? **Spring的单例是基于BeanFactory也就是Spring容器的,单例Bean在此容器内只有一个,Java的单例是基于 JVM,每个 JVM 内只有一个实例。** - -在大多数情况下。单例 bean 是很理想的方案。不过,有时候你可能会发现你所使用的类是易变的,它们会保持一些状态,因此重用是不安全的。在这种情况下,将 class 声明为单例的就不是那么明智了。因为对象会被污染,稍后重用的时候会出现意想不到的问题。所以 Spring 定义了多种作用域的bean。 - -# 一 bean的作用域 - -创建一个bean定义,其实质是用该bean定义对应的类来创建真正实例的“配方”。把bean定义看成一个配方很有意义,它与class很类似,只根据一张“处方”就可以创建多个实例。不仅可以控制注入到对象中的各种依赖和配置值,还可以控制该对象的作用域。这样可以灵活选择所建对象的作用域,而不必在Java Class级定义作用域。Spring Framework支持五种作用域,分别阐述如下表。 - - -![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-17/1188352.jpg) - -五种作用域中,**request、session** 和 **global session** 三种作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于 web 的 Spring ApplicationContext 环境。 - - - -### 1. singleton——唯一 bean 实例 - -**当一个 bean 的作用域为 singleton,那么Spring IoC容器中只会存在一个共享的 bean 实例,并且所有对 bean 的请求,只要 id 与该 bean 定义相匹配,则只会返回bean的同一实例。** singleton 是单例类型(对应于单例模式),就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,但我们可以指定Bean节点的 `lazy-init=”true”` 来延迟初始化bean,这时候,只有在第一次获取bean时才会初始化bean,即第一次请求该bean时才初始化。 每次获取到的对象都是同一个对象。注意,singleton 作用域是Spring中的缺省作用域。要在XML中将 bean 定义成 singleton ,可以这样配置: - -```xml - -``` - -也可以通过 `@Scope` 注解(它可以显示指定bean的作用范围。)的方式 - -```java -@Service -@Scope("singleton") -public class ServiceImpl{ - -} -``` - -### 2. prototype——每次请求都会创建一个新的 bean 实例 - -**当一个bean的作用域为 prototype,表示一个 bean 定义对应多个对象实例。** **prototype 作用域的 bean 会导致在每次对该 bean 请求**(将其注入到另一个 bean 中,或者以程序的方式调用容器的 getBean() 方法**)时都会创建一个新的 bean 实例。prototype 是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的 bean 应该使用 prototype 作用域,而对无状态的 bean 则应该使用 singleton 作用域。** 在 XML 中将 bean 定义成 prototype ,可以这样配置: - -```java - - 或者 - -``` -通过 `@Scope` 注解的方式实现就不做演示了。 - -### 3. request——每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效 - -**request只适用于Web程序,每一次 HTTP 请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效,当请求结束后,该对象的生命周期即告结束。** 在 XML 中将 bean 定义成 request ,可以这样配置: - -```java - -``` - -### 4. session——每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效 - -**session只适用于Web程序,session 作用域表示该针对每一次 HTTP 请求都会产生一个新的 bean,同时该 bean 仅在当前 HTTP session 内有效.与request作用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的 HTTP session 中根据 userPreferences 创建的实例,将不会看到这些特定于某个 HTTP session 的状态变化。当HTTP session最终被废弃的时候,在该HTTP session作用域内的bean也会被废弃掉。** - -```xml - -``` - -### 5. globalSession - -global session 作用域类似于标准的 HTTP session 作用域,不过仅仅在基于 portlet 的 web 应用中才有意义。Portlet 规范定义了全局 Session 的概念,它被所有构成某个 portlet web 应用的各种不同的 portle t所共享。在global session 作用域中定义的 bean 被限定于全局portlet Session的生命周期范围内。 - -```xml - -``` - -# 二 bean的生命周期 - -Spring Bean是Spring应用中最最重要的部分了。所以来看看Spring容器在初始化一个bean的时候会做那些事情,顺序是怎样的,在容器关闭的时候,又会做哪些事情。 - -> spring版本:4.2.3.RELEASE -鉴于Spring源码是用gradle构建的,我也决定舍弃我大maven,尝试下洪菊推荐过的gradle。运行beanLifeCycle模块下的junit test即可在控制台看到如下输出,可以清楚了解Spring容器在创建,初始化和销毁Bean的时候依次做了那些事情。 - -``` -Spring容器初始化 -===================================== -调用GiraffeService无参构造函数 -GiraffeService中利用set方法设置属性值 -调用setBeanName:: Bean Name defined in context=giraffeService -调用setBeanClassLoader,ClassLoader Name = sun.misc.Launcher$AppClassLoader -调用setBeanFactory,setBeanFactory:: giraffe bean singleton=true -调用setEnvironment -调用setResourceLoader:: Resource File Name=spring-beans.xml -调用setApplicationEventPublisher -调用setApplicationContext:: Bean Definition Names=[giraffeService, org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#0, com.giraffe.spring.service.GiraffeServicePostProcessor#0] -执行BeanPostProcessor的postProcessBeforeInitialization方法,beanName=giraffeService -调用PostConstruct注解标注的方法 -执行InitializingBean接口的afterPropertiesSet方法 -执行配置的init-method -执行BeanPostProcessor的postProcessAfterInitialization方法,beanName=giraffeService -Spring容器初始化完毕 -===================================== -从容器中获取Bean -giraffe Name=李光洙 -===================================== -调用preDestroy注解标注的方法 -执行DisposableBean接口的destroy方法 -执行配置的destroy-method -Spring容器关闭 -``` - -先来看看,Spring在Bean从创建到销毁的生命周期中可能做得事情。 - - -### initialization 和 destroy - -有时我们需要在Bean属性值set好之后和Bean销毁之前做一些事情,比如检查Bean中某个属性是否被正常的设置好值了。Spring框架提供了多种方法让我们可以在Spring Bean的生命周期中执行initialization和pre-destroy方法。 - -**1.实现InitializingBean和DisposableBean接口** - -这两个接口都只包含一个方法。通过实现InitializingBean接口的afterPropertiesSet()方法可以在Bean属性值设置好之后做一些操作,实现DisposableBean接口的destroy()方法可以在销毁Bean之前做一些操作。 - -例子如下: - -```java -public class GiraffeService implements InitializingBean,DisposableBean { - @Override - public void afterPropertiesSet() throws Exception { - System.out.println("执行InitializingBean接口的afterPropertiesSet方法"); - } - @Override - public void destroy() throws Exception { - System.out.println("执行DisposableBean接口的destroy方法"); - } -} -``` -这种方法比较简单,但是不建议使用。因为这样会将Bean的实现和Spring框架耦合在一起。 - -**2.在bean的配置文件中指定init-method和destroy-method方法** - -Spring允许我们创建自己的 init 方法和 destroy 方法,只要在 Bean 的配置文件中指定 init-method 和 destroy-method 的值就可以在 Bean 初始化时和销毁之前执行一些操作。 - -例子如下: - -```java -public class GiraffeService { - //通过的destroy-method属性指定的销毁方法 - public void destroyMethod() throws Exception { - System.out.println("执行配置的destroy-method"); - } - //通过的init-method属性指定的初始化方法 - public void initMethod() throws Exception { - System.out.println("执行配置的init-method"); - } -} -``` - -配置文件中的配置: - -``` - - -``` - -需要注意的是自定义的init-method和destroy-method方法可以抛异常但是不能有参数。 - -这种方式比较推荐,因为可以自己创建方法,无需将Bean的实现直接依赖于spring的框架。 - -**3.使用@PostConstruct和@PreDestroy注解** - -除了xml配置的方式,Spring 也支持用 `@PostConstruct`和 `@PreDestroy`注解来指定 `init` 和 `destroy` 方法。这两个注解均在`javax.annotation` 包中。为了注解可以生效,需要在配置文件中定义org.springframework.context.annotation.CommonAnnotationBeanPostProcessor或context:annotation-config - -例子如下: - -```java -public class GiraffeService { - @PostConstruct - public void initPostConstruct(){ - System.out.println("执行PostConstruct注解标注的方法"); - } - @PreDestroy - public void preDestroy(){ - System.out.println("执行preDestroy注解标注的方法"); - } -} -``` - -配置文件: - -```xml - - - -``` - -### 实现*Aware接口 在Bean中使用Spring框架的一些对象 - -有些时候我们需要在 Bean 的初始化中使用 Spring 框架自身的一些对象来执行一些操作,比如获取 ServletContext 的一些参数,获取 ApplicaitionContext 中的 BeanDefinition 的名字,获取 Bean 在容器中的名字等等。为了让 Bean 可以获取到框架自身的一些对象,Spring 提供了一组名为*Aware的接口。 - -这些接口均继承于`org.springframework.beans.factory.Aware`标记接口,并提供一个将由 Bean 实现的set*方法,Spring通过基于setter的依赖注入方式使相应的对象可以被Bean使用。 -网上说,这些接口是利用观察者模式实现的,类似于servlet listeners,目前还不明白,不过这也不在本文的讨论范围内。 -介绍一些重要的Aware接口: - -- **ApplicationContextAware**: 获得ApplicationContext对象,可以用来获取所有Bean definition的名字。 -- **BeanFactoryAware**:获得BeanFactory对象,可以用来检测Bean的作用域。 -- **BeanNameAware**:获得Bean在配置文件中定义的名字。 -- **ResourceLoaderAware**:获得ResourceLoader对象,可以获得classpath中某个文件。 -- **ServletContextAware**:在一个MVC应用中可以获取ServletContext对象,可以读取context中的参数。 -- **ServletConfigAware**: 在一个MVC应用中可以获取ServletConfig对象,可以读取config中的参数。 - -```java -public class GiraffeService implements ApplicationContextAware, - ApplicationEventPublisherAware, BeanClassLoaderAware, BeanFactoryAware, - BeanNameAware, EnvironmentAware, ImportAware, ResourceLoaderAware{ - @Override - public void setBeanClassLoader(ClassLoader classLoader) { - System.out.println("执行setBeanClassLoader,ClassLoader Name = " + classLoader.getClass().getName()); - } - @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - System.out.println("执行setBeanFactory,setBeanFactory:: giraffe bean singleton=" + beanFactory.isSingleton("giraffeService")); - } - @Override - public void setBeanName(String s) { - System.out.println("执行setBeanName:: Bean Name defined in context=" - + s); - } - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - System.out.println("执行setApplicationContext:: Bean Definition Names=" - + Arrays.toString(applicationContext.getBeanDefinitionNames())); - } - @Override - public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { - System.out.println("执行setApplicationEventPublisher"); - } - @Override - public void setEnvironment(Environment environment) { - System.out.println("执行setEnvironment"); - } - @Override - public void setResourceLoader(ResourceLoader resourceLoader) { - Resource resource = resourceLoader.getResource("classpath:spring-beans.xml"); - System.out.println("执行setResourceLoader:: Resource File Name=" - + resource.getFilename()); - } - @Override - public void setImportMetadata(AnnotationMetadata annotationMetadata) { - System.out.println("执行setImportMetadata"); - } -} -``` - -### BeanPostProcessor - -上面的*Aware接口是针对某个实现这些接口的Bean定制初始化的过程, -Spring同样可以针对容器中的所有Bean,或者某些Bean定制初始化过程,只需提供一个实现BeanPostProcessor接口的类即可。 该接口中包含两个方法,postProcessBeforeInitialization和postProcessAfterInitialization。 postProcessBeforeInitialization方法会在容器中的Bean初始化之前执行, postProcessAfterInitialization方法在容器中的Bean初始化之后执行。 - -例子如下: - -```java -public class CustomerBeanPostProcessor implements BeanPostProcessor { - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - System.out.println("执行BeanPostProcessor的postProcessBeforeInitialization方法,beanName=" + beanName); - return bean; - } - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - System.out.println("执行BeanPostProcessor的postProcessAfterInitialization方法,beanName=" + beanName); - return bean; - } -} -``` - -要将BeanPostProcessor的Bean像其他Bean一样定义在配置文件中 - -```xml - -``` - -### 总结 - -所以。。。结合第一节控制台输出的内容,Spring Bean的生命周期是这样纸的: - -- Bean容器找到配置文件中 Spring Bean 的定义。 -- Bean容器利用Java Reflection API创建一个Bean的实例。 -- 如果涉及到一些属性值 利用set方法设置一些属性值。 -- 如果Bean实现了BeanNameAware接口,调用setBeanName()方法,传入Bean的名字。 -- 如果Bean实现了BeanClassLoaderAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例。 -- 如果Bean实现了BeanFactoryAware接口,调用setBeanFactory()方法,传入BeanFactory对象的实例。 -- 与上面的类似,如果实现了其他*Aware接口,就调用相应的方法。 -- 如果有和加载这个Bean的Spring容器相关的BeanPostProcessor对象,执行postProcessBeforeInitialization()方法 -- 如果Bean实现了InitializingBean接口,执行afterPropertiesSet()方法。 -- 如果Bean在配置文件中的定义包含init-method属性,执行指定的方法。 -- 如果有和加载这个Bean的Spring容器相关的BeanPostProcessor对象,执行postProcessAfterInitialization()方法 -- 当要销毁Bean的时候,如果Bean实现了DisposableBean接口,执行destroy()方法。 -- 当要销毁Bean的时候,如果Bean在配置文件中的定义包含destroy-method属性,执行指定的方法。 - -用图表示一下(图来源:http://www.jianshu.com/p/d00539babca5): - -![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-17/48376272.jpg) - -与之比较类似的中文版本: - -![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-17/5496407.jpg) - - -**其实很多时候我们并不会真的去实现上面说描述的那些接口,那么下面我们就除去那些接口,针对bean的单例和非单例来描述下bean的生命周期:** - -### 单例管理的对象 - -当scope=”singleton”,即默认情况下,会在启动容器时(即实例化容器时)时实例化。但我们可以指定Bean节点的lazy-init=”true”来延迟初始化bean,这时候,只有在第一次获取bean时才会初始化bean,即第一次请求该bean时才初始化。如下配置: - -```xml - -``` - -如果想对所有的默认单例bean都应用延迟初始化,可以在根节点beans设置default-lazy-init属性为true,如下所示: - -```xml - -``` - -默认情况下,Spring 在读取 xml 文件的时候,就会创建对象。在创建对象的时候先调用构造器,然后调用 init-method 属性值中所指定的方法。对象在被销毁的时候,会调用 destroy-method 属性值中所指定的方法(例如调用Container.destroy()方法的时候)。写一个测试类,代码如下: - -```java -public class LifeBean { - private String name; - - public LifeBean(){ - System.out.println("LifeBean()构造函数"); - } - public String getName() { - return name; - } - - public void setName(String name) { - System.out.println("setName()"); - this.name = name; - } - - public void init(){ - System.out.println("this is init of lifeBean"); - } - - public void destory(){ - System.out.println("this is destory of lifeBean " + this); - } -} -``` - life.xml配置如下: - -```xml - -``` - -测试代码: - -```java -public class LifeTest { - @Test - public void test() { - AbstractApplicationContext container = - new ClassPathXmlApplicationContext("life.xml"); - LifeBean life1 = (LifeBean)container.getBean("life"); - System.out.println(life1); - container.close(); - } -} -``` - -运行结果: - -``` -LifeBean()构造函数 -this is init of lifeBean -com.bean.LifeBean@573f2bb1 -…… -this is destory of lifeBean com.bean.LifeBean@573f2bb1 -``` - -### 非单例管理的对象 - -当`scope=”prototype”`时,容器也会延迟初始化 bean,Spring 读取xml 文件的时候,并不会立刻创建对象,而是在第一次请求该 bean 时才初始化(如调用getBean方法时)。在第一次请求每一个 prototype 的bean 时,Spring容器都会调用其构造器创建这个对象,然后调用`init-method`属性值中所指定的方法。对象销毁的时候,Spring 容器不会帮我们调用任何方法,因为是非单例,这个类型的对象有很多个,Spring容器一旦把这个对象交给你之后,就不再管理这个对象了。 - -为了测试prototype bean的生命周期life.xml配置如下: - -```xml - -``` - -测试程序: - -```java -public class LifeTest { - @Test - public void test() { - AbstractApplicationContext container = new ClassPathXmlApplicationContext("life.xml"); - LifeBean life1 = (LifeBean)container.getBean("life_singleton"); - System.out.println(life1); - - LifeBean life3 = (LifeBean)container.getBean("life_prototype"); - System.out.println(life3); - container.close(); - } -} -``` - -运行结果: - -``` -LifeBean()构造函数 -this is init of lifeBean -com.bean.LifeBean@573f2bb1 -LifeBean()构造函数 -this is init of lifeBean -com.bean.LifeBean@5ae9a829 -…… -this is destory of lifeBean com.bean.LifeBean@573f2bb1 -``` - -可以发现,对于作用域为 prototype 的 bean ,其`destroy`方法并没有被调用。**如果 bean 的 scope 设为prototype时,当容器关闭时,`destroy` 方法不会被调用。对于 prototype 作用域的 bean,有一点非常重要,那就是 Spring不能对一个 prototype bean 的整个生命周期负责:容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。** 不管何种作用域,容器都会调用所有对象的初始化生命周期回调方法。但对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用。**清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责**(让Spring容器释放被prototype作用域bean占用资源的一种可行方式是,通过使用bean的后置处理器,该处理器持有要被清除的bean的引用)。谈及prototype作用域的bean时,在某些方面你可以将Spring容器的角色看作是Java new操作的替代者,任何迟于该时间点的生命周期事宜都得交由客户端来处理。 - -**Spring 容器可以管理 singleton 作用域下 bean 的生命周期,在此作用域下,Spring 能够精确地知道bean何时被创建,何时初始化完成,以及何时被销毁。而对于 prototype 作用域的bean,Spring只负责创建,当容器创建了 bean 的实例后,bean 的实例就交给了客户端的代码管理,Spring容器将不再跟踪其生命周期,并且不会管理那些被配置成prototype作用域的bean的生命周期。** - - -# 三 说明 - -本文的完成结合了下面两篇文章,并做了相应修改: - -- https://blog.csdn.net/fuzhongmin05/article/details/73389779 -- https://yemengying.com/2016/07/14/spring-bean-life-cycle/ - -由于本文非本人独立原创,所以未声明为原创!在此说明! diff --git a/docs/system-design/framework/spring/spring-annotations.md b/docs/system-design/framework/spring/SpringBoot+Spring常用注解总结.md similarity index 99% rename from docs/system-design/framework/spring/spring-annotations.md rename to docs/system-design/framework/spring/SpringBoot+Spring常用注解总结.md index 610c0908..0cdfb260 100644 --- a/docs/system-design/framework/spring/spring-annotations.md +++ b/docs/system-design/framework/spring/SpringBoot+Spring常用注解总结.md @@ -315,7 +315,7 @@ public class UserRegisterRequest { 这样我们的后端就可以直接把 json 格式的数据映射到我们的 `UserRegisterRequest` 类上。 -![](https://cdn.jsdelivr.net/gh/javaguide-tech/blog-images/2020-08/663d1ec1-7ebc-41ab-8431-159dc1ec6589.png) +![](images/spring-annotations/@RequestBody.png) 👉 需要注意的是:**一个请求方法只可以有一个`@RequestBody`,但是可以有多个`@RequestParam`和`@PathVariable`**。 如果你的方法必须要用两个 `@RequestBody`来接受数据的话,大概率是你的数据库设计或者系统设计出问题了! diff --git a/docs/system-design/framework/spring/SpringInterviewQuestions.md b/docs/system-design/framework/spring/SpringInterviewQuestions.md index 401b8255..fb265f99 100644 --- a/docs/system-design/framework/spring/SpringInterviewQuestions.md +++ b/docs/system-design/framework/spring/SpringInterviewQuestions.md @@ -291,7 +291,7 @@ MVC 是一种设计模式,Spring MVC 是一款很优秀的 MVC 框架。Spring M 当`@Transactional`注解作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。如果类或者方法加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚。 -在`@Transactional`注解中如果不配置`rollbackFor`属性,那么事物只会在遇到`RuntimeException`的时候才会回滚,加上`rollbackFor=Exception.class`,可以让事物在遇到非运行时异常时也回滚。 +在`@Transactional`注解中如果不配置`rollbackFor`属性,那么事务只会在遇到`RuntimeException`的时候才会回滚,加上`rollbackFor=Exception.class`,可以让事务在遇到非运行时异常时也回滚。 关于 `@Transactional ` 注解推荐阅读的文章: diff --git a/docs/system-design/framework/spring/SpringMVC-Principle.md b/docs/system-design/framework/spring/SpringMVC-Principle.md deleted file mode 100644 index 0a2cc5f4..00000000 --- a/docs/system-design/framework/spring/SpringMVC-Principle.md +++ /dev/null @@ -1,269 +0,0 @@ -> 本文整理自网络,原文出处暂不知,对原文做了较大的改动,在此说明! - -### 先来看一下什么是 MVC 模式 - -MVC 是一种设计模式. - -**MVC 的原理图如下:** - -![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-11/60679444.jpg) - - - -### SpringMVC 简单介绍 - -SpringMVC 框架是以请求为驱动,围绕 Servlet 设计,将请求发给控制器,然后通过模型对象,分派器来展示请求结果视图。其中核心类是 DispatcherServlet,它是一个 Servlet,顶层是实现的Servlet接口。 - -### SpringMVC 使用 - -需要在 web.xml 中配置 DispatcherServlet 。并且需要配置 Spring 监听器ContextLoaderListener - -```xml - - - org.springframework.web.context.ContextLoaderListener - - - - springmvc - org.springframework.web.servlet.DispatcherServlet - - - - contextConfigLocation - classpath:spring/springmvc-servlet.xml - - 1 - - - springmvc - / - - -``` - -### SpringMVC 工作原理(重要) - -**简单来说:** - -客户端发送请求-> 前端控制器 DispatcherServlet 接受客户端请求 -> 找到处理器映射 HandlerMapping 解析请求对应的 Handler-> HandlerAdapter 会根据 Handler 来调用真正的处理器来处理请求,并处理相应的业务逻辑 -> 处理器返回一个模型视图 ModelAndView -> 视图解析器进行解析 -> 返回一个视图对象->前端控制器 DispatcherServlet 渲染数据(Model)->将得到视图对象返回给用户 - - - -**如下图所示:** -![SpringMVC运行原理](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-11/49790288.jpg) - -上图的一个笔误的小问题:Spring MVC 的入口函数也就是前端控制器 DispatcherServlet 的作用是接收请求,响应结果。 - -**流程说明(重要):** - -(1)客户端(浏览器)发送请求,直接请求到 DispatcherServlet。 - -(2)DispatcherServlet 根据请求信息调用 HandlerMapping,解析请求对应的 Handler。 - -(3)解析到对应的 Handler(也就是我们平常说的 Controller 控制器)后,开始由 HandlerAdapter 适配器处理。 - -(4)HandlerAdapter 会根据 Handler 来调用真正的处理器开处理请求,并处理相应的业务逻辑。 - -(5)处理器处理完业务后,会返回一个 ModelAndView 对象,Model 是返回的数据对象,View 是个逻辑上的 View。 - -(6)ViewResolver 会根据逻辑 View 查找实际的 View。 - -(7)DispaterServlet 把返回的 Model 传给 View(视图渲染)。 - -(8)把 View 返回给请求者(浏览器) - - - -### SpringMVC 重要组件说明 - - -**1、前端控制器DispatcherServlet(不需要工程师开发),由框架提供(重要)** - -作用:**Spring MVC 的入口函数。接收请求,响应结果,相当于转发器,中央处理器。有了 DispatcherServlet 减少了其它组件之间的耦合度。用户请求到达前端控制器,它就相当于mvc模式中的c,DispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet的存在降低了组件之间的耦合性。** - -**2、处理器映射器HandlerMapping(不需要工程师开发),由框架提供** - -作用:根据请求的url查找Handler。HandlerMapping负责根据用户请求找到Handler即处理器(Controller),SpringMVC提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。 - -**3、处理器适配器HandlerAdapter** - -作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler -通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。 - -**4、处理器Handler(需要工程师开发)** - -注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler -Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。 -由于Handler涉及到具体的用户业务请求,所以一般情况需要工程师根据业务需求开发Handler。 - -**5、视图解析器View resolver(不需要工程师开发),由框架提供** - -作用:进行视图解析,根据逻辑视图名解析成真正的视图(view) -View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。 springmvc框架提供了很多的View视图类型,包括:jstlView、freemarkerView、pdfView等。 -一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由工程师根据业务需求开发具体的页面。 - -**6、视图View(需要工程师开发)** - -View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf...) - -**注意:处理器Handler(也就是我们平常说的Controller控制器)以及视图层view都是需要我们自己手动开发的。其他的一些组件比如:前端控制器DispatcherServlet、处理器映射器HandlerMapping、处理器适配器HandlerAdapter等等都是框架提供给我们的,不需要自己手动开发。** - -### DispatcherServlet详细解析 - -首先看下源码: - -```java -package org.springframework.web.servlet; - -@SuppressWarnings("serial") -public class DispatcherServlet extends FrameworkServlet { - - public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver"; - public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver"; - public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver"; - public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping"; - public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter"; - public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver"; - public static final String REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME = "viewNameTranslator"; - public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver"; - public static final String FLASH_MAP_MANAGER_BEAN_NAME = "flashMapManager"; - public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT"; - public static final String LOCALE_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".LOCALE_RESOLVER"; - public static final String THEME_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_RESOLVER"; - public static final String THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE"; - public static final String INPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".INPUT_FLASH_MAP"; - public static final String OUTPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".OUTPUT_FLASH_MAP"; - public static final String FLASH_MAP_MANAGER_ATTRIBUTE = DispatcherServlet.class.getName() + ".FLASH_MAP_MANAGER"; - public static final String EXCEPTION_ATTRIBUTE = DispatcherServlet.class.getName() + ".EXCEPTION"; - public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound"; - private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties"; - protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY); - private static final Properties defaultStrategies; - static { - try { - ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class); - defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); - } - catch (IOException ex) { - throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage()); - } - } - - /** Detect all HandlerMappings or just expect "handlerMapping" bean? */ - private boolean detectAllHandlerMappings = true; - - /** Detect all HandlerAdapters or just expect "handlerAdapter" bean? */ - private boolean detectAllHandlerAdapters = true; - - /** Detect all HandlerExceptionResolvers or just expect "handlerExceptionResolver" bean? */ - private boolean detectAllHandlerExceptionResolvers = true; - - /** Detect all ViewResolvers or just expect "viewResolver" bean? */ - private boolean detectAllViewResolvers = true; - - /** Throw a NoHandlerFoundException if no Handler was found to process this request? **/ - private boolean throwExceptionIfNoHandlerFound = false; - - /** Perform cleanup of request attributes after include request? */ - private boolean cleanupAfterInclude = true; - - /** MultipartResolver used by this servlet */ - private MultipartResolver multipartResolver; - - /** LocaleResolver used by this servlet */ - private LocaleResolver localeResolver; - - /** ThemeResolver used by this servlet */ - private ThemeResolver themeResolver; - - /** List of HandlerMappings used by this servlet */ - private List handlerMappings; - - /** List of HandlerAdapters used by this servlet */ - private List handlerAdapters; - - /** List of HandlerExceptionResolvers used by this servlet */ - private List handlerExceptionResolvers; - - /** RequestToViewNameTranslator used by this servlet */ - private RequestToViewNameTranslator viewNameTranslator; - - private FlashMapManager flashMapManager; - - /** List of ViewResolvers used by this servlet */ - private List viewResolvers; - - public DispatcherServlet() { - super(); - } - - public DispatcherServlet(WebApplicationContext webApplicationContext) { - super(webApplicationContext); - } - @Override - protected void onRefresh(ApplicationContext context) { - initStrategies(context); - } - - protected void initStrategies(ApplicationContext context) { - initMultipartResolver(context); - initLocaleResolver(context); - initThemeResolver(context); - initHandlerMappings(context); - initHandlerAdapters(context); - initHandlerExceptionResolvers(context); - initRequestToViewNameTranslator(context); - initViewResolvers(context); - initFlashMapManager(context); - } -} - -``` - -DispatcherServlet类中的属性beans: - -- HandlerMapping:用于handlers映射请求和一系列的对于拦截器的前处理和后处理,大部分用@Controller注解。 -- HandlerAdapter:帮助DispatcherServlet处理映射请求处理程序的适配器,而不用考虑实际调用的是 哪个处理程序。- - - -- ViewResolver:根据实际配置解析实际的View类型。 -- ThemeResolver:解决Web应用程序可以使用的主题,例如提供个性化布局。 -- MultipartResolver:解析多部分请求,以支持从HTML表单上传文件。- -- FlashMapManager:存储并检索可用于将一个请求属性传递到另一个请求的input和output的FlashMap,通常用于重定向。 - -在Web MVC框架中,每个DispatcherServlet都拥自己的WebApplicationContext,它继承了ApplicationContext。WebApplicationContext包含了其上下文和Servlet实例之间共享的所有的基础框架beans。 - -**HandlerMapping** - -![HandlerMapping](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-11/96666164.jpg) - -HandlerMapping接口处理请求的映射HandlerMapping接口的实现类: - -- SimpleUrlHandlerMapping类通过配置文件把URL映射到Controller类。 -- DefaultAnnotationHandlerMapping类通过注解把URL映射到Controller类。 - -**HandlerAdapter** - - -![HandlerAdapter](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-11/91433100.jpg) - -HandlerAdapter接口-处理请求映射 - -AnnotationMethodHandlerAdapter:通过注解,把请求URL映射到Controller类的方法上。 - -**HandlerExceptionResolver** - - -![HandlerExceptionResolver](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-11/50343885.jpg) - -HandlerExceptionResolver接口-异常处理接口 - -- SimpleMappingExceptionResolver通过配置文件进行异常处理。 -- AnnotationMethodHandlerExceptionResolver:通过注解进行异常处理。 - -**ViewResolver** - -![ViewResolver](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-11/49497279.jpg) - -ViewResolver接口解析View视图。 - -UrlBasedViewResolver类 通过配置文件,把一个视图名交给到一个View来处理。 diff --git a/docs/system-design/framework/spring/images/spring-annotations/@RequestBody.png b/docs/system-design/framework/spring/images/spring-annotations/@RequestBody.png new file mode 100644 index 00000000..75d43a72 Binary files /dev/null and b/docs/system-design/framework/spring/images/spring-annotations/@RequestBody.png differ diff --git a/docs/system-design/framework/spring/images/spring-transaction/ed279f05-f5ad-443e-84e9-513a9e777139.png b/docs/system-design/framework/spring/images/spring-transaction/ed279f05-f5ad-443e-84e9-513a9e777139.png deleted file mode 100644 index d868e051..00000000 Binary files a/docs/system-design/framework/spring/images/spring-transaction/ed279f05-f5ad-443e-84e9-513a9e777139.png and /dev/null differ diff --git a/docs/system-design/framework/spring/images/spring-transaction/接口使用原因.png b/docs/system-design/framework/spring/images/spring-transaction/接口使用原因.png new file mode 100644 index 00000000..eb7e6748 Binary files /dev/null and b/docs/system-design/framework/spring/images/spring-transaction/接口使用原因.png differ diff --git a/docs/system-design/framework/spring/spring-transaction.md b/docs/system-design/framework/spring/spring-transaction.md index f465cd0c..1e82a2aa 100644 --- a/docs/system-design/framework/spring/spring-transaction.md +++ b/docs/system-design/framework/spring/spring-transaction.md @@ -179,7 +179,7 @@ public interface PlatformTransactionManager { 主要是因为要将事务管理行为抽象出来,然后不同的平台去实现它,这样我们可以保证提供给外部的行为不变,方便我们扩展。我前段时间分享过:**“为什么我们要用接口?”** - +![](images/spring-transaction/接口使用原因.png) #### 3.2.2. TransactionDefinition:事务属性 diff --git a/docs/system-design/high-availability/BASE理论.md b/docs/system-design/high-availability/BASE理论.md new file mode 100644 index 00000000..770ffd62 --- /dev/null +++ b/docs/system-design/high-availability/BASE理论.md @@ -0,0 +1,50 @@ +## 简介 + +**BASE** 是 **Basically Available(基本可用)** 、**Soft-state(软状态)** 和 **Eventually Consistent(最终一致性)** 三个短语的缩写。BASE 理论是对 CAP 中一致性 C 和可用性 A 权衡的结果,其来源于对大规模互联网系统分布式实践的总结,是基于 CAP 定理逐步演化而来的,它大大降低了我们对系统的要求。 + +## BASE 理论的核心思想 + +即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。也就是牺牲数据的一致性来满足系统的高可用性,系统中一部分数据不可用或者不一致时,仍需要保持系统整体“主要可用”。 + +**BASE 理论本质上是对 CAP 的延伸和补充,更具体地说,是对 CAP 中 AP 方案的一个补充。** + +**为什么这样说呢?** + +CAP 理论这节我们也说过了: + +> 如果系统没有发生“分区”的话,节点间的网络连接通信正常的话,也就不存在 P 了。这个时候,我们就可以同时保证 C 和 A 了。因此,**如果系统发生“分区”,我们要考虑选择 CP 还是 AP。如果系统没有发生“分区”的话,我们要思考如何保证 CA 。** + +因此,AP 方案只是在系统发生分区的时候放弃一致性,而不是永远放弃一致性。在分区故障恢复后,系统应该达到最终一致性。这一点其实就是 BASE 理论延伸的地方。 + +## BASE 理论三要素 + +![BASE理论三要素](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91c2VyLWdvbGQtY2RuLnhpdHUuaW8vMjAxOC81LzI0LzE2MzkxNDgwNmQ5ZTE1YzY?x-oss-process=image/format,png) + +### 1. 基本可用 + +基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性。但是,这绝不等价于系统不可用。 + +**什么叫允许损失部分可用性呢?** + +- **响应时间上的损失**: 正常情况下,处理用户请求需要 0.5s 返回结果,但是由于系统出现故障,处理用户请求的时间变为 3 s。 +- **系统功能上的损失**:正常情况下,用户可以使用系统的全部功能,但是由于系统访问量突然剧增,系统的部分非核心功能无法使用。 + +### 2. 软状态 + +软状态指允许系统中的数据存在中间状态(**CAP 理论中的数据不一致**),并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。 + +### 3. 最终一致性 + +最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。 + +> 分布式一致性的 3 种级别: +> +> 1. **强一致性** :系统写入了什么,读出来的就是什么。 +> 2. **弱一致性** :不一定可以读取到最新写入的值,也不保证多少时间之后读取到的数据是最新的,只是会尽量保证某个时刻达到数据一致的状态。 +> 3. **最终一致性** :弱一致性的升级版。,系统会保证在一定时间内达到数据一致的状态, +> +> 业界比较推崇是最终一致性级别,但是某些对数据一致要求十分严格的场景比如银行转账还是要保证强一致性。 + +## 总结 + +**ACID 是数据库事务完整性的理论,CAP 是分布式系统设计理论,BASE 是 CAP 理论中 AP 方案的延伸。** \ No newline at end of file diff --git a/docs/system-design/high-availability/CAP理论.md b/docs/system-design/high-availability/CAP理论.md new file mode 100644 index 00000000..32993c3f --- /dev/null +++ b/docs/system-design/high-availability/CAP理论.md @@ -0,0 +1,65 @@ +![](images/cap/cap.png) + +## 简介 + +**CAP** 也就是 **Consistency(一致性)**、**Availability(可用性)**、**Partition Tolerance(分区容错性)** 这三个单词首字母组合。 + +CAP 理论的提出者布鲁尔在提出 CAP 猜想的时候,并没有详细定义 Consistency、Availability、Partition Tolerance 三个单词的明确定义。 + +因此,对于 CAP 的民间解读有很多,一般比较被大家推荐的是下面 👇 这种版本的解。 + +在理论计算机科学中,CAP 定理(CAP theorem),又被称作 **布鲁尔定理(Brewer’s theorem)**,它指出对于一个分布式系统来说,当设计读写操作时,只能能同时满足以下三点中的两个: + +- **一致性(Consistence)** : 所有节点访问同一份最新的数据副本 +- **可用性(Availability)**: 非故障的节点在合理的时间内返回合理的响应(不是错误和超时的响应)。 +- **分区容错性(Partition tolerance)** : 分布式系统出现网络分区的时候,仍然能够对外提供服务。 + +CAP 仅适用于原子读写的 NOSQL 场景中,并不适合数据库系统。现在的分布式系统具有更多特性比如扩展性、可用性等等,在进行系统设计和开发时,我们不应该仅仅局限在 CAP 问题上。 + +**什么是网络分区?** + +> 分布式系统中,多个节点之前的网络本来是连通的,但是因为某些故障(比如部分节点网络出了问题)某些节点之间不连通了,整个网络就分成了几块区域,这就叫网络分区。 + +## 不是所谓的“3 选 2” + +大部分人解释这一定律时,常常简单的表述为:“一致性、可用性、分区容忍性三者你只能同时达到其中两个,不可能同时达到”。实际上这是一个非常具有误导性质的说法,而且在 CAP 理论诞生 12 年之后,CAP 之父也在 2012 年重写了之前的论文。 + +> **当发生网络分区的时候,如果我们要继续服务,那么强一致性和可用性只能 2 选 1。也就是说当网络分区之后 P 是前提,决定了 P 之后才有 C 和 A 的选择。也就是说分区容错性(Partition tolerance)我们是必须要实现的。** +> +> 简而言之就是:CAP 理论中分区容错性 P 是一定要满足的,在此基础上,只能满足可用性 A 或者一致性 C。 + +因此,**分布式系统理论上不可能选择 CA 架构,只能选择 CP 或者 AP 架构。** + +**为啥无同时保证 CA 呢?** + +举个例子:若系统出现“分区”,系统中的某个节点在进行写操作。为了保证 C, 必须要禁止其他节点的读写操作,这就和 A 发生冲突了。如果为了保证 A,其他节点的读写操作正常的话,那就和 C 发生冲突了。 + +**选择的关键在于当前的业务场景,没有定论,比如对于需要确保强一致性的场景如银行一般会选择保证 CP 。** + +## CAP 实际应用案例 + +我这里以注册中心来探讨一下 CAP 的实际应用。考虑到很多小伙伴不知道注册中心是干嘛的,这里简单以 Dubbo 为例说一说。 + +下图是 Dubbo 的架构图。**注册中心 Registry 在其中扮演了什么角色呢?提供了什么服务呢?** + +注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小。 + +![](images/cap/dubbo-architecture.png) + +常见的可以作为注册中心的组件有:ZooKeeper、Eureka、Nacos...。 + +1. **ZooKeeper 保证的是 CP。** 任何时刻对 ZooKeeper 的读请求都能得到一致性的结果,但是, ZooKeeper 不保证每次请求的可用性比如在 Leader 选举过程中或者半数以上的机器不可用的时候服务就是不可用的。 +2. **Eureka 保证的则是 AP。** Eureka 在设计的时候就是优先保证 A (可用性)。在 Eureka 中不存在什么 Leader 节点,每个节点都是一样的、平等的。因此 Eureka 不会像 ZooKeeper 那样出现选举过程中或者半数以上的机器不可用的时候服务就是不可用的情况。 Eureka 保证即使大部分节点挂掉也不会影响正常提供服务,只要有一个节点是可用的就行了。只不过这个节点上的数据可能并不是最新的。 +3. **Nacos 不仅支持 CP 也支持 AP。** + +## 总结 + +在系统发生“分区”的情况下,CAP 理论只能满足 CP 或者 AP。要注意的是,这里的前提是系统发生了“分区” + +如果系统没有发生“分区”的话,节点间的网络连接通信正常的话,也就不存在 P 了。这个时候,我们就可以同时保证 C 和 A 了。因此,**如果系统发生“分区”,我们要考虑选择 CP 还是 AP。如果系统没有发生“分区”的话,我们要思考如何保证 CA 。** + +## 推荐阅读 + +1. [CAP 定理简化](https://medium.com/@ravindraprasad/cap-theorem-simplified-28499a67eab4) (英文,有趣的案例) +2. [神一样的 CAP 理论被应用在何方](https://juejin.im/post/6844903936718012430) (中文,列举了很多实际的例子) +3. [请停止呼叫数据库 CP 或 AP ](https://martin.kleppmann.com/2015/05/11/please-stop-calling-databases-cp-or-ap.html) (英文,带给你不一样的思考) \ No newline at end of file diff --git a/docs/system-design/high-availability/images/cap/cap.png b/docs/system-design/high-availability/images/cap/cap.png new file mode 100644 index 00000000..64ffddca Binary files /dev/null and b/docs/system-design/high-availability/images/cap/cap.png differ diff --git a/docs/system-design/high-availability/images/cap/dubbo-architecture.png b/docs/system-design/high-availability/images/cap/dubbo-architecture.png new file mode 100644 index 00000000..e00cd474 Binary files /dev/null and b/docs/system-design/high-availability/images/cap/dubbo-architecture.png differ diff --git a/docs/system-design/micro-service/limit-request.md b/docs/system-design/high-availability/limit-request.md similarity index 100% rename from docs/system-design/micro-service/limit-request.md rename to docs/system-design/high-availability/limit-request.md diff --git a/docs/system-design/website-architecture/如何设计一个高可用系统?要考虑哪些地方?.md b/docs/system-design/high-availability/如何设计一个高可用系统?要考虑哪些地方?.md similarity index 100% rename from docs/system-design/website-architecture/如何设计一个高可用系统?要考虑哪些地方?.md rename to docs/system-design/high-availability/如何设计一个高可用系统?要考虑哪些地方?.md diff --git a/docs/system-design/micro-service/分布式id生成方案总结.md b/docs/system-design/micro-service/分布式id生成方案总结.md index bf278047..d8771e2b 100644 --- a/docs/system-design/micro-service/分布式id生成方案总结.md +++ b/docs/system-design/micro-service/分布式id生成方案总结.md @@ -8,8 +8,6 @@ ID是数据的唯一标识,传统的做法是利用UUID和数据库的自增ID 这篇文章并不会分析的特别详细,主要是做一些总结,以后再出一些详细某个方案的文章。 - - ## 数据库自增ID 第一种方案仍然还是基于数据库的自增ID,需要单独使用一个数据库实例,在这个实例中新建一个单独的表: diff --git a/docs/system-design/naming.md b/docs/system-design/naming.md index aa4aee86..1c16523c 100644 --- a/docs/system-design/naming.md +++ b/docs/system-design/naming.md @@ -209,5 +209,6 @@ Guide 制作了一个涵盖上面所有重要内容的思维导图,便于小 1. 《阿里巴巴 Java 开发手册》 2. 《Clean Code》 3. Google Java 代码指南:https://google.github.io/styleguide/javaguide.html#s5.1-identifier-name +4. 告别编码5分钟,命名2小时!史上最全的Java命名规范参考:https://www.cnblogs.com/liqiangchn/p/12000361.html diff --git a/docs/system-design/pictures/session-cookie-intro.png b/docs/system-design/pictures/session-cookie-intro.png deleted file mode 100644 index b4ef47ac..00000000 Binary files a/docs/system-design/pictures/session-cookie-intro.png and /dev/null differ diff --git a/docs/system-design/设计模式.md b/docs/system-design/设计模式.md deleted file mode 100644 index c7a86716..00000000 --- a/docs/system-design/设计模式.md +++ /dev/null @@ -1,84 +0,0 @@ -# Java 设计模式 - -下面是自己学习设计模式的时候做的总结,有些是自己的原创文章,有些是网上写的比较好的文章,保存下来细细消化吧! - -**系列文章推荐:** - -## 创建型模式 - -### 创建型模式概述 - -- 创建型模式(Creational Pattern)对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离。为了使软件的结构更加清晰,外界对于这些对象只需要知道它们共同的接口,而不清楚其具体的实现细节,使整个系统的设计更加符合单一职责原则。 -- 创建型模式在创建什么(What),由谁创建(Who),何时创建(When)等方面都为软件设计者提供了尽可能大的灵活性。创建型模式隐藏了类的实例的创建细节,通过隐藏对象如何被创建和组合在一起达到使整个系统独立的目的。 - -![创建型模式](https://user-gold-cdn.xitu.io/2018/6/16/1640641afcb7559b?w=491&h=241&f=png&s=51443) - -### 常见创建型模式详解 - -- **单例模式:** [深入理解单例模式——只有一个实例](https://blog.csdn.net/qq_34337272/article/details/80455972) -- **工厂模式:** [深入理解工厂模式——由对象工厂生成对象](https://blog.csdn.net/qq_34337272/article/details/80472071) -- **建造者模式:** [深入理解建造者模式 ——组装复杂的实例](http://blog.csdn.net/qq_34337272/article/details/80540059) -- **原型模式:** [深入理解原型模式 ——通过复制生成实例](https://blog.csdn.net/qq_34337272/article/details/80706444) - -## 结构型模式 - -### 结构型模式概述 - -- **结构型模式(Structural Pattern):** 描述如何将类或者对象结合在一起形成更大的结构,就像搭积木,可以通过简单积木的组合形成复杂的、功能更为强大的结构 -![结构型模式(Structural Pattern)](https://user-gold-cdn.xitu.io/2018/6/16/164064d6b3c205e3?w=719&h=233&f=png&s=270293) -- **结构型模式可以分为类结构型模式和对象结构型模式:** - - 类结构型模式关心类的组合,由多个类可以组合成一个更大的系统,在类结构型模式中一般只存在继承关系和实现关系。 - - 对象结构型模式关心类与对象的组合,通过关联关系使得在一个类中定义另一个类的实例对象,然后通过该对象调用其方法。根据“合成复用原则”,在系统中尽量使用关联关系来替代继承关系,因此大部分结构型模式都是对象结构型模式。 - -![结构型模式](https://user-gold-cdn.xitu.io/2018/6/16/1640655459d766d2?w=378&h=266&f=png&s=59652) - -### 常见结构型模式详解 - -- **适配器模式:** - - [深入理解适配器模式——加个“适配器”以便于复用](https://segmentfault.com/a/1190000011856448) https://blog.csdn.net/carson_ho/article/details/54910430 - - [适配器模式原理及实例介绍-IBM](https://www.ibm.com/developerworks/cn/java/j-lo-adapter-pattern/index.html) -- **桥接模式:** [设计模式笔记16:桥接模式(Bridge Pattern)](https://blog.csdn.net/yangzl2008/article/details/7670996) -- **组合模式:** [大话设计模式—组合模式](https://blog.csdn.net/lmb55/article/details/51039781) -- **装饰模式:** [java模式—装饰者模式](https://www.cnblogs.com/chenxing818/p/4705919.html)、[Java设计模式-装饰者模式](https://blog.csdn.net/cauchyweierstrass/article/details/48240147) -- **外观模式:** [java设计模式之外观模式(门面模式)](https://www.cnblogs.com/lthIU/p/5860607.html) -- **享元模式:** [享元模式](http://www.jasongj.com/design_pattern/flyweight/) -- **代理模式:** - - [代理模式原理及实例讲解 (IBM出品,很不错)](https://www.ibm.com/developerworks/cn/java/j-lo-proxy-pattern/index.html) - - [轻松学,Java 中的代理模式及动态代理](https://blog.csdn.net/briblue/article/details/73928350) - - [Java代理模式及其应用](https://blog.csdn.net/justloveyou_/article/details/74203025) - - -## 行为型模式 - -### 行为型模式概述 - -- 行为型模式(Behavioral Pattern)是对在不同的对象之间划分责任和算法的抽象化。 -- 行为型模式不仅仅关注类和对象的结构,而且重点关注它们之间的相互作用。 -- 通过行为型模式,可以更加清晰地划分类与对象的职责,并研究系统在运行时实例对象之间的交互。在系统运行时,对象并不是孤立的,它们可以通过相互通信与协作完成某些复杂功能,一个对象在运行时也将影响到其他对象的运行。 - -**行为型模式分为类行为型模式和对象行为型模式两种:** - -- **类行为型模式:** 类的行为型模式使用继承关系在几个类之间分配行为,类行为型模式主要通过多态等方式来分配父类与子类的职责。 -- **对象行为型模式:** 对象的行为型模式则使用对象的聚合关联关系来分配行为,对象行为型模式主要是通过对象关联等方式来分配两个或多个类的职责。根据“合成复用原则”,系统中要尽量使用关联关系来取代继承关系,因此大部分行为型设计模式都属于对象行为型设计模式。 - -![行为型模式](https://user-gold-cdn.xitu.io/2018/6/28/164467dd92c6172c?w=453&h=269&f=png&s=63270) - -- **职责链模式:** -- [Java设计模式之责任链模式、职责链模式](https://blog.csdn.net/jason0539/article/details/45091639) -- [责任链模式实现的三种方式](https://www.cnblogs.com/lizo/p/7503862.html) -- **命令模式:** 在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计,使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活。命令模式可以对发送者和接收者完全解耦,发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求。这就是命令模式的模式动机。 -- **解释器模式:** -- **迭代器模式:** -- **中介者模式:** -- **备忘录模式:** -- **观察者模式:** -- **状态模式:** -- **策略模式:** - -策略模式作为设计原则中开闭原则最典型的体现,也是经常使用的。下面这篇博客介绍了策略模式一般的组成部分和概念,并用了一个小demo去说明了策略模式的应用。 - -[java设计模式之策略模式](https://blog.csdn.net/zlj1217/article/details/81230077) - -- **模板方法模式:** -- **访问者模式:** - diff --git a/docs/tools/github/github-star-ranking.md b/docs/tools/github/github-star-ranking.md deleted file mode 100644 index fa0c42ee..00000000 --- a/docs/tools/github/github-star-ranking.md +++ /dev/null @@ -1,84 +0,0 @@ - - -> 下面的 10 个项目还是很推荐的!JS 的项目占比挺大,其他基本都是文档/学习类型的仓库。 - -说明:数据统计于 2019-11-27。 - -### 1. freeCodeCamp - -- **Github地址**:[https://github.com/freeCodeCamp/freeCodeCamp](https://github.com/freeCodeCamp/freeCodeCamp) -- **star**: 307 k -- **介绍**: 开放源码代码库和课程。与数百万人一起免费学习编程。网站:[https://www.freeCodeCamp.org](https://www.freecodecamp.org/) (一个友好的社区,您可以在这里免费学习编码。它由捐助者支持、非营利组织运营,以帮助数百万忙碌的成年人学习编程技术。这个社区已经帮助10,000多人获得了第一份开发人员的工作。这里的全栈Web开发课程是完全免费的,并且可以自行调整进度。这里还有数以千计的交互式编码挑战,可帮助您扩展技能。) - -比如我想学习 ES6 的语法,学习界面是下面这样的,你可以很方便地边练习边学习: - -![Learn ES6](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/freecodemap-es6.jpg) - -### 2. 996.ICU - -- **Github地址**:[https://github.com/996icu/996.ICU](https://github.com/996icu/996.ICU) -- **star**: 248 k -- **介绍**: `996.ICU` 是指“工作 996, 生病 ICU” 。这是中国程序员之间的一种自嘲说法,意思是如果按照 996 的模式工作,那以后就得进 ICU 了。这个项目最早是某个中国程序员发起的,然后就火遍全网,甚至火到了全世界很多其他国家,其网站被翻译成了多种语言。网站地址:[https://996.icu](https://996.icu/)。 - -![996.ICU-website](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/996.icu.jpg) - -### 3. vue - -- **Github地址**:[https://github.com/vuejs/vue](https://github.com/vuejs/vue) -- **star**: 153 k -- **介绍**: 尤大的前端框架。国人用的最多(容易上手,文档比较丰富),所以 Star 数量比较多还是有道理的。Vue (读音 /vjuː/,类似于 **view**) 是一套用于构建用户界面的**渐进式框架**。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与[现代化的工具链](https://cn.vuejs.org/v2/guide/single-file-components.html)以及各种[支持类库](https://github.com/vuejs/awesome-vue#libraries--plugins)结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。 - -### 4. React - -- **Github地址**:[https://gitstar-ranking.com/facebook/react](https://gitstar-ranking.com/facebook/react) -- **star**: 140 k -- **介绍**: Facebook 开源的,大公司有保障。用于构建用户界面的声明式、基于组件开发,高效且灵活的JavaScript框架。我司大部分项目的前端都是 React ,我自己也用过一段时间,感觉还不错,但是也有一些小坑。 - -### 5. tensorflow - -- **Github地址**:[https://github.com/tensorflow/tensorflow](https://github.com/tensorflow/tensorflow) -- **star**: 138 k -- **介绍**: 适用于所有人的开源机器学习框架。[TensorFlow](https://www.tensorflow.org/)是用于机器学习的端到端开源平台。TensorFlow最初是由Google机器智能研究组织内Google Brain团队的研究人员和工程师开发的,用于进行机器学习和深度神经网络研究。该系统具有足够的通用性,也可以适用于多种其他领域。TensorFlow提供了稳定的[Python](https://www.tensorflow.org/api_docs/python) 和[C ++](https://www.tensorflow.org/api_docs/cc) API,以及[其他语言的](https://www.tensorflow.org/api_docs)非保证的向后兼容API 。 - -### 6. bootstrap - -- **Github地址**:[https://github.com/twbs/bootstrap](https://github.com/twbs/bootstrap) -- **star**: 137 k -- **介绍**: 相信初学前端的时候,大家一定或多或少地接触过这个框架。官网说它是最受欢迎的HTML,CSS和JavaScript框架,用于在网络上开发响应式,移动优先项目。 - -### 7. free-programming-books - -- **Github地址**:[https://github.com/EbookFoundation/free-programming-books](https://github.com/EbookFoundation/free-programming-books) -- **star**: 132 k -- **介绍**: 免费提供的编程书籍。我自己没太搞懂为啥这个项目 Star 数这么多,知道的麻烦评论区吱一声。 - -### 8. Awesome - -- **Github地址** : [https://github.com/sindresorhus/awesome](https://github.com/sindresorhus/awesome) -- **star**: 120 k -- **介绍**: github 上很多的各种 Awesome 系列合集。 - - 下面是这个开源仓库的目录,可以看出其涵盖了很多方面的内容。 - - - -举个例子,这个仓库里面就有两个让你的电脑更好用的开源仓库,Mac 和 Windows都有: - -- Awesome Mac:https://github.com/jaywcjlove/awesome-mac/blob/master/README-zh.m -- Awsome Windows: https://github.com/Awesome-Windows/Awesome/blob/master/README-cn.md - -### 9. You-Dont-Know-JS - -- **Github地址**:[https://github.com/getify/You-Dont-Know-JS](https://github.com/getify/You-Dont-Know-JS) -- **star**: 112 k -- **介绍**: 您还不认识JS(书籍系列)-第二版 - -### 10. oh-my-zsh - -- **Github地址**:[https://github.com/ohmyzsh/ohmyzsh](https://github.com/ohmyzsh/ohmyzsh) -- **star**: 99.4 k -- **介绍**: 一个令人愉快的社区驱动的框架(拥有近1500个贡献者),用于管理zsh配置。包括200多个可选插件(rails, git, OSX, hub, capistrano, brew, ant, php, python等),140多个主题,可为您的早晨增光添彩,以及一个自动更新工具,可让您轻松保持与来自社区的最新更新…… - -下面就是 oh-my-zsh 提供的一个花里胡哨的主题: - -![oh-my-zsh-theme](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/ohmyzsh-theme.png) diff --git a/docs/tools/阿里云服务器使用经验.md b/docs/tools/阿里云服务器使用经验.md deleted file mode 100644 index 55c89645..00000000 --- a/docs/tools/阿里云服务器使用经验.md +++ /dev/null @@ -1,149 +0,0 @@ -最近很多阿里云双 11 做活动,优惠力度还挺大的,很多朋友都买以最低的价格买到了自己的云服务器。不论是作为学习机还是部署自己的小型网站或者服务来说都是很不错的! - -但是,很多朋友都不知道如何正确去使用。下面我简单分享一下自己的使用经验。 - -总结一下,主要涉及下面几个部分,对于新手以及没有这么使用过云服务的朋友还是比较友好的: - -1. 善用阿里云镜像市场节省安装 Java 环境的时间,相关说明都在根目录下的 readme.txt. 文件里面; -2. 本地通过 SSH 连接阿里云服务器很容易,配置好 Host地址,通过 root 用户加上实例密码直接连接即可。 -3. 本地连接 MySQL 数据库需要简单配置一下安全组和并且允许 root 用户在任何地方进行远程登录。 -4. 通过 Alibaba Cloud Toolkit 部署 Spring Boot 项目到阿里云服务器真的很方便。 - -**[活动地址](https://www.aliyun.com/1111/2019/group-buying-share?ptCode=32AE103FC8249634736194795A3477C4647C88CF896EF535&userCode=hf47liqn&share_source=copy_link)** (仅限新人,老用户可以考虑使用家人或者朋友账号购买,推荐799/3年 2核4G 这个性价比和适用面更广) - -### 善用阿里云镜像市场节省安装环境的时间 - -基本的购买流程这里就不多说了,另外这里需要注意的是:其实 Java 环境是不需要我们手动安装配置的,阿里云提供的镜像市场有一些常用的环境。 - -> 阿里云镜像市场是指阿里云建立的、由镜像服务商向用户提供其镜像及相关服务的网络平台。这些镜像在操作系统上整合了具体的软件环境和功能,比如Java、PHP运行环境、控制面板等,供有相关需求的用户开通实例时选用。 - -具体如何在购买云服务器的时候通过镜像创建实例或者已有ECS用户如何使用镜像可以查看官方详细的介绍,地址: - -https://help.aliyun.com/knowledge_detail/41987.html?spm=a2c4g.11186631.2.1.561e2098dIdCGZ - -### 当我们成功购买服务器之后如何通过 SSH 连接呢? - -创建好 ECS 后,你绑定的手机会收到短信,会告知你初始密码的。你可以登录管理控制台对密码进行修改,修改密码需要在管理控制台重启服务器才能生效。 - -你也可以在阿里云 ECS 控制台重置实例密码,如下图所示。 - -![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-7/Screen Shot 2019-10-30 at 10.51.15 AM.png) - -**第一种连接方式是直接在阿里云服务器管理的网页上连接**。如上图所示, 点击远程连接,然后输入远程连接密码,这个并不是你重置实例密码得到的密码,如果忘记了直接修改远程连接密码即可。 - -**第二种方式是在本地通过命令或者软件连接。** 推荐使用这种方式,更加方便。 - - **Windows 推荐使用 Xshell 连接,具体方式如下:** - -> Window电脑在家,这里直接用找到的一些图片给大家展示一个。 - -![](https://img2018.cnblogs.com/blog/1070438/201812/1070438-20181226165727765-1335537850.png) - -![](https://img2018.cnblogs.com/blog/1070438/201812/1070438-20181226170155651-1407670048.png) - -接着点开,输入账号:root,命名输入刚才设置的密码,点ok就可以了 - -![](https://img2018.cnblogs.com/blog/1070438/201812/1070438-20181226170444344-411355334.png) - -**Mac 或者 Linux 系统都可以直接使用 ssh 命令进行连接,非常方便。** - -成功连接之后,控制台会打印出如下消息。 - -```shell -➜ ~ ssh root@47.107.159.12 -p 22 -root@47.107.159.12's password: -Last login: Wed Oct 30 09:31:31 2019 from 220.249.123.170 - -Welcome to Alibaba Cloud Elastic Compute Service ! - - 欢迎使用 Tomcat8 JDK8 Mysql5.7 环境 - - 使用说明请参考 /root/readme.txt 文件 -``` - -我当时选择是阿里云提供好的 Java 环境,自动就提供了 Tomcat、 JDK8 、Mysql5.7,所以不需要我们再进行安装配置了,节省了很多时间。另外,需要注意的是:**一定要看 /readme.txt ,Tomcat、 JDK8 、Mysql5.7相关配置以及安装路径等说明都在里面。** - -### 如何连接数据库? - - **如需外网远程访问mysql 请参考以上网址 设置mysql及阿里云安全组**。 - -![开放安全组](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-7/开放安全组.png) - -Mysql为了安全性,在默认情况下用户只允许在本地登录,但是可以使用 SSH 方式连接。如果我们不想通过 SSH 方式连接的话就需要对 MySQL 进行简单的配置。 - -```shell -#允许root用户在任何地方进行远程登录,并具有所有库任何操作权限: -# *.*代表所有库表 “%”代表所有IP地址 -mysql> GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY "自定义密码" WITH GRANT OPTION; -Query OK, 0 rows affected, 1 warning (0.00 sec) -#刷新权限。  -mysql>flush privileges; -#退出mysql -mysql>exit -#重启MySQL生效 -[root@snailclimb]# systemctl restart mysql -``` - -这样的话,我们就能在本地进行连接了。Windows 推荐使用Navicat或者SQLyog。 - -> Window电脑在家,这里用 Mac 上的MySQL可视化工具Sequel Pro给大家演示一下。 - - - -### 如何把一个Spring Boot 项目部署到服务器上呢? - -默认大家都是用 IDEA 进行开发。另外,你要有一个简单的 Spring Boot Web 项目。如果还不了解 Spring Boot 的话,一个简单的 Spring Boot 版 "Hello World "项目,地址如下: - -https://github.com/Snailclimb/springboot-guide/blob/master/docs/start/springboot-hello-world.md 。 - -**1.下载一个叫做 Alibaba Cloud Toolkit 的插件。** - - - -**2.进入 Preference 配置一个 Access Key ID 和 Access Key Secret。** - - - -**3.部署项目到 ECS 上。** - -![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-7/deploy-to-ecs1.png) - - - -按照上面这样填写完基本配置之后,然后点击 run 运行即可。运行成功,控制台会打印出如下信息: - -```shell -[INFO] Deployment File is Uploading... -[INFO] IDE Version:IntelliJ IDEA 2019.2 -[INFO] Alibaba Cloud Toolkit Version:2019.9.1 -[INFO] Start upload hello-world-0.0.1-SNAPSHOT.jar -[INFO][##################################################] 100% (18609645/18609645) -[INFO] Succeed to upload, 18609645 bytes have been uploaded. -[INFO] Upload Deployment File to OSS Success -[INFO] Target Deploy ECS: { 172.18.245.148 / 47.107.159.12 } -[INFO] Command: { source /etc/profile; cd /springboot; } - Tip: The deployment package will be temporarily stored in Alibaba Cloud Security OSS and will be - deleted after the deployment is complete. Please be assured that no one can access it except you. - -[INFO] Create Deploy Directory Success. - -[INFO] Deployment File is Downloading... -[INFO] Download Deployment File from OSS Success - -[INFO] File Upload Total time: 16.676 s -``` - - 通过控制台答应出的信息可以看出:通过这个插件会自动把这个 Spring Boot 项目打包成一个 jar 包,然后上传到你的阿里云服务器中指定的文件夹中,你只需要登录你的阿里云服务器,然后通过 `java -jar hello-world-0.0.1-SNAPSHOT.jar`命令运行即可。 - -```shell -[root@snailclimb springboot]# ll -total 18176 --rw-r--r-- 1 root root 18609645 Oct 30 08:25 hello-world-0.0.1-SNAPSHOT.jar -[root@snailclimb springboot]# java -jar hello-world-0.0.1-SNAPSHOT.jar -``` - - 然后你就可以在本地访问访问部署在你的阿里云 ECS 上的服务了。 - - - -**[推荐一下阿里云双11的活动:云服务器1折起,仅86元/年,限量抢购!](https://www.aliyun.com/1111/2019/group-buying-share?ptCode=32AE103FC8249634736194795A3477C4647C88CF896EF535&userCode=hf47liqn&share_source=copy_link)** (仅限新人,老用户可以考虑使用家人或者朋友账号购买,推荐799/3年 2核4G 这个性价比和适用面更广) \ No newline at end of file diff --git a/index.html b/index.html index f154126a..6b0768a5 100644 --- a/index.html +++ b/index.html @@ -8,11 +8,16 @@ - + + +

@@ -21,12 +26,12 @@ if (typeof navigator.serviceWorker !== 'undefined') { navigator.serviceWorker.register('sw.js') } - window.$docsify = { name: 'JavaGuide', repo: 'https://github.com/Snailclimb/JavaGuide', - maxLevel: 3,//最大支持渲染的标题层级 - coverpage: true,//封面,_coverpage.md + maxLevel: 4,//最大支持渲染的标题层级 + //封面,_coverpage.md + //coverpage: true, auto2top: true,//切换页面后是否自动跳转到页面顶部 //ga: 'UA-138586553-1', //logo: 'https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-3logo-透明.png' , @@ -38,10 +43,16 @@ // 搜索标题的最大程级, 1 - 6 depth: 3, }, + // 字数统计 + count: { + countable: true, + fontsize: '0.9em', + color: 'rgb(90,90,90)', + language: 'chinese' + }, plugins: [ EditOnGithubPlugin.create('https://github.com/Snailclimb/JavaGuide/blob/master/') ], - } @@ -57,6 +68,10 @@ + + + + \ No newline at end of file diff --git a/media/pictures/java/linux_io/BIO原理.png b/media/pictures/java/linux_io/BIO原理.png deleted file mode 100644 index 6e67cadd..00000000 Binary files a/media/pictures/java/linux_io/BIO原理.png and /dev/null differ diff --git a/media/pictures/java/linux_io/IO多路复用原理.png b/media/pictures/java/linux_io/IO多路复用原理.png deleted file mode 100644 index b0363574..00000000 Binary files a/media/pictures/java/linux_io/IO多路复用原理.png and /dev/null differ diff --git a/media/pictures/java/linux_io/NIO原理.png b/media/pictures/java/linux_io/NIO原理.png deleted file mode 100644 index d3bc63b3..00000000 Binary files a/media/pictures/java/linux_io/NIO原理.png and /dev/null differ diff --git a/media/pictures/java/linux_io/信号驱动IO原理.png b/media/pictures/java/linux_io/信号驱动IO原理.png deleted file mode 100644 index 1df98b20..00000000 Binary files a/media/pictures/java/linux_io/信号驱动IO原理.png and /dev/null differ diff --git a/media/pictures/java/linux_io/异步IO原理.png b/media/pictures/java/linux_io/异步IO原理.png deleted file mode 100644 index 098c9b7e..00000000 Binary files a/media/pictures/java/linux_io/异步IO原理.png and /dev/null differ diff --git a/media/pictures/java/linux_io/用户态与内核态.png b/media/pictures/java/linux_io/用户态与内核态.png deleted file mode 100644 index aa0dafc2..00000000 Binary files a/media/pictures/java/linux_io/用户态与内核态.png and /dev/null differ diff --git a/media/pictures/java/my-lru-cache/ConcurrentLinkedQueue-Diagram.png b/media/pictures/java/my-lru-cache/ConcurrentLinkedQueue-Diagram.png deleted file mode 100644 index 26b68b1f..00000000 Binary files a/media/pictures/java/my-lru-cache/ConcurrentLinkedQueue-Diagram.png and /dev/null differ diff --git a/media/pictures/java/my-lru-cache/MyLRUCachePut.png b/media/pictures/java/my-lru-cache/MyLRUCachePut.png deleted file mode 100644 index 2ffc0b7b..00000000 Binary files a/media/pictures/java/my-lru-cache/MyLRUCachePut.png and /dev/null differ diff --git a/media/pictures/java/my-lru-cache/ScheduledThreadPoolExecutor-diagram.png b/media/pictures/java/my-lru-cache/ScheduledThreadPoolExecutor-diagram.png deleted file mode 100644 index 00a11dc7..00000000 Binary files a/media/pictures/java/my-lru-cache/ScheduledThreadPoolExecutor-diagram.png and /dev/null differ diff --git a/media/pictures/kafka/Broker和集群.png b/media/pictures/kafka/Broker和集群.png deleted file mode 100644 index edd70355..00000000 Binary files a/media/pictures/kafka/Broker和集群.png and /dev/null differ diff --git a/media/pictures/kafka/Partition与消费模型.png b/media/pictures/kafka/Partition与消费模型.png deleted file mode 100644 index c87f44fe..00000000 Binary files a/media/pictures/kafka/Partition与消费模型.png and /dev/null differ diff --git a/media/pictures/kafka/kafka存在文件系统上.png b/media/pictures/kafka/kafka存在文件系统上.png deleted file mode 100644 index 3455f3ad..00000000 Binary files a/media/pictures/kafka/kafka存在文件系统上.png and /dev/null differ diff --git a/media/pictures/kafka/segment是kafka文件存储的最小单位.png b/media/pictures/kafka/segment是kafka文件存储的最小单位.png deleted file mode 100644 index fbf30884..00000000 Binary files a/media/pictures/kafka/segment是kafka文件存储的最小单位.png and /dev/null differ diff --git a/media/pictures/kafka/主题与分区.png b/media/pictures/kafka/主题与分区.png deleted file mode 100644 index 5cf07722..00000000 Binary files a/media/pictures/kafka/主题与分区.png and /dev/null differ diff --git a/media/pictures/kafka/前言.md b/media/pictures/kafka/前言.md deleted file mode 100644 index 24d6e370..00000000 --- a/media/pictures/kafka/前言.md +++ /dev/null @@ -1,202 +0,0 @@ -# 前言 - -谈到java的线程池最熟悉的莫过于ExecutorService接口了,jdk1.5新增的java.util.concurrent包下的这个api,大大的简化了多线程代码的开发。而不论你用FixedThreadPool还是CachedThreadPool其背后实现都是ThreadPoolExecutor。ThreadPoolExecutor是一个典型的缓存池化设计的产物,因为池子有大小,当池子体积不够承载时,就涉及到拒绝策略。JDK中已经预设了4种线程池拒绝策略,下面结合场景详细聊聊这些策略的使用场景,以及我们还能扩展哪些拒绝策略。 - -# 池化设计思想 - -池话设计应该不是一个新名词。我们常见的如java线程池、jdbc连接池、redis连接池等就是这类设计的代表实现。这种设计会初始预设资源,解决的问题就是抵消每次获取资源的消耗,如创建线程的开销,获取远程连接的开销等。就好比你去食堂打饭,打饭的大妈会先把饭盛好几份放那里,你来了就直接拿着饭盒加菜即可,不用再临时又盛饭又打菜,效率就高了。除了初始化资源,池化设计还包括如下这些特征:池子的初始值、池子的活跃值、池子的最大值等,这些特征可以直接映射到java线程池和数据库连接池的成员属性中。 - -# 线程池触发拒绝策略的时机 - -和数据源连接池不一样,线程池除了初始大小和池子最大值,还多了一个阻塞队列来缓冲。数据源连接池一般请求的连接数超过连接池的最大值的时候就会触发拒绝策略,策略一般是阻塞等待设置的时间或者直接抛异常。而线程池的触发时机如下图: - -![img](http://www.kailing.pub/Uploads/image/20190729/20190729193156_24469.png) - -如图,想要了解线程池什么时候触发拒绝粗略,需要明确上面三个参数的具体含义,是这三个参数总体协调的结果,而不是简单的超过最大线程数就会触发线程拒绝粗略,当提交的任务数大于corePoolSize时,会优先放到队列缓冲区,只有填满了缓冲区后,才会判断当前运行的任务是否大于maxPoolSize,小于时会新建线程处理。大于时就触发了拒绝策略,总结就是:当前提交任务数大于(maxPoolSize + queueCapacity)时就会触发线程池的拒绝策略了。 - -# JDK内置4种线程池拒绝策略 - -# 拒绝策略接口定义 - -在分析JDK自带的线程池拒绝策略前,先看下JDK定义的 拒绝策略接口,如下: - -```java -public interface RejectedExecutionHandler { - void rejectedExecution(Runnable r, ThreadPoolExecutor executor); -} -``` - -接口定义很明确,当触发拒绝策略时,线程池会调用你设置的具体的策略,将当前提交的任务以及线程池实例本身传递给你处理,具体作何处理,不同场景会有不同的考虑,下面看JDK为我们内置了哪些实现: - -# CallerRunsPolicy(调用者运行策略) - -```java - public static class CallerRunsPolicy implements RejectedExecutionHandler { - - public CallerRunsPolicy() { } - - public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { - if (!e.isShutdown()) { - r.run(); - } - } - } -``` - -功能:当触发拒绝策略时,只要线程池没有关闭,就由提交任务的当前线程处理。 - -使用场景:一般在不允许失败的、对性能要求不高、并发量较小的场景下使用,因为线程池一般情况下不会关闭,也就是提交的任务一定会被运行,但是由于是调用者线程自己执行的,当多次提交任务时,就会阻塞后续任务执行,性能和效率自然就慢了。 - -# AbortPolicy(中止策略) - -```java - public static class AbortPolicy implements RejectedExecutionHandler { - - public AbortPolicy() { } - - public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { - throw new RejectedExecutionException("Task " + r.toString() + - " rejected from " + - e.toString()); - } - } -``` - -功能:当触发拒绝策略时,直接抛出拒绝执行的异常,中止策略的意思也就是打断当前执行流程 - -使用场景:这个就没有特殊的场景了,但是一点要正确处理抛出的异常。ThreadPoolExecutor中默认的策略就是AbortPolicy,ExecutorService接口的系列ThreadPoolExecutor因为都没有显示的设置拒绝策略,所以默认的都是这个。但是请注意,ExecutorService中的线程池实例队列都是无界的,也就是说把内存撑爆了都不会触发拒绝策略。当自己自定义线程池实例时,使用这个策略一定要处理好触发策略时抛的异常,因为他会打断当前的执行流程。 - -# DiscardPolicy(丢弃策略) - -```java - public static class DiscardPolicy implements RejectedExecutionHandler { - - public DiscardPolicy() { } - - public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { - } - } -``` - -功能:如果线程池未关闭,就弹出队列头部的元素,然后尝试执行 - -使用场景:这个策略还是会丢弃任务,丢弃时也是毫无声息,但是特点是丢弃的是老的未执行的任务,而且是待执行优先级较高的任务。基于这个特性,我能想到的场景就是,发布消息,和修改消息,当消息发布出去后,还未执行,此时更新的消息又来了,这个时候未执行的消息的版本比现在提交的消息版本要低就可以被丢弃了。因为队列中还有可能存在消息版本更低的消息会排队执行,所以在真正处理消息的时候一定要做好消息的版本比较 - -# 第三方实现的拒绝策略 - -# dubbo中的线程拒绝策略 - -```java -public class AbortPolicyWithReport extends ThreadPoolExecutor.AbortPolicy { - - protected static final Logger logger = LoggerFactory.getLogger(AbortPolicyWithReport.class); - - private final String threadName; - - private final URL url; - - private static volatile long lastPrintTime = 0; - - private static Semaphore guard = new Semaphore(1); - - public AbortPolicyWithReport(String threadName, URL url) { - this.threadName = threadName; - this.url = url; - } - - @Override - public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { - String msg = String.format("Thread pool is EXHAUSTED!" + - " Thread Name: %s, Pool Size: %d (active: %d, core: %d, max: %d, largest: %d), Task: %d (completed: %d)," + - " Executor status:(isShutdown:%s, isTerminated:%s, isTerminating:%s), in %s://%s:%d!", - threadName, e.getPoolSize(), e.getActiveCount(), e.getCorePoolSize(), e.getMaximumPoolSize(), e.getLargestPoolSize(), - e.getTaskCount(), e.getCompletedTaskCount(), e.isShutdown(), e.isTerminated(), e.isTerminating(), - url.getProtocol(), url.getIp(), url.getPort()); - logger.warn(msg); - dumpJStack(); - throw new RejectedExecutionException(msg); - } - - private void dumpJStack() { - //省略实现 - } -} -``` - -可以看到,当dubbo的工作线程触发了线程拒绝后,主要做了三个事情,原则就是尽量让使用者清楚触发线程拒绝策略的真实原因 - -- 输出了一条警告级别的日志,日志内容为线程池的详细设置参数,以及线程池当前的状态,还有当前拒绝任务的一些详细信息。可以说,这条日志,使用dubbo的有过生产运维经验的或多或少是见过的,这个日志简直就是日志打印的典范,其他的日志打印的典范还有spring。得益于这么详细的日志,可以很容易定位到问题所在 -- 输出当前线程堆栈详情,这个太有用了,当你通过上面的日志信息还不能定位问题时,案发现场的dump线程上下文信息就是你发现问题的救命稻草。 -- 继续抛出拒绝执行异常,使本次任务失败,这个继承了JDK默认拒绝策略的特性 - -# Netty中的线程池拒绝策略 - -```java - private static final class NewThreadRunsPolicy implements RejectedExecutionHandler { - NewThreadRunsPolicy() { - super(); - } - - public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { - try { - final Thread t = new Thread(r, "Temporary task executor"); - t.start(); - } catch (Throwable e) { - throw new RejectedExecutionException( - "Failed to start a new thread", e); - } - } - } -``` - -Netty中的实现很像JDK中的CallerRunsPolicy,舍不得丢弃任务。不同的是,CallerRunsPolicy是直接在调用者线程执行的任务。而 Netty是新建了一个线程来处理的。所以,Netty的实现相较于调用者执行策略的使用面就可以扩展到支持高效率高性能的场景了。但是也要注意一点,Netty的实现里,在创建线程时未做任何的判断约束,也就是说只要系统还有资源就会创建新的线程来处理,直到new不出新的线程了,才会抛创建线程失败的异常 - -# ActiveMq中的线程池拒绝策略 - -```java -new RejectedExecutionHandler() { - @Override - public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) { - try { - executor.getQueue().offer(r, 60, TimeUnit.SECONDS); - } catch (InterruptedException e) { - throw new RejectedExecutionException("Interrupted waiting for BrokerService.worker"); - } - - throw new RejectedExecutionException("Timed Out while attempting to enqueue Task."); - } - }); -``` - -ActiveMq中的策略属于最大努力执行任务型,当触发拒绝策略时,在尝试一分钟的时间重新将任务塞进任务队列,当一分钟超时还没成功时,就抛出异常 - -# pinpoint中的线程池拒绝策略 - -```java -public class RejectedExecutionHandlerChain implements RejectedExecutionHandler { - private final RejectedExecutionHandler[] handlerChain; - - public static RejectedExecutionHandler build(List chain) { - Objects.requireNonNull(chain, "handlerChain must not be null"); - RejectedExecutionHandler[] handlerChain = chain.toArray(new RejectedExecutionHandler[0]); - return new RejectedExecutionHandlerChain(handlerChain); - } - - private RejectedExecutionHandlerChain(RejectedExecutionHandler[] handlerChain) { - this.handlerChain = Objects.requireNonNull(handlerChain, "handlerChain must not be null"); - } - - @Override - public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { - for (RejectedExecutionHandler rejectedExecutionHandler : handlerChain) { - rejectedExecutionHandler.rejectedExecution(r, executor); - } - } -} -``` - -pinpoint的拒绝策略实现很有特点,和其他的实现都不同。他定义了一个拒绝策略链,包装了一个拒绝策略列表,当触发拒绝策略时,会将策略链中的rejectedExecution依次执行一遍 - -# 结语 - -前文从线程池设计思想,以及线程池触发拒绝策略的时机引出java线程池拒绝策略接口的定义。并辅以JDK内置4种以及四个第三方开源软件的拒绝策略定义描述了线程池拒绝策略实现的各种思路和使用场景。希望阅读此文后能让你对java线程池拒绝策略有更加深刻的认识,能够根据不同的使用场景更加灵活的应用。 \ No newline at end of file diff --git a/media/pictures/kafka/发送消息.png b/media/pictures/kafka/发送消息.png deleted file mode 100644 index 82cedad9..00000000 Binary files a/media/pictures/kafka/发送消息.png and /dev/null differ diff --git a/media/pictures/kafka/启动服务.png b/media/pictures/kafka/启动服务.png deleted file mode 100644 index b7fe2423..00000000 Binary files a/media/pictures/kafka/启动服务.png and /dev/null differ diff --git a/media/pictures/kafka/消费者设计概要1.png b/media/pictures/kafka/消费者设计概要1.png deleted file mode 100644 index 67ff1bf9..00000000 Binary files a/media/pictures/kafka/消费者设计概要1.png and /dev/null differ diff --git a/media/pictures/kafka/消费者设计概要2.png b/media/pictures/kafka/消费者设计概要2.png deleted file mode 100644 index 5ea1f290..00000000 Binary files a/media/pictures/kafka/消费者设计概要2.png and /dev/null differ diff --git a/media/pictures/kafka/消费者设计概要3.png b/media/pictures/kafka/消费者设计概要3.png deleted file mode 100644 index 0c7383f9..00000000 Binary files a/media/pictures/kafka/消费者设计概要3.png and /dev/null differ diff --git a/media/pictures/kafka/消费者设计概要4.png b/media/pictures/kafka/消费者设计概要4.png deleted file mode 100644 index 0f4d51df..00000000 Binary files a/media/pictures/kafka/消费者设计概要4.png and /dev/null differ diff --git a/media/pictures/kafka/消费者设计概要5.png b/media/pictures/kafka/消费者设计概要5.png deleted file mode 100644 index 741302da..00000000 Binary files a/media/pictures/kafka/消费者设计概要5.png and /dev/null differ diff --git a/media/pictures/kafka/生产者和消费者.png b/media/pictures/kafka/生产者和消费者.png deleted file mode 100644 index 25c93e00..00000000 Binary files a/media/pictures/kafka/生产者和消费者.png and /dev/null differ diff --git a/media/pictures/kafka/生产者设计概要.png b/media/pictures/kafka/生产者设计概要.png deleted file mode 100644 index 58fe67bb..00000000 Binary files a/media/pictures/kafka/生产者设计概要.png and /dev/null differ diff --git a/media/pictures/linux/Debian10下IDEA的Markdown预渲染解决后.png b/media/pictures/linux/Debian10下IDEA的Markdown预渲染解决后.png deleted file mode 100644 index 303f3ee9..00000000 Binary files a/media/pictures/linux/Debian10下IDEA的Markdown预渲染解决后.png and /dev/null differ diff --git a/media/pictures/linux/Debian10下IDEA的Markdown预渲染问题.png b/media/pictures/linux/Debian10下IDEA的Markdown预渲染问题.png deleted file mode 100644 index aaa25e62..00000000 Binary files a/media/pictures/linux/Debian10下IDEA的Markdown预渲染问题.png and /dev/null differ diff --git a/media/pictures/linux/Fcitx候选框定位问题.png b/media/pictures/linux/Fcitx候选框定位问题.png deleted file mode 100644 index 22219303..00000000 Binary files a/media/pictures/linux/Fcitx候选框定位问题.png and /dev/null differ diff --git a/media/pictures/linux/Linux目录.png b/media/pictures/linux/Linux目录.png deleted file mode 100644 index a7c05810..00000000 Binary files a/media/pictures/linux/Linux目录.png and /dev/null differ diff --git a/media/pictures/linux/我的电脑配置.png b/media/pictures/linux/我的电脑配置.png deleted file mode 100644 index a07c78de..00000000 Binary files a/media/pictures/linux/我的电脑配置.png and /dev/null differ diff --git a/media/pictures/linux/文件inode信息.png b/media/pictures/linux/文件inode信息.png deleted file mode 100644 index b47551e8..00000000 Binary files a/media/pictures/linux/文件inode信息.png and /dev/null differ diff --git a/media/pictures/linux/用户态与内核态.png b/media/pictures/linux/用户态与内核态.png deleted file mode 100644 index aa0dafc2..00000000 Binary files a/media/pictures/linux/用户态与内核态.png and /dev/null differ diff --git a/media/pictures/linux/目录文件.png b/media/pictures/linux/目录文件.png deleted file mode 100644 index 52e93978..00000000 Binary files a/media/pictures/linux/目录文件.png and /dev/null differ diff --git a/media/pictures/linux/软链接和硬链接.png b/media/pictures/linux/软链接和硬链接.png deleted file mode 100644 index 214d436f..00000000 Binary files a/media/pictures/linux/软链接和硬链接.png and /dev/null differ