)
+
+本文我们就来围绕这个问题来分析一下为什么JDK自身提供的构建线程池的方式并不建议使用?到底应该如何创建一个线程池呢?
+
+### Executors
+
+Executors 是一个Java中的工具类。提供工厂方法来创建不同类型的线程池。
+
+![][2]
+
+从上图中也可以看出,Executors的创建线程池的方法,创建出来的线程池都实现了ExecutorService接口。常用方法有以下几个:
+
+`newFiexedThreadPool(int Threads)`:创建固定数目线程的线程池。
+
+`newCachedThreadPool()`:创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果没有可用的线程,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。
+
+`newSingleThreadExecutor()`创建一个单线程化的Executor。
+
+`newScheduledThreadPool(int corePoolSize)`创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。
+
+类看起来功能还是比较强大的,又用到了工厂模式、又有比较强的扩展性,重要的是用起来还比较方便,如:
+
+ExecutorService executor = Executors.newFixedThreadPool(nThreads) ;
+
+
+即可创建一个固定大小的线程池。
+
+但是为什么我说不建议大家使用这个类来创建线程池呢?
+
+我提到的是『不建议』,但是在阿里巴巴Java开发手册中也明确指出,而且用的词是『不允许』使用Executors创建线程池。
+

+
+### Executors存在什么问题
+
+在阿里巴巴Java开发手册中提到,使用Executors创建线程池可能会导致OOM(OutOfMemory ,内存溢出),但是并没有说明为什么,那么接下来我们就来看一下到底为什么不允许使用Executors?
+
+我们先来一个简单的例子,模拟一下使用Executors导致OOM的情况。
+
+/**
+ * @author Hollis
+ */
+public class ExecutorsDemo {
+ private static ExecutorService executor = Executors.newFixedThreadPool(15);
+ public static void main(String[] args) {
+ for (int i = 0; i < Integer.MAX_VALUE; i++) {
+ executor.execute(new SubThread());
+ }
+ }
+}
+
+class SubThread implements Runnable {
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(10000);
+ } catch (InterruptedException e) {
+ //do nothing
+ }
+ }
+}
+
+
+通过指定JVM参数:`-Xmx8m -Xms8m` 运行以上代码,会抛出OOM:
+
+Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
+ at java.util.concurrent.LinkedBlockingQueue.offer(LinkedBlockingQueue.java:416)
+ at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1371)
+ at com.hollis.ExecutorsDemo.main(ExecutorsDemo.java:16)
+
+
+以上代码指出,`ExecutorsDemo.java`的第16行,就是代码中的`executor.execute(new SubThread());`。
+
+### Executors为什么存在缺陷
+
+通过上面的例子,我们知道了`Executors`创建的线程池存在OOM的风险,那么到底是什么原因导致的呢?我们需要深入`Executors`的源码来分析一下。
+
+其实,在上面的报错信息中,我们是可以看出蛛丝马迹的,在以上的代码中其实已经说了,真正的导致OOM的其实是`LinkedBlockingQueue.offer`方法。
+
+Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
+ at java.util.concurrent.LinkedBlockingQueue.offer(LinkedBlockingQueue.java:416)
+ at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1371)
+ at com.hollis.ExecutorsDemo.main(ExecutorsDemo.java:16)
+
+
+如果读者翻看代码的话,也可以发现,其实底层确实是通过`LinkedBlockingQueue`实现的:
+
+public static ExecutorService newFixedThreadPool(int nThreads) {
+ return new ThreadPoolExecutor(nThreads, nThreads,
+ 0L, TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<Runnable>());
+
+
+如果读者对Java中的阻塞队列有所了解的话,看到这里或许就能够明白原因了。
+
+Java中的`BlockingQueue`主要有两种实现,分别是`ArrayBlockingQueue` 和 `LinkedBlockingQueue`。
+
+`ArrayBlockingQueue`是一个用数组实现的有界阻塞队列,必须设置容量。
+
+`LinkedBlockingQueue`是一个用链表实现的有界阻塞队列,容量可以选择进行设置,不设置的话,将是一个无边界的阻塞队列,最大长度为`Integer.MAX_VALUE`。
+
+这里的问题就出在:**不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE。**也就是说,如果我们不设置`LinkedBlockingQueue`的容量的话,其默认容量将会是`Integer.MAX_VALUE`。
+
+而`newFixedThreadPool`中创建`LinkedBlockingQueue`时,并未指定容量。此时,`LinkedBlockingQueue`就是一个无边界队列,对于一个无边界队列来说,是可以不断的向队列中加入任务的,这种情况下就有可能因为任务过多而导致内存溢出问题。
+
+上面提到的问题主要体现在`newFixedThreadPool`和`newSingleThreadExecutor`两个工厂方法上,并不是说`newCachedThreadPool`和`newScheduledThreadPool`这两个方法就安全了,这两种方式创建的最大线程数可能是`Integer.MAX_VALUE`,而创建这么多线程,必然就有可能导致OOM。
+
+### 创建线程池的正确姿势
+
+避免使用Executors创建线程池,主要是避免使用其中的默认实现,那么我们可以自己直接调用`ThreadPoolExecutor`的构造函数来自己创建线程池。在创建的同时,给`BlockQueue`指定容量就可以了。
+
+private static ExecutorService executor = new ThreadPoolExecutor(10, 10,
+ 60L, TimeUnit.SECONDS,
+ new ArrayBlockingQueue(10));
+
+
+这种情况下,一旦提交的线程数超过当前可用线程数时,就会抛出`java.util.concurrent.RejectedExecutionException`,这是因为当前线程池使用的队列是有边界队列,队列已经满了便无法继续处理新的请求。但是异常(Exception)总比发生错误(Error)要好。
+
+除了自己定义`ThreadPoolExecutor`外。还有其他方法。这个时候第一时间就应该想到开源类库,如apache和guava等。
+
+作者推荐使用guava提供的ThreadFactoryBuilder来创建线程池。
+
+public class ExecutorsDemo {
+
+ private static ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
+ .setNameFormat("demo-pool-%d").build();
+
+ private static ExecutorService pool = new ThreadPoolExecutor(5, 200,
+ 0L, TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
+
+ public static void main(String[] args) {
+
+ for (int i = 0; i < Integer.MAX_VALUE; i++) {
+ pool.execute(new SubThread());
+ }
+ }
+}
+
+
+通过上述方式创建线程时,不仅可以避免OOM的问题,还可以自定义线程名称,更加方便的出错的时候溯源。
+
+思考题,文中作者说:发生异常(Exception)要比发生错误(Error)好,为什么这么说?
+
+文中提到的《阿里巴巴Java开发手册》,请关注公众号Hollis,回复:手册。即可获得完整版PDF。
+
+ [1]: https://mp.weixin.qq.com/s/-89-CcDnSLBYy3THmcLEdQ
+ [2]: http://www.hollischuang.com/wp-content/uploads/2018/10/15406248096737.jpg
\ No newline at end of file
diff --git a/docs/basics/java-basic/CET-UTC-GMT-CST.md b/docs/basics/java-basic/CET-UTC-GMT-CST.md
index 37e0402b..86872a67 100644
--- a/docs/basics/java-basic/CET-UTC-GMT-CST.md
+++ b/docs/basics/java-basic/CET-UTC-GMT-CST.md
@@ -16,5 +16,7 @@
### 关系
CET=UTC/GMT + 1小时
+
CST=UTC/GMT +8 小时
-CST=CET+9
\ No newline at end of file
+
+CST=CET + 7 小时
diff --git a/docs/basics/java-basic/Runtime-Constant-Pool.md b/docs/basics/java-basic/Runtime-Constant-Pool.md
new file mode 100644
index 00000000..cb4d7531
--- /dev/null
+++ b/docs/basics/java-basic/Runtime-Constant-Pool.md
@@ -0,0 +1,37 @@
+运行时常量池( Runtime Constant Pool)是每一个类或接口的常量池( Constant_Pool)的运行时表示形式。
+
+它包括了若干种不同的常量:从编译期可知的数值字面量到必须运行期解析后才能获得的方法或字段引用。运行时常量池扮演了类似传统语言中符号表( SymbolTable)的角色,不过它存储数据范围比通常意义上的符号表要更为广泛。
+
+每一个运行时常量池都分配在 Java 虚拟机的方法区之中,在类和接口被加载到虚拟机后,对应的运行时常量池就被创建出来。
+
+以上,是Java虚拟机规范中关于运行时常量池的定义。
+
+### 运行时常量池在JDK各个版本中的实现
+
+根据Java虚拟机规范约定:每一个运行时常量池都在Java虚拟机的方法区中分配,在加载类和接口到虚拟机后,就创建对应的运行时常量池。
+
+在不同版本的JDK中,运行时常量池所处的位置也不一样。以HotSpot为例:
+
+在JDK 1.7之前,方法区位于堆内存的永久代中,运行时常量池作为方法区的一部分,也处于永久代中。
+
+因为使用永久代实现方法区可能导致内存泄露问题,所以,从JDK1.7开始,JVM尝试解决这一问题,在1.7中,将原本位于永久代中的运行时常量池移动到堆内存中。(永久代在JDK 1.7并没有完全移除,只是原来方法区中的运行时常量池、类的静态变量等移动到了堆内存中。)
+
+在JDK 1.8中,彻底移除了永久代,方法区通过元空间的方式实现。随之,运行时常量池也在元空间中实现。
+
+### 运行时常量池中常量的来源
+
+运行时常量池中包含了若干种不同的常量:
+
+编译期可知的字面量和符号引用(来自Class常量池)
+运行期解析后可获得的常量(如String的intern方法)
+
+所以,运行时常量池中的内容包含:Class常量池中的常量、字符串常量池中的内容
+
+### 运行时常量池、Class常量池、字符串常量池的区别与联系
+
+
+虚拟机启动过程中,会将各个Class文件中的常量池载入到运行时常量池中。
+
+所以, Class常量池只是一个媒介场所。在JVM真的运行时,需要把常量池中的常量加载到内存中,进入到运行时常量池。
+
+字符串常量池可以理解为运行时常量池分出来的部分。加载时,对于class的静态常量池,如果字符串会被装到字符串常量池中。
\ No newline at end of file
diff --git a/docs/basics/java-basic/Wildcard-Character.md b/docs/basics/java-basic/Wildcard-Character.md
index b9e2a1b0..3c173ac0 100644
--- a/docs/basics/java-basic/Wildcard-Character.md
+++ b/docs/basics/java-basic/Wildcard-Character.md
@@ -1,10 +1,10 @@
`限定通配符`对类型进⾏限制, 泛型中有两种限定通配符:
-表示类型的上界,格式为:<? extends T>,即类型必须为T类型或者T子类
-表示类型的下界,格式为:<? super T>,即类型必须为T类型或者T的父类
+表示类型的上界,格式为:`<? extends T>`,即类型必须为T类型或者T子类
+表示类型的下界,格式为:`<? super T>`,即类型必须为T类型或者T的父类
泛型类型必须⽤限定内的类型来进⾏初始化,否则会导致编译错误。
-`⾮限定通配符`表⽰可以⽤任意泛型类型来替代,类型为
\ No newline at end of file
+`⾮限定通配符`表⽰可以⽤任意泛型类型来替代,类型为``
\ No newline at end of file
diff --git a/docs/basics/java-basic/_sidebar.md b/docs/basics/java-basic/_sidebar.md
deleted file mode 100644
index ca96911f..00000000
--- a/docs/basics/java-basic/_sidebar.md
+++ /dev/null
@@ -1,357 +0,0 @@
-
-* 基本数据类型
-
- * [8种基本数据类型](/basics/java-basic/basic-data-types.md)
-
- * [整型中byte、short、int、long的取值范围](/basics/java-basic/integer-scope.md)
-
- * [什么是浮点型?](/basics/java-basic/float.md)
-
- * [什么是单精度和双精度?](/basics/java-basic/single-double-float.md)
-
- * [为什么不能用浮点型表示金额?](/basics/java-basic/float-amount.md)
-
-* 自动拆装箱
-
- * [自动拆装箱](/basics/java-basic/boxing-unboxing.md)
-
- * [Integer的缓存机制](/basics/java-basic/integer-cache.md)
-
- * 如何正确定义接口的返回值(boolean/Boolean)类型及命名(success/Success)
-
-* String
-
- * [字符串的不可变性](/basics/java-basic/final-string.md)
-
- * [JDK 6和JDK 7中substring的原理及区别](/basics/java-basic/substring.md)
-
- * replaceFirst、replaceAll、replace区别
-
- * [String对“+”的重载](/basics/java-basic/string-append.md)
-
- * [字符串拼接的几种方式和区别](/basics/java-basic/string-concat.md)
-
- * [String.valueOf和Integer.toString的区别](/basics/java-basic/value-of-vs-to-string.md)
-
- * [switch对String的支持](/basics/java-basic/switch-string.md)
-
- * 字符串池
-
- * 常量池(运行时常量池、Class常量池)
-
- * intern
-
-* Java中各种关键字
-
- * transient
-
- * instanceof
-
- * volatile
-
- * synchronized
-
- * final
-
- * static
-
- * const
-
-* 集合类
-
- * [Collection和Collections区别](/basics/java-basic/Collection-vs-Collections.md)
-
- * 常用集合类的使用
-
- * [Set和List区别?](/basics/java-basic/set-vs-list.md)
-
- * [ArrayList和LinkedList和Vector的区别](/basics/java-basic/arraylist-vs-linkedlist-vs-vector.md)
-
- * [SynchronizedList和Vector的区别](/basics/java-basic/synchronizedlist-vector.md)
-
- * [Set如何保证元素不重复?](/basics/java-basic/set-repetition.md)
-
- * [HashMap、HashTable、ConcurrentHashMap区别](/basics/java-basic/HashMap-HashTable-ConcurrentHashMap.md)
-
- * Java 8中Map相关的红黑树的引用背景、原理等
-
- * HashMap的容量、扩容、hash等原理
-
- * [Java 8中stream相关用法](/basics/java-basic/stream.md)
-
- * Apache集合处理工具类的使用
-
- * 不同版本的JDK中HashMap的实现的区别以及原因
-
- * [Arrays.asList获得的List使用时需要注意什么](/basics/java-basic/Arrays-asList.md)
-
- * Collection如何迭代
-
- * [Enumeration和Iterator区别](/basics/java-basic/Enumeration-vs-Iterator.md)
-
- * 如何在遍历的同时删除ArrayList中的元素
-
- * [fail-fast 和 fail-safe](/basics/java-basic/fail-fast-vs-fail-safe.md)
-
- * [CopyOnWriteArrayList](/basics/java-basic/CopyOnWriteArrayList.md)
-
- * [ConcurrentSkipListMap](/basics/java-basic/ConcurrentSkipListMap.md)
-
-* 枚举
-
- * [枚举的用法](/basics/java-basic/enum-usage.md)
-
- * [枚举的实现](/basics/java-basic/enum-impl.md)
-
- * [枚举与单例](/basics/java-basic/enum-singleton.md)
-
- * Enum类
-
- * [Java枚举如何比较](/basics/java-basic/enum-compare.md)
-
- * [switch对枚举的支持](/basics/java-basic/enum-switch.md)
-
- * [枚举的序列化如何实现](/basics/java-basic/enum-serializable.md)
-
- * [枚举的线程安全性问题](/basics/java-basic/enum-thread-safe.md)
-
-* IO
-
- * [字符流、字节流](/basics/java-basic/byte-stream-vs-character-stream.md)
-
- * [输入流、输出流](/basics/java-basic/input-stream-vs-output-stream.md)
-
- * 字节流和字符流之间的相互转换
-
- * [同步、异步](/basics/java-basic/synchronized-vs-asynchronization.md)
-
- * [阻塞、非阻塞](/basics/java-basic/block-vs-non-blocking.md)
-
- * [Linux 5种IO模型](/basics/java-basic/linux-io.md)
-
- * [BIO、NIO和AIO的区别、三种IO的用法与原理](/basics/java-basic/bio-vs-nio-vs-aio.md)
-
- * netty
-
-* 反射
-
- * [反射](/basics/java-basic/reflection.md)与工厂模式、
-
- * [反射有什么作用](/basics/java-basic/usage-of-reflection.md)
-
- * [Class类](/basics/java-basic/Class.md)
-
- * `java.lang.reflect.*`
-
-* 动态代理
-
- * [静态代理](/basics/java-basic/static-proxy.md)
-
- * [动态代理](/basics/java-basic/dynamic-proxy.md)
-
- * [动态代理和反射的关系](/basics/java-basic/dynamic-proxy-vs-reflection.md)
-
- * [动态代理的几种实现方式](/basics/java-basic/dynamic-proxy-implementation.md)
-
- * [AOP](/basics/java-basic/aop-vs-proxy.md)
-
-* 序列化
-
- * [什么是序列化与反序列化](basics/java-basic/serialize.md)
-
- * [Java如何实现序列化与反序列化](basics/java-basic/serialize-in-java.md)
-
- * [Serializable 和 Externalizable 有何不同](basics/java-basic/diff-serializable-vs-externalizable.md)
-
- * 为什么序列化
-
- * [serialVersionUID](basics/java-basic/serialVersionUID.md)
-
- * [为什么serialVersionUID不能随便改](basics/java-basic/serialVersionUID-modify.md)
-
- * [transient](basics/java-basic/transient.md)
-
- * [序列化底层原理](basics/java-basic/serialize-principle.md)
-
- * [序列化如何破坏单例模式](basics/java-basic/serialize-singleton.md)
-
- * [protobuf](basics/java-basic/protobuf.md)
-
- * 为什么说序列化并不安全
-
-* 注解
-
- * [元注解](/basics/java-basic/meta-annotation.md)
-
- * [自定义注解](/basics/java-basic/custom-annotation.md)
-
- * Java中常用注解使用
-
- * 注解与反射的结合
-
- * [如何自定义一个注解?](/basics/java-basic/create-annotation.md)
-
- * [Spring常用注解](/basics/java-basic/annotation-in-spring.md)
-
-* 泛型
-
- * [什么是泛型](/basics/java-basic/generics.md)
-
- * [类型擦除](/basics/java-basic/type-erasue.md)
-
- * [泛型带来的问题](/basics/java-basic/generics-problem.md)
-
- * [泛型中K T V E ? object等的含义](/basics/java-basic/k-t-v-e.md)
-
- * 泛型各种用法
-
- * [限定通配符和非限定通配符](/basics/java-basic/Wildcard-Character.md)
-
- * [上下界限定符extends 和 super](/basics/java-basic/extends-vs-super.md)
-
- * [`List