From d7356a07cbc52263b017e53ba4cdcd8f0fe173ee Mon Sep 17 00:00:00 2001 From: Snoopy Date: Tue, 24 Aug 2021 08:11:10 +0800 Subject: [PATCH 01/11] =?UTF-8?q?Update=20Java=E5=9F=BA=E7=A1=80=E7=9F=A5?= =?UTF-8?q?=E8=AF=86.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修正拼写错误 --- .../Java\345\237\272\347\241\200\347\237\245\350\257\206.md" | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7c03a11179d..88bead11e1a 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" @@ -1408,7 +1408,7 @@ String s = input.readLine(); - 按照操作单元划分,可以划分为字节流和字符流; - 按照流的角色划分为节点流和处理流。 -Java Io 流共涉及 40 多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java I0 流的 40 多个类都是从如下 4 个抽象类基类中派生出来的。 +Java IO 流共涉及 40 多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java IO 流的 40 多个类都是从如下 4 个抽象类基类中派生出来的。 - InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。 - OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。 From c58b5a2525121054aae0dbe403fda1d9448729a6 Mon Sep 17 00:00:00 2001 From: anaer Date: Tue, 24 Aug 2021 10:37:53 +0800 Subject: [PATCH 02/11] Update Shell.md typo --- docs/cs-basics/operating-system/Shell.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/cs-basics/operating-system/Shell.md b/docs/cs-basics/operating-system/Shell.md index 099bcaf6678..ce54f0e8aaa 100644 --- a/docs/cs-basics/operating-system/Shell.md +++ b/docs/cs-basics/operating-system/Shell.md @@ -57,7 +57,7 @@ W3Cschool 上的一篇文章是这样介绍 Shell的,如下图所示。 ### Shell 编程的 Hello World -学习任何一门编程语言第一件事就是输出HelloWord了!下面我会从新建文件到shell代码编写来说下Shell 编程如何输出Hello World。 +学习任何一门编程语言第一件事就是输出HelloWorld了!下面我会从新建文件到shell代码编写来说下Shell 编程如何输出Hello World。 (1)新建一个文件 helloworld.sh :`touch helloworld.sh`,扩展名为 sh(sh代表Shell)(扩展名并不影响脚本执行,见名知意就好,如果你用 php 写 shell 脚本,扩展名就用 php 好了) @@ -94,14 +94,14 @@ shell中 # 符号表示注释。**shell 的第一行比较特殊,一般都会 3. **Shell变量** :Shell变量是由 Shell 程序设置的特殊变量。Shell 变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了 Shell 的正常运行 **常用的环境变量:** -> PATH 决定了shell将到哪些目录中寻找命令或程序 -HOME 当前用户主目录 -HISTSIZE 历史记录数 -LOGNAME 当前用户的登录名 -HOSTNAME 指主机的名称 -SHELL 当前用户Shell类型 -LANGUGE  语言相关的环境变量,多语言可以修改此环境变量 -MAIL 当前用户的邮件存放目录 +> PATH 决定了shell将到哪些目录中寻找命令或程序 +HOME 当前用户主目录 +HISTSIZE 历史记录数 +LOGNAME 当前用户的登录名 +HOSTNAME 指主机的名称 +SHELL 当前用户Shell类型 +LANGUGE  语言相关的环境变量,多语言可以修改此环境变量 +MAIL 当前用户的邮件存放目录 PS1 基本提示符,对于root用户是#,对于普通用户是$ **使用 Linux 已定义的环境变量:** From 39836b0d9a00421917db696835b9287f435e9a40 Mon Sep 17 00:00:00 2001 From: anaer Date: Tue, 24 Aug 2021 10:43:33 +0800 Subject: [PATCH 03/11] Update Shell.md --- docs/cs-basics/operating-system/Shell.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cs-basics/operating-system/Shell.md b/docs/cs-basics/operating-system/Shell.md index ce54f0e8aaa..0a7c15080af 100644 --- a/docs/cs-basics/operating-system/Shell.md +++ b/docs/cs-basics/operating-system/Shell.md @@ -100,7 +100,7 @@ HISTSIZE 历史记录数 LOGNAME 当前用户的登录名 HOSTNAME 指主机的名称 SHELL 当前用户Shell类型 -LANGUGE  语言相关的环境变量,多语言可以修改此环境变量 +LANGUAGE  语言相关的环境变量,多语言可以修改此环境变量 MAIL 当前用户的邮件存放目录 PS1 基本提示符,对于root用户是#,对于普通用户是$ From d8cae225956aece69d64f5ae28e910df4834e66c Mon Sep 17 00:00:00 2001 From: anaer Date: Tue, 24 Aug 2021 10:58:24 +0800 Subject: [PATCH 04/11] Update naming.md --- docs/system-design/naming.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/system-design/naming.md b/docs/system-design/naming.md index 33579c9160c..f6e1dd59396 100644 --- a/docs/system-design/naming.md +++ b/docs/system-design/naming.md @@ -69,7 +69,7 @@ 驼峰命名法应该我们最常见的一个,这种命名方式使用大小写混合的格式来区别各个单词,并且单词之间不使用空格隔开或者连接字符连接的命名方式 -#### 大驼峰命名法(CamelCase) +#### 大驼峰命名法(UpperCamelCase) **类名需要使用大驼峰命名法(UpperCamelCase)** From b55ee4a63420d0c025fe0c4dfb75f6d9cdca5493 Mon Sep 17 00:00:00 2001 From: anaer Date: Tue, 24 Aug 2021 11:28:43 +0800 Subject: [PATCH 05/11] =?UTF-8?q?Update=20Spring=E5=B8=B8=E8=A7=81?= =?UTF-8?q?=E9=97=AE=E9=A2=98=E6=80=BB=E7=BB=93.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...56\351\242\230\346\200\273\347\273\223.md" | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) 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 529406be945..cf8ffffa00c 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" @@ -94,9 +94,9 @@ Spring 时代我们一般通过 XML 文件来配置 Bean,后来开发人员觉 ### 谈谈自己对于 AOP 的了解 -AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码**,**降低模块间的耦合度,并有利于未来的可拓展性和可维护性。 +AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。 -Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 **JDK Proxy**,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 **Cglib** ,这时候 Spring AOP 会使用 **Cglib** 生成一个被代理对象的子类来作为代理,如下图所示: +Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 **JDK Proxy**,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 **Cglib** 生成一个被代理对象的子类来作为代理,如下图所示: ![SpringAOPProcess](https://images.xiaozhuanlan.com/photo/2019/926dfc549b06d280a37397f9fd49bf9d.jpg) @@ -116,7 +116,7 @@ Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系 简单来说,bean 代指的就是那些被 IoC 容器所管理的对象。 -我们需要告诉 IoC 容器帮助我们管理哪些对象,这个是通过配置元数据的定义的。配置元数据可以是 XML 文件、注解或者 Java 配置类。 +我们需要告诉 IoC 容器帮助我们管理哪些对象,这个是通过配置元数据来定义的。配置元数据可以是 XML 文件、注解或者 Java 配置类。 ```xml @@ -133,7 +133,7 @@ Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系 ### bean 的作用域有哪些? -Spring 中 Bean 的作用域通常由下面几种: +Spring 中 Bean 的作用域通常有下面几种: - **singleton** : 唯一 bean 实例,Spring 中的 bean 默认都是单例的,对单例设计模式的应用。 - **prototype** : 每次请求都会创建一个新的 bean 实例。 @@ -173,8 +173,8 @@ public Person personPrototype() { ### @Component 和 @Bean 的区别是什么? 1. `@Component` 注解作用于类,而`@Bean`注解作用于方法。 -2. `@Component`通常是通过类路径扫描来自动侦测以及自动装配到 Spring 容器中(我们可以使用 `@ComponentScan` 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。`@Bean` 注解通常是我们在标有该注解的方法中定义产生这个 bean,`@Bean`告诉了 Spring 这是某个类的示例,当我需要用它的时候还给我。 -3. `@Bean` 注解比 `Component` 注解的自定义性更强,而且很多地方我们只能通过 `@Bean` 注解来注册 bean。比如当我们引用第三方库中的类需要装配到 `Spring`容器时,则只能通过 `@Bean`来实现。 +2. `@Component`通常是通过类路径扫描来自动侦测以及自动装配到 Spring 容器中(我们可以使用 `@ComponentScan` 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。`@Bean` 注解通常是我们在标有该注解的方法中定义产生这个 bean,`@Bean`告诉了 Spring 这是某个类的实例,当我需要用它的时候还给我。 +3. `@Bean` 注解比 `@Component` 注解的自定义性更强,而且很多地方我们只能通过 `@Bean` 注解来注册 bean。比如当我们引用第三方库中的类需要装配到 `Spring`容器时,则只能通过 `@Bean`来实现。 `@Bean`注解使用示例: @@ -407,16 +407,16 @@ public enum Isolation { 当 `@Transactional` 注解作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。如果类或者方法加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚。 -在 `@Transactional` 注解中如果不配置`rollbackFor`属性,那么事物只会在遇到`RuntimeException`的时候才会回滚,加上 `rollbackFor=Exception.class`,可以让事物在遇到非运行时异常时也回滚。 +在 `@Transactional` 注解中如果不配置`rollbackFor`属性,那么事务只会在遇到`RuntimeException`的时候才会回滚,加上 `rollbackFor=Exception.class`,可以让事务在遇到非运行时异常时也回滚。 ## JPA ### 如何使用 JPA 在数据库中非持久化一个字段? -假如我们有有下面一个类: +假如我们有下面一个类: ```java -Entity(name="USER") +@Entity(name="USER") public class User { @Id @@ -439,7 +439,7 @@ public class User { ```java static String transient1; // not persistent because of static -final String transient2 = “Satish”; // not persistent because of final +final String transient2 = "Satish"; // not persistent because of final transient String transient3; // not persistent because of transient @Transient String transient4; // not persistent because of @Transient From 64fb03bf9c10804b6ee7b723d63130ad88088163 Mon Sep 17 00:00:00 2001 From: anaer Date: Wed, 25 Aug 2021 17:32:35 +0800 Subject: [PATCH 06/11] Update java8-common-new-features.md typo --- docs/java/new-features/java8-common-new-features.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/java/new-features/java8-common-new-features.md b/docs/java/new-features/java8-common-new-features.md index f0630eff520..c518a149683 100644 --- a/docs/java/new-features/java8-common-new-features.md +++ b/docs/java/new-features/java8-common-new-features.md @@ -78,7 +78,7 @@ public class InterfaceNewImpl implements InterfaceNew , InterfaceNew1{ 2. interface 的方法是更像是一个扩展插件。而 abstract class 的方法是要继承的。 -开始我们也提到,interface 新增`default`,和`static`修饰的方法,为了解决接口的修改与现有的实现不兼容的问题,并不是为了要替代`abstract class`。在使用上,该用 abstract class 的地方还是要用 abstract class,不要因为 interface 的新特性而降之替换。 +开始我们也提到,interface 新增`default`和`static`修饰的方法,为了解决接口的修改与现有的实现不兼容的问题,并不是为了要替代`abstract class`。在使用上,该用 abstract class 的地方还是要用 abstract class,不要因为 interface 的新特性而将之替换。 **记住接口永远和类不一样。** @@ -463,7 +463,7 @@ Predicate.test 执行 按执行顺序应该是先打印 4 次「`Predicate.test` 执行」,再打印「`count` 执行」。实际结果恰恰相反。说明 filter 中的方法并没有立刻执行,而是等调用`count()`方法后才执行。 -上面都是串行 `Stream` 的实例。并行 `parallelStream` 在使用方法上和串行一样。主要区别是 `parallelStream` 可多线程执行,是基于 ForkJoin 框架实现的,有时间大家可以了解一下 `ForkJoin` 框架和 `ForkJoinPool`。这里可以简单的理解它是通过线程池来实现的,这样就会涉及到线程安全,线程消耗等问题。下面我们通过代码来体验一下串行流的多线程执行。 +上面都是串行 `Stream` 的实例。并行 `parallelStream` 在使用方法上和串行一样。主要区别是 `parallelStream` 可多线程执行,是基于 ForkJoin 框架实现的,有时间大家可以了解一下 `ForkJoin` 框架和 `ForkJoinPool`。这里可以简单的理解它是通过线程池来实现的,这样就会涉及到线程安全,线程消耗等问题。下面我们通过代码来体验一下并行流的多线程执行。 ```java @Test @@ -733,7 +733,7 @@ public Optional filter(Predicate predicate) { ### 小结 -看完 `Optional` 源码,`Optional` 的方法真的非常简单,值得注意的是如果坚决不想看见 `NPE`,就不要用 `of() `、 `get()` 、`flatMap(..)`\。最后再综合用一下 `Optional` 的高频方法。 +看完 `Optional` 源码,`Optional` 的方法真的非常简单,值得注意的是如果坚决不想看见 `NPE`,就不要用 `of() `、 `get()` 、`flatMap(..)`。最后再综合用一下 `Optional` 的高频方法。 ```java Optional.ofNullable(zoo).map(o -> o.getDog()).map(d -> d.getAge()).filter(v->v==1).orElse(3); @@ -947,7 +947,7 @@ public void getDayNew() { 1. `Date` ---> `LocalDate` 2. `Time` ---> `LocalTime` -3. `TimesSamp` ---> `LocalDateTime` +3. `Timestamp` ---> `LocalDateTime` 而之前统统对应 `Date`,也只有 `Date`。 @@ -1017,4 +1017,4 @@ System.out.println("本地时区时间: " + localZoned); - Optional - Date time-api -这些都是开发当中比较常用的特征。梳理下来发现它们真香,而我却没有更早的应用。总觉得学习 java 8 新特性比较麻烦,一致使用老的实现方式。其实这些新特性几天就可以掌握,一但掌握,效率会有很大的提高。其实我们涨工资也是涨的学习的钱,不学习终究会被淘汰,35 岁危机会提前来临。 +这些都是开发当中比较常用的特性。梳理下来发现它们真香,而我却没有更早的应用。总觉得学习 java 8 新特性比较麻烦,一直使用老的实现方式。其实这些新特性几天就可以掌握,一但掌握,效率会有很大的提高。其实我们涨工资也是涨的学习的钱,不学习终究会被淘汰,35 岁危机会提前来临。 From dfda4a4a6a9f1c689077209b6be3175ce7f27876 Mon Sep 17 00:00:00 2001 From: Aaron Ge <525032143@qq.com> Date: Wed, 25 Aug 2021 23:37:22 +0800 Subject: [PATCH 07/11] =?UTF-8?q?Update=20IO=E6=A8=A1=E5=9E=8B.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新“网络请求和相应”为“网络请求和响应” --- "docs/java/basis/IO\346\250\241\345\236\213.md" | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git "a/docs/java/basis/IO\346\250\241\345\236\213.md" "b/docs/java/basis/IO\346\250\241\345\236\213.md" index 243fdc00107..730e1e24f06 100644 --- "a/docs/java/basis/IO\346\250\241\345\236\213.md" +++ "b/docs/java/basis/IO\346\250\241\345\236\213.md" @@ -36,7 +36,7 @@ I/O(**I**nput/**O**utpu) 即**输入/输出** 。 因此,用户进程想要执行 IO 操作的话,必须通过 **系统调用** 来间接访问内核空间 -我们在平常开发过程中接触最多的就是 **磁盘 IO(读写文件)** 和 **网络 IO(网络请求和相应)**。 +我们在平常开发过程中接触最多的就是 **磁盘 IO(读写文件)** 和 **网络 IO(网络请求和响应)**。 **从应用程序的视角来看的话,我们的应用程序对操作系统的内核发起 IO 调用(系统调用),操作系统负责的内核执行具体的 IO 操作。也就是说,我们的应用程序实际上只是发起了 IO 操作的调用而已,具体 IO 的执行是由操作系统的内核来完成的。** From 413e15cc5ed3ca4e2f3b45162315990fbe45b86f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=9C=A8?= Date: Thu, 26 Aug 2021 10:52:09 +0800 Subject: [PATCH 08/11] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=B8=83=E9=9A=86?= =?UTF-8?q?=E8=BF=87=E6=BB=A4=E5=99=A8=E4=B8=80=E6=96=87=E9=93=BE=E6=8E=A5?= =?UTF-8?q?=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 布隆过滤器链接失效 --- docs/database/Redis/redis-all.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/database/Redis/redis-all.md b/docs/database/Redis/redis-all.md index 93227374d2e..74d0d6b0184 100644 --- a/docs/database/Redis/redis-all.md +++ b/docs/database/Redis/redis-all.md @@ -743,7 +743,7 @@ _为什么会出现误判的情况呢? 我们还要从布隆过滤器的原理 然后,一定会出现这样一种情况:**不同的字符串可能哈希出来的位置相同。** (可以适当增加位数组大小或者调整我们的哈希函数来降低概率) -更多关于布隆过滤器的内容可以看我的这篇原创:[《不了解布隆过滤器?一文给你整的明明白白!》](https://github.com/Snailclimb/JavaGuide/blob/master/docs/dataStructures-algorithms/data-structure/bloom-filter.md) ,强烈推荐,个人感觉网上应该找不到总结的这么明明白白的文章了。 +更多关于布隆过滤器的内容可以看我的这篇原创:[《不了解布隆过滤器?一文给你整的明明白白!》](https://github.com/Snailclimb/JavaGuide/blob/master/docs/cs-basics/data-structure/bloom-filter.md) ,强烈推荐,个人感觉网上应该找不到总结的这么明明白白的文章了。 ### 缓存雪崩 From b5db9e9bdabb93b122761b4b14da24f2174a8915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=9C=A8?= Date: Thu, 26 Aug 2021 11:04:57 +0800 Subject: [PATCH 09/11] =?UTF-8?q?=E8=B7=B3=E8=BD=AC=E9=93=BE=E6=8E=A5?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 链接地址处理 --- docs/cs-basics/data-structure/bloom-filter.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/cs-basics/data-structure/bloom-filter.md b/docs/cs-basics/data-structure/bloom-filter.md index 9b5afd6d5c5..1431e97b1c8 100644 --- a/docs/cs-basics/data-structure/bloom-filter.md +++ b/docs/cs-basics/data-structure/bloom-filter.md @@ -232,7 +232,8 @@ System.out.println(filter.mightContain(2)); Redis v4.0 之后有了 Module(模块/插件) 功能,Redis Modules 让 Redis 可以使用外部模块扩展其功能 。布隆过滤器就是其中的 Module。详情可以查看 Redis 官方对 Redis Modules 的介绍 :https://redis.io/modules -另外,官网推荐了一个 RedisBloom 作为 Redis 布隆过滤器的 Module,地址:https://github.com/RedisBloom/RedisBloom。其他还有: +另外,官网推荐了一个 RedisBloom 作为 Redis 布隆过滤器的 Module,地址:https://github.com/RedisBloom/RedisBloom +其他还有: - redis-lua-scaling-bloom-filter(lua 脚本实现):https://github.com/erikdubbelboer/redis-lua-scaling-bloom-filter - pyreBloom(Python 中的快速 Redis 布隆过滤器) :https://github.com/seomoz/pyreBloom From b552b3fbe47745771e4c50811b11c9d40392e09e Mon Sep 17 00:00:00 2001 From: chengcjk <739009651@qq.com> Date: Thu, 26 Aug 2021 11:05:58 +0800 Subject: [PATCH 10/11] =?UTF-8?q?=E6=A0=A1=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 365d5aec088..e4e8d19331c 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" @@ -616,7 +616,7 @@ public ThreadPoolExecutor(int corePoolSize, **`ThreadPoolExecutor` 饱和策略定义:** -如果当前同时运行的线程数量达到最大线程数量并且队列也已经被放满了任时,`ThreadPoolTaskExecutor` 定义一些策略: +如果当前同时运行的线程数量达到最大线程数量并且队列也已经被放满了任务时,`ThreadPoolTaskExecutor` 定义一些策略: - **`ThreadPoolExecutor.AbortPolicy`:** 抛出 `RejectedExecutionException`来拒绝新任务的处理。 - **`ThreadPoolExecutor.CallerRunsPolicy`:** 调用执行自己的线程运行任务,也就是直接在调用`execute`方法的线程中运行(`run`)被拒绝的任务,如果执行程序已关闭,则会丢弃该任务。因此这种策略会降低对于新任务提交速度,影响程序的整体性能。如果您的应用程序可以承受此延迟并且你要求任何一个任务请求都要被执行的话,你可以选择这个策略。 From c2f10ad2d5f2577797bc7ed3f11ffe7267081dd2 Mon Sep 17 00:00:00 2001 From: guide Date: Thu, 26 Aug 2021 23:05:40 +0800 Subject: [PATCH 11/11] =?UTF-8?q?Delete=20ThreadLocal=EF=BC=88=E6=9C=AA?= =?UTF-8?q?=E5=AE=8C=E6=88=90=EF=BC=89.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...52\345\256\214\346\210\220\357\274\211.md" | 170 ------------------ 1 file changed, 170 deletions(-) delete mode 100644 "docs/java/multi-thread/ThreadLocal\357\274\210\346\234\252\345\256\214\346\210\220\357\274\211.md" diff --git "a/docs/java/multi-thread/ThreadLocal\357\274\210\346\234\252\345\256\214\346\210\220\357\274\211.md" "b/docs/java/multi-thread/ThreadLocal\357\274\210\346\234\252\345\256\214\346\210\220\357\274\211.md" deleted file mode 100644 index f69cc1d60e9..00000000000 --- "a/docs/java/multi-thread/ThreadLocal\357\274\210\346\234\252\345\256\214\346\210\220\357\274\211.md" +++ /dev/null @@ -1,170 +0,0 @@ -[ThreadLocal造成OOM内存溢出案例演示与原理分析](https://blog.csdn.net/xlgen157387/article/details/78298840) - -[深入理解 Java 之 ThreadLocal 工作原理]() - -## ThreadLocal - -### ThreadLocal简介 - -通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。**如果想实现每一个线程都有自己的专属本地变量该如何解决呢?** JDK中提供的`ThreadLocal`类正是为了解决这样的问题。 **`ThreadLocal`类主要解决的就是让每个线程绑定自己的值,可以将`ThreadLocal`类形象的比喻成存放数据的盒子,盒子中可以存储每个线程的私有数据。** - -**如果你创建了一个`ThreadLocal`变量,那么访问这个变量的每个线程都会有这个变量的本地副本,这也是`ThreadLocal`变量名的由来。他们可以使用 `get()` 和 `set()` 方法来获取默认值或将其值更改为当前线程所存的副本的值,从而避免了线程安全问题。** - -再举个简单的例子: - -比如有两个人去宝屋收集宝物,这两个共用一个袋子的话肯定会产生争执,但是给他们两个人每个人分配一个袋子的话就不会出现这样的问题。如果把这两个人比作线程的话,那么ThreadLocal就是用来这两个线程竞争的。 - -### ThreadLocal示例 - -相信看了上面的解释,大家已经搞懂 ThreadLocal 类是个什么东西了。 - -```java -import java.text.SimpleDateFormat; -import java.util.Random; - -public class ThreadLocalExample implements Runnable{ - - // SimpleDateFormat 不是线程安全的,所以每个线程都要有自己独立的副本 - private static final ThreadLocal formatter = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMdd HHmm")); - - public static void main(String[] args) throws InterruptedException { - ThreadLocalExample obj = new ThreadLocalExample(); - for(int i=0 ; i<10; i++){ - Thread t = new Thread(obj, ""+i); - Thread.sleep(new Random().nextInt(1000)); - t.start(); - } - } - - @Override - public void run() { - System.out.println("Thread Name= "+Thread.currentThread().getName()+" default Formatter = "+formatter.get().toPattern()); - try { - Thread.sleep(new Random().nextInt(1000)); - } catch (InterruptedException e) { - e.printStackTrace(); - } - //formatter pattern is changed here by thread, but it won't reflect to other threads - formatter.set(new SimpleDateFormat()); - - System.out.println("Thread Name= "+Thread.currentThread().getName()+" formatter = "+formatter.get().toPattern()); - } - -} - -``` - -Output: - -``` -Thread Name= 0 default Formatter = yyyyMMdd HHmm -Thread Name= 0 formatter = yy-M-d ah:mm -Thread Name= 1 default Formatter = yyyyMMdd HHmm -Thread Name= 2 default Formatter = yyyyMMdd HHmm -Thread Name= 1 formatter = yy-M-d ah:mm -Thread Name= 3 default Formatter = yyyyMMdd HHmm -Thread Name= 2 formatter = yy-M-d ah:mm -Thread Name= 4 default Formatter = yyyyMMdd HHmm -Thread Name= 3 formatter = yy-M-d ah:mm -Thread Name= 4 formatter = yy-M-d ah:mm -Thread Name= 5 default Formatter = yyyyMMdd HHmm -Thread Name= 5 formatter = yy-M-d ah:mm -Thread Name= 6 default Formatter = yyyyMMdd HHmm -Thread Name= 6 formatter = yy-M-d ah:mm -Thread Name= 7 default Formatter = yyyyMMdd HHmm -Thread Name= 7 formatter = yy-M-d ah:mm -Thread Name= 8 default Formatter = yyyyMMdd HHmm -Thread Name= 9 default Formatter = yyyyMMdd HHmm -Thread Name= 8 formatter = yy-M-d ah:mm -Thread Name= 9 formatter = yy-M-d ah:mm -``` - -从输出中可以看出,Thread-0已经改变了formatter的值,但仍然是thread-2默认格式化程序与初始化值相同,其他线程也一样。 - -上面有一段代码用到了创建 `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"); - } - }; -``` - -### ThreadLocal原理 - -从 `Thread`类源代码入手。 - -```java -public class Thread implements Runnable { - ...... -//与此线程有关的ThreadLocal值。由ThreadLocal类维护 -ThreadLocal.ThreadLocalMap threadLocals = null; - -//与此线程有关的InheritableThreadLocal值。由InheritableThreadLocal类维护 -ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; - ...... -} -``` - -从上面`Thread`类 源代码可以看出`Thread` 类中有一个 `threadLocals` 和 一个 `inheritableThreadLocals` 变量,它们都是 `ThreadLocalMap` 类型的变量,我们可以把 `ThreadLocalMap` 理解为`ThreadLocal` 类实现的定制化的 `HashMap`。默认情况下这两个变量都是null,只有当前线程调用 `ThreadLocal` 类的 `set`或`get`方法时才创建它们,实际上调用这两个方法的时候,我们调用的是`ThreadLocalMap`类对应的 `get()`、`set() `方法。 - -`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; - } -``` - -通过上面这些内容,我们足以通过猜测得出结论:**最终的变量是放在了当前线程的 `ThreadLocalMap` 中,并不是存在 `ThreadLocal` 上,ThreadLocal 可以理解为只是ThreadLocalMap的封装,传递了变量值。** - -**每个Thread中都具备一个ThreadLocalMap,而ThreadLocalMap可以存储以ThreadLocal为key的键值对。** 比如我们在同一个线程中声明了两个 `ThreadLocal` 对象的话,会使用 `Thread`内部都是使用仅有那个`ThreadLocalMap` 存放数据的,`ThreadLocalMap`的 key 就是 `ThreadLocal`对象,value 就是 `ThreadLocal` 对象调用`set`方法设置的值。`ThreadLocal` 是 map结构是为了让每个线程可以关联多个 `ThreadLocal`变量。这也就解释了ThreadLocal声明的变量为什么在每一个线程都有自己的专属本地变量。 - -```java -public class Thread implements Runnable { - ...... -//与此线程有关的ThreadLocal值。由ThreadLocal类维护 -ThreadLocal.ThreadLocalMap threadLocals = null; - -//与此线程有关的InheritableThreadLocal值。由InheritableThreadLocal类维护 -ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; - ...... -} -``` - -`ThreadLocalMap`是`ThreadLocal`的静态内部类。 - - - -### ThreadLocal 内存泄露问题 - -`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; - - Entry(ThreadLocal k, Object v) { - super(k); - value = v; - } - } -``` - -**弱引用介绍:** - -> 如果一个对象只具有弱引用,那就类似于**可有可无的生活用品**。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。 -> -> 弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。