28
28
* [ join()] ( #join )
29
29
* [ wait() notify() notifyAll()] ( #wait-notify-notifyall )
30
30
* [ await() signal() signalAll()] ( #await-signal-signalall )
31
+ * [ 七、J.U.C - AQS] ( #七juc---aqs )
32
+ * [ CountdownLatch] ( #countdownlatch )
33
+ * [ CyclicBarrier] ( #cyclicbarrier )
34
+ * [ Semaphore] ( #semaphore )
35
+ * [ 八、J.U.C - 其它组件] ( #八juc---其它组件 )
36
+ * [ FutureTask] ( #futuretask )
31
37
* [ BlockingQueue] ( #blockingqueue )
32
- * [ 七、线程不安全示例] ( #七线程不安全示例 )
33
- * [ 八、Java 内存模型] ( #八java-内存模型 )
38
+ * [ ForkJoin] ( #forkjoin )
39
+ * [ 九、线程不安全示例] ( #九线程不安全示例 )
40
+ * [ 十、Java 内存模型] ( #十java-内存模型 )
34
41
* [ 主内存与工作内存] ( #主内存与工作内存 )
35
42
* [ 内存间交互操作] ( #内存间交互操作 )
36
43
* [ 内存模型三大特性] ( #内存模型三大特性 )
37
44
* [ 先行发生原则] ( #先行发生原则 )
38
- * [ 九 、线程安全] ( #九线程安全 )
45
+ * [ 十一 、线程安全] ( #十一线程安全 )
39
46
* [ 线程安全分类] ( #线程安全分类 )
40
47
* [ 线程安全的实现方法] ( #线程安全的实现方法 )
41
- * [ 十 、锁优化] ( #十锁优化 )
48
+ * [ 十二 、锁优化] ( #十二锁优化 )
42
49
* [ 自旋锁与自适应自旋] ( #自旋锁与自适应自旋 )
43
50
* [ 锁消除] ( #锁消除 )
44
51
* [ 锁粗化] ( #锁粗化 )
45
52
* [ 轻量级锁] ( #轻量级锁 )
46
53
* [ 偏向锁] ( #偏向锁 )
47
- * [ 九 、多线程开发良好的实践] ( #九多线程开发良好的实践 )
54
+ * [ 十三 、多线程开发良好的实践] ( #十三多线程开发良好的实践 )
48
55
* [ 参考资料] ( #参考资料 )
49
56
<!-- GFM-TOC -->
50
57
@@ -683,6 +690,168 @@ public class AwaitSignalExample {
683
690
}
684
691
```
685
692
693
+ # 七、J.U.C - AQS
694
+
695
+ java.util.concurrent(J.U.C)大大提高了并发性能,AQS 被认为是 J.U.C 的核心。
696
+
697
+ ## CountdownLatch
698
+
699
+ 用来控制一个线程等待多个线程。
700
+
701
+ 维护了一个计数器 cnt,每次调用 countDown() 方法会让计数器的值减 1,减到 0 的时候,那些因为调用 await() 方法而在等待的线程就会被唤醒。
702
+
703
+ <div align =" center " > <img src =" ../pics//CountdownLatch.png " /> </div ><br >
704
+
705
+ ``` java
706
+ public class CountdownLatchExample {
707
+
708
+ public static void main (String [] args ) throws InterruptedException {
709
+ final int totalTread = 10 ;
710
+ CountDownLatch countDownLatch = new CountDownLatch (totalTread);
711
+ ExecutorService executorService = Executors . newCachedThreadPool();
712
+ for (int i = 0 ; i < totalTread; i++ ) {
713
+ executorService. execute(() - > {
714
+ System . out. print(" run.." );
715
+ countDownLatch. countDown();
716
+ });
717
+ }
718
+ countDownLatch. await();
719
+ System . out. println(" end" );
720
+ executorService. shutdown();
721
+ }
722
+ }
723
+ ```
724
+
725
+ ``` html
726
+ run..run..run..run..run..run..run..run..run..run..end
727
+ ```
728
+
729
+ ## CyclicBarrier
730
+
731
+ 用来控制多个线程互相等待,只有当多个线程都到达时,这些线程才会继续执行。
732
+
733
+ 和 CountdownLatch 相似,都是通过维护计数器来实现的。但是它的计数器是递增的,每次执行 await() 方法之后,计数器会加 1,直到计数器的值和设置的值相等,等待的所有线程才会继续执行。和 CountdownLatch 的另一个区别是 CyclicBarrier 的计数器可以循环使用,所以它才叫做循环屏障。
734
+
735
+ 下图应该从下往上看才正确。
736
+
737
+ <div align =" center " > <img src =" ../pics//CyclicBarrier.png " /> </div ><br >
738
+
739
+ ``` java
740
+ public class CyclicBarrierExample {
741
+ public static void main (String [] args ) throws InterruptedException {
742
+ final int totalTread = 10 ;
743
+ CyclicBarrier cyclicBarrier = new CyclicBarrier (totalTread);
744
+ ExecutorService executorService = Executors . newCachedThreadPool();
745
+ for (int i = 0 ; i < totalTread; i++ ) {
746
+ executorService. execute(() - > {
747
+ System . out. print(" before.." );
748
+ try {
749
+ cyclicBarrier. await();
750
+ } catch (InterruptedException e) {
751
+ e. printStackTrace();
752
+ } catch (BrokenBarrierException e) {
753
+ e. printStackTrace();
754
+ }
755
+ System . out. print(" after.." );
756
+ });
757
+ }
758
+ executorService. shutdown();
759
+ }
760
+ }
761
+ ```
762
+
763
+ ``` html
764
+ before..before..before..before..before..before..before..before..before..before..after..after..after..after..after..after..after..after..after..after..
765
+ ```
766
+
767
+ ## Semaphore
768
+
769
+ Semaphore 就是操作系统中的信号量,可以控制对互斥资源的访问线程数。
770
+
771
+ <div align =" center " > <img src =" ../pics//Semaphore.png " /> </div ><br >
772
+
773
+ 以下代码模拟了对某个服务的并发请求,每次只能由 3 个客户端同时访问,请求总数为 10。
774
+
775
+ ``` java
776
+ public class SemaphoreExample {
777
+ public static void main (String [] args ) {
778
+ final int clientCount = 3 ;
779
+ final int totalRequestCount = 10 ;
780
+ Semaphore semaphore = new Semaphore (clientCount);
781
+ ExecutorService executorService = Executors . newCachedThreadPool();
782
+ for (int i = 0 ; i < totalRequestCount; i++ ) {
783
+ executorService. execute(()- > {
784
+ try {
785
+ semaphore. acquire();
786
+ System . out. print(semaphore. availablePermits() + " " );
787
+ semaphore. release();
788
+ } catch (InterruptedException e) {
789
+ e. printStackTrace();
790
+ }
791
+ });
792
+ }
793
+ executorService. shutdown();
794
+ }
795
+ }
796
+ ```
797
+
798
+ ``` html
799
+ 2 1 2 2 2 2 2 1 2 2
800
+ ```
801
+
802
+ # 八、J.U.C - 其它组件
803
+
804
+ ## FutureTask
805
+
806
+ 在介绍 Callable 时我们知道它可以有返回值,返回值通过 Future 进行封装。FutureTask 实现了 RunnableFuture 接口,该接口继承自 Runnable 和 Future<V > 接口,这使得 FutureTask 既可以当做一个任务执行,也可以有返回值。
807
+
808
+ ``` java
809
+ public class FutureTask <V> implements RunnableFuture<V >
810
+ ```
811
+
812
+ ```java
813
+ public interface RunnableFuture<V > extends Runnable , Future<V >
814
+ ```
815
+
816
+ 当一个计算任务需要执行很长时间,那么就可以用 FutureTask 来封装这个任务,用一个线程去执行该任务,然后执行其它任务。当需要该任务的计算结果时,再通过 FutureTask 的 get() 方法获取。
817
+
818
+ ```java
819
+ public class FutureTaskExample {
820
+ public static void main (String [] args ) throws ExecutionException , InterruptedException {
821
+ FutureTask<Integer > futureTask = new FutureTask<Integer > (new Callable<Integer > () {
822
+ @Override
823
+ public Integer call () throws Exception {
824
+ int result = 0 ;
825
+ for (int i = 0 ; i < 100 ; i++ ) {
826
+ Thread . sleep(10 );
827
+ result += i;
828
+ }
829
+ return result;
830
+ }
831
+ });
832
+
833
+ Thread computeThread = new Thread (futureTask);
834
+ computeThread. start();
835
+
836
+ Thread otherThread = new Thread (() - > {
837
+ System . out. println(" other task is running..." );
838
+ try {
839
+ Thread . sleep(1000 );
840
+ } catch (InterruptedException e) {
841
+ e. printStackTrace();
842
+ }
843
+ });
844
+ otherThread. start();
845
+ System . out. println(futureTask. get());
846
+ }
847
+ }
848
+ ```
849
+
850
+ ``` html
851
+ other task is running...
852
+ 4950
853
+ ```
854
+
686
855
## BlockingQueue
687
856
688
857
java.util.concurrent.BlockingQueue 接口有以下阻塞队列的实现:
@@ -692,10 +861,6 @@ java.util.concurrent.BlockingQueue 接口有以下阻塞队列的实现:
692
861
693
862
提供了阻塞的 take() 和 put() 方法:如果队列为空 take() 将阻塞,直到队列中有内容;如果队列为满 put() 将阻塞,指到队列有空闲位置。
694
863
695
- 它们响应中断,当收到中断请求的时候会抛出 InterruptedException,从而提前结束阻塞状态。
696
-
697
- 是线程安全的。
698
-
699
864
** 使用 BlockingQueue 实现生产者消费者问题**
700
865
701
866
``` java
@@ -774,7 +939,13 @@ Consumer-3 is consuming product.( Made By Producer-3 )
774
939
Consumer-4 is consuming product.( Made By Producer-4 )
775
940
```
776
941
777
- # 七、线程不安全示例
942
+
943
+ ## ForkJoin
944
+
945
+ // TODO
946
+
947
+
948
+ # 九、线程不安全示例
778
949
779
950
如果多个线程对同一个共享数据进行访问而不采取同步操作的话,那么操作的结果是不一致的。
780
951
@@ -815,7 +986,7 @@ public class ThreadUnsafeExample {
815
986
997
816
987
```
817
988
818
- # 八 、Java 内存模型
989
+ # 十 、Java 内存模型
819
990
820
991
Java 内存模型视图屏蔽各种硬件和操作系统的内存访问差异,以实现让 Java 程序在各种平台下都能达到一致的内存访问效果。
821
992
@@ -1019,7 +1190,7 @@ join() 方法返回先行发生于 Thread 对象的结束。
1019
1190
1020
1191
如果操作 A 先行发生于操作 B,操作 B 先行发生于操作 C,那么操作 A 先行发生于操作 C。
1021
1192
1022
- # 九 、线程安全
1193
+ # 十一 、线程安全
1023
1194
1024
1195
## 线程安全分类
1025
1196
@@ -1334,7 +1505,7 @@ public T get() {
1334
1505
1335
1506
ThreadLocal 从理论上讲并不是用来解决多线程并发问题的,因为根本不存在多线程竞争。在一些场景 (尤其是使用线程池) 下,由于 ThreadLocal.ThreadLocalMap 的底层数据结构导致 ThreadLocal 有内存泄漏的情况,尽可能在每次使用 ThreadLocal 后手动调用 remove(),以避免出现 ThreadLocal 经典的内存泄漏甚至是造成自身业务混乱的风险。
1336
1507
1337
- # 十 、锁优化
1508
+ # 十二 、锁优化
1338
1509
1339
1510
高效并发是从 JDK 1.5 到 JDK 1.6 的一个重要改进,HotSpot 虚拟机开发团队在这个版本上花费了大量的精力去实现各种锁优化技术,如适应性自旋(Adaptive Spinning)、锁消除(Lock Elimination)、锁粗化(Lock Coarsening)、轻量级锁(Lightweight Locking)和偏向锁(Biased Locking)等。这些技术都是为了在线程之间更高效地共享数据,以及解决竞争问题,从而提高程序的执行效率。
1340
1511
@@ -1409,7 +1580,8 @@ public static String concatString(String s1, String s2, String s3) {
1409
1580
1410
1581
偏向锁可以提高带有同步但无竞争的程序性能。它同样是一个带有效益权衡(Trade Off)性质的优化,也就是说,它并不一定总是对程序运行有利,如果程序中大多数的锁总是被多个不同的线程访问,那偏向模式就是多余的。在具体问题具体分析的前提下,有时候使用参数 -XX:-UseBiasedLocking 来禁止偏向锁优化反而可以提升性能。
1411
1582
1412
- # 九、多线程开发良好的实践
1583
+
1584
+ # 十三、多线程开发良好的实践
1413
1585
1414
1586
1 . 给线程起个有意义的名字,这样可以方便找 Bug;
1415
1587
@@ -1432,3 +1604,4 @@ public static String concatString(String s1, String s2, String s3) {
1432
1604
- [ Java - Understanding Happens-before relationship] ( https://www.logicbig.com/tutorials/core-java-tutorial/java-multi-threading/happens-before.html )
1433
1605
- [ 6장 Thread Synchronization] ( https://www.slideshare.net/novathinker/6-thread-synchronization )
1434
1606
- [ How is Java's ThreadLocal implemented under the hood?] ( https://stackoverflow.com/questions/1202444/how-is-javas-threadlocal-implemented-under-the-hood/15653015 )
1607
+ - [ Concurrent] ( https://sites.google.com/site/webdevelopart/21-compile/06-java/javase/concurrent?tmpl=%2Fsystem%2Fapp%2Ftemplates%2Fprint%2F&showPrintDialog=1 )
0 commit comments