diff --git a/README.md b/README.md index dd9f96bffc9..e8db97f4773 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ > 1. **介绍**:关于 JavaGuide 的相关介绍请看:[关于 JavaGuide 的一些说明](https://www.yuque.com/snailclimb/dr6cvl/mr44yt) 。 > 2. **PDF版本** : [《JavaGuide 面试突击版》PDF 版本](#公众号) 。 -> 3. **图解计算机基础** :[图解计算机基础 PDF 下载](http://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=100021725&idx=1&sn=2db9664ca25363139a81691043e9fd8f&chksm=4ea19a1679d61300d8990f7e43bfc7f476577a81b712cf0f9c6f6552a8b219bc081efddb5c54#rd) 。 +> 3. **图解计算机基础** :[图解计算机基础 PDF 下载](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=100021725&idx=1&sn=2db9664ca25363139a81691043e9fd8f&chksm=4ea19a1679d61300d8990f7e43bfc7f476577a81b712cf0f9c6f6552a8b219bc081efddb5c54#rd) 。 > 4. **知识星球** : 简历指导/Java学习/面试指导/面试小册。欢迎加入[我的知识星球](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=100015911&idx=1&sn=2e8a0f5acb749ecbcbb417aa8a4e18cc&chksm=4ea1b0ec79d639fae37df1b86f196e8ce397accfd1dd2004bcadb66b4df5f582d90ae0d62448#rd) 。星球内部更新的[《Java面试进阶指北 打造个人的技术竞争力》](https://www.yuque.com/docs/share/f37fc804-bfe6-4b0d-b373-9c462188fec7)这个小册的质量很高,专为面试打造。 > 5. **面试专版** :准备面试的小伙伴可以考虑面试专版:[《Java 面试进阶指南》](https://xiaozhuanlan.com/javainterview?rel=javaguide) > 6. **转载须知** :以下所有文章如非文首说明皆为我(Guide哥)的原创,转载在文首注明出处,如发现恶意抄袭/搬运,会动用法律武器维护自己的权益。让我们一起维护一个良好的技术创作环境!⛽️ @@ -94,7 +94,7 @@ ## 计算机基础 -👉 **[图解计算机基础 PDF 下载](http://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=100021725&idx=1&sn=2db9664ca25363139a81691043e9fd8f&chksm=4ea19a1679d61300d8990f7e43bfc7f476577a81b712cf0f9c6f6552a8b219bc081efddb5c54#rd)** 。 +👉 **[图解计算机基础 PDF 下载](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=100021725&idx=1&sn=2db9664ca25363139a81691043e9fd8f&chksm=4ea19a1679d61300d8990f7e43bfc7f476577a81b712cf0f9c6f6552a8b219bc081efddb5c54#rd)** 。 ### 操作系统 diff --git "a/docs/dataStructures-algorithms/\345\207\240\351\201\223\345\270\270\350\247\201\347\232\204\351\223\276\350\241\250\347\256\227\346\263\225\351\242\230.md" "b/docs/dataStructures-algorithms/\345\207\240\351\201\223\345\270\270\350\247\201\347\232\204\351\223\276\350\241\250\347\256\227\346\263\225\351\242\230.md" index 9daa0fc159c..7b82436891d 100644 --- "a/docs/dataStructures-algorithms/\345\207\240\351\201\223\345\270\270\350\247\201\347\232\204\351\223\276\350\241\250\347\256\227\346\263\225\351\242\230.md" +++ "b/docs/dataStructures-algorithms/\345\207\240\351\201\223\345\270\270\350\247\201\347\232\204\351\223\276\350\241\250\347\256\227\346\263\225\351\242\230.md" @@ -50,7 +50,7 @@ Leetcode官方详细解答地址: 我们使用变量来跟踪进位,并从包含最低有效位的表头开始模拟逐 位相加的过程。 -![图1,对两数相加方法的可视化: 342 + 465 = 807342+465=807, 每个结点都包含一个数字,并且数字按位逆序存储。](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-20/34910956.jpg) +![图1,对两数相加方法的可视化: 342 + 465 = 807342+465=807, 每个结点都包含一个数字,并且数字按位逆序存储。](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-20/34910956.jpg) ### Solution @@ -98,7 +98,7 @@ public ListNode addTwoNumbers(ListNode l1, ListNode l2) { ### 题目描述 > 剑指 offer:输入一个链表,反转链表后,输出链表的所有元素。 -![翻转链表](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-20/81431871.jpg) +![翻转链表](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-20/81431871.jpg) ### 问题分析 @@ -269,7 +269,7 @@ public class Solution { 我们注意到这个问题可以容易地简化成另一个问题:删除从列表开头数起的第 (L - n + 1)个结点,其中 L是列表的长度。只要我们找到列表的长度 L,这个问题就很容易解决。 -![图 1. 删除列表中的第 L - n + 1 个元素](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-20/94354387.jpg) +![图 1. 删除列表中的第 L - n + 1 个元素](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-20/94354387.jpg) ### Solution diff --git "a/docs/dataStructures-algorithms/\345\211\221\346\214\207offer\351\203\250\345\210\206\347\274\226\347\250\213\351\242\230.md" "b/docs/dataStructures-algorithms/\345\211\221\346\214\207offer\351\203\250\345\210\206\347\274\226\347\250\213\351\242\230.md" index b7b077fb041..84a21a5feee 100644 --- "a/docs/dataStructures-algorithms/\345\211\221\346\214\207offer\351\203\250\345\210\206\347\274\226\347\250\213\351\242\230.md" +++ "b/docs/dataStructures-algorithms/\345\211\221\346\214\207offer\351\203\250\345\210\206\347\274\226\347\250\213\351\242\230.md" @@ -566,7 +566,7 @@ public ListNode Merge(ListNode list1,ListNode list2) { **栈:**后进先出(LIFO) **队列:** 先进先出 很明显我们需要根据JDK给我们提供的栈的一些基本方法来实现。先来看一下Stack类的一些基本方法: -![Stack类的一些常见方法](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-4-4/5985000.jpg) +![Stack类的一些常见方法](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-4-4/5985000.jpg) 既然题目给了我们两个栈,我们可以这样考虑当push的时候将元素push进stack1,pop的时候我们先把stack1的元素pop到stack2,然后再对stack2执行pop操作,这样就可以保证是先进先出的。(负[pop]负[pop]得正[先进先出]) diff --git "a/docs/dataStructures-algorithms/\346\225\260\346\215\256\347\273\223\346\236\204.md" "b/docs/dataStructures-algorithms/\346\225\260\346\215\256\347\273\223\346\236\204.md" index 314b2a855ab..5caffaac901 100644 --- "a/docs/dataStructures-algorithms/\346\225\260\346\215\256\347\273\223\346\236\204.md" +++ "b/docs/dataStructures-algorithms/\346\225\260\346\215\256\347\273\223\346\236\204.md" @@ -165,7 +165,7 @@ B+树最大的性能问题是会产生大量的随机IO 为了克服B+树的弱点,HBase引入了LSM树的概念,即Log-Structured Merge-Trees。 -[LSM树由来、设计思想以及应用到HBase的索引](http://www.cnblogs.com/yanghuahui/p/3483754.html) +[LSM树由来、设计思想以及应用到HBase的索引](https://www.cnblogs.com/yanghuahui/p/3483754.html) ## 图 diff --git a/docs/database/MySQL Index.md b/docs/database/MySQL Index.md index 69d606f106e..ea683c155d7 100644 --- a/docs/database/MySQL Index.md +++ b/docs/database/MySQL Index.md @@ -44,9 +44,9 @@ select username , age from user where username = 'Java' and age = 22 MySQL的基本存储结构是页(记录都存在页里边): -![MySQL的基本存储结构是页](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-2/28559421.jpg) +![MySQL的基本存储结构是页](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-2/28559421.jpg) -![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-2/82053134.jpg) +![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-2/82053134.jpg) - **各个数据页可以组成一个双向链表** - **每个数据页中的记录又可以组成一个单向链表** @@ -65,11 +65,11 @@ MySQL的基本存储结构是页(记录都存在页里边): 索引做了些什么可以让我们查询加快速度呢?其实就是将无序的数据变成有序(相对): -![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-2/5373082.jpg) +![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-2/5373082.jpg) 要找到id为8的记录简要步骤: -![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-2/89338047.jpg) +![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-2/89338047.jpg) 很明显的是:没有用索引我们是需要遍历双向链表来定位对应的页,现在通过 **“目录”** 就可以很快地定位到对应的页上了!(二分查找,时间复杂度近似为O(logn)) diff --git "a/docs/database/MySQL\351\253\230\346\200\247\350\203\275\344\274\230\345\214\226\350\247\204\350\214\203\345\273\272\350\256\256.md" "b/docs/database/MySQL\351\253\230\346\200\247\350\203\275\344\274\230\345\214\226\350\247\204\350\214\203\345\273\272\350\256\256.md" index 60f175838ce..1e38050838f 100644 --- "a/docs/database/MySQL\351\253\230\346\200\247\350\203\275\344\274\230\345\214\226\350\247\204\350\214\203\345\273\272\350\256\256.md" +++ "b/docs/database/MySQL\351\253\230\346\200\247\350\203\275\344\274\230\345\214\226\350\247\204\350\214\203\345\273\272\350\256\256.md" @@ -168,7 +168,7 @@ MySQL 内存临时表不支持 TEXT、BLOB 这样的大数据类型,如果查 **2、TEXT 或 BLOB 类型只能使用前缀索引** -因为[MySQL](http://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==&mid=2247487885&idx=1&sn=65b1bf5f7d4505502620179669a9c2df&chksm=ebd62ea1dca1a7b7bf884bcd9d538d78ba064ee03c09436ca8e57873b1d98a55afd6d7884cfc&scene=21#wechat_redirect) 对索引字段长度是有限制的,所以 TEXT 类型只能使用前缀索引,并且 TEXT 列上是不能有默认值的 +因为[MySQL](https://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==&mid=2247487885&idx=1&sn=65b1bf5f7d4505502620179669a9c2df&chksm=ebd62ea1dca1a7b7bf884bcd9d538d78ba064ee03c09436ca8e57873b1d98a55afd6d7884cfc&scene=21#wechat_redirect) 对索引字段长度是有限制的,所以 TEXT 类型只能使用前缀索引,并且 TEXT 列上是不能有默认值的 ### 3. 避免使用 ENUM 类型 diff --git "a/docs/java/basis/Java\345\237\272\347\241\200\347\237\245\350\257\206.md" "b/docs/java/basis/Java\345\237\272\347\241\200\347\237\245\350\257\206.md" index 4911d88fd94..e624f6f117d 100644 --- "a/docs/java/basis/Java\345\237\272\347\241\200\347\237\245\350\257\206.md" +++ "b/docs/java/basis/Java\345\237\272\347\241\200\347\237\245\350\257\206.md" @@ -175,7 +175,7 @@ Java 语言既具有编译型语言的特征,也具有解释型语言的特征 > 字符封装类 `Character` 有一个成员常量 `Character.SIZE` 值为 16,单位是`bits`,该值除以 8(`1byte=8bits`)后就可以得到 2 个字节 > java 编程思想第四版:2.2.2 节 -> ![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-15/86735519.jpg) +> ![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-15/86735519.jpg) ### 注释 @@ -274,7 +274,7 @@ System.out.println(list); ```java //此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型 //在实例化泛型类时,必须指定T的具体类型 -public class Generic{ +public class Generic { private T key; @@ -282,7 +282,7 @@ public class Generic{ this.key = key; } - public T getKey(){ + public T getKey() { return key; } } @@ -316,7 +316,7 @@ class GeneratorImpl implements Generator{ 实现泛型接口,指定类型: ```java -class GeneratorImpl implements Generator{ +class GeneratorImpl implements Generator{ @Override public String method() { return "hello"; @@ -327,13 +327,12 @@ class GeneratorImpl implements Generator{ **3.泛型方法** : ```java - public static < E > void printArray( E[] inputArray ) - { - for ( E element : inputArray ){ - System.out.printf( "%s ", element ); - } - System.out.println(); +public static void printArray(E[] inputArray) { + for (E element : inputArray) { + System.out.printf("%s ", element); } + System.out.println(); +} ``` 使用: @@ -342,8 +341,8 @@ class GeneratorImpl implements Generator{ // 创建不同类型数组: Integer, Double 和 Character Integer[] intArray = { 1, 2, 3 }; String[] stringArray = { "Hello", "World" }; -printArray( intArray ); -printArray( stringArray ); +printArray(intArray); +printArray(stringArray); ``` **常用的通配符为: T,E,K,V,?** @@ -457,7 +456,7 @@ public native int hashCode(); 在这里解释一位小伙伴的问题。以下内容摘自《Head Fisrt Java》。 -因为 `hashCode()` 所使用的杂凑算法也许刚好会让多个对象传回相同的杂凑值。越糟糕的杂凑算法越容易碰撞,但这也与数据值域分布的特性有关(所谓碰撞也就是指的是不同的对象得到相同的 `hashCode`。 +因为 `hashCode()` 所使用的哈希算法也许刚好会让多个对象传回相同的哈希值。越糟糕的哈希算法越容易碰撞,但这也与数据值域分布的特性有关(所谓碰撞也就是指的是不同的对象得到相同的 `hashCode`。 我们刚刚也提到了 `HashSet`,如果 `HashSet` 在对比的时候,同样的 hashcode 有多个对象,它会使用 `equals()` 来判断是否真的相同。也就是说 `hashcode` 只是用来缩小查找成本。 @@ -750,7 +749,7 @@ public void f5(int a) { 首先,我们回顾一下在程序设计语言中有关将参数传递给方法(或函数)的一些专业术语。 -**按值调用(call by value)** 表示方法接收的是调用者提供的值,**按引用调用(call by reference)** 表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。它用来描述各种程序设计语言(不只是 Java)中方法参数传递方式。 +**按值调用(call by value)** 表示方法接收的是调用者提供的值,**按引用调用(call by reference)** 表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。它用来描述各种程序设计语言(不只是 Java)中方法参数传递方式。 **Java 程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。** @@ -790,7 +789,7 @@ num2 = 20 **解析:** -![example 1 ](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-27/22191348.jpg) +![example 1 ](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-27/22191348.jpg) 在 swap 方法中,a、b 的值进行交换,并不会影响到 num1、num2。因为,a、b 中的值,只是从 num1、num2 的复制过来的。也就是说,a、b 相当于 num1、num2 的副本,副本的内容无论怎么修改,都不会影响到原件本身。 @@ -821,7 +820,7 @@ num2 = 20 **解析:** -![example 2](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-27/3825204.jpg) +![example 2](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-27/3825204.jpg) array 被初始化 arr 的拷贝也就是一个对象的引用,也就是说 array 和 arr 指向的是同一个数组对象。 因此,外部对引用对象的改变会反映到所对应的对象上。 @@ -866,11 +865,11 @@ s2:小李 交换之前: -![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-27/88729818.jpg) +![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-27/88729818.jpg) 交换之后: -![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-27/34384414.jpg) +![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-27/34384414.jpg) 通过上面两张图可以很清晰的看出: **方法并没有改变存储在变量 s1 和 s2 中的对象引用。swap 方法的参数 x 和 y 被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝** @@ -1324,7 +1323,7 @@ try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new F } ``` -## I\O 流 +## I/O 流 ### 什么是序列化?什么是反序列化? @@ -1349,7 +1348,7 @@ try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new F ### Java 序列化中如果有些字段不想进行序列化,怎么办? -`对于不想进行序列化的变量,使用`transient`关键字修饰。` +对于不想进行序列化的变量,使用`transient`关键字修饰。` `transient` 关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 `transient` 修饰的变量值不会被持久化和恢复。`transient` 只能修饰变量,不能修饰类和方法。 @@ -1399,4 +1398,4 @@ Java Io 流共涉及 40 多个类,这些类看上去很杂乱,但实际上 - https://stackoverflow.com/questions/1906445/what-is-the-difference-between-jdk-and-jre - https://www.educba.com/oracle-vs-openjdk/ -- https://stackoverflow.com/questions/22358071/differences-between-oracle-jdk-and-openjdk?answertab=active#tab-top## 基础概念与常识 +- https://stackoverflow.com/questions/22358071/differences-between-oracle-jdk-and-openjdk 基础概念与常识 diff --git "a/docs/java/basis/Java\345\270\270\350\247\201\345\205\263\351\224\256\345\255\227\346\200\273\347\273\223.md" "b/docs/java/basis/Java\345\270\270\350\247\201\345\205\263\351\224\256\345\255\227\346\200\273\347\273\223.md" index 01fcdf2c333..a37ef4b6bc1 100644 --- "a/docs/java/basis/Java\345\270\270\350\247\201\345\205\263\351\224\256\345\255\227\346\200\273\347\273\223.md" +++ "b/docs/java/basis/Java\345\270\270\350\247\201\345\205\263\351\224\256\345\255\227\346\200\273\347\273\223.md" @@ -186,7 +186,7 @@ static { 一个类中的静态代码块可以有多个,位置可以随便放,它不在任何的方法体内,JVM加载类时会执行这些静态的代码块,如果静态代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次。 -![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-14/88531075.jpg) +![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-14/88531075.jpg) 静态代码块对于定义在它之后的静态变量,可以赋值,但是不能访问. diff --git "a/docs/java/collection/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230.md" "b/docs/java/collection/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230.md" index 39bf90f7c8e..2db5c4b13c9 100644 --- "a/docs/java/collection/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230.md" +++ "b/docs/java/collection/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230.md" @@ -532,13 +532,13 @@ static int hash(int h) { ![HashTable全表锁](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/HashTable全表锁.png) -

http://www.cnblogs.com/chengxiao/p/6842045.html>

+

https://www.cnblogs.com/chengxiao/p/6842045.html>

**JDK1.7 的 ConcurrentHashMap:** ![JDK1.7的ConcurrentHashMap](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/ConcurrentHashMap分段锁.jpg) -

http://www.cnblogs.com/chengxiao/p/6842045.html>

+

https://www.cnblogs.com/chengxiao/p/6842045.html>

**JDK1.8 的 ConcurrentHashMap:** diff --git "a/docs/java/jvm/JVM\345\236\203\345\234\276\345\233\236\346\224\266.md" "b/docs/java/jvm/JVM\345\236\203\345\234\276\345\233\236\346\224\266.md" index d25667a5ab4..13af498732e 100644 --- "a/docs/java/jvm/JVM\345\236\203\345\234\276\345\233\236\346\224\266.md" +++ "b/docs/java/jvm/JVM\345\236\203\345\234\276\345\233\236\346\224\266.md" @@ -131,7 +131,7 @@ public class GCTest { 运行结果 (红色字体描述有误,应该是对应于 JDK1.7 的永久代): -![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-8-26/28954286.jpg) +![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-8-26/28954286.jpg) 从上图我们可以看出 eden 区内存几乎已经被分配完全(即使程序什么也不做,新生代也会使用 2000 多 k 内存)。假如我们再为 allocation2 分配内存会出现什么情况呢? @@ -139,7 +139,7 @@ public class GCTest { allocation2 = new byte[900*1024]; ``` -![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-8-26/28128785.jpg) +![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-8-26/28128785.jpg) **简单解释一下为什么会出现这种情况:** 因为给 allocation2 分配内存的时候 eden 区内存几乎已经被分配完了,我们刚刚讲了当 Eden 区没有足够空间进行分配时,虚拟机将发起一次 Minor GC.GC 期间虚拟机又发现 allocation1 无法存入 Survivor 空间,所以只好通过 **分配担保机制** 把新生代的对象提前转移到老年代中去,老年代上的空间足够存放 allocation1,所以不会出现 Full GC。执行 Minor GC 后,后面分配的对象如果能够存在 eden 区的话,还是会在 eden 区分配内存。可以执行如下代码验证: diff --git "a/docs/java/multi-thread/2020\346\234\200\346\226\260Java\345\271\266\345\217\221\350\277\233\351\230\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" "b/docs/java/multi-thread/2020\346\234\200\346\226\260Java\345\271\266\345\217\221\350\277\233\351\230\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" index 699f434bcab..a817d041397 100644 --- "a/docs/java/multi-thread/2020\346\234\200\346\226\260Java\345\271\266\345\217\221\350\277\233\351\230\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" +++ "b/docs/java/multi-thread/2020\346\234\200\346\226\260Java\345\271\266\345\217\221\350\277\233\351\230\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" @@ -12,7 +12,7 @@ - [1.3. 构造方法可以使用 synchronized 关键字修饰么?](#13-构造方法可以使用-synchronized-关键字修饰么) - [1.3. 讲一下 synchronized 关键字的底层原理](#13-讲一下-synchronized-关键字的底层原理) - [1.3.1. synchronized 同步语句块的情况](#131-synchronized-同步语句块的情况) - - [1.3.2. `synchronized` 修饰方法的的情况](#132-synchronized-修饰方法的的情况) + - [1.3.2. synchronized 修饰方法的的情况](#132-synchronized-修饰方法的的情况) - [1.3.3.总结](#133总结) - [1.4. 说说 JDK1.6 之后的 synchronized 关键字底层做了哪些优化,可以详细介绍一下这些优化吗](#14-说说-jdk16-之后的-synchronized-关键字底层做了哪些优化可以详细介绍一下这些优化吗) - [1.5. 谈谈 synchronized 和 ReentrantLock 的区别](#15-谈谈-synchronized-和-reentrantlock-的区别) @@ -86,7 +86,7 @@ ```java synchronized void method() { - //业务代码 + //业务代码 } ``` @@ -94,7 +94,7 @@ synchronized void method() { ```java synchronized static void method() { -//业务代码 + //业务代码 } ``` @@ -102,7 +102,7 @@ synchronized static void method() { ```java synchronized(this) { - //业务代码 + //业务代码 } ``` @@ -167,11 +167,11 @@ public class Singleton { ```java public class SynchronizedDemo { - public void method() { - synchronized (this) { - System.out.println("synchronized 代码块"); - } - } + public void method() { + synchronized (this) { + System.out.println("synchronized 代码块"); + } + } } ``` @@ -194,13 +194,13 @@ public class SynchronizedDemo { 在执行 `monitorexit` 指令后,将锁计数器设为 0,表明锁被释放。如果获取对象锁失败,那当前线程就要阻塞等待,直到锁被另外一个线程释放为止。 -#### 1.3.2. `synchronized` 修饰方法的的情况 +#### 1.3.2. synchronized 修饰方法的的情况 ```java public class SynchronizedDemo2 { - public synchronized void method() { - System.out.println("synchronized 方法"); - } + public synchronized void method() { + System.out.println("synchronized 方法"); + } } ``` @@ -277,7 +277,7 @@ JDK1.6 对锁的实现引入了大量的优化,如偏向锁、轻量级锁、 ![JMM(Java内存模型)](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/2020-8/0ac7e663-7db8-4b95-8d8e-7d2b179f67e8.png) -要解决这个问题,就需要把变量声明为**`volatile`**,这就指示 JVM,这个变量是共享且不稳定的,每次使用它都到主存中进行读取。 +要解决这个问题,就需要把变量声明为 **`volatile`** ,这就指示 JVM,这个变量是共享且不稳定的,每次使用它都到主存中进行读取。 所以,**`volatile` 关键字 除了防止 JVM 的指令重排 ,还有一个重要的作用就是保证变量的可见性。** @@ -293,7 +293,7 @@ JDK1.6 对锁的实现引入了大量的优化,如偏向锁、轻量级锁、 `synchronized` 关键字和 `volatile` 关键字是两个互补的存在,而不是对立的存在! -- **`volatile` 关键字**是线程同步的**轻量级实现**,所以**`volatile `性能肯定比` synchronized `关键字要好**。但是**`volatile` 关键字只能用于变量而 `synchronized` 关键字可以修饰方法以及代码块**。 +- **`volatile` 关键字**是线程同步的**轻量级实现**,所以 **`volatile `性能肯定比` synchronized `关键字要好** 。但是 **`volatile` 关键字只能用于变量而 `synchronized` 关键字可以修饰方法以及代码块** 。 - **`volatile` 关键字能保证数据的可见性,但不能保证数据的原子性。`synchronized` 关键字两者都能保证。** - **`volatile`关键字主要用于解决变量在多个线程之间的可见性,而 `synchronized` 关键字解决的是多个线程之间访问资源的同步性。** @@ -379,13 +379,12 @@ Thread Name= 9 formatter = yy-M-d ah:mm 上面有一段代码用到了创建 `ThreadLocal` 变量的那段代码用到了 Java8 的知识,它等于下面这段代码,如果你写了下面这段代码的话,IDEA 会提示你转换为 Java8 的格式(IDEA 真的不错!)。因为 ThreadLocal 类在 Java 8 中扩展,使用一个新的方法`withInitial()`,将 Supplier 功能接口作为参数。 ```java - private static final ThreadLocal formatter = new ThreadLocal(){ - @Override - protected SimpleDateFormat initialValue() - { - return new SimpleDateFormat("yyyyMMdd HHmm"); - } - }; +private static final ThreadLocal formatter = new ThreadLocal(){ + @Override + protected SimpleDateFormat initialValue(){ + return new SimpleDateFormat("yyyyMMdd HHmm"); + } +}; ``` ### 3.3. ThreadLocal 原理 @@ -394,13 +393,13 @@ Thread Name= 9 formatter = yy-M-d ah:mm ```java public class Thread implements Runnable { - ...... -//与此线程有关的ThreadLocal值。由ThreadLocal类维护 -ThreadLocal.ThreadLocalMap threadLocals = null; + //...... + //与此线程有关的ThreadLocal值。由ThreadLocal类维护 + ThreadLocal.ThreadLocalMap threadLocals = null; -//与此线程有关的InheritableThreadLocal值。由InheritableThreadLocal类维护 -ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; - ...... + //与此线程有关的InheritableThreadLocal值。由InheritableThreadLocal类维护 + ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; + //...... } ``` @@ -409,17 +408,17 @@ ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; `ThreadLocal`类的`set()`方法 ```java - public void set(T value) { - Thread t = Thread.currentThread(); - ThreadLocalMap map = getMap(t); - if (map != null) - map.set(this, value); - else - createMap(t, value); - } - ThreadLocalMap getMap(Thread t) { - return t.threadLocals; - } +public void set(T value) { + Thread t = Thread.currentThread(); + ThreadLocalMap map = getMap(t); + if (map != null) + map.set(this, value); + else + createMap(t, value); +} +ThreadLocalMap getMap(Thread t) { + return t.threadLocals; +} ``` 通过上面这些内容,我们足以通过猜测得出结论:**最终的变量是放在了当前线程的 `ThreadLocalMap` 中,并不是存在 `ThreadLocal` 上,`ThreadLocal` 可以理解为只是`ThreadLocalMap`的封装,传递了变量值。** `ThrealLocal` 类中可以通过`Thread.currentThread()`获取到当前线程对象后,直接通过`getMap(Thread t)`可以访问到该线程的`ThreadLocalMap`对象。 @@ -428,7 +427,7 @@ ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; ```java ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { - ...... + //...... } ``` @@ -445,15 +444,15 @@ ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { `ThreadLocalMap` 中使用的 key 为 `ThreadLocal` 的弱引用,而 value 是强引用。所以,如果 `ThreadLocal` 没有被外部强引用的情况下,在垃圾回收的时候,key 会被清理掉,而 value 不会被清理掉。这样一来,`ThreadLocalMap` 中就会出现 key 为 null 的 Entry。假如我们不做任何措施的话,value 永远无法被 GC 回收,这个时候就可能会产生内存泄露。ThreadLocalMap 实现中已经考虑了这种情况,在调用 `set()`、`get()`、`remove()` 方法的时候,会清理掉 key 为 null 的记录。使用完 `ThreadLocal`方法后 最好手动调用`remove()`方法 ```java - static class Entry extends WeakReference> { - /** The value associated with this ThreadLocal. */ - Object value; +static class Entry extends WeakReference> { + /** The value associated with this ThreadLocal. */ + Object value; - Entry(ThreadLocal k, Object v) { - super(k); - value = v; - } - } + Entry(ThreadLocal k, Object v) { + super(k); + value = v; + } +} ``` **弱引用介绍:** @@ -478,7 +477,7 @@ ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { ### 4.2. 实现 Runnable 接口和 Callable 接口的区别 -`Runnable`自 Java 1.0 以来一直存在,但`Callable`仅在 Java 1.5 中引入,目的就是为了来处理`Runnable`不支持的用例。**`Runnable` 接口**不会返回结果或抛出检查异常,但是**`Callable` 接口**可以。所以,如果任务不需要返回结果或抛出异常推荐使用 **`Runnable` 接口**,这样代码看起来会更加简洁。 +`Runnable`自 Java 1.0 以来一直存在,但`Callable`仅在 Java 1.5 中引入,目的就是为了来处理`Runnable`不支持的用例。**`Runnable` 接口** 不会返回结果或抛出检查异常,但是 **`Callable` 接口** 可以。所以,如果任务不需要返回结果或抛出异常推荐使用 **`Runnable` 接口** ,这样代码看起来会更加简洁。 工具类 `Executors` 可以实现 `Runnable` 对象和 `Callable` 对象之间的相互转换。(`Executors.callable(Runnable task`)或 `Executors.callable(Runnable task,Object resule)`)。 @@ -513,31 +512,31 @@ public interface Callable { 1. **`execute()`方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;** 2. **`submit()`方法用于提交需要返回值的任务。线程池会返回一个 `Future` 类型的对象,通过这个 `Future` 对象可以判断任务是否执行成功**,并且可以通过 `Future` 的 `get()`方法来获取返回值,`get()`方法会阻塞当前线程直到任务完成,而使用 `get(long timeout,TimeUnit unit)`方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。 -我们以**`AbstractExecutorService`**接口中的一个 `submit` 方法为例子来看看源代码: +我们以** `AbstractExecutorService` **接口中的一个 `submit` 方法为例子来看看源代码: ```java - public Future submit(Runnable task) { - if (task == null) throw new NullPointerException(); - RunnableFuture ftask = newTaskFor(task, null); - execute(ftask); - return ftask; - } +public Future submit(Runnable task) { + if (task == null) throw new NullPointerException(); + RunnableFuture ftask = newTaskFor(task, null); + execute(ftask); + return ftask; +} ``` 上面方法调用的 `newTaskFor` 方法返回了一个 `FutureTask` 对象。 ```java - protected RunnableFuture newTaskFor(Runnable runnable, T value) { - return new FutureTask(runnable, value); - } +protected RunnableFuture newTaskFor(Runnable runnable, T value) { + return new FutureTask(runnable, value); +} ``` 我们再来看看`execute()`方法: ```java - public void execute(Runnable command) { - ... - } +public void execute(Runnable command) { + ... +} ``` ### 4.4. 如何创建线程池 @@ -550,8 +549,11 @@ public interface Callable { > - **CachedThreadPool 和 ScheduledThreadPool** : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致 OOM。 **方式一:通过构造方法实现** + ![ThreadPoolExecutor构造方法](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/ThreadPoolExecutor构造方法.png) + **方式二:通过 Executor 框架的工具类 Executors 来实现** + 我们可以创建三种类型的 ThreadPoolExecutor: - **FixedThreadPool** : 该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。 @@ -559,6 +561,7 @@ public interface Callable { - **CachedThreadPool:** 该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。 对应 Executors 工具类中的方法如图所示: + ![Executor框架的工具类](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-6/Executor框架的工具类.png) ### 4.5 ThreadPoolExecutor 类分析 @@ -566,30 +569,30 @@ public interface Callable { `ThreadPoolExecutor` 类中提供的四个构造方法。我们来看最长的那个,其余三个都是在这个构造方法的基础上产生(其他几个构造方法说白点都是给定某些默认参数的构造方法比如默认制定拒绝策略是什么),这里就不贴代码讲了,比较简单。 ```java - /** - * 用给定的初始参数创建一个新的ThreadPoolExecutor。 - */ - public ThreadPoolExecutor(int corePoolSize, - int maximumPoolSize, - long keepAliveTime, - TimeUnit unit, - BlockingQueue workQueue, - ThreadFactory threadFactory, - RejectedExecutionHandler handler) { - if (corePoolSize < 0 || - maximumPoolSize <= 0 || - maximumPoolSize < corePoolSize || - keepAliveTime < 0) +/** + * 用给定的初始参数创建一个新的ThreadPoolExecutor。 + */ +public ThreadPoolExecutor(int corePoolSize, + int maximumPoolSize, + long keepAliveTime, + TimeUnit unit, + BlockingQueue workQueue, + ThreadFactory threadFactory, + RejectedExecutionHandler handler) { + if (corePoolSize < 0 || + maximumPoolSize <= 0 || + maximumPoolSize < corePoolSize || + keepAliveTime < 0) throw new IllegalArgumentException(); - if (workQueue == null || threadFactory == null || handler == null) - throw new NullPointerException(); - this.corePoolSize = corePoolSize; - this.maximumPoolSize = maximumPoolSize; - this.workQueue = workQueue; - this.keepAliveTime = unit.toNanos(keepAliveTime); - this.threadFactory = threadFactory; - this.handler = handler; - } + if (workQueue == null || threadFactory == null || handler == null) + throw new NullPointerException(); + this.corePoolSize = corePoolSize; + this.maximumPoolSize = maximumPoolSize; + this.workQueue = workQueue; + this.keepAliveTime = unit.toNanos(keepAliveTime); + this.threadFactory = threadFactory; + this.handler = handler; +} ``` **下面这些对创建 非常重要,在后面使用线程池的过程中你一定会用到!所以,务必拿着小本本记清楚。** @@ -615,8 +618,9 @@ public interface Callable { 如果当前同时运行的线程数量达到最大线程数量并且队列也已经被放满了任时,`ThreadPoolTaskExecutor` 定义一些策略: -- **`ThreadPoolExecutor.AbortPolicy`**:抛出 `RejectedExecutionException`来拒绝新任务的处理。 -- **`ThreadPoolExecutor.CallerRunsPolicy`**:调用执行自己的线程运行任务,也就是直接在调用`execute`方法的线程中运行(`run`)被拒绝的任务,如果执行程序已关闭,则会丢弃该任务。因此这种策略会降低对于新任务提交速度,影响程序的整体性能。如果您的应用程序可以承受此延迟并且你要求任何一个任务请求都要被执行的话,你可以选择这个策略。 +- **`ThreadPoolExecutor.AbortPolicy`:** 抛出 `RejectedExecutionException`来拒绝新任务的处理。 +- **`ThreadPoolExecutor.CallerRunsPolicy`:** +调用执行自己的线程运行任务,也就是直接在调用`execute`方法的线程中运行(`run`)被拒绝的任务,如果执行程序已关闭,则会丢弃该任务。因此这种策略会降低对于新任务提交速度,影响程序的整体性能。如果您的应用程序可以承受此延迟并且你要求任何一个任务请求都要被执行的话,你可以选择这个策略。 - **`ThreadPoolExecutor.DiscardPolicy`:** 不处理新任务,直接丢弃掉。 - **`ThreadPoolExecutor.DiscardOldestPolicy`:** 此策略将丢弃最早的未处理的任务请求。 @@ -708,7 +712,6 @@ public class ThreadPoolExecutorDemo { System.out.println("Finished all threads"); } } - ``` 可以看到我们上面的代码指定了: @@ -752,49 +755,49 @@ pool-1-thread-1 End. Time = Tue Nov 12 20:59:54 CST 2019 现在,我们就分析上面的输出内容来简单分析一下线程池原理。 -**为了搞懂线程池的原理,我们需要首先分析一下 `execute`方法。**在 4.6 节中的 Demo 中我们使用 `executor.execute(worker)`来提交一个任务到线程池中去,这个方法非常重要,下面我们来看看它的源码: + **为了搞懂线程池的原理,我们需要首先分析一下 `execute`方法。** 在 4.6 节中的 Demo 中我们使用 `executor.execute(worker)`来提交一个任务到线程池中去,这个方法非常重要,下面我们来看看它的源码: ```java - // 存放线程池的运行状态 (runState) 和线程池内有效线程的数量 (workerCount) - private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); +// 存放线程池的运行状态 (runState) 和线程池内有效线程的数量 (workerCount) +private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); - private static int workerCountOf(int c) { - return c & CAPACITY; - } +private static int workerCountOf(int c) { + return c & CAPACITY; +} - private final BlockingQueue workQueue; - - public void execute(Runnable command) { - // 如果任务为null,则抛出异常。 - if (command == null) - throw new NullPointerException(); - // ctl 中保存的线程池当前的一些状态信息 - int c = ctl.get(); - - // 下面会涉及到 3 步 操作 - // 1.首先判断当前线程池中执行的任务数量是否小于 corePoolSize - // 如果小于的话,通过addWorker(command, true)新建一个线程,并将任务(command)添加到该线程中;然后,启动该线程从而执行任务。 - if (workerCountOf(c) < corePoolSize) { - if (addWorker(command, true)) - return; - c = ctl.get(); - } - // 2.如果当前执行的任务数量大于等于 corePoolSize 的时候就会走到这里 - // 通过 isRunning 方法判断线程池状态,线程池处于 RUNNING 状态才会被并且队列可以加入任务,该任务才会被加入进去 - if (isRunning(c) && workQueue.offer(command)) { - int recheck = ctl.get(); - // 再次获取线程池状态,如果线程池状态不是 RUNNING 状态就需要从任务队列中移除任务,并尝试判断线程是否全部执行完毕。同时执行拒绝策略。 - if (!isRunning(recheck) && remove(command)) - reject(command); - // 如果当前线程池为空就新创建一个线程并执行。 - else if (workerCountOf(recheck) == 0) - addWorker(null, false); - } - //3. 通过addWorker(command, false)新建一个线程,并将任务(command)添加到该线程中;然后,启动该线程从而执行任务。 - //如果addWorker(command, false)执行失败,则通过reject()执行相应的拒绝策略的内容。 - else if (!addWorker(command, false)) +private final BlockingQueue workQueue; + +public void execute(Runnable command) { + // 如果任务为null,则抛出异常。 + if (command == null) + throw new NullPointerException(); + // ctl 中保存的线程池当前的一些状态信息 + int c = ctl.get(); + + // 下面会涉及到 3 步 操作 + // 1.首先判断当前线程池中执行的任务数量是否小于 corePoolSize + // 如果小于的话,通过addWorker(command, true)新建一个线程,并将任务(command)添加到该线程中;然后,启动该线程从而执行任务。 + if (workerCountOf(c) < corePoolSize) { + if (addWorker(command, true)) + return; + c = ctl.get(); + } + // 2.如果当前执行的任务数量大于等于 corePoolSize 的时候就会走到这里 + // 通过 isRunning 方法判断线程池状态,线程池处于 RUNNING 状态才会被并且队列可以加入任务,该任务才会被加入进去 + if (isRunning(c) && workQueue.offer(command)) { + int recheck = ctl.get(); + // 再次获取线程池状态,如果线程池状态不是 RUNNING 状态就需要从任务队列中移除任务,并尝试判断线程是否全部执行完毕。同时执行拒绝策略。 + if (!isRunning(recheck) && remove(command)) reject(command); + // 如果当前线程池为空就新创建一个线程并执行。 + else if (workerCountOf(recheck) == 0) + addWorker(null, false); } + //3. 通过addWorker(command, false)新建一个线程,并将任务(command)添加到该线程中;然后,启动该线程从而执行任务。 + //如果addWorker(command, false)执行失败,则通过reject()执行相应的拒绝策略的内容。 + else if (!addWorker(command, false)) + reject(command); +} ``` 通过下图可以更好的对上面这 3 步做一个展示,下图是我为了省事直接从网上找到,原地址不明。 @@ -869,15 +872,15 @@ public final void lazySet(int newValue)//最终设置为newValue,使用 lazySet ```java class AtomicIntegerTest { - private AtomicInteger count = new AtomicInteger(); - //使用AtomicInteger之后,不需要对该方法加锁,也可以实现线程安全。 - public void increment() { - count.incrementAndGet(); - } + private AtomicInteger count = new AtomicInteger(); + //使用AtomicInteger之后,不需要对该方法加锁,也可以实现线程安全。 + public void increment() { + count.incrementAndGet(); + } - public int getCount() { - return count.get(); - } + public int getCount() { + return count.get(); + } } ``` @@ -889,18 +892,18 @@ AtomicInteger 线程安全原理简单分析 AtomicInteger 类的部分源码: ```java - // setup to use Unsafe.compareAndSwapInt for updates(更新操作时提供“比较并替换”的作用) - private static final Unsafe unsafe = Unsafe.getUnsafe(); - private static final long valueOffset; - - static { - try { - valueOffset = unsafe.objectFieldOffset - (AtomicInteger.class.getDeclaredField("value")); - } catch (Exception ex) { throw new Error(ex); } - } +// setup to use Unsafe.compareAndSwapInt for updates(更新操作时提供“比较并替换”的作用) +private static final Unsafe unsafe = Unsafe.getUnsafe(); +private static final long valueOffset; + +static { + try { + valueOffset = unsafe.objectFieldOffset + (AtomicInteger.class.getDeclaredField("value")); + } catch (Exception ex) { throw new Error(ex); } +} - private volatile int value; +private volatile int value; ``` AtomicInteger 类主要利用 CAS (compare and swap) + volatile 和 native 方法来保证原子操作,从而避免 synchronized 的高开销,执行效率大为提升。 @@ -949,15 +952,15 @@ private volatile int state;//共享变量,使用volatile修饰保证线程可 //返回同步状态的当前值 protected final int getState() { - return state; + return state; } - // 设置同步状态的值 +//设置同步状态的值 protected final void setState(int newState) { - state = newState; + state = newState; } //原子地(CAS操作)将同步状态值设置为给定值update如果当前同步状态的值等于expect(期望值) protected final boolean compareAndSetState(int expect, int update) { - return unsafe.compareAndSwapInt(this, stateOffset, expect, update); + return unsafe.compareAndSwapInt(this, stateOffset, expect, update); } ``` @@ -1004,7 +1007,7 @@ tryReleaseShared(int)//共享方式。尝试释放资源,成功则返回true 推荐两篇 AQS 原理和相关源码分析的文章: -- http://www.cnblogs.com/waterystone/p/4920797.html +- https://www.cnblogs.com/waterystone/p/4920797.html - https://www.cnblogs.com/chengxiao/archive/2017/07/24/7141160.html ### 6.3. AQS 组件总结 @@ -1025,33 +1028,32 @@ tryReleaseShared(int)//共享方式。尝试释放资源,成功则返回true ```java public class CountDownLatchExample1 { - // 处理文件的数量 - private static final int threadCount = 6; - - public static void main(String[] args) throws InterruptedException { - // 创建一个具有固定线程数量的线程池对象(推荐使用构造方法创建) - ExecutorService threadPool = Executors.newFixedThreadPool(10); - final CountDownLatch countDownLatch = new CountDownLatch(threadCount); - for (int i = 0; i < threadCount; i++) { - final int threadnum = i; - threadPool.execute(() -> { - try { - //处理文件的业务操作 - ...... - } catch (InterruptedException e) { - e.printStackTrace(); - } finally { - //表示一个文件已经被完成 - countDownLatch.countDown(); - } + // 处理文件的数量 + private static final int threadCount = 6; - }); - } - countDownLatch.await(); - threadPool.shutdown(); - System.out.println("finish"); - } + public static void main(String[] args) throws InterruptedException { + // 创建一个具有固定线程数量的线程池对象(推荐使用构造方法创建) + ExecutorService threadPool = Executors.newFixedThreadPool(10); + final CountDownLatch countDownLatch = new CountDownLatch(threadCount); + for (int i = 0; i < threadCount; i++) { + final int threadnum = i; + threadPool.execute(() -> { + try { + //处理文件的业务操作 + //...... + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + //表示一个文件已经被完成 + countDownLatch.countDown(); + } + }); + } + countDownLatch.await(); + threadPool.shutdown(); + System.out.println("finish"); + } } ``` @@ -1061,22 +1063,22 @@ public class CountDownLatchExample1 { ```java CompletableFuture task1 = - CompletableFuture.supplyAsync(()->{ - //自定义业务操作 - }); + CompletableFuture.supplyAsync(()->{ + //自定义业务操作 + }); ...... CompletableFuture task6 = - CompletableFuture.supplyAsync(()->{ + CompletableFuture.supplyAsync(()->{ //自定义业务操作 - }); + }); ...... - CompletableFuture headerFuture=CompletableFuture.allOf(task1,.....,task6); +CompletableFuture headerFuture=CompletableFuture.allOf(task1,.....,task6); - try { +try { headerFuture.join(); - } catch (Exception ex) { - ...... - } +} catch (Exception ex) { + //...... +} System.out.println("all done. "); ``` @@ -1087,11 +1089,11 @@ System.out.println("all done. "); List filePaths = Arrays.asList(...) // 异步处理所有文件 List> fileFutures = filePaths.stream() - .map(filePath -> doSomeThing(filePath)) - .collect(Collectors.toList()); + .map(filePath -> doSomeThing(filePath)) + .collect(Collectors.toList()); // 将他们合并起来 CompletableFuture allFutures = CompletableFuture.allOf( - fileFutures.toArray(new CompletableFuture[fileFutures.size()]) + fileFutures.toArray(new CompletableFuture[fileFutures.size()]) ); ``` @@ -1101,7 +1103,7 @@ CompletableFuture allFutures = CompletableFuture.allOf( - 《深入理解 Java 虚拟机》 - 《实战 Java 高并发程序设计》 - 《Java 并发编程的艺术》 -- http://www.cnblogs.com/waterystone/p/4920797.html +- https://www.cnblogs.com/waterystone/p/4920797.html - https://www.cnblogs.com/chengxiao/archive/2017/07/24/7141160.html - diff --git "a/docs/java/multi-thread/AQS\345\216\237\347\220\206\344\273\245\345\217\212AQS\345\220\214\346\255\245\347\273\204\344\273\266\346\200\273\347\273\223.md" "b/docs/java/multi-thread/AQS\345\216\237\347\220\206\344\273\245\345\217\212AQS\345\220\214\346\255\245\347\273\204\344\273\266\346\200\273\347\273\223.md" index 832cbbc9193..936809f5d2f 100644 --- "a/docs/java/multi-thread/AQS\345\216\237\347\220\206\344\273\245\345\217\212AQS\345\220\214\346\255\245\347\273\204\344\273\266\346\200\273\347\273\223.md" +++ "b/docs/java/multi-thread/AQS\345\216\237\347\220\206\344\273\245\345\217\212AQS\345\220\214\346\255\245\347\273\204\344\273\266\346\200\273\347\273\223.md" @@ -233,7 +233,7 @@ tryReleaseShared(int)//共享方式。尝试释放资源,成功则返回true 推荐两篇 AQS 原理和相关源码分析的文章: -- http://www.cnblogs.com/waterystone/p/4920797.html +- https://www.cnblogs.com/waterystone/p/4920797.html - https://www.cnblogs.com/chengxiao/archive/2017/07/24/7141160.html ### 3 Semaphore(信号量)-允许多个线程同时访问 diff --git "a/docs/java/multi-thread/\345\271\266\345\217\221\345\256\271\345\231\250\346\200\273\347\273\223.md" "b/docs/java/multi-thread/\345\271\266\345\217\221\345\256\271\345\231\250\346\200\273\347\273\223.md" index ed606342d2c..fcc900e6ea6 100644 --- "a/docs/java/multi-thread/\345\271\266\345\217\221\345\256\271\345\231\250\346\200\273\347\273\223.md" +++ "b/docs/java/multi-thread/\345\271\266\345\217\221\345\256\271\345\231\250\346\200\273\347\273\223.md" @@ -129,7 +129,7 @@ ConcurrentLinkedQueue 适合在对性能要求相对较高,同时对队列的 BlockingQueue 是一个接口,继承自 Queue,所以其实现类也可以作为 Queue 的实现来使用,而 Queue 又继承自 Collection 接口。下面是 BlockingQueue 的相关实现类: -![BlockingQueue 的实现类](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-9/51622268.jpg) +![BlockingQueue 的实现类](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-9/51622268.jpg) **下面主要介绍一下:ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue,这三个 BlockingQueue 的实现类。** @@ -198,13 +198,13 @@ PriorityBlockingQueue 并发控制采用的是 **ReentrantLock**,队列为无 跳表的本质是同时维护了多个链表,并且链表是分层的, -![2级索引跳表](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-9/93666217.jpg) +![2级索引跳表](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-9/93666217.jpg) 最低层的链表维护了跳表内所有的元素,每上面一层链表都是下面一层的子集。 跳表内的所有链表的元素都是排序的。查找时,可以从顶级链表开始找。一旦发现被查找的元素大于当前链表中的取值,就会转入下一层链表继续找。这也就是说在查找过程中,搜索是跳跃式的。如上图所示,在跳表中查找元素 18。 -![在跳表中查找元素18](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-9/32005738.jpg) +![在跳表中查找元素18](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-9/32005738.jpg) 查找 18 的时候原来需要遍历 18 次,现在只需要 7 次即可。针对链表长度比较大的时候,构建索引查找效率的提升就会非常明显。 diff --git "a/docs/java/new-features/images/\344\270\200\346\226\207\345\270\246\344\275\240\347\234\213\351\201\215JDK9~14 \347\232\204\351\207\215\350\246\201\346\226\260\347\211\271\346\200\247/java\347\211\210\346\234\254\345\217\221\345\270\203.png" "b/docs/java/new-features/images/\344\270\200\346\226\207\345\270\246\344\275\240\347\234\213\351\201\215JDK9~14 \347\232\204\351\207\215\350\246\201\346\226\260\347\211\271\346\200\247/java\347\211\210\346\234\254\345\217\221\345\270\203.png" deleted file mode 100644 index 7529c73fb98..00000000000 Binary files "a/docs/java/new-features/images/\344\270\200\346\226\207\345\270\246\344\275\240\347\234\213\351\201\215JDK9~14 \347\232\204\351\207\215\350\246\201\346\226\260\347\211\271\346\200\247/java\347\211\210\346\234\254\345\217\221\345\270\203.png" and /dev/null differ diff --git "a/docs/java/new-features/\344\270\200\346\226\207\345\270\246\344\275\240\347\234\213\351\201\215JDK9\345\210\26014\347\232\204\351\207\215\350\246\201\346\226\260\347\211\271\346\200\247.md" "b/docs/java/new-features/\344\270\200\346\226\207\345\270\246\344\275\240\347\234\213\351\201\215JDK9\345\210\26014\347\232\204\351\207\215\350\246\201\346\226\260\347\211\271\346\200\247.md" index 64d70b3abde..2d56e512255 100644 --- "a/docs/java/new-features/\344\270\200\346\226\207\345\270\246\344\275\240\347\234\213\351\201\215JDK9\345\210\26014\347\232\204\351\207\215\350\246\201\346\226\260\347\211\271\346\200\247.md" +++ "b/docs/java/new-features/\344\270\200\346\226\207\345\270\246\344\275\240\347\234\213\351\201\215JDK9\345\210\26014\347\232\204\351\207\215\350\246\201\346\226\260\347\211\271\346\200\247.md" @@ -52,7 +52,7 @@ Java 9 允许为 JDK 和应用配置同样的日志实现。新增了 `System.Lo ### 其它新特性 - **接口私有方法** :Java 9 允许在接口中使用私有方法 -- **try-with-resources 增强** :在 try-with-resources 语句中可以使用 effectively-final 变量(什么是 effectively-final 变量,见这篇文章 [http://ilkinulas.github.io/programming/java/2016/03/27/effectively-final-java.html](http://ilkinulas.github.io/programming/java/2016/03/27/effectively-final-java.html)) +- **try-with-resources 增强** :在 try-with-resources 语句中可以使用 effectively-final 变量(什么是 effectively-final 变量,见这篇文章 [https://ilkinulas.github.io/programming/java/2016/03/27/effectively-final-java.html](https://ilkinulas.github.io/programming/java/2016/03/27/effectively-final-java.html)) - **类 `CompletableFuture` 中增加了几个新的方法(`completeAsync` ,`orTimeout` 等)** - **Nashorn 引擎的增强** :Nashorn 从 Java8 开始引入的 JavaScript 引擎,Java9 对 Nashorn 做了些增强,实现了一些 ES6 的新特性 - **I/O 流的新特性** :增加了新的方法来读取和复制 InputStream 中包含的数据 @@ -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/一文带你看遍JDK9~14 的重要新特性/java版本发布.png) +![](https://img-blog.csdnimg.cn/20210603202746605.png) ### 字符串加强 @@ -280,7 +280,7 @@ client.sendAsync(request, HttpResponse.BodyHandlers.ofString()) ``` ```java - + String json = """ { "name":"mkyong", @@ -323,12 +323,12 @@ client.sendAsync(request, HttpResponse.BodyHandlers.ofString()) final class Rectangle implements Shape { final double length; final double width; - + public Rectangle(double length, double width) { this.length = length; this.width = width; } - + double length() { return length; } double width() { return width; } } diff --git a/docs/operating-system/Shell.md b/docs/operating-system/Shell.md index c88ebdd6939..53466d1113a 100644 --- a/docs/operating-system/Shell.md +++ b/docs/operating-system/Shell.md @@ -45,7 +45,7 @@ 另外,了解 shell 编程也是大部分互联网公司招聘后端开发人员的要求。下图是我截取的一些知名互联网公司对于 Shell 编程的要求。 -![大型互联网公司对于shell编程技能的要求](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-11-16/60190220.jpg) +![大型互联网公司对于shell编程技能的要求](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-11-16/60190220.jpg) ### 什么是 Shell? @@ -53,7 +53,7 @@ W3Cschool 上的一篇文章是这样介绍 Shell的,如下图所示。 -![什么是 Shell?](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-11-26/19456505.jpg) +![什么是 Shell?](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-11-26/19456505.jpg) ### Shell 编程的 Hello World @@ -80,7 +80,7 @@ shell中 # 符号表示注释。**shell 的第一行比较特殊,一般都会 (4) 运行脚本:`./helloworld.sh` 。(注意,一定要写成 `./helloworld.sh` ,而不是 `helloworld.sh` ,运行其它二进制的程序也一样,直接写 `helloworld.sh` ,linux 系统会去 PATH 里寻找有没有叫 helloworld.sh 的,而只有 /bin, /sbin, /usr/bin,/usr/sbin 等在 PATH 里,你的当前目录通常不在 PATH 里,所以写成 `helloworld.sh` 是会找不到命令的,要用`./helloworld.sh` 告诉系统说,就在当前目录找。) -![shell 编程Hello World](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-11-16/55296212.jpg) +![shell 编程Hello World](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-11-16/55296212.jpg) ## Shell 变量 @@ -118,7 +118,7 @@ hello="hello world" echo $hello echo "helloworld!" ``` -![使用自己定义的变量](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-11-17/19835037.jpg) +![使用自己定义的变量](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-11-17/19835037.jpg) **Shell 编程中的变量名的命名的注意事项:** @@ -183,7 +183,7 @@ echo $greeting_2 $greeting_3 输出结果: -![输出结果](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-11-17/51148933.jpg) +![输出结果](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-11-17/51148933.jpg) **获取字符串长度:** @@ -234,10 +234,10 @@ echo ${str:0:10} #输出:SnailClimb #!bin/bash #author:amau -var="http://www.runoob.com/linux/linux-shell-variable.html" +var="https://www.runoob.com/linux/linux-shell-variable.html" s1=${var%%t*}#h -s2=${var%t*}#http://www.runoob.com/linux/linux-shell-variable.h +s2=${var%t*}#https://www.runoob.com/linux/linux-shell-variable.h s3=${var%%.*}#http://www s4=${var#*/}#/www.runoob.com/linux/linux-shell-variable.html s5=${var##*/}#linux-shell-variable.html @@ -281,7 +281,7 @@ for i in ${array[@]};do echo $i ;done # 遍历数组,数组元素为空,没 ### 算数运算符 -![算数运算符](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-11-22/4937342.jpg) +![算数运算符](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-11-22/4937342.jpg) 我以加法运算符做一个简单的示例(注意:不是单引号,是反引号): @@ -298,7 +298,7 @@ echo "Total value : $val" 关系运算符只支持数字,不支持字符串,除非字符串的值是数字。 -![shell关系运算符](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-11-22/64391380.jpg) +![shell关系运算符](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-11-22/64391380.jpg) 通过一个简单的示例演示关系运算符的使用,下面shell程序的作用是当score=100的时候输出A否则输出B。 @@ -322,7 +322,7 @@ B ### 逻辑运算符 -![逻辑运算符](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-11-22/60545848.jpg) +![逻辑运算符](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-11-22/60545848.jpg) 示例: @@ -336,13 +336,13 @@ echo $a; ### 布尔运算符 -![布尔运算符](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-11-22/93961425.jpg) +![布尔运算符](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-11-22/93961425.jpg) 这里就不做演示了,应该挺简单的。 ### 字符串运算符 -![ 字符串运算符](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-11-22/309094.jpg) +![ 字符串运算符](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-11-22/309094.jpg) 简单示例: @@ -366,7 +366,7 @@ a 不等于 b ### 文件相关运算符 -![文件相关运算符](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-11-22/60359774.jpg) +![文件相关运算符](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-11-22/60359774.jpg) 使用方式很简单,比如我们定义好了一个文件路径`file="/usr/learnshell/test.sh"` 如果我们想判断这个文件是否可读,可以这样`if [ -r $file ]` 如果想判断这个文件是否可写,可以这样`-w $file`,是不是很简单。 diff --git "a/docs/system-design/authority-certification/SSO\345\215\225\347\202\271\347\231\273\345\275\225\347\234\213\350\277\231\344\270\200\347\257\207\345\260\261\345\244\237\344\272\206.md" "b/docs/system-design/authority-certification/SSO\345\215\225\347\202\271\347\231\273\345\275\225\347\234\213\350\277\231\344\270\200\347\257\207\345\260\261\345\244\237\344\272\206.md" index a2042c61913..7dab034ea54 100644 --- "a/docs/system-design/authority-certification/SSO\345\215\225\347\202\271\347\231\273\345\275\225\347\234\213\350\277\231\344\270\200\347\257\207\345\260\261\345\244\237\344\272\206.md" +++ "b/docs/system-design/authority-certification/SSO\345\215\225\347\202\271\347\231\273\345\275\225\347\234\213\350\277\231\344\270\200\347\257\207\345\260\261\345\244\237\344\272\206.md" @@ -10,12 +10,12 @@ SSO英文全称Single Sign On,单点登录。SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。https://baike.baidu.com/item/SSO/3451380 -例如访问在网易账号中心(http://reg.163.com/ )登录之后 +例如访问在网易账号中心(https://reg.163.com/ )登录之后 访问以下站点都是登录状态 -- 网易直播 [http://v.163.com](http://v.163.com/) -- 网易博客 [http://blog.163.com](http://blog.163.com/) -- 网易花田 [http://love.163.com](http://love.163.com/) +- 网易直播 [https://v.163.com](https://v.163.com/) +- 网易博客 [https://blog.163.com](https://blog.163.com/) +- 网易花田 [https://love.163.com](https://love.163.com/) - 网易考拉 [https://www.kaola.com](https://www.kaola.com/) - 网易Lofter [http://www.lofter.com](http://www.lofter.com/) diff --git "a/docs/system-design/distributed-system/message-queue/RabbitMQ\345\205\245\351\227\250\347\234\213\350\277\231\344\270\200\347\257\207\345\260\261\345\244\237\344\272\206.md" "b/docs/system-design/distributed-system/message-queue/RabbitMQ\345\205\245\351\227\250\347\234\213\350\277\231\344\270\200\347\257\207\345\260\261\345\244\237\344\272\206.md" index 83a1ed80525..fb6beae75bf 100644 --- "a/docs/system-design/distributed-system/message-queue/RabbitMQ\345\205\245\351\227\250\347\234\213\350\277\231\344\270\200\347\257\207\345\260\261\345\244\237\344\272\206.md" +++ "b/docs/system-design/distributed-system/message-queue/RabbitMQ\345\205\245\351\227\250\347\234\213\350\277\231\344\270\200\347\257\207\345\260\261\345\244\237\344\272\206.md" @@ -46,7 +46,7 @@ RabbitMQ 整体上是一个生产者与消费者模型,主要负责接收、 下面再来看看图1—— RabbitMQ 的整体模型架构。 -![图1-RabbitMQ 的整体模型架构](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-16/96388546.jpg) +![图1-RabbitMQ 的整体模型架构](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-16/96388546.jpg) 下面我会一一介绍上图中的一些概念。 @@ -67,7 +67,7 @@ RabbitMQ 整体上是一个生产者与消费者模型,主要负责接收、 Exchange(交换器) 示意图如下: -![Exchange(交换器) 示意图](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-16/24007899.jpg) +![Exchange(交换器) 示意图](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-16/24007899.jpg) 生产者将消息发给交换器的时候,一般会指定一个 **RoutingKey(路由键)**,用来指定这个消息的路由规则,而这个 **RoutingKey 需要与交换器类型和绑定键(BindingKey)联合使用才能最终生效**。 @@ -75,7 +75,7 @@ RabbitMQ 中通过 **Binding(绑定)** 将 **Exchange(交换器)** 与 **Queue( Binding(绑定) 示意图: -![Binding(绑定) 示意图](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-16/70553134.jpg) +![Binding(绑定) 示意图](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-16/70553134.jpg) 生产者将消息发送给交换器时,需要一个RoutingKey,当 BindingKey 和 RoutingKey 相匹配时,消息会被路由到对应的队列中。在绑定多个队列到同一个交换器的时候,这些绑定允许使用相同的 BindingKey。BindingKey 并不是在所有的情况下都生效,它依赖于交换器类型,比如fanout类型的交换器就会无视,而是将消息路由到所有绑定到该交换器的队列中。 @@ -95,7 +95,7 @@ Binding(绑定) 示意图: 下图展示了生产者将消息存入 RabbitMQ Broker,以及消费者从Broker中消费数据的整个流程。 -![消息队列的运转过程](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-16/67952922.jpg) +![消息队列的运转过程](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-16/67952922.jpg) 这样图1中的一些关于 RabbitMQ 的基本概念我们就介绍完毕了,下面再来介绍一下 **Exchange Types(交换器类型)** 。 @@ -111,7 +111,7 @@ fanout 类型的Exchange路由规则非常简单,它会把所有发送到该Ex direct 类型的Exchange路由规则也很简单,它会把消息路由到那些 Bindingkey 与 RoutingKey 完全匹配的 Queue 中。 -![direct 类型交换器](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-16/37008021.jpg) +![direct 类型交换器](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-16/37008021.jpg) 以上图为例,如果发送消息的时候设置路由键为“warning”,那么消息会路由到 Queue1 和 Queue2。如果在发送消息的时候设置路由键为"Info”或者"debug”,消息只会路由到Queue2。如果以其他的路由键发送消息,则消息不会路由到这两个队列中。 @@ -125,7 +125,7 @@ direct 类型常用在处理有优先级的任务,根据任务的优先级把 - BindingKey 和 RoutingKey 一样也是点号“.”分隔的字符串; - BindingKey 中可以存在两种特殊字符串“\*”和“#”,用于做模糊匹配,其中“\*”用于匹配一个单词,“#”用于匹配多个单词(可以是零个)。 -![topic 类型交换器](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-16/73843.jpg) +![topic 类型交换器](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-16/73843.jpg) 以上图为例: @@ -156,10 +156,10 @@ headers 类型的交换器不依赖于路由键的匹配规则来路由消息, 在官网下载然后上传到 Linux 上或者直接使用下面的命令下载对应的版本。 ```shell -[root@SnailClimb local]#wget http://erlang.org/download/otp_src_19.3.tar.gz +[root@SnailClimb local]#wget https://erlang.org/download/otp_src_19.3.tar.gz ``` -erlang 官网下载:[http://www.erlang.org/downloads](http://www.erlang.org/downloads) +erlang 官网下载:[https://www.erlang.org/downloads](https://www.erlang.org/downloads) **2 解压 erlang 安装包** @@ -211,7 +211,7 @@ make && make install ```erlang io:format("hello world~n", []). ``` -![输出“hello world”](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-12/49570541.jpg) +![输出“hello world”](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-12/49570541.jpg) 大功告成,我们的 erlang 已经安装完成。 @@ -242,7 +242,7 @@ export ERL_HOME PATH [root@SnailClimb etc]# erl ``` -![输入 erl 查看 erlang 环境变量是否配置正确](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-12/62504246.jpg) +![输入 erl 查看 erlang 环境变量是否配置正确](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-12/62504246.jpg) ### 2.2 安装 RabbitMQ @@ -319,6 +319,6 @@ Setting permissions for user "root" in vhost "/" ... 再次访问:http://你的ip地址:15672/ ,输入用户名和密码:root root -![RabbitMQ控制台](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-12/45835332.jpg) +![RabbitMQ控制台](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-12/45835332.jpg) diff --git a/docs/system-design/distributed-system/rpc/Dubbo.md b/docs/system-design/distributed-system/rpc/Dubbo.md index 135ab53e751..452c7f1b2f0 100644 --- a/docs/system-design/distributed-system/rpc/Dubbo.md +++ b/docs/system-design/distributed-system/rpc/Dubbo.md @@ -30,7 +30,7 @@ 具体原理图如下,后面我会串起来将整个RPC的过程给大家说一下。 -![RPC原理图](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-6/37345851.jpg) +![RPC原理图](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-6/37345851.jpg) 1. 服务消费端(client)以本地调用的方式调用远程服务; 1. 客户端 Stub(client stub) 接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体(序列化):`RpcRequest`; @@ -90,7 +90,7 @@ Dubbo 是由阿里开源,后来加入了 Apache 。正式由于 Dubbo 的出 3. **服务访问压力以及时长统计、资源调度和治理** :基于访问压力实时管理集群容量,提高集群利用率。 4. ...... -![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-26/43050183.jpg) +![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-26/43050183.jpg) 另外,Dubbo 除了能够应用在分布式系统中,也可以应用在现在比较火的微服务系统中。不过,由于 Spring Cloud 在微服务中应用更加广泛,所以,我觉得一般我们提 Dubbo 的话,大部分是分布式系统的情况。 diff --git "a/docs/system-design/distributed-system/rpc/\346\234\215\345\212\241\344\271\213\351\227\264\347\232\204\350\260\203\347\224\250\344\270\272\345\225\245\344\270\215\347\233\264\346\216\245\347\224\250HTTP\350\200\214\347\224\250RPC.md" "b/docs/system-design/distributed-system/rpc/\346\234\215\345\212\241\344\271\213\351\227\264\347\232\204\350\260\203\347\224\250\344\270\272\345\225\245\344\270\215\347\233\264\346\216\245\347\224\250HTTP\350\200\214\347\224\250RPC.md" index 4a139911268..73953f0aa78 100644 --- "a/docs/system-design/distributed-system/rpc/\346\234\215\345\212\241\344\271\213\351\227\264\347\232\204\350\260\203\347\224\250\344\270\272\345\225\245\344\270\215\347\233\264\346\216\245\347\224\250HTTP\350\200\214\347\224\250RPC.md" +++ "b/docs/system-design/distributed-system/rpc/\346\234\215\345\212\241\344\271\213\351\227\264\347\232\204\350\260\203\347\224\250\344\270\272\345\225\245\344\270\215\347\233\264\346\216\245\347\224\250HTTP\350\200\214\347\224\250RPC.md" @@ -10,7 +10,7 @@ RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络 http://www.importnew.com/22003.html -![RPC原理图](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-6/37345851.jpg) +![RPC原理图](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-6/37345851.jpg) 1. 服务消费方(client)调用以本地调用方式调用服务; 2. client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体; @@ -24,7 +24,7 @@ http://www.importnew.com/22003.html 下面再贴一个网上的时序图: -![RPC原理时序图](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-6/32527396.jpg) +![RPC原理时序图](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-12-6/32527396.jpg) ### RPC 解决了什么问题? diff --git a/docs/system-design/framework/spring/Spring.md b/docs/system-design/framework/spring/Spring.md index 18efadcceea..82fe4ef759c 100644 --- a/docs/system-design/framework/spring/Spring.md +++ b/docs/system-design/framework/spring/Spring.md @@ -17,7 +17,7 @@ ### 视频 -- [网易云课堂——58集精通java教程Spring框架开发](http://study.163.com/course/courseMain.htm?courseId=1004475015#/courseDetail?tab=1&35) +- [网易云课堂——58集精通java教程Spring框架开发](https://study.163.com/course/courseMain.htm?courseId=1004475015#/courseDetail?tab=1&35) - [慕课网相关视频](https://www.imooc.com/) - **黑马视频和尚硅谷视频(非常推荐):** 微信公众号:“**JavaGuide**”后台回复关键字 “**1**” 免费领取。 @@ -44,7 +44,7 @@ AOP思想的实现一般都是基于 **代理模式** ,在JAVA中一般采用J 推荐阅读: -- [静态代理、JDK动态代理、CGLIB动态代理讲解](http://www.cnblogs.com/puyangsky/p/6218925.html) :我们知道AOP思想的实现一般都是基于 **代理模式** ,所以在看下面的文章之前建议先了解一下静态代理以及JDK动态代理、CGLIB动态代理的实现方式。 +- [静态代理、JDK动态代理、CGLIB动态代理讲解](https://www.cnblogs.com/puyangsky/p/6218925.html) :我们知道AOP思想的实现一般都是基于 **代理模式** ,所以在看下面的文章之前建议先了解一下静态代理以及JDK动态代理、CGLIB动态代理的实现方式。 - [Spring AOP 入门](https://juejin.im/post/5aa7818af265da23844040c6) :带你入门的一篇文章。这篇文章主要介绍了AOP中的基本概念:5种类型的通知(Before,After,After-returning,After-throwing,Around);Spring中对AOP的支持:AOP思想的实现一般都是基于代理模式,在Java中一般采用JDK动态代理模式,Spring AOP 同时支持 CGLIB、ASPECTJ、JDK动态代理, - [Spring AOP 基于AspectJ注解如何实现AOP](https://juejin.im/post/5a55af9e518825734d14813f) : **AspectJ是一个AOP框架,它能够对java代码进行AOP编译(一般在编译期进行),让java代码具有AspectJ的AOP功能(当然需要特殊的编译器)**,可以这样说AspectJ是目前实现AOP框架中最成熟,功能最丰富的语言,更幸运的是,AspectJ与java程序完全兼容,几乎是无缝关联,因此对于有java编程基础的工程师,上手和使用都非常容易。Spring注意到AspectJ在AOP的实现方式上依赖于特殊编译器(ajc编译器),因此Spring很机智回避了这点,转向采用动态代理技术的实现原理来构建Spring AOP的内部机制(动态织入),这是与AspectJ(静态织入)最根本的区别。**Spring 只是使用了与 AspectJ 5 一样的注解,但仍然没有使用 AspectJ 的编译器,底层依是动态代理技术的实现,因此并不依赖于 AspectJ 的编译器**。 Spring AOP虽然是使用了那一套注解,其实实现AOP的底层是使用了动态代理(JDK或者CGLib)来动态植入。至于AspectJ的静态植入,不是本文重点,所以只提一提。 - [探秘Spring AOP(慕课网视频,很不错)](https://www.imooc.com/learn/869):慕课网视频,讲解的很不错,详细且深入 @@ -65,7 +65,7 @@ AOP思想的实现一般都是基于 **代理模式** ,在JAVA中一般采用J ### Spring单例与线程安全 -- [Spring框架中的单例模式(源码解读)](http://www.cnblogs.com/chengxuyuanzhilu/p/6404991.html):单例模式是一种常用的软件设计模式。通过单例模式可以保证系统中一个类只有一个实例。spring依赖注入时,使用了 多重判断加锁 的单例模式。 +- [Spring框架中的单例模式(源码解读)](https://www.cnblogs.com/chengxuyuanzhilu/p/6404991.html):单例模式是一种常用的软件设计模式。通过单例模式可以保证系统中一个类只有一个实例。spring依赖注入时,使用了 多重判断加锁 的单例模式。 ### Spring源码阅读 diff --git "a/docs/system-design/framework/spring/Spring\344\272\213\345\212\241\346\200\273\347\273\223.md" "b/docs/system-design/framework/spring/Spring\344\272\213\345\212\241\346\200\273\347\273\223.md" index db47ee4bde0..27f49ec7611 100644 --- "a/docs/system-design/framework/spring/Spring\344\272\213\345\212\241\346\200\273\347\273\223.md" +++ "b/docs/system-design/framework/spring/Spring\344\272\213\345\212\241\346\200\273\347\273\223.md" @@ -430,7 +430,7 @@ Class B { - **`TransactionDefinition.PROPAGATION_NOT_SUPPORTED`**: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。 - **`TransactionDefinition.PROPAGATION_NEVER`**: 以非事务方式运行,如果当前存在事务,则抛出异常。 -更多关于事务传播行为的内容请看这篇文章:[《太难了~面试官让我结合案例讲讲自己对 Spring 事务传播行为的理解。》](http://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247486668&idx=2&sn=0381e8c836442f46bdc5367170234abb&chksm=cea24307f9d5ca11c96943b3ccfa1fc70dc97dd87d9c540388581f8fe6d805ff548dff5f6b5b&token=1776990505&lang=zh_CN#rd) +更多关于事务传播行为的内容请看这篇文章:[《太难了~面试官让我结合案例讲讲自己对 Spring 事务传播行为的理解。》](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247486668&idx=2&sn=0381e8c836442f46bdc5367170234abb&chksm=cea24307f9d5ca11c96943b3ccfa1fc70dc97dd87d9c540388581f8fe6d805ff548dff5f6b5b&token=1776990505&lang=zh_CN#rd) #### 3.3.2 事务隔离级别 diff --git "a/docs/system-design/framework/spring/Spring\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223.md" "b/docs/system-design/framework/spring/Spring\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223.md" index 870d3b3db31..99b0d98173d 100644 --- "a/docs/system-design/framework/spring/Spring\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223.md" +++ "b/docs/system-design/framework/spring/Spring\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223.md" @@ -192,11 +192,11 @@ public OneService getService(status) { 图示: -![Spring Bean 生命周期](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-17/48376272.jpg) +![Spring Bean 生命周期](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-17/48376272.jpg) 与之比较类似的中文版本: -![Spring Bean 生命周期](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-17/5496407.jpg) +![Spring Bean 生命周期](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-17/5496407.jpg) ## 6. Spring MVC @@ -213,12 +213,12 @@ MVC 是一种设计模式,Spring MVC 是一款很优秀的 MVC 框架。Spring M **Spring MVC 的简单原理图如下:** -![](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-11/60679444.jpg) +![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-11/60679444.jpg) ### 6.2 SpringMVC 工作原理了解吗? **原理如下图所示:** -![SpringMVC运行原理](http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-11/49790288.jpg) +![SpringMVC运行原理](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-10-11/49790288.jpg) 上图的一个笔误的小问题:Spring MVC 的入口函数也就是前端控制器 `DispatcherServlet` 的作用是接收请求,响应结果。 @@ -340,7 +340,7 @@ String transient4; // not persistent because of @Transient ## 参考 - 《Spring 技术内幕》 -- +- - - - https://www.cnblogs.com/clwydjgs/p/9317849.html diff --git "a/docs/system-design/website-architecture/\345\205\263\344\272\216\345\244\247\345\236\213\347\275\221\347\253\231\347\263\273\347\273\237\346\236\266\346\236\204\344\275\240\344\270\215\345\276\227\344\270\215\346\207\202\347\232\20410\344\270\252\351\227\256\351\242\230.md" "b/docs/system-design/website-architecture/\345\205\263\344\272\216\345\244\247\345\236\213\347\275\221\347\253\231\347\263\273\347\273\237\346\236\266\346\236\204\344\275\240\344\270\215\345\276\227\344\270\215\346\207\202\347\232\20410\344\270\252\351\227\256\351\242\230.md" index f1a1662e52a..194cc52bc7b 100644 --- "a/docs/system-design/website-architecture/\345\205\263\344\272\216\345\244\247\345\236\213\347\275\221\347\253\231\347\263\273\347\273\237\346\236\266\346\236\204\344\275\240\344\270\215\345\276\227\344\270\215\346\207\202\347\232\20410\344\270\252\351\227\256\351\242\230.md" +++ "b/docs/system-design/website-architecture/\345\205\263\344\272\216\345\244\247\345\236\213\347\275\221\347\253\231\347\263\273\347\273\237\346\236\266\346\236\204\344\275\240\344\270\215\345\276\227\344\270\215\346\207\202\347\232\20410\344\270\252\351\227\256\351\242\230.md" @@ -164,7 +164,7 @@ CAP仅适用于原子读写的NOSQL场景中,并不适合数据库系统。现 **当发生网络分区的时候,如果我们要继续服务,那么强一致性和可用性只能2选1。也就是说当网络分区之后P是前提,决定了P之后才有C和A的选择。也就是说分区容错性(Partition tolerance)我们是必须要实现的。** -我在网上找了很多文章想看一下有没有文章提到这个不是所谓的3选2,用百度半天没找到了一篇,用谷歌搜索找到一篇比较不错的,如果想深入学习一下CAP就看这篇文章把,我这里就不多BB了:**《分布式系统之CAP理论》 :** [http://www.cnblogs.com/hxsyl/p/4381980.html](http://www.cnblogs.com/hxsyl/p/4381980.html) +我在网上找了很多文章想看一下有没有文章提到这个不是所谓的3选2,用百度半天没找到了一篇,用谷歌搜索找到一篇比较不错的,如果想深入学习一下CAP就看这篇文章把,我这里就不多BB了:**《分布式系统之CAP理论》 :** [https://www.cnblogs.com/hxsyl/p/4381980.html](https://www.cnblogs.com/hxsyl/p/4381980.html) #### BASE 理论 diff --git a/docs/tools/Git.md b/docs/tools/Git.md index 78ea28054a8..d2ac8f2dbc1 100644 --- a/docs/tools/Git.md +++ b/docs/tools/Git.md @@ -268,8 +268,8 @@ git push origin **推荐阅读:** -- [Git - 简明指南](http://rogerdudler.github.io/git-guide/index.zh.html) -- [图解Git](http://marklodato.github.io/visual-git-guide/index-zh-cn.html) +- [Git - 简明指南](https://rogerdudler.github.io/git-guide/index.zh.html) +- [图解Git](https://marklodato.github.io/visual-git-guide/index-zh-cn.html) - [猴子都能懂得Git入门](https://backlog.com/git-tutorial/cn/intro/intro1_1.html) - https://git-scm.com/book/en/v2 - [Generating a new SSH key and adding it to the ssh-agent](https://help.github.com/en/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent) diff --git "a/docs/tools/Github\346\212\200\345\267\247.md" "b/docs/tools/Github\346\212\200\345\267\247.md" index ba7dc82406c..5a334ae369c 100644 --- "a/docs/tools/Github\346\212\200\345\267\247.md" +++ "b/docs/tools/Github\346\212\200\345\267\247.md" @@ -6,7 +6,7 @@ ## 1. 一键生成 Github 简历 -通过 [http://resume.github.io/](http://resume.github.io/) 这个网站你可以一键生成一个在线的 Github 简历。 +通过 [https://resume.github.io/](https://resume.github.io/) 这个网站你可以一键生成一个在线的 Github 简历。 当时我参加的校招的时候,个人信息那里就放了一个在线的 Github 简历。我觉得这样会让面试官感觉你是一个内行,会提高一些印象分。 diff --git "a/docs/\345\205\254\344\274\227\345\217\267\345\216\206\345\217\262\346\226\207\347\253\240\346\261\207\346\200\273.md" "b/docs/\345\205\254\344\274\227\345\217\267\345\216\206\345\217\262\346\226\207\347\253\240\346\261\207\346\200\273.md" index da66dbc9c13..5e698dba2f3 100644 --- "a/docs/\345\205\254\344\274\227\345\217\267\345\216\206\345\217\262\346\226\207\347\253\240\346\261\207\346\200\273.md" +++ "b/docs/\345\205\254\344\274\227\345\217\267\345\216\206\345\217\262\346\226\207\347\253\240\346\261\207\346\200\273.md" @@ -186,12 +186,12 @@ ## 杂文闲记 -- [一只准准程序员的唠叨](http://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247484932&idx=1&sn=a4f3057ecd4412cb8b18e92058b29680&chksm=cea249cff9d5c0d97580310f6666a4e0c42cfd1ba13dd759850e489c9386dce8c5c35b0a1a95&token=1082669959&lang=zh_CN#rd)(2018-06-09) +- [一只准准程序员的唠叨](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247484932&idx=1&sn=a4f3057ecd4412cb8b18e92058b29680&chksm=cea249cff9d5c0d97580310f6666a4e0c42cfd1ba13dd759850e489c9386dce8c5c35b0a1a95&token=1082669959&lang=zh_CN#rd)(2018-06-09) - [说几件近期的小事](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247484900&idx=1&sn=745e8f6027da369ef5f8e349cb5d6657&chksm=cea24a2ff9d5c339c925322ffacdc72dd37bda63238dfb3452540085c1695681e2e79070e2a7&token=1082669959&lang=zh_CN#rd)(2018-08-02) - [选择技术方向都要考虑哪些因素](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247484898&idx=1&sn=fd2ebf9ffd37ab5de1de09cd3272ac0a&chksm=cea24a29f9d5c33fa48f5a57de864cd9382a730578fb18d78b7f06b45504aede18b235b9bc9e&token=1082669959&lang=zh_CN#rd)(2018-08-04) - [结束了我短暂的秋招,说点自己的感受](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247484842&idx=1&sn=4489dfab0ef2479122b71407855afc71&chksm=cea24a61f9d5c3774a8ed67c5fcc3234cb0741fbe831152986e5d1c8fb4f36a003f4fb2f247e&token=1082669959&lang=zh_CN#rd)(2018-10-22) - [【周日闲谈】最近想说的几件小事](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247484818&idx=1&sn=e14b0a08537456eb15ba49cf5e70ff74&chksm=cea24a59f9d5c34fe0a9e0567d867b85a81d1f19b0ea8e6a3c14161e436508de9adfeb2a5e6a&token=1082669959&lang=zh_CN#rd)(2018-11-18) -- [做公众号这一年的经历和一件“大事”](http://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247484746&idx=1&sn=a519a9e3d638bff5c65008f7de167e4b&chksm=cea24a81f9d5c397ca9ac5668ba6cb18b38065e0e282a34ebc077a2dea98de3f1eb285ea5f09&token=1082669959&lang=zh_CN#rd)(2019-03-10) +- [做公众号这一年的经历和一件“大事”](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247484746&idx=1&sn=a519a9e3d638bff5c65008f7de167e4b&chksm=cea24a81f9d5c397ca9ac5668ba6cb18b38065e0e282a34ebc077a2dea98de3f1eb285ea5f09&token=1082669959&lang=zh_CN#rd)(2019-03-10) - [几经周折,公众号终于留言功能啦!](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485007&idx=1&sn=096a436cd6a9251c23b8effc3cfa5076&chksm=cea24984f9d5c092d1f7740d1c3e0ea347562ba0aa597507ce275c5bdf1c8bd608328ee756ba&token=1082669959&lang=zh_CN#rd)(2019-03-15) - [写在毕业季的大学总结!细数一下大学干过的“傻事”。](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485358&idx=1&sn=3aaf1163fe13351e06c76b70f2bd33bd&chksm=cea24865f9d5c1735b51c707c8f5ade16af7eca304540205ab0fb1284f99034d418b9858d7db&token=1667678311&lang=zh_CN#rd) (2019-06-11) - [入职一个月的职场小白,谈谈自己这段时间的感受](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485540&idx=1&sn=4492eece8f7738e99350118040e14a79&chksm=cea247aff9d5ceb9e7c67f418d8a8518c550fd7dd269bf2c9bdef83309502273b4b9f1e7021f&token=1333232257&lang=zh_CN&scene=21#wechat_redirect) diff --git "a/docs/\350\264\241\347\214\256\346\214\207\345\215\227.md" "b/docs/\350\264\241\347\214\256\346\214\207\345\215\227.md" new file mode 100644 index 00000000000..e69de29bb2d