From f68597c89f22d3fa9d161866f599dc157fb16aa5 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Mon, 26 Feb 2018 14:16:19 +0800 Subject: [PATCH 001/323] :bulb: Documenting source code. --- src/test/java/com/crossoverjie/proxy/JDKProxyTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/crossoverjie/proxy/JDKProxyTest.java b/src/test/java/com/crossoverjie/proxy/JDKProxyTest.java index 7bea80a4..56aaab44 100644 --- a/src/test/java/com/crossoverjie/proxy/JDKProxyTest.java +++ b/src/test/java/com/crossoverjie/proxy/JDKProxyTest.java @@ -12,7 +12,7 @@ import java.lang.reflect.Proxy; /** - * Function:JDK 代理单测 + * Function: JDK 代理单测 * * @author crossoverJie * Date: 23/12/2017 22:40 From 2881c77e422219ba2e5d3a05a697665bfe7edb81 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Tue, 27 Feb 2018 23:07:40 +0800 Subject: [PATCH 002/323] :bulb: Documenting source code. --- MD/SpringAOP.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MD/SpringAOP.md b/MD/SpringAOP.md index a7da979a..808cfa8f 100644 --- a/MD/SpringAOP.md +++ b/MD/SpringAOP.md @@ -114,7 +114,7 @@ public class CustomizeHandle implements InvocationHandler { } ``` -首先传入被代理类的类类型构建代理处理器。接着使用 Proxy的`newProxyInstance` 方法动态创建代理类。第一个参数为类加载器,第二个参数为代理类需要实现的接口列表,最后一个则是处理器。 +首先传入被代理类的类类型构建代理处理器。接着使用 `Proxy` 的`newProxyInstance` 方法动态创建代理类。第一个参数为类加载器,第二个参数为代理类需要实现的接口列表,最后一个则是处理器。 其实代理类是由 From fdf7290b49736a1c3f491c7703b45827b0d6b4c7 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Wed, 28 Feb 2018 18:40:54 +0800 Subject: [PATCH 003/323] :sparkles: two stack implement queue --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index aec5ad74..148739a2 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ - [限流算法](https://github.com/crossoverJie/Java-Interview/blob/master/MD/Limiting.md) - [三种方式反向打印单向链表](https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/algorithm/ReverseNode.java) - [合并两个排好序的链表](https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/algorithm/MergeTwoSortedLists.java) +- [两个栈实现队列](https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/algorithm/TwoStackQueue.java) ### 附加技能 From 95c9caad5c0ea186548ab9f1342588d718fb4745 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Fri, 2 Mar 2018 10:11:50 +0800 Subject: [PATCH 004/323] :bulb: Documenting source code. --- MD/newObject.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MD/newObject.md b/MD/newObject.md index f6090969..50c80d35 100644 --- a/MD/newObject.md +++ b/MD/newObject.md @@ -54,7 +54,7 @@ 当在 `Eden` 区分配内存不足时,则会发生 `minorGC` ,由于 `Java` 对象多数是**朝生夕灭**的特性,所以 `minorGC` 通常会比较频繁,效率也比较高。 -当发生 `minorGC` 时,JVM 会根据[复制算法](https://github.com/crossoverJie/Java-Interview/blob/145064ecf867e898ad025f3467b7ada9086fc8dd/MD/GarbageCollection.md#%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95)将存活的对象拷贝到另一个未使用的 `Survivor` 区,如果 `Survivor` 区内存不足时,则会使用分配担保策略将对象移动到老年代中。 +当发生 `minorGC` 时,JVM 会根据[复制算法](https://github.com/crossoverJie/Java-Interview/blob/master/MD/GarbageCollection.md#%E5%A4%8D%E5%88%B6%E7%AE%97%E6%B3%95)将存活的对象拷贝到另一个未使用的 `Survivor` 区,如果 `Survivor` 区内存不足时,则会使用分配担保策略将对象移动到老年代中。 谈到 `minorGC` 时,就不得不提到 `fullGC(majorGC)` ,这是指发生在老年代的 `GC` ,不论是效率还是速度都比 `minorGC` 慢的多,回收时还会发生 `stop the world` 使程序发生停顿,所以应当尽量避免发生 `fullGC` 。 From 5d6ebe6db93713fbaf716d8bd856dd618359c791 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Mon, 5 Mar 2018 23:17:52 +0800 Subject: [PATCH 005/323] :sparkles: Introducing new features. ClassLoader --- .../crossoverjie/classloader/ChildClass.java | 14 +++++++++++++ .../com/crossoverjie/classloader/Main.java | 14 +++++++++++++ .../crossoverjie/classloader/SuperClass.java | 20 +++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 src/main/java/com/crossoverjie/classloader/ChildClass.java create mode 100644 src/main/java/com/crossoverjie/classloader/Main.java create mode 100644 src/main/java/com/crossoverjie/classloader/SuperClass.java diff --git a/src/main/java/com/crossoverjie/classloader/ChildClass.java b/src/main/java/com/crossoverjie/classloader/ChildClass.java new file mode 100644 index 00000000..16331c3f --- /dev/null +++ b/src/main/java/com/crossoverjie/classloader/ChildClass.java @@ -0,0 +1,14 @@ +package com.crossoverjie.classloader; + +/** + * Function: + * + * @author crossoverJie + * Date: 05/03/2018 23:11 + * @since JDK 1.8 + */ +public class ChildClass extends SuperClass { + static { + System.out.println("ChildClass init"); + } +} diff --git a/src/main/java/com/crossoverjie/classloader/Main.java b/src/main/java/com/crossoverjie/classloader/Main.java new file mode 100644 index 00000000..24f6d0c9 --- /dev/null +++ b/src/main/java/com/crossoverjie/classloader/Main.java @@ -0,0 +1,14 @@ +package com.crossoverjie.classloader; + +/** + * Function: + * + * @author crossoverJie + * Date: 05/03/2018 23:12 + * @since JDK 1.8 + */ +public class Main { + public static void main(String[] args) { + System.out.println(ChildClass.A); + } +} diff --git a/src/main/java/com/crossoverjie/classloader/SuperClass.java b/src/main/java/com/crossoverjie/classloader/SuperClass.java new file mode 100644 index 00000000..d0d67a15 --- /dev/null +++ b/src/main/java/com/crossoverjie/classloader/SuperClass.java @@ -0,0 +1,20 @@ +package com.crossoverjie.classloader; + +/** + * Function: + * + * @author crossoverJie + * Date: 05/03/2018 23:11 + * @since JDK 1.8 + */ +public class SuperClass { + + /** + * 如果使用了 final 修饰的常量,再使用时父类也不会初始化 + */ + public static int A = 1; + + static { + System.out.println("SuperClass init"); + } +} From b6f81a0b3d7b3a4bcb5a1bce2f4dad1cf89d3488 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Tue, 6 Mar 2018 21:21:25 +0800 Subject: [PATCH 006/323] :bulb: Documenting source code. --- src/main/java/com/crossoverjie/actual/TwoThread.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/crossoverjie/actual/TwoThread.java b/src/main/java/com/crossoverjie/actual/TwoThread.java index cdeea05c..5ef8195e 100644 --- a/src/main/java/com/crossoverjie/actual/TwoThread.java +++ b/src/main/java/com/crossoverjie/actual/TwoThread.java @@ -67,6 +67,7 @@ public void run() { } } else { try { + //防止线程空转 Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); @@ -104,6 +105,7 @@ public void run() { } } else { try { + //防止线程空转 Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); From 2aa2c56d010d7e6b0c086719f039243954fd46ad Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Tue, 6 Mar 2018 22:01:59 +0800 Subject: [PATCH 007/323] :memo: Writing docs. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 148739a2..3d6a7f60 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ - [线程间通信](https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/actual/ThreadCommunication.java) - [交替打印奇偶数](https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/actual/TwoThread.java) -### Java 底层 +### JMM(Java 内存模型) - [Java 运行时内存划分](https://github.com/crossoverJie/Java-Interview/blob/master/MD/MemoryAllocation.md) - [类加载机制](https://github.com/crossoverJie/Java-Interview/blob/master/MD/ClassLoad.md) - [OOM 分析](https://github.com/crossoverJie/Java-Interview/blob/master/MD/OOM-analysis.md) From 2dcdfaa86a034b8f9d9f6a9029ca483496185733 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Tue, 6 Mar 2018 23:17:02 +0800 Subject: [PATCH 008/323] :sparkles: Introducing new features. thread state --- 79884.log | 93 +++++++++++++++++++ .../crossoverjie/concurrent/ThreadState.java | 67 +++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 79884.log create mode 100644 src/main/java/com/crossoverjie/concurrent/ThreadState.java diff --git a/79884.log b/79884.log new file mode 100644 index 00000000..d09b8c77 --- /dev/null +++ b/79884.log @@ -0,0 +1,93 @@ +2018-03-06 23:11:05 +Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.131-b11 mixed mode): + +"Attach Listener" #15 daemon prio=9 os_prio=31 tid=0x00007ffd7b08b800 nid=0x1407 waiting on condition [0x0000000000000000] + java.lang.Thread.State: RUNNABLE + +"DestroyJavaVM" #14 prio=5 os_prio=31 tid=0x00007ffd7b001800 nid=0x1c03 waiting on condition [0x0000000000000000] + java.lang.Thread.State: RUNNABLE + +"Blocked2" #13 prio=5 os_prio=31 tid=0x00007ffd7b08b000 nid=0x5503 waiting for monitor entry [0x00007000083d1000] + java.lang.Thread.State: BLOCKED (on object monitor) + at com.crossoverjie.concurrent.ThreadState$Blocked.run(ThreadState.java:59) + - waiting to lock <0x000000079576cd60> (a java.lang.Class for com.crossoverjie.concurrent.ThreadState$Blocked) + at java.lang.Thread.run(Thread.java:748) + +"Blocked1" #12 prio=5 os_prio=31 tid=0x00007ffd7b08a000 nid=0x5303 waiting on condition [0x00007000082ce000] + java.lang.Thread.State: TIMED_WAITING (sleeping) + at java.lang.Thread.sleep(Native Method) + at com.crossoverjie.concurrent.ThreadState$Blocked.run(ThreadState.java:59) + - locked <0x000000079576cd60> (a java.lang.Class for com.crossoverjie.concurrent.ThreadState$Blocked) + at java.lang.Thread.run(Thread.java:748) + +"Waiting" #11 prio=5 os_prio=31 tid=0x00007ffd7b089800 nid=0x5103 in Object.wait() [0x00007000081cb000] + java.lang.Thread.State: WAITING (on object monitor) + at java.lang.Object.wait(Native Method) + - waiting on <0x0000000795768db0> (a java.lang.Class for com.crossoverjie.concurrent.ThreadState$Waiting) + at java.lang.Object.wait(Object.java:502) + at com.crossoverjie.concurrent.ThreadState$Waiting.run(ThreadState.java:42) + - locked <0x0000000795768db0> (a java.lang.Class for com.crossoverjie.concurrent.ThreadState$Waiting) + at java.lang.Thread.run(Thread.java:748) + +"TimeWaiting" #10 prio=5 os_prio=31 tid=0x00007ffd7b82c800 nid=0x4f03 waiting on condition [0x00007000080c8000] + java.lang.Thread.State: TIMED_WAITING (sleeping) + at java.lang.Thread.sleep(Native Method) + at com.crossoverjie.concurrent.ThreadState$TimeWaiting.run(ThreadState.java:27) + at java.lang.Thread.run(Thread.java:748) + +"Monitor Ctrl-Break" #9 daemon prio=5 os_prio=31 tid=0x00007ffd7a97e000 nid=0x4d03 runnable [0x0000700007fc5000] + java.lang.Thread.State: RUNNABLE + at java.net.PlainSocketImpl.socketAccept(Native Method) + at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409) + at java.net.ServerSocket.implAccept(ServerSocket.java:545) + at java.net.ServerSocket.accept(ServerSocket.java:513) + at com.intellij.rt.execution.application.AppMain$1.run(AppMain.java:79) + at java.lang.Thread.run(Thread.java:748) + +"Service Thread" #8 daemon prio=9 os_prio=31 tid=0x00007ffd7a837800 nid=0x4903 runnable [0x0000000000000000] + java.lang.Thread.State: RUNNABLE + +"C1 CompilerThread2" #7 daemon prio=9 os_prio=31 tid=0x00007ffd7b030800 nid=0x4703 waiting on condition [0x0000000000000000] + java.lang.Thread.State: RUNNABLE + +"C2 CompilerThread1" #6 daemon prio=9 os_prio=31 tid=0x00007ffd7b029800 nid=0x4503 waiting on condition [0x0000000000000000] + java.lang.Thread.State: RUNNABLE + +"C2 CompilerThread0" #5 daemon prio=9 os_prio=31 tid=0x00007ffd7a813800 nid=0x4303 waiting on condition [0x0000000000000000] + java.lang.Thread.State: RUNNABLE + +"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007ffd7a839000 nid=0x4103 runnable [0x0000000000000000] + java.lang.Thread.State: RUNNABLE + +"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007ffd7b00b800 nid=0x3103 in Object.wait() [0x00007000078b0000] + java.lang.Thread.State: WAITING (on object monitor) + at java.lang.Object.wait(Native Method) + - waiting on <0x0000000795588ec8> (a java.lang.ref.ReferenceQueue$Lock) + at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143) + - locked <0x0000000795588ec8> (a java.lang.ref.ReferenceQueue$Lock) + at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164) + at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209) + +"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007ffd7a800800 nid=0x2f03 in Object.wait() [0x00007000077ad000] + java.lang.Thread.State: WAITING (on object monitor) + at java.lang.Object.wait(Native Method) + - waiting on <0x0000000795586b68> (a java.lang.ref.Reference$Lock) + at java.lang.Object.wait(Object.java:502) + at java.lang.ref.Reference.tryHandlePending(Reference.java:191) + - locked <0x0000000795586b68> (a java.lang.ref.Reference$Lock) + at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153) + +"VM Thread" os_prio=31 tid=0x00007ffd7a01a800 nid=0x2d03 runnable + +"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007ffd7b005800 nid=0x2503 runnable + +"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007ffd7b006000 nid=0x2703 runnable + +"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007ffd7b006800 nid=0x2903 runnable + +"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007ffd7b808000 nid=0x2b03 runnable + +"VM Periodic Task Thread" os_prio=31 tid=0x00007ffd7a03e000 nid=0x4b03 waiting on condition + +JNI global references: 21 + diff --git a/src/main/java/com/crossoverjie/concurrent/ThreadState.java b/src/main/java/com/crossoverjie/concurrent/ThreadState.java new file mode 100644 index 00000000..ea0ec909 --- /dev/null +++ b/src/main/java/com/crossoverjie/concurrent/ThreadState.java @@ -0,0 +1,67 @@ +package com.crossoverjie.concurrent; + +import com.crossoverjie.classloader.Main; + +/** + * Function: 线程状态测试 + * + * @author crossoverJie + * Date: 06/03/2018 22:56 + * @since JDK 1.8 + */ +public class ThreadState { + + public static void main(String[] args) { + new Thread(new TimeWaiting(),"TimeWaiting").start(); + new Thread(new Waiting(),"Waiting").start(); + new Thread(new Blocked(),"Blocked1").start(); + new Thread(new Blocked(),"Blocked2").start(); + } + + static class TimeWaiting implements Runnable{ + + @Override + public void run() { + while (true){ + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + + static class Waiting implements Runnable{ + + @Override + public void run() { + while (true){ + synchronized (Waiting.class){ + try { + Waiting.class.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + } + + + static class Blocked implements Runnable{ + + @Override + public void run() { + while (true){ + synchronized (Blocked.class){ + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + } +} From 1a0b60c9acb4fee3b337017a44acf07e033f9521 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Wed, 7 Mar 2018 11:37:02 +0800 Subject: [PATCH 009/323] :pencil2: Fixing typos. --- MD/Threadcore.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/MD/Threadcore.md b/MD/Threadcore.md index 8f50d1b0..c2c964d7 100644 --- a/MD/Threadcore.md +++ b/MD/Threadcore.md @@ -1,7 +1,7 @@ # Java 多线程三大核心 ## 原子性 -Java 的原子性就和数据库事物的原子性差不多,一个操作中要么全部执行成功或者失败。 +`Java` 的原子性就和数据库事物的原子性差不多,一个操作中要么全部执行成功或者失败。 `JMM` 只是保证了基本的原子性,但类似于 `i++` 之类的操作,看似是原子操作,其实里面涉及到: @@ -11,7 +11,7 @@ Java 的原子性就和数据库事物的原子性差不多,一个操作中要 这三步操作,所以想要实现 `i++` 这样的原子操作就需要用到 `synchronize` 或者是 `lock` 进行加锁处理。 -如果是基础类的自增操作可以使用 `AtomicInteger` 这样的原子类来实现(其本质是利用了 CPU 级别的 的 `CAS` 指令来完成的)。 +如果是基础类的自增操作可以使用 `AtomicInteger` 这样的原子类来实现(其本质是利用了 `CPU` 级别的 的 `CAS` 指令来完成的)。 其中用的最多的方法就是: `incrementAndGet()` 以原子的方式自增。 源码如下: @@ -46,7 +46,7 @@ public final boolean compareAndSet(long expect, long update) { ## 可见性 -现代计算机中,由于 `CPU` 直接从主内存中读取数据的效率不高,所以都会对应的 CPU 高速缓存,先将主内存中的数据读取到缓存中,线程修改数据之后首先更新到缓存,之后才会更新到主内存。如果此时还没有将数据更新到主内存其他的线程此时来读取就是修改之前的数据。 +现代计算机中,由于 `CPU` 直接从主内存中读取数据的效率不高,所以都会对应的 `CPU` 高速缓存,先将主内存中的数据读取到缓存中,线程修改数据之后首先更新到缓存,之后才会更新到主内存。如果此时还没有将数据更新到主内存其他的线程此时来读取就是修改之前的数据。 ![](https://ws2.sinaimg.cn/large/006tKfTcly1fmouu3fpokj31ae0osjt1.jpg) @@ -81,7 +81,7 @@ Java 中可以使用 `volatile` 来保证顺序性,`synchronize 和 lock` 也 #### 双重检查锁的单例模式 -可以用 volatile 实现一个双重检查锁的单例模式: +可以用 `volatile` 实现一个双重检查锁的单例模式: ```java public class Singleton{ @@ -101,14 +101,14 @@ public class Singleton{ } ``` -这里的 volatile 关键字主要是为了防止指令重排。 -如果不用 volatile ,`singleton = new Singleton();`,这段代码其实是分为三步: +这里的 `volatile` 关键字主要是为了防止指令重排。 +如果不用 `volatile` ,`singleton = new Singleton();`,这段代码其实是分为三步: - 分配内存空间。(1) - 初始化对象。(2) - 将 `singleton` 对象指向分配的内存地址。(3) -加上 volatile 是为了让以上的三步操作顺序执行,反之有可能第二步在第三步之前被执行就有可能某个线程拿到的单例对象是还没有初始化的,以致于报错。 +加上 `volatile` 是为了让以上的三步操作顺序执行,反之有可能第二步在第三步之前被执行就有可能某个线程拿到的单例对象是还没有初始化的,以致于报错。 #### 控制停止线程的标记 @@ -132,7 +132,7 @@ private void stop(){ 这里主要利用的是 `volatile` 的内存可见性。 总结一下: -- volatile 关键字只能保证可见性,顺序性,**不能保证原子性**。 +- `volatile` 关键字只能保证可见性,顺序性,**不能保证原子性**。 From 4ec03bb20df115be86c13872f57795298e0b3859 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Wed, 7 Mar 2018 13:33:19 +0800 Subject: [PATCH 010/323] =?UTF-8?q?:sparkles:=20Introducing=20new=20featur?= =?UTF-8?q?es.=20wait=20notify=20=E4=BA=A4=E6=9B=BF=E6=89=93=E5=8D=B0?= =?UTF-8?q?=E5=A5=87=E5=81=B6=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/crossoverjie/actual/TwoThread.java | 2 + .../actual/TwoThreadWaitNotify.java | 89 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 src/main/java/com/crossoverjie/actual/TwoThreadWaitNotify.java diff --git a/src/main/java/com/crossoverjie/actual/TwoThread.java b/src/main/java/com/crossoverjie/actual/TwoThread.java index 5ef8195e..90c83927 100644 --- a/src/main/java/com/crossoverjie/actual/TwoThread.java +++ b/src/main/java/com/crossoverjie/actual/TwoThread.java @@ -6,6 +6,8 @@ /** * Function: 两个线程交替执行打印 1~100 * + * lock 版 + * * @author crossoverJie * Date: 11/02/2018 10:04 * @since JDK 1.8 diff --git a/src/main/java/com/crossoverjie/actual/TwoThreadWaitNotify.java b/src/main/java/com/crossoverjie/actual/TwoThreadWaitNotify.java new file mode 100644 index 00000000..b8f61aac --- /dev/null +++ b/src/main/java/com/crossoverjie/actual/TwoThreadWaitNotify.java @@ -0,0 +1,89 @@ +package com.crossoverjie.actual; + +/** + * Function:两个线程交替执行打印 1~100 + * 等待通知机制版 + * + * @author crossoverJie + * Date: 07/03/2018 13:19 + * @since JDK 1.8 + */ +public class TwoThreadWaitNotify { + + private int start = 1 ; + + private boolean flag = false ; + + public static void main(String[] args) { + TwoThread twoThread = new TwoThread(); + + Thread t1 = new Thread(new TwoThread.OuNum(twoThread)); + t1.setName("t1"); + + + Thread t2 = new Thread(new TwoThread.JiNum(twoThread)); + t2.setName("t2"); + + t1.start(); + t2.start(); + } + + /** + * 偶数线程 + */ + public static class OuNum implements Runnable { + private TwoThreadWaitNotify number; + + public OuNum(TwoThreadWaitNotify number) { + this.number = number; + } + + @Override + public void run() { + synchronized (TwoThreadWaitNotify.class){ + while (number.start <= 100){ + if (number.flag){ + System.out.println(Thread.currentThread().getName() + "+-+" + number.start); + number.start ++ ; + + number.flag = false ; + try { + TwoThreadWaitNotify.class.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + } + } + } + + + /** + * 奇数线程 + */ + public static class JiNum implements Runnable{ + private TwoThreadWaitNotify number; + + public JiNum(TwoThreadWaitNotify number) { + this.number = number; + } + + @Override + public void run() { + synchronized (TwoThreadWaitNotify.class){ + while (number.start <= 100){ + if (!number.flag){ + System.out.println(Thread.currentThread().getName() + "+-+" + number.start); + number.start ++ ; + + number.flag = true ; + + TwoThreadWaitNotify.class.notify(); + } + } + } + } + } +} From 3a2be9eabb0d754bab9ec0194d643ef19d9468f8 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Wed, 7 Mar 2018 14:20:51 +0800 Subject: [PATCH 011/323] =?UTF-8?q?:sparkles:=20Introducing=20new=20featur?= =?UTF-8?q?es.=20wait=20notify=20=E4=BA=A4=E6=9B=BF=E6=89=93=E5=8D=B0?= =?UTF-8?q?=E5=A5=87=E5=81=B6=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../actual/TwoThreadWaitNotify.java | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/crossoverjie/actual/TwoThreadWaitNotify.java b/src/main/java/com/crossoverjie/actual/TwoThreadWaitNotify.java index b8f61aac..1c69da14 100644 --- a/src/main/java/com/crossoverjie/actual/TwoThreadWaitNotify.java +++ b/src/main/java/com/crossoverjie/actual/TwoThreadWaitNotify.java @@ -10,18 +10,18 @@ */ public class TwoThreadWaitNotify { - private int start = 1 ; + private int start = 1; - private boolean flag = false ; + private boolean flag = false; public static void main(String[] args) { - TwoThread twoThread = new TwoThread(); + TwoThreadWaitNotify twoThread = new TwoThreadWaitNotify(); - Thread t1 = new Thread(new TwoThread.OuNum(twoThread)); + Thread t1 = new Thread(new OuNum(twoThread)); t1.setName("t1"); - Thread t2 = new Thread(new TwoThread.JiNum(twoThread)); + Thread t2 = new Thread(new JiNum(twoThread)); t2.setName("t2"); t1.start(); @@ -40,13 +40,18 @@ public OuNum(TwoThreadWaitNotify number) { @Override public void run() { - synchronized (TwoThreadWaitNotify.class){ - while (number.start <= 100){ - if (number.flag){ - System.out.println(Thread.currentThread().getName() + "+-+" + number.start); - number.start ++ ; - number.flag = false ; + while (number.start <= 100) { + synchronized (TwoThreadWaitNotify.class) { + System.out.println("偶数线程抢到锁了"); + if (number.flag) { + System.out.println(Thread.currentThread().getName() + "+-+偶数" + number.start); + number.start++; + + number.flag = false; + TwoThreadWaitNotify.class.notify(); + + }else { try { TwoThreadWaitNotify.class.wait(); } catch (InterruptedException e) { @@ -63,7 +68,7 @@ public void run() { /** * 奇数线程 */ - public static class JiNum implements Runnable{ + public static class JiNum implements Runnable { private TwoThreadWaitNotify number; public JiNum(TwoThreadWaitNotify number) { @@ -72,15 +77,22 @@ public JiNum(TwoThreadWaitNotify number) { @Override public void run() { - synchronized (TwoThreadWaitNotify.class){ - while (number.start <= 100){ - if (!number.flag){ - System.out.println(Thread.currentThread().getName() + "+-+" + number.start); - number.start ++ ; + while (number.start <= 100) { + synchronized (TwoThreadWaitNotify.class) { + System.out.println("奇数线程抢到锁了"); + if (!number.flag) { + System.out.println(Thread.currentThread().getName() + "+-+奇数" + number.start); + number.start++; - number.flag = true ; + number.flag = true; TwoThreadWaitNotify.class.notify(); + }else { + try { + TwoThreadWaitNotify.class.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } } } } From b0545fd376ab52df362daa402b5829ad05bcac07 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Wed, 7 Mar 2018 23:27:44 +0800 Subject: [PATCH 012/323] =?UTF-8?q?:pencil2:=20Fixing=20typos.=20=E6=B0=B8?= =?UTF-8?q?=E4=B9=85=E4=BB=A3=E6=94=B9=E4=B8=BA=E8=80=81=E5=B9=B4=E4=BB=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MD/GarbageCollection.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MD/GarbageCollection.md b/MD/GarbageCollection.md index 6c185379..a233f8ae 100644 --- a/MD/GarbageCollection.md +++ b/MD/GarbageCollection.md @@ -49,7 +49,7 @@ > 在新生代会使用该算法。 -新生代中分为一个 `Eden` 区和两个 `Survivor` 区。通常两个区域的比例是 `8:1:1` ,使用时会用到 `Eden` 区和其中一个 `Survivor` 区。当发生回收时则会将还存活的对象从 `Eden` ,`Survivor` 区拷贝到另一个 `Survivor` 区,当该区域内存也不足时则会使用分配担保利用永久代来存放内存。 +新生代中分为一个 `Eden` 区和两个 `Survivor` 区。通常两个区域的比例是 `8:1:1` ,使用时会用到 `Eden` 区和其中一个 `Survivor` 区。当发生回收时则会将还存活的对象从 `Eden` ,`Survivor` 区拷贝到另一个 `Survivor` 区,当该区域内存也不足时则会使用分配担保利用老年代来存放内存。 复制算法过程: From 26533eebe67108e912911a74555e0bda7b5ab0dc Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Wed, 7 Mar 2018 23:34:19 +0800 Subject: [PATCH 013/323] :pencil2: Fixing typos. redis --- MD/Java-lock.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MD/Java-lock.md b/MD/Java-lock.md index d370a912..0faa1371 100644 --- a/MD/Java-lock.md +++ b/MD/Java-lock.md @@ -34,6 +34,6 @@ 风险点: -- 如果在 `setNX` 之后释放锁的时候挂掉了那么这个 key 将永远挂起,等到超时之后自动删除,如果在超时时间之类这个操作还没有完成就容易发生并发问题。 +- 如果在 `setNX` 之后释放锁的时候挂掉了那么这个 `key` 将永远挂起,等到超时之后自动删除,如果在超时时间之内这个操作还没有完成就容易发生并发问题(如果超时时间设置过短,任务还没有执行完毕锁就释放了)。 ### 基于 ZK From 2da49892cae23fb42f8cf32cd390b19e78aac3bf Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Thu, 8 Mar 2018 22:10:24 +0800 Subject: [PATCH 014/323] :sparkles: Introducing new features. volatile --- MD/concurrent/volatile.md | 2 ++ .../com/crossoverjie/basic/StringTest.java | 23 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 MD/concurrent/volatile.md create mode 100644 src/main/java/com/crossoverjie/basic/StringTest.java diff --git a/MD/concurrent/volatile.md b/MD/concurrent/volatile.md new file mode 100644 index 00000000..2982af42 --- /dev/null +++ b/MD/concurrent/volatile.md @@ -0,0 +1,2 @@ +# 你应该知道的 volatile + diff --git a/src/main/java/com/crossoverjie/basic/StringTest.java b/src/main/java/com/crossoverjie/basic/StringTest.java new file mode 100644 index 00000000..4c4e9053 --- /dev/null +++ b/src/main/java/com/crossoverjie/basic/StringTest.java @@ -0,0 +1,23 @@ +package com.crossoverjie.basic; + +import java.lang.reflect.Field; + +/** + * Function: + * + * @author crossoverJie + * Date: 08/03/2018 13:56 + * @since JDK 1.8 + */ +public class StringTest { + + public static void main(String[] args) throws NoSuchFieldException { + String a = "123"; + System.out.println("a=" + a); + + a = "456"; + System.out.println("a=" + a); + + Field value = a.getClass().getField("value"); + } +} From 4042e12b35a9bfe9194c9f5d0abb0f387fb1e744 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Fri, 9 Mar 2018 00:30:08 +0800 Subject: [PATCH 015/323] :sparkles: Introducing new features. volatile --- MD/concurrent/volatile.md | 29 ++++++++++++++ .../com/crossoverjie/concurrent/Volatile.java | 40 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 src/main/java/com/crossoverjie/concurrent/Volatile.java diff --git a/MD/concurrent/volatile.md b/MD/concurrent/volatile.md index 2982af42..1ed3354f 100644 --- a/MD/concurrent/volatile.md +++ b/MD/concurrent/volatile.md @@ -1,2 +1,31 @@ # 你应该知道的 volatile +## 前言 + +不管是在面试,还是实际开发中 `volatile` 都是一个应该掌握的技能。 + +首先来看看为什么会出现这个关键字。 + +## 内存可见性 +由于 Java 内存模型(JMM)规定,所有的变量都存放在主内存中,而每个线程都有着自己的工作内存(高速缓存)。 + +线程在工作时,需要将主内存中的数据拷贝到工作内存中。这样对数据的任何操作都是基于工作内存的(效率提高),不能直接操作主内存以及其他线程工作内存中的数据,之后再将更新之后的数据刷新到主内存中。 + +> 这里所提到的主内存可以简单认为是**堆内存**,而工作内存则可以认为是**栈内存**。 + +如下图所示: + +![](https://ws2.sinaimg.cn/large/006tKfTcly1fmouu3fpokj31ae0osjt1.jpg) + +所以在并发运行时会出现线程 B 所读取到的数据是线程 A 更新之前的数据。 + +显然这肯定是会出问题的,因此 `volatile` 的作用出现了: + +> 当一个变量被 `volatile` 修饰时,任何线程对它的写操作都会立即刷新到主内存中,并且会强制让缓存了该变量的线程中的数据清空,必须从主内存重新读取最新数据。 + +### 内存可见性的应用 + + +## 指令重排 + +### 指令重排的的应用 \ No newline at end of file diff --git a/src/main/java/com/crossoverjie/concurrent/Volatile.java b/src/main/java/com/crossoverjie/concurrent/Volatile.java new file mode 100644 index 00000000..80b05512 --- /dev/null +++ b/src/main/java/com/crossoverjie/concurrent/Volatile.java @@ -0,0 +1,40 @@ +package com.crossoverjie.concurrent; + +import java.util.concurrent.TimeUnit; + +/** + * Function: + * + * @author crossoverJie + * Date: 09/03/2018 00:09 + * @since JDK 1.8 + */ +public class Volatile implements Runnable{ + + private static volatile boolean flag = true ; + + @Override + public void run() { + while (flag){ + System.out.println(Thread.currentThread().getName() + "正在运行。。。"); + } + System.out.println(Thread.currentThread().getName() +"执行完毕"); + } + + public static void main(String[] args) throws InterruptedException { + Volatile aVolatile = new Volatile(); + new Thread(aVolatile,"thread A").start(); + + + System.out.println("main 线程正在运行") ; + + TimeUnit.MILLISECONDS.sleep(100) ; + + aVolatile.stopThread(); + + } + + private void stopThread(){ + flag = false ; + } +} From 7f8ab3df158c0b877380774c0c4b32a2a15d97d8 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Fri, 9 Mar 2018 01:20:38 +0800 Subject: [PATCH 016/323] :sparkles: Introducing new features. volatile over --- MD/concurrent/volatile.md | 173 +++++++++++++++++- .../crossoverjie/concurrent/Singleton.java | 28 +++ .../crossoverjie/concurrent/VolatileInc.java | 43 +++++ 3 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/crossoverjie/concurrent/Singleton.java create mode 100644 src/main/java/com/crossoverjie/concurrent/VolatileInc.java diff --git a/MD/concurrent/volatile.md b/MD/concurrent/volatile.md index 1ed3354f..1ea67d28 100644 --- a/MD/concurrent/volatile.md +++ b/MD/concurrent/volatile.md @@ -25,7 +25,178 @@ ### 内存可见性的应用 +当我们需要在两个线程间依据主内存通信时,通信的那个变量就必须的用 volatile 来修饰: + +```java +public class Volatile implements Runnable{ + + private static volatile boolean flag = true ; + + @Override + public void run() { + while (flag){ + System.out.println(Thread.currentThread().getName() + "正在运行。。。"); + } + System.out.println(Thread.currentThread().getName() +"执行完毕"); + } + + public static void main(String[] args) throws InterruptedException { + Volatile aVolatile = new Volatile(); + new Thread(aVolatile,"thread A").start(); + + + System.out.println("main 线程正在运行") ; + + TimeUnit.MILLISECONDS.sleep(100) ; + + aVolatile.stopThread(); + + } + + private void stopThread(){ + flag = false ; + } +} +``` + +主线程在修改了标志位使得线程 A 立即停止,如果没有用 `volatile` 修饰,就有可能出现延迟。 + +但这里有个误区,这样的使用方式容易给人的感觉是: + +> 对 `volatile` 修饰的变量进行并发操作是线程安全的。 + +这里要重点强调,`volatile` 并**不能**保证线程安全性! + +如下程序: + +```java +public class VolatileInc implements Runnable{ + + private static volatile int count = 0 ; //使用 volatile 修饰基本数据内存不能保证原子性 + + //private static AtomicInteger count = new AtomicInteger() ; + + @Override + public void run() { + for (int i=0;i<10000 ;i++){ + count ++ ; + //count.incrementAndGet() ; + } + } + + public static void main(String[] args) throws InterruptedException { + VolatileInc volatileInc = new VolatileInc() ; + Thread t1 = new Thread(volatileInc,"t1") ; + Thread t2 = new Thread(volatileInc,"t2") ; + t1.start(); + //t1.join(); + + t2.start(); + //t2.join(); + for (int i=0;i<10000 ;i++){ + count ++ ; + //count.incrementAndGet(); + } + + + System.out.println("最终Count="+count); + } +} +``` + +当我们三个线程(t1,t2,main)同时对一个 `int` 进行累加时会发现最终的值都会小于 30000。 + +> 这是因为虽然 `volatile` 保证了内存可见性,保证了每个线程拿到的值都是最新值,但 `count ++` 这个操作并不是原子的,这里面涉及到获取值、自增、赋值的操作并不能同时完成。 +> + +- 所以想到达到线程安全可以使这三个线程串行执行(那其实就是单线程,没有发挥多线程的优势)。 + +- 也可以使用 `synchronize` 或者是锁的方式来保证原子性。 + +- 还可以用 `Atomic` 包中 `AtomicInteger` 来替换 `int`,它利用了 `CAS` 算法来保证了原子性。 + ## 指令重排 -### 指令重排的的应用 \ No newline at end of file +内存可见性只是 volatile 的其中一个作用,它还可以防止 JVM 进行指令重排优化。 + +举一个伪代码: + +```java +int a=10 ;//1 +int b=20 ;//2 +int c= a+b ;//3 +``` + +一段特别简单的代码,理想情况下它的执行顺序是:`1>2>3`。但有可能经过 JVM 优化之后的执行顺序变为了 `2>1>3`。 + +可以发现不管 JVM 怎么优化,前提都是保证最终结果不变的情况下进行的。 + +可能这里还看不出有什么问题,那看下一段伪代码: + +```java +private static Map value ; +private static volatile boolean flag = fasle ; + +//以下方法发生在线程 A 中 初始化 Map +public void initMap(){ + //耗时操作 + value = getMapValue() ;//1 + flag = true ;//2 +} + + +//发生在线程 B中 等到 Map 初始化成功进行其他操作 +public void doSomeThing(){ + while(!flag){ + sleep() ; + } + //dosomething + doSomeThing(value); +} + +``` + +这里就能看出问题了,当 `flag` 没有被 `volatile` 修饰时,`JVM` 对 1 和 2 进行重排,导致 value 都还没有被初始化就有可能被线程 B 使用了。 + +所以加上 `volatile` 之后可以防止这样的重排优化,保证业务的正确性。 +### 指令重排的的应用 + +一个经典的使用场景就是双重懒加载的单例模式了: + +```java +public class Singleton { + + private static volatile Singleton singleton; + + private Singleton() { + } + + public static Singleton getInstance() { + if (singleton == null) { + synchronized (Singleton.class) { + if (singleton == null) { + //防止指令重排 + singleton = new Singleton(); + } + } + } + return singleton; + } +} +``` + +这里的 `volatile` 关键字主要是为了防止指令重排。 + +如果不用 ,`singleton = new Singleton();`,这段代码其实是分为三步: +- 分配内存空间。(1) +- 初始化对象。(2) +- 将 `singleton` 对象指向分配的内存地址。(3) + +加上 `volatile` 是为了让以上的三步操作顺序执行,反之有可能第二步在第三步之前被执行就有可能某个线程拿到的单例对象是还没有初始化的,以致于报错。 + +## 总结 + +`volatile` 在 `Java` 并发中用的很多,比如像 `Atomic` 包中的 `value`、以及 `AbstractQueuedLongSynchronizer` 中的 `state` 都是被定义为 `volatile` 来用于保证内存可见性。 + +将这块理解透彻对我们编写并发程序时可以提供很大帮助。 \ No newline at end of file diff --git a/src/main/java/com/crossoverjie/concurrent/Singleton.java b/src/main/java/com/crossoverjie/concurrent/Singleton.java new file mode 100644 index 00000000..88d0fc22 --- /dev/null +++ b/src/main/java/com/crossoverjie/concurrent/Singleton.java @@ -0,0 +1,28 @@ +package com.crossoverjie.concurrent; + +/** + * Function: + * + * @author crossoverJie + * Date: 09/03/2018 01:14 + * @since JDK 1.8 + */ +public class Singleton { + + private static volatile Singleton singleton; + + private Singleton() { + } + + public static Singleton getInstance() { + if (singleton == null) { + synchronized (Singleton.class) { + if (singleton == null) { + //防止指令重排 + singleton = new Singleton(); + } + } + } + return singleton; + } +} diff --git a/src/main/java/com/crossoverjie/concurrent/VolatileInc.java b/src/main/java/com/crossoverjie/concurrent/VolatileInc.java new file mode 100644 index 00000000..2a247776 --- /dev/null +++ b/src/main/java/com/crossoverjie/concurrent/VolatileInc.java @@ -0,0 +1,43 @@ +package com.crossoverjie.concurrent; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Function: + * + * @author crossoverJie + * Date: 09/03/2018 00:34 + * @since JDK 1.8 + */ +public class VolatileInc implements Runnable{ + + private static volatile int count = 0 ; //使用 volatile 修饰基本数据内存不能保证原子性 + + //private static AtomicInteger count = new AtomicInteger() ; + + @Override + public void run() { + for (int i=0;i<10000 ;i++){ + count ++ ; + //count.incrementAndGet() ; + } + } + + public static void main(String[] args) throws InterruptedException { + VolatileInc volatileInc = new VolatileInc() ; + Thread t1 = new Thread(volatileInc,"t1") ; + Thread t2 = new Thread(volatileInc,"t2") ; + t1.start(); + //t1.join(); + + t2.start(); + //t2.join(); + for (int i=0;i<10000 ;i++){ + count ++ ; + //count.incrementAndGet(); + } + + + System.out.println("最终Count="+count); + } +} From 57608cbe4f964c26290d0021ee50e5c2a5f4fbf7 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Fri, 9 Mar 2018 01:31:22 +0800 Subject: [PATCH 017/323] :pencil2: Fixing typos. --- MD/concurrent/volatile.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/MD/concurrent/volatile.md b/MD/concurrent/volatile.md index 1ea67d28..6508097b 100644 --- a/MD/concurrent/volatile.md +++ b/MD/concurrent/volatile.md @@ -2,14 +2,14 @@ ## 前言 -不管是在面试,还是实际开发中 `volatile` 都是一个应该掌握的技能。 +不管是在面试还是实际开发中 `volatile` 都是一个应该掌握的技能。 首先来看看为什么会出现这个关键字。 ## 内存可见性 -由于 Java 内存模型(JMM)规定,所有的变量都存放在主内存中,而每个线程都有着自己的工作内存(高速缓存)。 +由于 `Java` 内存模型(`JMM`)规定,所有的变量都存放在主内存中,而每个线程都有着自己的工作内存(高速缓存)。 -线程在工作时,需要将主内存中的数据拷贝到工作内存中。这样对数据的任何操作都是基于工作内存的(效率提高),不能直接操作主内存以及其他线程工作内存中的数据,之后再将更新之后的数据刷新到主内存中。 +线程在工作时,需要将主内存中的数据拷贝到工作内存中。这样对数据的任何操作都是基于工作内存(效率提高),并且不能直接操作主内存以及其他线程工作内存中的数据,之后再将更新之后的数据刷新到主内存中。 > 这里所提到的主内存可以简单认为是**堆内存**,而工作内存则可以认为是**栈内存**。 @@ -17,12 +17,14 @@ ![](https://ws2.sinaimg.cn/large/006tKfTcly1fmouu3fpokj31ae0osjt1.jpg) -所以在并发运行时会出现线程 B 所读取到的数据是线程 A 更新之前的数据。 +所以在并发运行时可能会出现线程 B 所读取到的数据是线程 A 更新之前的数据。 显然这肯定是会出问题的,因此 `volatile` 的作用出现了: > 当一个变量被 `volatile` 修饰时,任何线程对它的写操作都会立即刷新到主内存中,并且会强制让缓存了该变量的线程中的数据清空,必须从主内存重新读取最新数据。 +*`volatile` 修饰之后并不是让线程直接从主内存中获取数据,依然需要将变量拷贝到工作内存中*。 + ### 内存可见性的应用 当我们需要在两个线程间依据主内存通信时,通信的那个变量就必须的用 volatile 来修饰: From 337845f76fa65dd341e6f0093797440d36b43524 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Fri, 9 Mar 2018 01:33:57 +0800 Subject: [PATCH 018/323] :pencil2: Fixing typos. --- MD/concurrent/volatile.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MD/concurrent/volatile.md b/MD/concurrent/volatile.md index 6508097b..cb56035d 100644 --- a/MD/concurrent/volatile.md +++ b/MD/concurrent/volatile.md @@ -27,7 +27,7 @@ ### 内存可见性的应用 -当我们需要在两个线程间依据主内存通信时,通信的那个变量就必须的用 volatile 来修饰: +当我们需要在两个线程间依据主内存通信时,通信的那个变量就必须的用 `volatile` 来修饰: ```java public class Volatile implements Runnable{ @@ -108,10 +108,10 @@ public class VolatileInc implements Runnable{ 当我们三个线程(t1,t2,main)同时对一个 `int` 进行累加时会发现最终的值都会小于 30000。 -> 这是因为虽然 `volatile` 保证了内存可见性,保证了每个线程拿到的值都是最新值,但 `count ++` 这个操作并不是原子的,这里面涉及到获取值、自增、赋值的操作并不能同时完成。 +> 这是因为虽然 `volatile` 保证了内存可见性,每个线程拿到的值都是最新值,但 `count ++` 这个操作并不是原子的,这里面涉及到获取值、自增、赋值的操作并不能同时完成。 > -- 所以想到达到线程安全可以使这三个线程串行执行(那其实就是单线程,没有发挥多线程的优势)。 +- 所以想到达到线程安全可以使这三个线程串行执行(其实就是单线程,没有发挥多线程的优势)。 - 也可以使用 `synchronize` 或者是锁的方式来保证原子性。 @@ -120,7 +120,7 @@ public class VolatileInc implements Runnable{ ## 指令重排 -内存可见性只是 volatile 的其中一个作用,它还可以防止 JVM 进行指令重排优化。 +内存可见性只是 `volatile` 的其中一个语义,它还可以防止 JVM 进行指令重排优化。 举一个伪代码: From 70a33503b9ee18a923a1e80e85b4fc3d053caa1e Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Fri, 9 Mar 2018 01:35:14 +0800 Subject: [PATCH 019/323] :pencil2: Fixing typos. --- MD/concurrent/volatile.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MD/concurrent/volatile.md b/MD/concurrent/volatile.md index cb56035d..6f1993a5 100644 --- a/MD/concurrent/volatile.md +++ b/MD/concurrent/volatile.md @@ -120,7 +120,7 @@ public class VolatileInc implements Runnable{ ## 指令重排 -内存可见性只是 `volatile` 的其中一个语义,它还可以防止 JVM 进行指令重排优化。 +内存可见性只是 `volatile` 的其中一个语义,它还可以防止 `JVM` 进行指令重排优化。 举一个伪代码: @@ -132,7 +132,7 @@ int c= a+b ;//3 一段特别简单的代码,理想情况下它的执行顺序是:`1>2>3`。但有可能经过 JVM 优化之后的执行顺序变为了 `2>1>3`。 -可以发现不管 JVM 怎么优化,前提都是保证最终结果不变的情况下进行的。 +可以发现不管 JVM 怎么优化,前提都是保证单线程中最终结果不变的情况下进行的。 可能这里还看不出有什么问题,那看下一段伪代码: From ecb4cae91d1f59451c48681dbd47eb6059a6b2df Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Fri, 9 Mar 2018 01:36:37 +0800 Subject: [PATCH 020/323] :pencil2: Fixing typos. --- MD/concurrent/volatile.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MD/concurrent/volatile.md b/MD/concurrent/volatile.md index 6f1993a5..c608af01 100644 --- a/MD/concurrent/volatile.md +++ b/MD/concurrent/volatile.md @@ -159,7 +159,7 @@ public void doSomeThing(){ ``` -这里就能看出问题了,当 `flag` 没有被 `volatile` 修饰时,`JVM` 对 1 和 2 进行重排,导致 value 都还没有被初始化就有可能被线程 B 使用了。 +这里就能看出问题了,当 `flag` 没有被 `volatile` 修饰时,`JVM` 对 1 和 2 进行重排,导致 `value` 都还没有被初始化就有可能被线程 B 使用了。 所以加上 `volatile` 之后可以防止这样的重排优化,保证业务的正确性。 ### 指令重排的的应用 From 220c88548fe3c116a7e8fc9d6ed0153534c0b0f8 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Fri, 9 Mar 2018 01:42:42 +0800 Subject: [PATCH 021/323] :pencil2: Fixing typos. --- MD/concurrent/volatile.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MD/concurrent/volatile.md b/MD/concurrent/volatile.md index c608af01..0ec9200c 100644 --- a/MD/concurrent/volatile.md +++ b/MD/concurrent/volatile.md @@ -1,4 +1,4 @@ -# 你应该知道的 volatile +# 你应该知道的 volatile 关键字 ## 前言 From 227f5d18122f859ea49fef442ed542f9100a6740 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Fri, 9 Mar 2018 01:50:44 +0800 Subject: [PATCH 022/323] :memo: Writing docs. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3d6a7f60..b8c2b533 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ - [OOM 分析](https://github.com/crossoverJie/Java-Interview/blob/master/MD/OOM-analysis.md) - [垃圾回收](https://github.com/crossoverJie/Java-Interview/blob/master/MD/GarbageCollection.md) - [对象的创建与内存分配](https://github.com/crossoverJie/Java-Interview/blob/master/MD/newObject.md) +- [你应该知道的 volatile 关键字](https://github.com/crossoverJie/Java-Interview/blob/master/MD/concurrent/volatile.md) ### 常用框架 From fadec017e1e1268fd6d1aed4ca889bc1443bd5ec Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Fri, 9 Mar 2018 13:53:56 +0800 Subject: [PATCH 023/323] :memo: Writing docs. --- src/main/java/com/crossoverjie/concurrent/Singleton.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/crossoverjie/concurrent/Singleton.java b/src/main/java/com/crossoverjie/concurrent/Singleton.java index 88d0fc22..fec49248 100644 --- a/src/main/java/com/crossoverjie/concurrent/Singleton.java +++ b/src/main/java/com/crossoverjie/concurrent/Singleton.java @@ -1,7 +1,7 @@ package com.crossoverjie.concurrent; /** - * Function: + * Function:单例模式-双重检查锁 * * @author crossoverJie * Date: 09/03/2018 01:14 From 4d01e00e6036cd2a73e35df8c168017a6507e100 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Fri, 9 Mar 2018 17:30:11 +0800 Subject: [PATCH 024/323] :sparkles: Introducing new features. String test --- .../com/crossoverjie/basic/StringTest.java | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/crossoverjie/basic/StringTest.java b/src/main/java/com/crossoverjie/basic/StringTest.java index 4c4e9053..29216e98 100644 --- a/src/main/java/com/crossoverjie/basic/StringTest.java +++ b/src/main/java/com/crossoverjie/basic/StringTest.java @@ -11,13 +11,29 @@ */ public class StringTest { - public static void main(String[] args) throws NoSuchFieldException { + public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { String a = "123"; + //这里的 a 和 b 都是同一个对象,指向同一个字符串常量池对象。 + String b = "123" ; + String c = new String("123") ; + + System.out.println("a=b:" + (a == b)); + System.out.println("a=c:" + (a == c)); + System.out.println("a=" + a); a = "456"; System.out.println("a=" + a); - Field value = a.getClass().getField("value"); + + //用反射的方式改变字符串的值 + Field value = a.getClass().getDeclaredField("value"); + //改变 value 的访问属性 + value.setAccessible(true) ; + + char[] values = (char[]) value.get(a); + values[0] = '9' ; + + System.out.println(a); } } From eefee5c8bbf530ca27ec14162ce34d64172a6128 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Fri, 9 Mar 2018 18:02:27 +0800 Subject: [PATCH 025/323] :bulb: Documenting source code. --- src/main/java/com/crossoverjie/actual/AbstractMap.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/crossoverjie/actual/AbstractMap.java b/src/main/java/com/crossoverjie/actual/AbstractMap.java index de11b77b..2c1358fd 100644 --- a/src/main/java/com/crossoverjie/actual/AbstractMap.java +++ b/src/main/java/com/crossoverjie/actual/AbstractMap.java @@ -85,7 +85,7 @@ public AbstractMap() { } /** - * 开启一个线程检查最先放入队列的值是否超期 + * 开启一个线程检查最先放入队列的值是否超期 设置为守护线程 */ private void executeCheckTime() { ThreadFactory namedThreadFactory = new ThreadFactoryBuilder() From 1f4f6d63aaa0eefd2ca32b131ab362620b98ff5d Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Sun, 11 Mar 2018 17:33:33 +0800 Subject: [PATCH 026/323] :sparkles: Introducing new features. HashSet --- MD/collection/HashSet.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 MD/collection/HashSet.md diff --git a/MD/collection/HashSet.md b/MD/collection/HashSet.md new file mode 100644 index 00000000..3dc4cfe7 --- /dev/null +++ b/MD/collection/HashSet.md @@ -0,0 +1,25 @@ +# HashSet 底层分析 + +`HashSet` 是一个不允许存储重复元素的集合,它的实现比较简单,只要理解了 `HashMap`,`HashSet` 就水到渠成了。 + +## 成员变量 +首先了解下 `HashSet` 的成员变量: + +```java + private transient HashMap map; + + // Dummy value to associate with an Object in the backing Map + private static final Object PRESENT = new Object(); +``` + +发现主要就两个变量: + +- `map` :用于存放最终数据的。 +- `PRESENT` :是所有写入 map 的 `value` 值。 + +## 构造函数 + +## add + +## get + From ab1441604147ef61b87feafaf4ed958c696ed7fd Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Sun, 11 Mar 2018 18:31:40 +0800 Subject: [PATCH 027/323] :sparkles: Introducing new features. HashSet --- MD/collection/HashSet.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/MD/collection/HashSet.md b/MD/collection/HashSet.md index 3dc4cfe7..5df569d7 100644 --- a/MD/collection/HashSet.md +++ b/MD/collection/HashSet.md @@ -19,7 +19,31 @@ ## 构造函数 +``` + public HashSet() { + map = new HashMap<>(); + } + + public HashSet(int initialCapacity, float loadFactor) { + map = new HashMap<>(initialCapacity, loadFactor); + } +``` +构造函数很简单,利用了 `HashMap` 初始化了 `map` 。 + ## add -## get +```java + public boolean add(E e) { + return map.put(e, PRESENT)==null; + } +``` + +比较关键的就是这个 `add()` 方法。 +可以看出它是将存放的对象当做了 `HashMap` 的健,`value` 都是相同的 `PRESENT` 。由于 `HashMap` 的 `key` 是不能重复的,所以每当有重复的值写入到 `HashSet` 时,`value` 会被覆盖,但 `key` 不会收到影响,这样就保证了 `HashSet` 中只能存放不重复的元素。 + +## 总结 + +`HashSet` 的原理比较简单,几乎全部借助于 `HashMap` 来实现的。 + +所以 `HashMap` 会出现的问题 `HashSet` 依然不能避免。 From da88a827ee699b5fe57472cccfcb0555e4f7c9de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=AD=E6=96=87=E6=B5=B7?= Date: Mon, 12 Mar 2018 19:15:52 +0800 Subject: [PATCH 028/323] =?UTF-8?q?=E5=88=A0=E9=99=A4=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E7=9A=84=E9=9A=8F=E6=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MD/ArrayList.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MD/ArrayList.md b/MD/ArrayList.md index 06271b12..cee9a6ca 100644 --- a/MD/ArrayList.md +++ b/MD/ArrayList.md @@ -2,7 +2,7 @@ ## ArrayList -`ArrayList` 实现于 `List`、`RandomAccess` 接口。可以插入空数据,也支持随机随机访问。 +`ArrayList` 实现于 `List`、`RandomAccess` 接口。可以插入空数据,也支持随机访问。 `ArrayList `相当于动态数据,其中最重要的两个属性分别是: `elementData` 数组,以及 `size` 大小。 From 38af54384241abf932fc16f8726574af3c9a70c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E9=BB=84=E8=9C=82coder?= Date: Thu, 15 Mar 2018 14:16:01 +0800 Subject: [PATCH 029/323] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改说明 --- MD/Thread-common-problem.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MD/Thread-common-problem.md b/MD/Thread-common-problem.md index 5a792643..a8a07347 100644 --- a/MD/Thread-common-problem.md +++ b/MD/Thread-common-problem.md @@ -21,10 +21,10 @@ CPU 通过给每个线程分配一定的时间片,由于时间非常短通常 - 尽量一个线程只获取一个锁。 - 一个线程只占用一个资源。 -- 尝试是用定时锁,至少能保证锁最终会被释放。 +- 尝试使用定时锁,至少能保证锁最终会被释放。 ## 资源限制 当在带宽有限的情况下一个线程下载某个资源需要 `1M/S`,当开 10 个线程时速度并不会乘 10 倍,反而还会增加时间,毕竟上下文切换比较耗时。 -如果是受限于资源的话可以采用集群来处理任务,不同的机器来处理不同的数据,就类似于开始提到的无锁编程。 \ No newline at end of file +如果是受限于资源的话可以采用集群来处理任务,不同的机器来处理不同的数据,就类似于开始提到的无锁编程。 From b940191b97cb15bf5b75003b090695c5dabe5f60 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Fri, 16 Mar 2018 00:43:36 +0800 Subject: [PATCH 030/323] =?UTF-8?q?:sparkles:=20Introducing=20new=20featur?= =?UTF-8?q?es.=20=E7=AD=89=E5=BE=85=E9=80=9A=E7=9F=A5=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MD/concurrent/thread-communication.md | 169 ++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 MD/concurrent/thread-communication.md diff --git a/MD/concurrent/thread-communication.md b/MD/concurrent/thread-communication.md new file mode 100644 index 00000000..455316a2 --- /dev/null +++ b/MD/concurrent/thread-communication.md @@ -0,0 +1,169 @@ +# 深入理解线程通信 + +## 前言 + +开发中不免会遇到需要所有子线程执行完毕通知主线程处理某些逻辑的场景。 + +或者是线程 A 在执行到某个条件通知线程 B 执行某个操作。 + +可以通过以下几种方式实现: + + +## 等待通知机制 +> 等待通知模式是 Java 中比较经典的线程通信方式。 + +两个线程通过对同一对象调用等待 wait() 和通知 notify() 方法来进行通讯。 + +如两个线程交替打印奇偶数: + +```java +public class TwoThreadWaitNotify { + + private int start = 1; + + private boolean flag = false; + + public static void main(String[] args) { + TwoThreadWaitNotify twoThread = new TwoThreadWaitNotify(); + + Thread t1 = new Thread(new OuNum(twoThread)); + t1.setName("A"); + + + Thread t2 = new Thread(new JiNum(twoThread)); + t2.setName("B"); + + t1.start(); + t2.start(); + } + + /** + * 偶数线程 + */ + public static class OuNum implements Runnable { + private TwoThreadWaitNotify number; + + public OuNum(TwoThreadWaitNotify number) { + this.number = number; + } + + @Override + public void run() { + + while (number.start <= 100) { + synchronized (TwoThreadWaitNotify.class) { + System.out.println("偶数线程抢到锁了"); + if (number.flag) { + System.out.println(Thread.currentThread().getName() + "+-+偶数" + number.start); + number.start++; + + number.flag = false; + TwoThreadWaitNotify.class.notify(); + + }else { + try { + TwoThreadWaitNotify.class.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + } + } + } + + + /** + * 奇数线程 + */ + public static class JiNum implements Runnable { + private TwoThreadWaitNotify number; + + public JiNum(TwoThreadWaitNotify number) { + this.number = number; + } + + @Override + public void run() { + while (number.start <= 100) { + synchronized (TwoThreadWaitNotify.class) { + System.out.println("奇数线程抢到锁了"); + if (!number.flag) { + System.out.println(Thread.currentThread().getName() + "+-+奇数" + number.start); + number.start++; + + number.flag = true; + + TwoThreadWaitNotify.class.notify(); + }else { + try { + TwoThreadWaitNotify.class.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + } + } +} +``` + +这里的线程 A 和线程 B 都对同一个对象 `TwoThreadWaitNotify.class` 获取锁,A 线程调用了同步对象的 wait() 方法释放了锁并进入 `WAITING` 状态。 + +B 线程调用了 notify() 方法,这样 A 线程收到通知之后就可以从 wait() 方法中返回。利用了 `TwoThreadWaitNotify.class` 对象完成了交互。 + +这里有一些注意点: + +- wait() 、nofify() 、nofityAll() 调用的前提都是获得了对象的锁(也可成为对象监视器)。 +- 调用 wait() 方法后线程会释放锁,进入 `WAITING` 状态,该线程也会被移动到**等待队列**中。 +- 调用 notify() 方法会将**等待队列**中的线程移动到**同步队列**中,线程状态也会更新为 `BLOCKED` +- 从 wait() 方法返回的前提是调用 notify() 方法的线程释放锁,wait() 方法的线程获得锁。 + +等待通知有着一个经典范式: + +线程 A 作为消费者: + +1. 获取对象的锁。 +2. 进入 while(判断条件),并调用 wait() 方法。 +3. 当条件满足跳出循环执行具体处理逻辑。 + +线程 B 作为生产者: + +1. 获取对象锁。 +2. 更改与线程 A 共用的判断条件。 +3. 调用 notify() 方法。 + +伪代码如下: + +``` +//Thread A + +synchronized(Object){ + while(条件){ + Object.wait(); + } + //do something +} + +//Thread B +synchronized(Object){ + 条件=false;//改变条件 + Object.notify(); +} + +``` + + +## join() 方法 + +## volatile 共享内存 + +## CountDownLatch 并发工具 + +## 线程响应中断 + +## 线程池 awaitTermination() 方法 + +## 管道通信 \ No newline at end of file From d8655b0266077393c2b5edaa3422e91bcb6d7da2 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Fri, 16 Mar 2018 01:02:05 +0800 Subject: [PATCH 031/323] :sparkles: Introducing new features. join --- MD/concurrent/thread-communication.md | 52 +++++++++++++++++++ .../actual/ThreadCommunication.java | 7 +-- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/MD/concurrent/thread-communication.md b/MD/concurrent/thread-communication.md index 455316a2..9d5fc6f8 100644 --- a/MD/concurrent/thread-communication.md +++ b/MD/concurrent/thread-communication.md @@ -158,6 +158,58 @@ synchronized(Object){ ## join() 方法 +```java + private static void join() throws InterruptedException { + Thread t1 = new Thread(new Runnable() { + @Override + public void run() { + LOGGER.info("running"); + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }) ; + Thread t2 = new Thread(new Runnable() { + @Override + public void run() { + LOGGER.info("running2"); + try { + Thread.sleep(4000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }) ; + + t1.start(); + t2.start(); + + //等待线程1终止 + t1.join(); + + //等待线程2终止 + t2.join(); + + LOGGER.info("main over"); + } +``` + +在 `t1.join()` 时会一直阻塞到 t1 执行完毕,所以最终主线程会等待 t1 和 t2 线程执行完毕。 + +其实从源码可以看出,join() 也是利用的等待通知机制: + +核心逻辑: + +```java + while (isAlive()) { + wait(0); + } +``` + +在 join 线程完成后会调用 notifyAll() 方法,是在 JVM 实现中调用,所以这里看不出来。 + ## volatile 共享内存 ## CountDownLatch 并发工具 diff --git a/src/main/java/com/crossoverjie/actual/ThreadCommunication.java b/src/main/java/com/crossoverjie/actual/ThreadCommunication.java index d5ef46f0..0efb5794 100644 --- a/src/main/java/com/crossoverjie/actual/ThreadCommunication.java +++ b/src/main/java/com/crossoverjie/actual/ThreadCommunication.java @@ -15,9 +15,9 @@ public class ThreadCommunication { private final static Logger LOGGER = LoggerFactory.getLogger(ThreadCommunication.class); public static void main(String[] args) throws Exception { - //join(); + join(); //executorService(); - countDownLatch(); + //countDownLatch(); } @@ -118,10 +118,11 @@ public void run() { }) ; t1.start(); + t2.start(); + //等待线程1终止 t1.join(); - t2.start(); //等待线程2终止 t2.join(); From 123c8a5d9caf4aaa0137d6ca6a2df70960397776 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Fri, 16 Mar 2018 01:40:03 +0800 Subject: [PATCH 032/323] :sparkles: Introducing new features. CountDownLatch --- MD/concurrent/thread-communication.md | 75 +++++++++++++++++++ .../actual/ThreadCommunication.java | 4 +- 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/MD/concurrent/thread-communication.md b/MD/concurrent/thread-communication.md index 9d5fc6f8..09dd6056 100644 --- a/MD/concurrent/thread-communication.md +++ b/MD/concurrent/thread-communication.md @@ -212,8 +212,83 @@ synchronized(Object){ ## volatile 共享内存 +因为 Java 其实是采用共享内存的方式进行线程通信的,所以可以采用以下方式用主线程关闭 A 线程: + +```java +public class Volatile implements Runnable{ + + private static volatile boolean flag = true ; + + @Override + public void run() { + while (flag){ + System.out.println(Thread.currentThread().getName() + "正在运行。。。"); + } + System.out.println(Thread.currentThread().getName() +"执行完毕"); + } + + public static void main(String[] args) throws InterruptedException { + Volatile aVolatile = new Volatile(); + new Thread(aVolatile,"thread A").start(); + + + System.out.println("main 线程正在运行") ; + + TimeUnit.MILLISECONDS.sleep(100) ; + + aVolatile.stopThread(); + + } + + private void stopThread(){ + flag = false ; + } +} +``` + +这里的 flag 存放于主内存中,所以主线程和线程 A 都可以看到。 + +flag 采用 volatile 修饰主要是为了内存可见性,具体内容可以查看[这里](http://crossoverjie.top/2018/03/09/volatile/)。 + + ## CountDownLatch 并发工具 +CountDownLatch 可以实现 join 相同的功能,但是更加的灵活。 + +```java + private static void countDownLatch() throws Exception{ + int thread = 3 ; + long start = System.currentTimeMillis(); + final CountDownLatch countDown = new CountDownLatch(thread); + for (int i= 0 ;i Date: Fri, 16 Mar 2018 02:15:52 +0800 Subject: [PATCH 033/323] =?UTF-8?q?:sparkles:=20Introducing=20new=20featur?= =?UTF-8?q?es.=20=E7=BA=BF=E7=A8=8B=E5=93=8D=E5=BA=94=E4=B8=AD=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MD/concurrent/thread-communication.md | 34 ++++++++++++++++++ .../crossoverjie/concurrent/StopThread.java | 36 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 src/main/java/com/crossoverjie/concurrent/StopThread.java diff --git a/MD/concurrent/thread-communication.md b/MD/concurrent/thread-communication.md index 09dd6056..d3bd65a0 100644 --- a/MD/concurrent/thread-communication.md +++ b/MD/concurrent/thread-communication.md @@ -291,6 +291,40 @@ CountDownLatch 也是基于 AQS(AbstractQueuedSynchronizer) 实现的,更多 ## 线程响应中断 +```java +public class StopThread implements Runnable { + @Override + public void run() { + + while ( !Thread.currentThread().isInterrupted()) { + // 线程执行具体逻辑 + System.out.println(Thread.currentThread().getName() + "运行中。。"); + } + + System.out.println(Thread.currentThread().getName() + "退出。。"); + + } + + public static void main(String[] args) throws InterruptedException { + Thread thread = new Thread(new StopThread(), "thread A"); + thread.start(); + + System.out.println("main 线程正在运行") ; + + TimeUnit.MILLISECONDS.sleep(10) ; + thread.interrupt(); + } + + +} +``` + +可以采用中断线程的方式来通信,调用了 `thread.interrupt()` 方法其实就是将 thread 中的一个标志属性置为了 true。 + +并不是说调用了该方法就可以中断线程,如果不对这个标志进行响应其实是没有什么作用的(这里对这个标志进行了判断)。 + +但是如果抛出了 InterruptedException 异常,该标志就会被 JVM 重置为 false。 + ## 线程池 awaitTermination() 方法 ## 管道通信 \ No newline at end of file diff --git a/src/main/java/com/crossoverjie/concurrent/StopThread.java b/src/main/java/com/crossoverjie/concurrent/StopThread.java new file mode 100644 index 00000000..eb5ebc58 --- /dev/null +++ b/src/main/java/com/crossoverjie/concurrent/StopThread.java @@ -0,0 +1,36 @@ +package com.crossoverjie.concurrent; + +import java.util.concurrent.TimeUnit; + +/** + * Function: + * + * @author crossoverJie + * Date: 16/03/2018 01:41 + * @since JDK 1.8 + */ +public class StopThread implements Runnable { + @Override + public void run() { + + while ( !Thread.currentThread().isInterrupted()) { + // 线程执行具体逻辑 + System.out.println(Thread.currentThread().getName() + "运行中。。"); + } + + System.out.println(Thread.currentThread().getName() + "退出。。"); + + } + + public static void main(String[] args) throws InterruptedException { + Thread thread = new Thread(new StopThread(), "thread A"); + thread.start(); + + System.out.println("main 线程正在运行") ; + + TimeUnit.MILLISECONDS.sleep(10) ; + thread.interrupt(); + } + + +} From 6c4c8fafcc31c392931add99aeb71e04e5f29ab2 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Fri, 16 Mar 2018 20:06:38 +0800 Subject: [PATCH 034/323] =?UTF-8?q?:sparkles:=20Introducing=20new=20featur?= =?UTF-8?q?es.=20=E7=AE=A1=E9=81=93=E9=80=9A=E4=BF=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MD/concurrent/thread-communication.md | 117 +++++++++++++++++- .../actual/ThreadCommunication.java | 81 ++++++++++-- 2 files changed, 186 insertions(+), 12 deletions(-) diff --git a/MD/concurrent/thread-communication.md b/MD/concurrent/thread-communication.md index d3bd65a0..2b461de2 100644 --- a/MD/concurrent/thread-communication.md +++ b/MD/concurrent/thread-communication.md @@ -327,4 +327,119 @@ public class StopThread implements Runnable { ## 线程池 awaitTermination() 方法 -## 管道通信 \ No newline at end of file +如果是用线程池来管理线程,可以使用以下方式来让主线程等待线程池中所有任务执行完毕: + +```java + private static void executorService() throws Exception{ + BlockingQueue queue = new LinkedBlockingQueue<>(10) ; + ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5,5,1, TimeUnit.MILLISECONDS,queue) ; + poolExecutor.execute(new Runnable() { + @Override + public void run() { + LOGGER.info("running"); + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); + poolExecutor.execute(new Runnable() { + @Override + public void run() { + LOGGER.info("running2"); + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); + + poolExecutor.shutdown(); + while (!poolExecutor.awaitTermination(1,TimeUnit.SECONDS)){ + LOGGER.info("线程还在执行。。。"); + } + LOGGER.info("main over"); + } +``` + +使用这个 `awaitTermination()` 方法的前提需要关闭线程池,如调用了 `shutdown()` 方法。 + +调用了 `shutdown()` 之后线程池会停止接受新任务,并且会平滑的关闭线程池中现有的任务。 + + +## 管道通信 + +```java + public static void piped() throws IOException { + //面向于字符 PipedInputStream 面向于字节 + PipedWriter writer = new PipedWriter(); + PipedReader reader = new PipedReader(); + + //输入输出流建立连接 + writer.connect(reader); + + + Thread t1 = new Thread(new Runnable() { + @Override + public void run() { + LOGGER.info("running"); + try { + for (int i = 0; i < 10; i++) { + + writer.write(i+""); + Thread.sleep(10); + } + } catch (Exception e) { + + } finally { + try { + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + } + }); + Thread t2 = new Thread(new Runnable() { + @Override + public void run() { + LOGGER.info("running2"); + int msg = 0; + try { + while ((msg = reader.read()) != -1) { + LOGGER.info("msg={}", (char) msg); + } + + } catch (Exception e) { + + } + } + }); + t1.start(); + t2.start(); + } +``` + +输出结果: + +``` +2018-03-16 19:56:43.014 [Thread-0] INFO c.c.actual.ThreadCommunication - running +2018-03-16 19:56:43.014 [Thread-1] INFO c.c.actual.ThreadCommunication - running2 +2018-03-16 19:56:43.130 [Thread-1] INFO c.c.actual.ThreadCommunication - msg=0 +2018-03-16 19:56:43.132 [Thread-1] INFO c.c.actual.ThreadCommunication - msg=1 +2018-03-16 19:56:43.132 [Thread-1] INFO c.c.actual.ThreadCommunication - msg=2 +2018-03-16 19:56:43.133 [Thread-1] INFO c.c.actual.ThreadCommunication - msg=3 +2018-03-16 19:56:43.133 [Thread-1] INFO c.c.actual.ThreadCommunication - msg=4 +2018-03-16 19:56:43.133 [Thread-1] INFO c.c.actual.ThreadCommunication - msg=5 +2018-03-16 19:56:43.133 [Thread-1] INFO c.c.actual.ThreadCommunication - msg=6 +2018-03-16 19:56:43.134 [Thread-1] INFO c.c.actual.ThreadCommunication - msg=7 +2018-03-16 19:56:43.134 [Thread-1] INFO c.c.actual.ThreadCommunication - msg=8 +2018-03-16 19:56:43.134 [Thread-1] INFO c.c.actual.ThreadCommunication - msg=9 +``` + +Java 虽说是基于内存通信的,但也可以使用管道通信。 + +需要注意的是,输入流和输出流需要首先建立连接。这样线程 B 就可以收到线程 A 发出的消息了。 \ No newline at end of file diff --git a/src/main/java/com/crossoverjie/actual/ThreadCommunication.java b/src/main/java/com/crossoverjie/actual/ThreadCommunication.java index 1dd6b4b6..70ab87ed 100644 --- a/src/main/java/com/crossoverjie/actual/ThreadCommunication.java +++ b/src/main/java/com/crossoverjie/actual/ThreadCommunication.java @@ -3,6 +3,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.io.PipedInputStream; +import java.io.PipedReader; +import java.io.PipedWriter; import java.util.concurrent.*; /** @@ -14,22 +18,25 @@ */ public class ThreadCommunication { private final static Logger LOGGER = LoggerFactory.getLogger(ThreadCommunication.class); + public static void main(String[] args) throws Exception { //join(); //executorService(); - countDownLatch(); + //countDownLatch(); + piped(); } /** * 使用countDownLatch 每执行完一个就减一,最后等待全部完成 + * * @throws Exception */ - private static void countDownLatch() throws Exception{ - int thread = 3 ; + private static void countDownLatch() throws Exception { + int thread = 3; long start = System.currentTimeMillis(); final CountDownLatch countDown = new CountDownLatch(thread); - for (int i= 0 ;i queue = new LinkedBlockingQueue<>(10) ; - ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5,5,1, TimeUnit.MILLISECONDS,queue) ; + private static void executorService() throws Exception { + BlockingQueue queue = new LinkedBlockingQueue<>(10); + ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5, 5, 1, TimeUnit.MILLISECONDS, queue); poolExecutor.execute(new Runnable() { @Override public void run() { @@ -82,7 +90,7 @@ public void run() { }); poolExecutor.shutdown(); - while (!poolExecutor.awaitTermination(1,TimeUnit.SECONDS)){ + while (!poolExecutor.awaitTermination(1, TimeUnit.SECONDS)) { LOGGER.info("线程还在执行。。。"); } LOGGER.info("main over"); @@ -91,6 +99,7 @@ public void run() { /** * 采用 join 线程间通信 + * * @throws InterruptedException */ private static void join() throws InterruptedException { @@ -104,7 +113,7 @@ public void run() { e.printStackTrace(); } } - }) ; + }); Thread t2 = new Thread(new Runnable() { @Override public void run() { @@ -115,7 +124,7 @@ public void run() { e.printStackTrace(); } } - }) ; + }); t1.start(); t2.start(); @@ -128,4 +137,54 @@ public void run() { LOGGER.info("main over"); } + + public static void piped() throws IOException { + //面向于字符 PipedInputStream 面向于字节 + PipedWriter writer = new PipedWriter(); + PipedReader reader = new PipedReader(); + + //输入输出流建立连接 + writer.connect(reader); + + + Thread t1 = new Thread(new Runnable() { + @Override + public void run() { + LOGGER.info("running"); + try { + for (int i = 0; i < 10; i++) { + + writer.write(i+""); + Thread.sleep(10); + } + } catch (Exception e) { + + } finally { + try { + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + } + }); + Thread t2 = new Thread(new Runnable() { + @Override + public void run() { + LOGGER.info("running2"); + int msg = 0; + try { + while ((msg = reader.read()) != -1) { + LOGGER.info("msg={}", (char) msg); + } + + } catch (Exception e) { + + } + } + }); + t1.start(); + t2.start(); + } } From 25e1428deec3fb96454b63df20e33cff04380a0d Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Fri, 16 Mar 2018 20:22:54 +0800 Subject: [PATCH 035/323] :pencil2: Fixing typos. --- MD/concurrent/thread-communication.md | 79 +++++++++++++++++-- .../actual/ThreadCommunication.java | 4 +- .../crossoverjie/concurrent/StopThread.java | 2 +- 3 files changed, 74 insertions(+), 11 deletions(-) diff --git a/MD/concurrent/thread-communication.md b/MD/concurrent/thread-communication.md index 2b461de2..5bc54b43 100644 --- a/MD/concurrent/thread-communication.md +++ b/MD/concurrent/thread-communication.md @@ -110,13 +110,28 @@ public class TwoThreadWaitNotify { } ``` +输出结果: + +``` +t2+-+奇数93 +t1+-+偶数94 +t2+-+奇数95 +t1+-+偶数96 +t2+-+奇数97 +t1+-+偶数98 +t2+-+奇数99 +t1+-+偶数100 +``` + 这里的线程 A 和线程 B 都对同一个对象 `TwoThreadWaitNotify.class` 获取锁,A 线程调用了同步对象的 wait() 方法释放了锁并进入 `WAITING` 状态。 -B 线程调用了 notify() 方法,这样 A 线程收到通知之后就可以从 wait() 方法中返回。利用了 `TwoThreadWaitNotify.class` 对象完成了交互。 +B 线程调用了 notify() 方法,这样 A 线程收到通知之后就可以从 wait() 方法中返回。 -这里有一些注意点: +这里利用了 `TwoThreadWaitNotify.class` 对象完成了通信。 -- wait() 、nofify() 、nofityAll() 调用的前提都是获得了对象的锁(也可成为对象监视器)。 +有一些需要注意: + +- wait() 、nofify() 、nofityAll() 调用的前提都是获得了对象的锁(也可称为对象监视器)。 - 调用 wait() 方法后线程会释放锁,进入 `WAITING` 状态,该线程也会被移动到**等待队列**中。 - 调用 notify() 方法会将**等待队列**中的线程移动到**同步队列**中,线程状态也会更新为 `BLOCKED` - 从 wait() 方法返回的前提是调用 notify() 方法的线程释放锁,wait() 方法的线程获得锁。 @@ -196,6 +211,15 @@ synchronized(Object){ } ``` +输出结果: + +``` +2018-03-16 20:21:30.967 [Thread-1] INFO c.c.actual.ThreadCommunication - running2 +2018-03-16 20:21:30.967 [Thread-0] INFO c.c.actual.ThreadCommunication - running +2018-03-16 20:21:34.972 [main] INFO c.c.actual.ThreadCommunication - main over + +``` + 在 `t1.join()` 时会一直阻塞到 t1 执行完毕,所以最终主线程会等待 t1 和 t2 线程执行完毕。 其实从源码可以看出,join() 也是利用的等待通知机制: @@ -212,7 +236,7 @@ synchronized(Object){ ## volatile 共享内存 -因为 Java 其实是采用共享内存的方式进行线程通信的,所以可以采用以下方式用主线程关闭 A 线程: +因为 Java 是采用共享内存的方式进行线程通信的,所以可以采用以下方式用主线程关闭 A 线程: ```java public class Volatile implements Runnable{ @@ -246,9 +270,18 @@ public class Volatile implements Runnable{ } ``` +输出结果: +``` +thread A正在运行。。。 +thread A正在运行。。。 +thread A正在运行。。。 +thread A正在运行。。。 +thread A执行完毕 +``` + 这里的 flag 存放于主内存中,所以主线程和线程 A 都可以看到。 -flag 采用 volatile 修饰主要是为了内存可见性,具体内容可以查看[这里](http://crossoverjie.top/2018/03/09/volatile/)。 +flag 采用 volatile 修饰主要是为了内存可见性,更多内容可以查看[这里](http://crossoverjie.top/2018/03/09/volatile/)。 ## CountDownLatch 并发工具 @@ -283,11 +316,23 @@ CountDownLatch 可以实现 join 相同的功能,但是更加的灵活。 } ``` +输出结果: + +``` +2018-03-16 20:19:44.126 [Thread-0] INFO c.c.actual.ThreadCommunication - thread run +2018-03-16 20:19:44.126 [Thread-2] INFO c.c.actual.ThreadCommunication - thread run +2018-03-16 20:19:44.126 [Thread-1] INFO c.c.actual.ThreadCommunication - thread run +2018-03-16 20:19:46.136 [Thread-2] INFO c.c.actual.ThreadCommunication - thread end +2018-03-16 20:19:46.136 [Thread-1] INFO c.c.actual.ThreadCommunication - thread end +2018-03-16 20:19:46.136 [Thread-0] INFO c.c.actual.ThreadCommunication - thread end +2018-03-16 20:19:46.136 [main] INFO c.c.actual.ThreadCommunication - main over total time=2012 +``` + CountDownLatch 也是基于 AQS(AbstractQueuedSynchronizer) 实现的,更多实现参考 [ReentrantLock 实现原理](http://crossoverjie.top/2018/01/25/ReentrantLock/) - 初始化一个 CountDownLatch 时告诉并发的线程,然后在每个线程处理完毕之后调用 countDown() 方法。 - 该方法会将 AQS 内置的一个 state 状态 -1 。 -- 最终在主线程调用 await() 方法,它会知道 `state == 0` 的时候返回。 +- 最终在主线程调用 await() 方法,它会阻塞直到 `state == 0` 的时候返回。 ## 线程响应中断 @@ -319,11 +364,19 @@ public class StopThread implements Runnable { } ``` +输出结果: + +``` +thread A运行中。。 +thread A运行中。。 +thread A退出。。 +``` + 可以采用中断线程的方式来通信,调用了 `thread.interrupt()` 方法其实就是将 thread 中的一个标志属性置为了 true。 -并不是说调用了该方法就可以中断线程,如果不对这个标志进行响应其实是没有什么作用的(这里对这个标志进行了判断)。 +并不是说调用了该方法就可以中断线程,如果不对这个标志进行响应其实是没有什么作用(这里对这个标志进行了判断)。 -但是如果抛出了 InterruptedException 异常,该标志就会被 JVM 重置为 false。 +**但是如果抛出了 InterruptedException 异常,该标志就会被 JVM 重置为 false。** ## 线程池 awaitTermination() 方法 @@ -364,6 +417,16 @@ public class StopThread implements Runnable { } ``` +输出结果: + +``` +2018-03-16 20:18:01.273 [pool-1-thread-2] INFO c.c.actual.ThreadCommunication - running2 +2018-03-16 20:18:01.273 [pool-1-thread-1] INFO c.c.actual.ThreadCommunication - running +2018-03-16 20:18:02.273 [main] INFO c.c.actual.ThreadCommunication - 线程还在执行。。。 +2018-03-16 20:18:03.278 [main] INFO c.c.actual.ThreadCommunication - 线程还在执行。。。 +2018-03-16 20:18:04.278 [main] INFO c.c.actual.ThreadCommunication - main over +``` + 使用这个 `awaitTermination()` 方法的前提需要关闭线程池,如调用了 `shutdown()` 方法。 调用了 `shutdown()` 之后线程池会停止接受新任务,并且会平滑的关闭线程池中现有的任务。 diff --git a/src/main/java/com/crossoverjie/actual/ThreadCommunication.java b/src/main/java/com/crossoverjie/actual/ThreadCommunication.java index 70ab87ed..f2ca0256 100644 --- a/src/main/java/com/crossoverjie/actual/ThreadCommunication.java +++ b/src/main/java/com/crossoverjie/actual/ThreadCommunication.java @@ -20,10 +20,10 @@ public class ThreadCommunication { private final static Logger LOGGER = LoggerFactory.getLogger(ThreadCommunication.class); public static void main(String[] args) throws Exception { - //join(); + join(); //executorService(); //countDownLatch(); - piped(); + //piped(); } diff --git a/src/main/java/com/crossoverjie/concurrent/StopThread.java b/src/main/java/com/crossoverjie/concurrent/StopThread.java index eb5ebc58..1b5b860a 100644 --- a/src/main/java/com/crossoverjie/concurrent/StopThread.java +++ b/src/main/java/com/crossoverjie/concurrent/StopThread.java @@ -3,7 +3,7 @@ import java.util.concurrent.TimeUnit; /** - * Function: + * Function:响应中断 * * @author crossoverJie * Date: 16/03/2018 01:41 From 07a0692bcfdb27ee982a06308ea933292202e77d Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Fri, 16 Mar 2018 20:28:33 +0800 Subject: [PATCH 036/323] :pencil2: Fixing typos. --- MD/concurrent/thread-communication.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/MD/concurrent/thread-communication.md b/MD/concurrent/thread-communication.md index 5bc54b43..69c60338 100644 --- a/MD/concurrent/thread-communication.md +++ b/MD/concurrent/thread-communication.md @@ -505,4 +505,7 @@ thread A退出。。 Java 虽说是基于内存通信的,但也可以使用管道通信。 -需要注意的是,输入流和输出流需要首先建立连接。这样线程 B 就可以收到线程 A 发出的消息了。 \ No newline at end of file +需要注意的是,输入流和输出流需要首先建立连接。这样线程 B 就可以收到线程 A 发出的消息了。 + + +实际开发中可以灵活根据需求选择最适合的线程通信方式。 \ No newline at end of file From f837b2db03b95fe3254a23dc3794481ada11368b Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Fri, 16 Mar 2018 20:30:43 +0800 Subject: [PATCH 037/323] :sparkles: Introducing new features. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b8c2b533..0c85ee99 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ - [ReentrantLock 实现原理 ](https://github.com/crossoverJie/Java-Interview/blob/master/MD/ReentrantLock.md) - [ConcurrentHashMap 的实现原理](https://github.com/crossoverJie/Java-Interview/blob/master/MD/ConcurrentHashMap.md) - [线程池原理](https://github.com/crossoverJie/Java-Interview/blob/master/MD/ThreadPoolExecutor.md) -- [线程间通信](https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/actual/ThreadCommunication.java) +- [深入理解线程通信](https://github.com/crossoverJie/Java-Interview/blob/master/MD/concurrent/thread-communication.md) - [交替打印奇偶数](https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/actual/TwoThread.java) ### JMM(Java 内存模型) From 79210c949bb51b42237345baa3ee761eb34febab Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Sun, 18 Mar 2018 23:44:56 +0800 Subject: [PATCH 038/323] =?UTF-8?q?:sparkles:=20Introducing=20new=20featur?= =?UTF-8?q?es.=20=E6=96=B0=E5=A2=9E=E4=B8=80=E7=A7=8D=E7=BA=BF=E7=A8=8B?= =?UTF-8?q?=E9=80=9A=E4=BF=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MD/concurrent/thread-communication.md | 75 +++++++++++++++++++ .../actual/ThreadCommunication.java | 55 +++++++++++++- 2 files changed, 127 insertions(+), 3 deletions(-) diff --git a/MD/concurrent/thread-communication.md b/MD/concurrent/thread-communication.md index 69c60338..8bf754ba 100644 --- a/MD/concurrent/thread-communication.md +++ b/MD/concurrent/thread-communication.md @@ -334,6 +334,81 @@ CountDownLatch 也是基于 AQS(AbstractQueuedSynchronizer) 实现的,更多 - 该方法会将 AQS 内置的一个 state 状态 -1 。 - 最终在主线程调用 await() 方法,它会阻塞直到 `state == 0` 的时候返回。 +## CyclicBarrier 并发工具 + +```java + private static void cyclicBarrier() throws Exception { + CyclicBarrier cyclicBarrier = new CyclicBarrier(3) ; + + new Thread(new Runnable() { + @Override + public void run() { + LOGGER.info("thread run"); + try { + cyclicBarrier.await() ; + } catch (Exception e) { + e.printStackTrace(); + } + + LOGGER.info("thread end do something"); + } + }).start(); + + new Thread(new Runnable() { + @Override + public void run() { + LOGGER.info("thread run"); + try { + cyclicBarrier.await() ; + } catch (Exception e) { + e.printStackTrace(); + } + + LOGGER.info("thread end do something"); + } + }).start(); + + new Thread(new Runnable() { + @Override + public void run() { + LOGGER.info("thread run"); + try { + Thread.sleep(5000); + cyclicBarrier.await() ; + } catch (Exception e) { + e.printStackTrace(); + } + + LOGGER.info("thread end do something"); + } + }).start(); + + LOGGER.info("main thread"); + } +``` + +CyclicBarrier 中文名叫做屏障或者是栅栏,也可以用于线程间通信。 + +它可以等待 N 个线程都达到某个状态后继续运行的效果。 + +1. 首先初始化线程参与者。 +2. 调用 `await()` 将会在所有参与者线程都调用之前等待。 +3. 直到所有参与者都调用了 `await()` 后,所有线程从 `await()` 返回继续后续逻辑。 + +运行结果: + +``` +2018-03-18 22:40:00.731 [Thread-0] INFO c.c.actual.ThreadCommunication - thread run +2018-03-18 22:40:00.731 [Thread-1] INFO c.c.actual.ThreadCommunication - thread run +2018-03-18 22:40:00.731 [Thread-2] INFO c.c.actual.ThreadCommunication - thread run +2018-03-18 22:40:00.731 [main] INFO c.c.actual.ThreadCommunication - main thread +2018-03-18 22:40:05.741 [Thread-0] INFO c.c.actual.ThreadCommunication - thread end do something +2018-03-18 22:40:05.741 [Thread-1] INFO c.c.actual.ThreadCommunication - thread end do something +2018-03-18 22:40:05.741 [Thread-2] INFO c.c.actual.ThreadCommunication - thread end do something +``` + +可以看出由于其中一个线程休眠了五秒,所有其余所有的线程都得等待这个线程调用 `await()` 。 + ## 线程响应中断 ```java diff --git a/src/main/java/com/crossoverjie/actual/ThreadCommunication.java b/src/main/java/com/crossoverjie/actual/ThreadCommunication.java index f2ca0256..ecbda200 100644 --- a/src/main/java/com/crossoverjie/actual/ThreadCommunication.java +++ b/src/main/java/com/crossoverjie/actual/ThreadCommunication.java @@ -20,18 +20,67 @@ public class ThreadCommunication { private final static Logger LOGGER = LoggerFactory.getLogger(ThreadCommunication.class); public static void main(String[] args) throws Exception { - join(); + //join(); //executorService(); //countDownLatch(); //piped(); - + cyclicBarrier(); } /** - * 使用countDownLatch 每执行完一个就减一,最后等待全部完成 + * CyclicBarrier * * @throws Exception */ + private static void cyclicBarrier() throws Exception { + CyclicBarrier cyclicBarrier = new CyclicBarrier(3) ; + + new Thread(new Runnable() { + @Override + public void run() { + LOGGER.info("thread run"); + try { + cyclicBarrier.await() ; + } catch (Exception e) { + e.printStackTrace(); + } + + LOGGER.info("thread end do something"); + } + }).start(); + + new Thread(new Runnable() { + @Override + public void run() { + LOGGER.info("thread run"); + try { + cyclicBarrier.await() ; + } catch (Exception e) { + e.printStackTrace(); + } + + LOGGER.info("thread end do something"); + } + }).start(); + + new Thread(new Runnable() { + @Override + public void run() { + LOGGER.info("thread run"); + try { + Thread.sleep(5000); + cyclicBarrier.await() ; + } catch (Exception e) { + e.printStackTrace(); + } + + LOGGER.info("thread end do something"); + } + }).start(); + + LOGGER.info("main thread"); + } + private static void countDownLatch() throws Exception { int thread = 3; long start = System.currentTimeMillis(); From 7cc13f6a2d08e184d2c6eaf55a5916d6e90f69f9 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Mon, 19 Mar 2018 00:00:27 +0800 Subject: [PATCH 039/323] :pencil2: Fixing typos. --- MD/concurrent/thread-communication.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MD/concurrent/thread-communication.md b/MD/concurrent/thread-communication.md index 8bf754ba..75707021 100644 --- a/MD/concurrent/thread-communication.md +++ b/MD/concurrent/thread-communication.md @@ -409,6 +409,8 @@ CyclicBarrier 中文名叫做屏障或者是栅栏,也可以用于线程间通 可以看出由于其中一个线程休眠了五秒,所有其余所有的线程都得等待这个线程调用 `await()` 。 +该工具可以实现 CountDownLatch 同样的功能,但是要更加灵活。甚至可以调用 `reset()` 方法重置 CyclicBarrier (需要自行捕获 BrokenBarrierException 处理) 然后重新执行。 + ## 线程响应中断 ```java From 54cb08227d60b6c876f3ed657fe7cf55462f9ccd Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Mon, 19 Mar 2018 13:00:49 +0800 Subject: [PATCH 040/323] :bug: Fixing a bug. LinkedHashMap --- MD/collection/LinkedHashMap.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/MD/collection/LinkedHashMap.md b/MD/collection/LinkedHashMap.md index 5a2a6ef9..6290f976 100644 --- a/MD/collection/LinkedHashMap.md +++ b/MD/collection/LinkedHashMap.md @@ -238,10 +238,24 @@ LinkedHashMap 的 `get()` 方法也重写了: if (e == null) return null; - //多了一个判断是否是按照访问顺序排序,是则将当前的 Entry 移动到链表末尾 + //多了一个判断是否是按照访问顺序排序,是则将当前的 Entry 移动到链表头部。 e.recordAccess(this); return e.value; } + + void recordAccess(HashMap m) { + LinkedHashMap lm = (LinkedHashMap)m; + if (lm.accessOrder) { + lm.modCount++; + + //删除 + remove(); + //添加到头部 + addBefore(lm.header); + } + } + + ``` `clear()` 清空就要比较简单了: From bf5b52ca85c042f0fc9660066a6c0d0505f48c18 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Mon, 19 Mar 2018 15:27:36 +0800 Subject: [PATCH 041/323] =?UTF-8?q?:sparkles:=20Introducing=20new=20featur?= =?UTF-8?q?es.=20=E8=AE=BE=E8=AE=A1=E6=A8=A1=E5=BC=8F--->=E5=B7=A5?= =?UTF-8?q?=E5=8E=82=E6=96=B9=E6=B3=95=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../design/pattern/factorymethod/Animal.java | 26 +++++++++++++++++++ .../pattern/factorymethod/AnimalFactory.java | 13 ++++++++++ .../design/pattern/factorymethod/Cat.java | 15 +++++++++++ .../pattern/factorymethod/CatFactory.java | 15 +++++++++++ .../design/pattern/factorymethod/Fish.java | 17 ++++++++++++ .../pattern/factorymethod/FishFactory.java | 15 +++++++++++ .../design/pattern/factorymethod/Main.java | 17 ++++++++++++ 7 files changed, 118 insertions(+) create mode 100644 src/main/java/com/crossoverjie/design/pattern/factorymethod/Animal.java create mode 100644 src/main/java/com/crossoverjie/design/pattern/factorymethod/AnimalFactory.java create mode 100644 src/main/java/com/crossoverjie/design/pattern/factorymethod/Cat.java create mode 100644 src/main/java/com/crossoverjie/design/pattern/factorymethod/CatFactory.java create mode 100644 src/main/java/com/crossoverjie/design/pattern/factorymethod/Fish.java create mode 100644 src/main/java/com/crossoverjie/design/pattern/factorymethod/FishFactory.java create mode 100644 src/main/java/com/crossoverjie/design/pattern/factorymethod/Main.java diff --git a/src/main/java/com/crossoverjie/design/pattern/factorymethod/Animal.java b/src/main/java/com/crossoverjie/design/pattern/factorymethod/Animal.java new file mode 100644 index 00000000..04556a40 --- /dev/null +++ b/src/main/java/com/crossoverjie/design/pattern/factorymethod/Animal.java @@ -0,0 +1,26 @@ +package com.crossoverjie.design.pattern.factorymethod; + +/** + * Function: + * + * @author crossoverJie + * Date: 19/03/2018 14:29 + * @since JDK 1.8 + */ +public abstract class Animal { + + private String name ; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + /** + * 描述抽象方法 + */ + protected abstract void desc() ; +} diff --git a/src/main/java/com/crossoverjie/design/pattern/factorymethod/AnimalFactory.java b/src/main/java/com/crossoverjie/design/pattern/factorymethod/AnimalFactory.java new file mode 100644 index 00000000..fb7f571f --- /dev/null +++ b/src/main/java/com/crossoverjie/design/pattern/factorymethod/AnimalFactory.java @@ -0,0 +1,13 @@ +package com.crossoverjie.design.pattern.factorymethod; + +/** + * Function:工厂方法模式 + * + * @author crossoverJie + * Date: 19/03/2018 14:29 + * @since JDK 1.8 + */ +public interface AnimalFactory { + + Animal createAnimal() ; +} diff --git a/src/main/java/com/crossoverjie/design/pattern/factorymethod/Cat.java b/src/main/java/com/crossoverjie/design/pattern/factorymethod/Cat.java new file mode 100644 index 00000000..7d0393cb --- /dev/null +++ b/src/main/java/com/crossoverjie/design/pattern/factorymethod/Cat.java @@ -0,0 +1,15 @@ +package com.crossoverjie.design.pattern.factorymethod; + +/** + * Function: + * + * @author crossoverJie + * Date: 19/03/2018 14:33 + * @since JDK 1.8 + */ +public class Cat extends Animal { + @Override + protected void desc() { + System.out.println("Cat name is=" + this.getName()); + } +} diff --git a/src/main/java/com/crossoverjie/design/pattern/factorymethod/CatFactory.java b/src/main/java/com/crossoverjie/design/pattern/factorymethod/CatFactory.java new file mode 100644 index 00000000..a8f90d65 --- /dev/null +++ b/src/main/java/com/crossoverjie/design/pattern/factorymethod/CatFactory.java @@ -0,0 +1,15 @@ +package com.crossoverjie.design.pattern.factorymethod; + +/** + * Function: + * + * @author crossoverJie + * Date: 19/03/2018 15:21 + * @since JDK 1.8 + */ +public class CatFactory implements AnimalFactory { + @Override + public Animal createAnimal() { + return new Cat(); + } +} diff --git a/src/main/java/com/crossoverjie/design/pattern/factorymethod/Fish.java b/src/main/java/com/crossoverjie/design/pattern/factorymethod/Fish.java new file mode 100644 index 00000000..9820c1a7 --- /dev/null +++ b/src/main/java/com/crossoverjie/design/pattern/factorymethod/Fish.java @@ -0,0 +1,17 @@ +package com.crossoverjie.design.pattern.factorymethod; + +/** + * Function: + * + * @author crossoverJie + * Date: 19/03/2018 14:32 + * @since JDK 1.8 + */ +public class Fish extends Animal { + + + @Override + protected void desc() { + System.out.println("fish name is=" + this.getName()); + } +} diff --git a/src/main/java/com/crossoverjie/design/pattern/factorymethod/FishFactory.java b/src/main/java/com/crossoverjie/design/pattern/factorymethod/FishFactory.java new file mode 100644 index 00000000..8e59610a --- /dev/null +++ b/src/main/java/com/crossoverjie/design/pattern/factorymethod/FishFactory.java @@ -0,0 +1,15 @@ +package com.crossoverjie.design.pattern.factorymethod; + +/** + * Function: + * + * @author crossoverJie + * Date: 19/03/2018 15:21 + * @since JDK 1.8 + */ +public class FishFactory implements AnimalFactory { + @Override + public Animal createAnimal() { + return new Fish() ; + } +} diff --git a/src/main/java/com/crossoverjie/design/pattern/factorymethod/Main.java b/src/main/java/com/crossoverjie/design/pattern/factorymethod/Main.java new file mode 100644 index 00000000..440aaa43 --- /dev/null +++ b/src/main/java/com/crossoverjie/design/pattern/factorymethod/Main.java @@ -0,0 +1,17 @@ +package com.crossoverjie.design.pattern.factorymethod; + +/** + * Function: + * + * @author crossoverJie + * Date: 19/03/2018 14:34 + * @since JDK 1.8 + */ +public class Main { + public static void main(String[] args) { + AnimalFactory factory = new CatFactory() ; + Animal animal = factory.createAnimal(); + animal.setName("猫咪"); + animal.desc(); + } +} From b70fd0d50ca191a2edd3bde95b970ebc64a12e5e Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Tue, 20 Mar 2018 18:55:00 +0800 Subject: [PATCH 042/323] :sparkles: Introducing new features.three method bean lifecycle --- .../crossoverjie/spring/LifeCycleConfig.java | 25 +++++++++++++++ .../crossoverjie/spring/SpringLifeCycle.java | 28 +++++++++++++++++ .../spring/annotation/AnnotationBean.java | 31 +++++++++++++++++++ .../service/SpringLifeCycleService.java | 30 ++++++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 src/main/java/com/crossoverjie/spring/LifeCycleConfig.java create mode 100644 src/main/java/com/crossoverjie/spring/SpringLifeCycle.java create mode 100644 src/main/java/com/crossoverjie/spring/annotation/AnnotationBean.java create mode 100644 src/main/java/com/crossoverjie/spring/service/SpringLifeCycleService.java diff --git a/src/main/java/com/crossoverjie/spring/LifeCycleConfig.java b/src/main/java/com/crossoverjie/spring/LifeCycleConfig.java new file mode 100644 index 00000000..37723233 --- /dev/null +++ b/src/main/java/com/crossoverjie/spring/LifeCycleConfig.java @@ -0,0 +1,25 @@ +package com.crossoverjie.spring; + +import com.crossoverjie.concurrent.Singleton; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +/** + * Function: + * + * @author crossoverJie + * Date: 19/03/2018 22:37 + * @since JDK 1.8 + */ +@Configuration +public class LifeCycleConfig { + + + @Bean(initMethod = "start", destroyMethod = "destroy") + public SpringLifeCycle create(){ + SpringLifeCycle springLifeCycle = new SpringLifeCycle() ; + + return springLifeCycle ; + } +} diff --git a/src/main/java/com/crossoverjie/spring/SpringLifeCycle.java b/src/main/java/com/crossoverjie/spring/SpringLifeCycle.java new file mode 100644 index 00000000..0a016b3a --- /dev/null +++ b/src/main/java/com/crossoverjie/spring/SpringLifeCycle.java @@ -0,0 +1,28 @@ +package com.crossoverjie.spring; + +import com.crossoverjie.Application; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +/** + * Function: + * + * @author crossoverJie + * Date: 20/03/2018 18:23 + * @since JDK 1.8 + */ +public class SpringLifeCycle{ + + private final static Logger LOGGER = LoggerFactory.getLogger(SpringLifeCycle.class); + public void start(){ + LOGGER.info("SpringLifeCycle start"); + } + + + public void destroy(){ + LOGGER.info("SpringLifeCycle destroy"); + } +} diff --git a/src/main/java/com/crossoverjie/spring/annotation/AnnotationBean.java b/src/main/java/com/crossoverjie/spring/annotation/AnnotationBean.java new file mode 100644 index 00000000..9f8423da --- /dev/null +++ b/src/main/java/com/crossoverjie/spring/annotation/AnnotationBean.java @@ -0,0 +1,31 @@ +package com.crossoverjie.spring.annotation; + +import com.crossoverjie.spring.SpringLifeCycle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; + +/** + * Function: + * + * @author crossoverJie + * Date: 20/03/2018 18:46 + * @since JDK 1.8 + */ +@Component +public class AnnotationBean { + private final static Logger LOGGER = LoggerFactory.getLogger(AnnotationBean.class); + + @PostConstruct + public void start(){ + LOGGER.info("AnnotationBean start"); + } + + @PreDestroy + public void destroy(){ + LOGGER.info("AnnotationBean destroy"); + } +} diff --git a/src/main/java/com/crossoverjie/spring/service/SpringLifeCycleService.java b/src/main/java/com/crossoverjie/spring/service/SpringLifeCycleService.java new file mode 100644 index 00000000..432e28da --- /dev/null +++ b/src/main/java/com/crossoverjie/spring/service/SpringLifeCycleService.java @@ -0,0 +1,30 @@ +package com.crossoverjie.spring.service; + +import com.crossoverjie.spring.SpringLifeCycle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +/** + * Function:首先执行 + * + * @author crossoverJie + * Date: 20/03/2018 18:38 + * @since JDK 1.8 + */ +@Service +public class SpringLifeCycleService implements InitializingBean,DisposableBean{ + private final static Logger LOGGER = LoggerFactory.getLogger(SpringLifeCycleService.class); + @Override + public void destroy() throws Exception { + LOGGER.info("SpringLifeCycleService destroy"); + } + + @Override + public void afterPropertiesSet() throws Exception { + LOGGER.info("SpringLifeCycleService start"); + } +} From 10f06bb5b58a09827a36f8768b4e1b976587e25d Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Tue, 20 Mar 2018 19:00:11 +0800 Subject: [PATCH 043/323] :bulb: moment --- src/main/java/com/crossoverjie/spring/LifeCycleConfig.java | 2 +- .../java/com/crossoverjie/spring/annotation/AnnotationBean.java | 2 +- .../com/crossoverjie/spring/service/SpringLifeCycleService.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/crossoverjie/spring/LifeCycleConfig.java b/src/main/java/com/crossoverjie/spring/LifeCycleConfig.java index 37723233..fd44aa6d 100644 --- a/src/main/java/com/crossoverjie/spring/LifeCycleConfig.java +++ b/src/main/java/com/crossoverjie/spring/LifeCycleConfig.java @@ -6,7 +6,7 @@ import org.springframework.context.annotation.Configuration; /** - * Function: + * Function:使用 initMethod 和 destroyMethod 的方式 * * @author crossoverJie * Date: 19/03/2018 22:37 diff --git a/src/main/java/com/crossoverjie/spring/annotation/AnnotationBean.java b/src/main/java/com/crossoverjie/spring/annotation/AnnotationBean.java index 9f8423da..e848581b 100644 --- a/src/main/java/com/crossoverjie/spring/annotation/AnnotationBean.java +++ b/src/main/java/com/crossoverjie/spring/annotation/AnnotationBean.java @@ -9,7 +9,7 @@ import javax.annotation.PreDestroy; /** - * Function: + * Function:用注解的方法 * * @author crossoverJie * Date: 20/03/2018 18:46 diff --git a/src/main/java/com/crossoverjie/spring/service/SpringLifeCycleService.java b/src/main/java/com/crossoverjie/spring/service/SpringLifeCycleService.java index 432e28da..7918553d 100644 --- a/src/main/java/com/crossoverjie/spring/service/SpringLifeCycleService.java +++ b/src/main/java/com/crossoverjie/spring/service/SpringLifeCycleService.java @@ -9,7 +9,7 @@ import org.springframework.stereotype.Service; /** - * Function:首先执行 + * Function:实现 InitializingBean DisposableBean 接口 * * @author crossoverJie * Date: 20/03/2018 18:38 From 75fbc43a727830790c2ac0980f592491f647be72 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Tue, 20 Mar 2018 23:38:57 +0800 Subject: [PATCH 044/323] :bulb: moment --- .../spring/aware/SpringLifeCycleAware.java | 29 +++++++++++++++++++ .../processor/SpringLifeCycleProcessor.java | 29 +++++++++++++++++++ .../service/SpringLifeCycleService.java | 8 ++--- 3 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/crossoverjie/spring/aware/SpringLifeCycleAware.java create mode 100644 src/main/java/com/crossoverjie/spring/processor/SpringLifeCycleProcessor.java diff --git a/src/main/java/com/crossoverjie/spring/aware/SpringLifeCycleAware.java b/src/main/java/com/crossoverjie/spring/aware/SpringLifeCycleAware.java new file mode 100644 index 00000000..d3b288a1 --- /dev/null +++ b/src/main/java/com/crossoverjie/spring/aware/SpringLifeCycleAware.java @@ -0,0 +1,29 @@ +package com.crossoverjie.spring.aware; + +import com.crossoverjie.Application; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +/** + * Function: + * + * @author crossoverJie + * Date: 20/03/2018 21:54 + * @since JDK 1.8 + */ +@Component +public class SpringLifeCycleAware implements ApplicationContextAware { + private final static Logger LOGGER = LoggerFactory.getLogger(SpringLifeCycleAware.class); + + private ApplicationContext applicationContext ; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext ; + LOGGER.info("SpringLifeCycleAware start"); + } +} diff --git a/src/main/java/com/crossoverjie/spring/processor/SpringLifeCycleProcessor.java b/src/main/java/com/crossoverjie/spring/processor/SpringLifeCycleProcessor.java new file mode 100644 index 00000000..6e43a848 --- /dev/null +++ b/src/main/java/com/crossoverjie/spring/processor/SpringLifeCycleProcessor.java @@ -0,0 +1,29 @@ +package com.crossoverjie.spring.processor; + +import com.crossoverjie.Application; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.stereotype.Component; + +/** + * Function: + * + * @author crossoverJie + * Date: 20/03/2018 22:11 + * @since JDK 1.8 + */ +@Component +public class SpringLifeCycleProcessor implements BeanPostProcessor { + private final static Logger LOGGER = LoggerFactory.getLogger(SpringLifeCycleProcessor.class); + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + return bean; + } +} diff --git a/src/main/java/com/crossoverjie/spring/service/SpringLifeCycleService.java b/src/main/java/com/crossoverjie/spring/service/SpringLifeCycleService.java index 7918553d..302aac1d 100644 --- a/src/main/java/com/crossoverjie/spring/service/SpringLifeCycleService.java +++ b/src/main/java/com/crossoverjie/spring/service/SpringLifeCycleService.java @@ -19,12 +19,12 @@ public class SpringLifeCycleService implements InitializingBean,DisposableBean{ private final static Logger LOGGER = LoggerFactory.getLogger(SpringLifeCycleService.class); @Override - public void destroy() throws Exception { - LOGGER.info("SpringLifeCycleService destroy"); + public void afterPropertiesSet() throws Exception { + LOGGER.info("SpringLifeCycleService start"); } @Override - public void afterPropertiesSet() throws Exception { - LOGGER.info("SpringLifeCycleService start"); + public void destroy() throws Exception { + LOGGER.info("SpringLifeCycleService destroy"); } } From 7c8fb5764b0d010895265e7f77a41e479b054044 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Tue, 20 Mar 2018 23:40:39 +0800 Subject: [PATCH 045/323] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0c85ee99..23261664 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -`Java` 日常开发知识点,继续完善中。 +Java 知识点,继续完善中。 -> 多数是一些 `Java` 基础知识、底层原理、算法详解。也有上层应用设计,其中不乏一些大厂面试真题。 +> 多数是一些 Java 基础知识、底层原理、算法详解。也有上层应用设计,其中不乏一些大厂面试真题。 如果对你有帮助请点下 `Star`,有疑问欢迎提 [Issues](https://github.com/crossoverJie/Java-Interview/issues),有好的想法请提 [PR](https://github.com/crossoverJie/Java-Interview/pulls)。 @@ -72,4 +72,4 @@ > crossoverJie#gmail.com -![](https://ws4.sinaimg.cn/large/006tKfTcly1fochm4as0sj30by0bydgh.jpg) \ No newline at end of file +![](https://ws4.sinaimg.cn/large/006tKfTcly1fochm4as0sj30by0bydgh.jpg) From 4a489dcad45a50f4713cd2f6a4f15f6957c361f4 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Wed, 21 Mar 2018 00:11:18 +0800 Subject: [PATCH 046/323] Revert "Update README.md" This reverts commit 7c8fb5764b0d010895265e7f77a41e479b054044. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 23261664..0c85ee99 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -Java 知识点,继续完善中。 +`Java` 日常开发知识点,继续完善中。 -> 多数是一些 Java 基础知识、底层原理、算法详解。也有上层应用设计,其中不乏一些大厂面试真题。 +> 多数是一些 `Java` 基础知识、底层原理、算法详解。也有上层应用设计,其中不乏一些大厂面试真题。 如果对你有帮助请点下 `Star`,有疑问欢迎提 [Issues](https://github.com/crossoverJie/Java-Interview/issues),有好的想法请提 [PR](https://github.com/crossoverJie/Java-Interview/pulls)。 @@ -72,4 +72,4 @@ Java 知识点,继续完善中。 > crossoverJie#gmail.com -![](https://ws4.sinaimg.cn/large/006tKfTcly1fochm4as0sj30by0bydgh.jpg) +![](https://ws4.sinaimg.cn/large/006tKfTcly1fochm4as0sj30by0bydgh.jpg) \ No newline at end of file From 0a8b885dffdf26a02d8c6c020b46c794aba5b6b6 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Wed, 21 Mar 2018 00:14:31 +0800 Subject: [PATCH 047/323] Update README.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0c85ee99..19c6a208 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ -`Java` 日常开发知识点,继续完善中。 +Java 知识点,继续完善中。 + +> 多数是一些 Java 基础知识、底层原理、算法详解。也有上层应用设计,其中不乏一些大厂面试真题。 -> 多数是一些 `Java` 基础知识、底层原理、算法详解。也有上层应用设计,其中不乏一些大厂面试真题。 如果对你有帮助请点下 `Star`,有疑问欢迎提 [Issues](https://github.com/crossoverJie/Java-Interview/issues),有好的想法请提 [PR](https://github.com/crossoverJie/Java-Interview/pulls)。 @@ -72,4 +73,4 @@ > crossoverJie#gmail.com -![](https://ws4.sinaimg.cn/large/006tKfTcly1fochm4as0sj30by0bydgh.jpg) \ No newline at end of file +![](https://ws4.sinaimg.cn/large/006tKfTcly1fochm4as0sj30by0bydgh.jpg) From ca117bfa773cedb561b15063864e98138611bf8d Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Wed, 21 Mar 2018 01:01:22 +0800 Subject: [PATCH 048/323] :sparkles: Introducing new features.Spring lifecycle --- .../spring/processor/SpringLifeCycleProcessor.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/com/crossoverjie/spring/processor/SpringLifeCycleProcessor.java b/src/main/java/com/crossoverjie/spring/processor/SpringLifeCycleProcessor.java index 6e43a848..67d4ffb8 100644 --- a/src/main/java/com/crossoverjie/spring/processor/SpringLifeCycleProcessor.java +++ b/src/main/java/com/crossoverjie/spring/processor/SpringLifeCycleProcessor.java @@ -19,11 +19,17 @@ public class SpringLifeCycleProcessor implements BeanPostProcessor { private final static Logger LOGGER = LoggerFactory.getLogger(SpringLifeCycleProcessor.class); @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if ("annotationBean".equals(beanName)){ + LOGGER.info("SpringLifeCycleProcessor start beanName={}",beanName); + } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if ("annotationBean".equals(beanName)){ + LOGGER.info("SpringLifeCycleProcessor end beanName={}",beanName); + } return bean; } } From c873be2c81d9c0036eed7addc605de1dd1176d9b Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Wed, 21 Mar 2018 01:02:05 +0800 Subject: [PATCH 049/323] :sparkles: Introducing new features.Spring lifecycle --- MD/spring/spring-bean-lifecycle.md | 160 +++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 MD/spring/spring-bean-lifecycle.md diff --git a/MD/spring/spring-bean-lifecycle.md b/MD/spring/spring-bean-lifecycle.md new file mode 100644 index 00000000..0486b5ad --- /dev/null +++ b/MD/spring/spring-bean-lifecycle.md @@ -0,0 +1,160 @@ +## Spring Bean 生命周期 + + +### 前言 + +Spring Bean 的生命周期在整个 Spring 中占有很重要的位置,掌握这些可以加深对 Spring 的理解。 + +首先放一张很经典的生命周期图: + +![](https://ws2.sinaimg.cn/large/006tNc79gy1fpjqo8lkc6j30fi0eemye.jpg) + +再谈生命周期之前有一点需要先明确: + +> Spring 只帮我们管理单例模式 Bean 的**完整**生命周期,对于 prototype 的 bean ,Spring 在创建好交给使用者之后则不会再管理后续的生命周期。 + + +### 注解方式 + +在 bean 初始化时会经历几个阶段,首先可以使用注解 `@PostConstruct`, `@PreDestroy` 来再 bean 的创建和销毁阶段进行调用: + +```java +@Component +public class AnnotationBean { + private final static Logger LOGGER = LoggerFactory.getLogger(AnnotationBean.class); + + @PostConstruct + public void start(){ + LOGGER.info("AnnotationBean start"); + } + + @PreDestroy + public void destroy(){ + LOGGER.info("AnnotationBean destroy"); + } +} +``` + +### InitializingBean, DisposableBean 接口 + +还可以实现 `InitializingBean,DisposableBean` 这两个接口,也是在初始化已经销毁阶段调用: + +```java +@Service +public class SpringLifeCycleService implements InitializingBean,DisposableBean{ + private final static Logger LOGGER = LoggerFactory.getLogger(SpringLifeCycleService.class); + @Override + public void afterPropertiesSet() throws Exception { + LOGGER.info("SpringLifeCycleService start"); + } + + @Override + public void destroy() throws Exception { + LOGGER.info("SpringLifeCycleService destroy"); + } +} +``` + +### 自定义初始化和销毁方法 + +也可以自定义方法用于在初始化、销毁阶段进行调用: + +```java +@Configuration +public class LifeCycleConfig { + + + @Bean(initMethod = "start", destroyMethod = "destroy") + public SpringLifeCycle create(){ + SpringLifeCycle springLifeCycle = new SpringLifeCycle() ; + + return springLifeCycle ; + } +} + +public class SpringLifeCycle{ + + private final static Logger LOGGER = LoggerFactory.getLogger(SpringLifeCycle.class); + public void start(){ + LOGGER.info("SpringLifeCycle start"); + } + + + public void destroy(){ + LOGGER.info("SpringLifeCycle destroy"); + } +} +``` + +以上是在 SpringBoot 中可以这样配置,如果是原始的基于 XML 也是可以利用: + +``` + + +``` + +来达到同样的效果。 + +### 实现 *Aware 接口 + +`*Aware` 接口可以用于在初始化 bean 时获得 Spring 中的一些对象,如获取 `Spring 上下文`等。 + +```java +@Component +public class SpringLifeCycleAware implements ApplicationContextAware { + private final static Logger LOGGER = LoggerFactory.getLogger(SpringLifeCycleAware.class); + + private ApplicationContext applicationContext ; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext ; + LOGGER.info("SpringLifeCycleAware start"); + } +} +``` + +这样在 `springLifeCycleAware` 这个 bean 初始化会就会调用 `setApplicationContext` 方法,并可以获得 applicationContext 对象。 + +### BeanPostProcessor 后处理器 + +也可以实现 BeanPostProcessor 接口,Spring 中所有 bean 在做初始化时都会调用该接口中的两个方法,可以用于对一些特殊的 bean 进行处理: + +```java +@Component +public class SpringLifeCycleProcessor implements BeanPostProcessor { + private final static Logger LOGGER = LoggerFactory.getLogger(SpringLifeCycleProcessor.class); + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + return bean; + } +} +``` + +执行之后观察结果: + +``` +018-03-21 00:40:24.856 [restartedMain] INFO c.c.s.p.SpringLifeCycleProcessor - SpringLifeCycleProcessor start beanName=annotationBean +2018-03-21 00:40:24.860 [restartedMain] INFO c.c.spring.annotation.AnnotationBean - AnnotationBean start +2018-03-21 00:40:24.861 [restartedMain] INFO c.c.s.p.SpringLifeCycleProcessor - SpringLifeCycleProcessor end beanName=annotationBean +2018-03-21 00:40:24.864 [restartedMain] INFO c.c.s.aware.SpringLifeCycleAware - SpringLifeCycleAware start +2018-03-21 00:40:24.867 [restartedMain] INFO c.c.s.service.SpringLifeCycleService - SpringLifeCycleService start +2018-03-21 00:40:24.887 [restartedMain] INFO c.c.spring.SpringLifeCycle - SpringLifeCycle start +2018-03-21 00:40:25.062 [restartedMain] INFO o.s.b.d.a.OptionalLiveReloadServer - LiveReload server is running on port 35729 +2018-03-21 00:40:25.122 [restartedMain] INFO o.s.j.e.a.AnnotationMBeanExporter - Registering beans for JMX exposure on startup +2018-03-21 00:40:25.140 [restartedMain] INFO com.crossoverjie.Application - Started Application in 2.309 seconds (JVM running for 3.681) +2018-03-21 00:40:25.143 [restartedMain] INFO com.crossoverjie.Application - start ok! +2018-03-21 00:40:25.153 [Thread-8] INFO o.s.c.a.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@3913adad: startup date [Wed Mar 21 00:40:23 CST 2018]; root of context hierarchy +2018-03-21 00:40:25.155 [Thread-8] INFO o.s.j.e.a.AnnotationMBeanExporter - Unregistering JMX-exposed beans on shutdown +2018-03-21 00:40:25.156 [Thread-8] INFO c.c.spring.SpringLifeCycle - SpringLifeCycle destroy +2018-03-21 00:40:25.156 [Thread-8] INFO c.c.s.service.SpringLifeCycleService - SpringLifeCycleService destroy +2018-03-21 00:40:25.156 [Thread-8] INFO c.c.spring.annotation.AnnotationBean - AnnotationBean destroy +``` + + + From 0d6f2a86a93d4a81e7299777010a1abcf318629c Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Wed, 21 Mar 2018 01:06:48 +0800 Subject: [PATCH 050/323] :sparkles: Introducing new features.Spring lifecycle --- MD/spring/spring-bean-lifecycle.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MD/spring/spring-bean-lifecycle.md b/MD/spring/spring-bean-lifecycle.md index 0486b5ad..fa2f2fd4 100644 --- a/MD/spring/spring-bean-lifecycle.md +++ b/MD/spring/spring-bean-lifecycle.md @@ -5,7 +5,7 @@ Spring Bean 的生命周期在整个 Spring 中占有很重要的位置,掌握这些可以加深对 Spring 的理解。 -首先放一张很经典的生命周期图: +首先放一张经典的生命周期图: ![](https://ws2.sinaimg.cn/large/006tNc79gy1fpjqo8lkc6j30fi0eemye.jpg) @@ -156,5 +156,5 @@ public class SpringLifeCycleProcessor implements BeanPostProcessor { 2018-03-21 00:40:25.156 [Thread-8] INFO c.c.spring.annotation.AnnotationBean - AnnotationBean destroy ``` - +之后则是进入 bean 的销毁阶段。 From e9ab450db034e240becd6343999f44e9ff8a0190 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Wed, 21 Mar 2018 01:07:22 +0800 Subject: [PATCH 051/323] :sparkles: Introducing new features.Spring lifecycle --- MD/spring/spring-bean-lifecycle.md | 1 - 1 file changed, 1 deletion(-) diff --git a/MD/spring/spring-bean-lifecycle.md b/MD/spring/spring-bean-lifecycle.md index fa2f2fd4..ec77bb80 100644 --- a/MD/spring/spring-bean-lifecycle.md +++ b/MD/spring/spring-bean-lifecycle.md @@ -1,7 +1,6 @@ ## Spring Bean 生命周期 -### 前言 Spring Bean 的生命周期在整个 Spring 中占有很重要的位置,掌握这些可以加深对 Spring 的理解。 From 8d99cb76ac5c193bd2e73ad96dcf1008451eecd0 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Wed, 21 Mar 2018 01:12:06 +0800 Subject: [PATCH 052/323] :sparkles: Introducing new features.Spring lifecycle --- MD/spring/spring-bean-lifecycle.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/MD/spring/spring-bean-lifecycle.md b/MD/spring/spring-bean-lifecycle.md index ec77bb80..f40527b5 100644 --- a/MD/spring/spring-bean-lifecycle.md +++ b/MD/spring/spring-bean-lifecycle.md @@ -36,7 +36,7 @@ public class AnnotationBean { ### InitializingBean, DisposableBean 接口 -还可以实现 `InitializingBean,DisposableBean` 这两个接口,也是在初始化已经销毁阶段调用: +还可以实现 `InitializingBean,DisposableBean` 这两个接口,也是在初始化以及销毁阶段调用: ```java @Service @@ -56,7 +56,7 @@ public class SpringLifeCycleService implements InitializingBean,DisposableBean{ ### 自定义初始化和销毁方法 -也可以自定义方法用于在初始化、销毁阶段进行调用: +也可以自定义方法用于在初始化、销毁阶段调用: ```java @Configuration @@ -85,7 +85,7 @@ public class SpringLifeCycle{ } ``` -以上是在 SpringBoot 中可以这样配置,如果是原始的基于 XML 也是可以利用: +以上是在 SpringBoot 中可以这样配置,如果是原始的基于 XML 也是可以使用: ``` @@ -113,11 +113,11 @@ public class SpringLifeCycleAware implements ApplicationContextAware { } ``` -这样在 `springLifeCycleAware` 这个 bean 初始化会就会调用 `setApplicationContext` 方法,并可以获得 applicationContext 对象。 +这样在 `springLifeCycleAware` 这个 bean 初始化会就会调用 `setApplicationContext` 方法,并可以获得 `applicationContext` 对象。 ### BeanPostProcessor 后处理器 -也可以实现 BeanPostProcessor 接口,Spring 中所有 bean 在做初始化时都会调用该接口中的两个方法,可以用于对一些特殊的 bean 进行处理: +实现 BeanPostProcessor 接口,Spring 中所有 bean 在做初始化时都会调用该接口中的两个方法,可以用于对一些特殊的 bean 进行处理: ```java @Component @@ -125,11 +125,17 @@ public class SpringLifeCycleProcessor implements BeanPostProcessor { private final static Logger LOGGER = LoggerFactory.getLogger(SpringLifeCycleProcessor.class); @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if ("annotationBean".equals(beanName)){ + LOGGER.info("SpringLifeCycleProcessor start beanName={}",beanName); + } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if ("annotationBean".equals(beanName)){ + LOGGER.info("SpringLifeCycleProcessor end beanName={}",beanName); + } return bean; } } From d30e8b951e309856e3e70f50b0320a924d2bf681 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Wed, 21 Mar 2018 01:13:54 +0800 Subject: [PATCH 053/323] :sparkles: Introducing new features.Spring lifecycle --- MD/spring/spring-bean-lifecycle.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MD/spring/spring-bean-lifecycle.md b/MD/spring/spring-bean-lifecycle.md index f40527b5..9a71923e 100644 --- a/MD/spring/spring-bean-lifecycle.md +++ b/MD/spring/spring-bean-lifecycle.md @@ -4,9 +4,9 @@ Spring Bean 的生命周期在整个 Spring 中占有很重要的位置,掌握这些可以加深对 Spring 的理解。 -首先放一张经典的生命周期图: +首先看下生命周期图: -![](https://ws2.sinaimg.cn/large/006tNc79gy1fpjqo8lkc6j30fi0eemye.jpg) +![](https://ws3.sinaimg.cn/large/006tNc79gy1fpjsamy6uoj30nt0cqq4i.jpg) 再谈生命周期之前有一点需要先明确: From ebffa511170e664116c62cbeac68b1ad6e1fb949 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Wed, 21 Mar 2018 01:21:06 +0800 Subject: [PATCH 054/323] :sparkles: Introducing new features.Spring lifecycle --- MD/spring/spring-bean-lifecycle.md | 17 ++++++++++++++++- .../processor/SpringLifeCycleProcessor.java | 15 +++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/MD/spring/spring-bean-lifecycle.md b/MD/spring/spring-bean-lifecycle.md index 9a71923e..903d06a6 100644 --- a/MD/spring/spring-bean-lifecycle.md +++ b/MD/spring/spring-bean-lifecycle.md @@ -115,7 +115,7 @@ public class SpringLifeCycleAware implements ApplicationContextAware { 这样在 `springLifeCycleAware` 这个 bean 初始化会就会调用 `setApplicationContext` 方法,并可以获得 `applicationContext` 对象。 -### BeanPostProcessor 后处理器 +### BeanPostProcessor 增强处理器 实现 BeanPostProcessor 接口,Spring 中所有 bean 在做初始化时都会调用该接口中的两个方法,可以用于对一些特殊的 bean 进行处理: @@ -123,6 +123,14 @@ public class SpringLifeCycleAware implements ApplicationContextAware { @Component public class SpringLifeCycleProcessor implements BeanPostProcessor { private final static Logger LOGGER = LoggerFactory.getLogger(SpringLifeCycleProcessor.class); + + /** + * 预初始化 初始化之前调用 + * @param bean + * @param beanName + * @return + * @throws BeansException + */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if ("annotationBean".equals(beanName)){ @@ -131,6 +139,13 @@ public class SpringLifeCycleProcessor implements BeanPostProcessor { return bean; } + /** + * 后初始化 bean 初始化完成调用 + * @param bean + * @param beanName + * @return + * @throws BeansException + */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if ("annotationBean".equals(beanName)){ diff --git a/src/main/java/com/crossoverjie/spring/processor/SpringLifeCycleProcessor.java b/src/main/java/com/crossoverjie/spring/processor/SpringLifeCycleProcessor.java index 67d4ffb8..d0d664be 100644 --- a/src/main/java/com/crossoverjie/spring/processor/SpringLifeCycleProcessor.java +++ b/src/main/java/com/crossoverjie/spring/processor/SpringLifeCycleProcessor.java @@ -17,6 +17,14 @@ @Component public class SpringLifeCycleProcessor implements BeanPostProcessor { private final static Logger LOGGER = LoggerFactory.getLogger(SpringLifeCycleProcessor.class); + + /** + * 预初始化 初始化之前调用 + * @param bean + * @param beanName + * @return + * @throws BeansException + */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if ("annotationBean".equals(beanName)){ @@ -25,6 +33,13 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) thro return bean; } + /** + * 后初始化 bean 初始化完成调用 + * @param bean + * @param beanName + * @return + * @throws BeansException + */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if ("annotationBean".equals(beanName)){ From 76de08195cecba14e021887cfeed783372fe28a6 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Wed, 21 Mar 2018 01:23:48 +0800 Subject: [PATCH 055/323] :sparkles: Introducing new features.Spring lifecycle --- MD/spring/spring-bean-lifecycle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MD/spring/spring-bean-lifecycle.md b/MD/spring/spring-bean-lifecycle.md index 903d06a6..0ef8b9ea 100644 --- a/MD/spring/spring-bean-lifecycle.md +++ b/MD/spring/spring-bean-lifecycle.md @@ -176,5 +176,5 @@ public class SpringLifeCycleProcessor implements BeanPostProcessor { 2018-03-21 00:40:25.156 [Thread-8] INFO c.c.spring.annotation.AnnotationBean - AnnotationBean destroy ``` -之后则是进入 bean 的销毁阶段。 +直到 Spring 上下文销毁时则会调用自定义的销毁方法以及实现了 `DisposableBean` 的 `destroy()` 方法。 From 438b610961add29335e6d80776fe533369132f80 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Wed, 21 Mar 2018 01:25:43 +0800 Subject: [PATCH 056/323] :sparkles: Introducing new features.Spring lifecycle --- MD/spring/spring-bean-lifecycle.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MD/spring/spring-bean-lifecycle.md b/MD/spring/spring-bean-lifecycle.md index 0ef8b9ea..64c0b5ae 100644 --- a/MD/spring/spring-bean-lifecycle.md +++ b/MD/spring/spring-bean-lifecycle.md @@ -15,7 +15,7 @@ Spring Bean 的生命周期在整个 Spring 中占有很重要的位置,掌握 ### 注解方式 -在 bean 初始化时会经历几个阶段,首先可以使用注解 `@PostConstruct`, `@PreDestroy` 来再 bean 的创建和销毁阶段进行调用: +在 bean 初始化时会经历几个阶段,首先可以使用注解 `@PostConstruct`, `@PreDestroy` 来在 bean 的创建和销毁阶段进行调用: ```java @Component @@ -87,7 +87,7 @@ public class SpringLifeCycle{ 以上是在 SpringBoot 中可以这样配置,如果是原始的基于 XML 也是可以使用: -``` +```xml ``` From 41eac20ae3fb884ac094bc72b702d087bda0e9b3 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Wed, 21 Mar 2018 01:44:18 +0800 Subject: [PATCH 057/323] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 19c6a208..2b80d031 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Java 知识点,继续完善中。 ### 常用框架 -- Spring IOC 的实现原理 +- [Spring Bean 生命周期](https://github.com/crossoverJie/Java-Interview/blob/master/MD/spring/spring-bean-lifecycle.md) - [Spring AOP 的实现原理](https://github.com/crossoverJie/Java-Interview/blob/master/MD/SpringAOP.md) ### SpringBoot From 133b5e9dda90435edf740b32fa6b266ae4af1da3 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Wed, 21 Mar 2018 09:30:13 +0800 Subject: [PATCH 058/323] :sparkles: Introducing new features.Spring lifecycle --- MD/spring/spring-bean-lifecycle.md | 1 + 1 file changed, 1 insertion(+) diff --git a/MD/spring/spring-bean-lifecycle.md b/MD/spring/spring-bean-lifecycle.md index 64c0b5ae..b9693567 100644 --- a/MD/spring/spring-bean-lifecycle.md +++ b/MD/spring/spring-bean-lifecycle.md @@ -1,6 +1,7 @@ ## Spring Bean 生命周期 +### 前言 Spring Bean 的生命周期在整个 Spring 中占有很重要的位置,掌握这些可以加深对 Spring 的理解。 From bc283e77801086b1189b0ae0b77eadd967bcecb2 Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Fri, 23 Mar 2018 00:00:13 +0800 Subject: [PATCH 059/323] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2b80d031..27780171 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Java 知识点,继续完善中。 - [多线程中的常见问题](https://github.com/crossoverJie/Java-Interview/blob/master/MD/Thread-common-problem.md) - [synchronize 关键字原理](https://github.com/crossoverJie/Java-Interview/blob/master/MD/Synchronize.md) - [多线程的三大核心](https://github.com/crossoverJie/Java-Interview/blob/master/MD/Threadcore.md) -- [对锁的一些认知,有哪些锁](https://github.com/crossoverJie/Java-Interview/blob/master/MD/Java-lock.md) +- [对锁的一些认知](https://github.com/crossoverJie/Java-Interview/blob/master/MD/Java-lock.md) - [ReentrantLock 实现原理 ](https://github.com/crossoverJie/Java-Interview/blob/master/MD/ReentrantLock.md) - [ConcurrentHashMap 的实现原理](https://github.com/crossoverJie/Java-Interview/blob/master/MD/ConcurrentHashMap.md) - [线程池原理](https://github.com/crossoverJie/Java-Interview/blob/master/MD/ThreadPoolExecutor.md) From 979b0a352411663ea6ad166bcf0e1ac18e403efd Mon Sep 17 00:00:00 2001 From: crossoverJie Date: Sat, 24 Mar 2018 13:35:19 +0800 Subject: [PATCH 060/323] :sparkles: Introducing new features.ArrayList --- MD/ArrayList.md | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/MD/ArrayList.md b/MD/ArrayList.md index cee9a6ca..4334db04 100644 --- a/MD/ArrayList.md +++ b/MD/ArrayList.md @@ -55,6 +55,65 @@ 由此可见 `ArrayList` 的主要消耗是数组扩容以及在指定位置添加数据,在日常使用时最好是指定大小,尽量减少扩容。更要减少在指定位置插入数据的操作。 +### 序列化 + +由于 ArrayList 是基于动态数组实现的,所以并不是所有的空间都被使用,所以使用了 `transient` 修饰,可以防止被序列化。 + +```java +transient Object[] elementData; +``` + +因此 ArrayList 自定义了序列化与反序列化: + +```java + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException{ + // Write out element count, and any hidden stuff + int expectedModCount = modCount; + s.defaultWriteObject(); + + // Write out size as capacity for behavioural compatibility with clone() + s.writeInt(size); + + // Write out all elements in the proper order. + //只序列化了被使用的数据 + for (int i=0; i